Roo/bootstrap/Menu.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         
405     },
406     /**
407      * Hide a component - adds 'hidden' class
408      */
409     hide: function()
410     {
411         if(!this.getVisibilityEl()){
412             return;
413         }
414         
415         this.getVisibilityEl().addClass('hidden');
416         
417     }
418 });
419
420  /*
421  * - LGPL
422  *
423  * Body
424  *
425  */
426
427 /**
428  * @class Roo.bootstrap.Body
429  * @extends Roo.bootstrap.Component
430  * Bootstrap Body class
431  *
432  * @constructor
433  * Create a new body
434  * @param {Object} config The config object
435  */
436
437 Roo.bootstrap.Body = function(config){
438
439     config = config || {};
440
441     Roo.bootstrap.Body.superclass.constructor.call(this, config);
442     this.el = Roo.get(config.el ? config.el : document.body );
443     if (this.cls && this.cls.length) {
444         Roo.get(document.body).addClass(this.cls);
445     }
446 };
447
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
449
450     is_body : true,// just to make sure it's constructed?
451
452         autoCreate : {
453         cls: 'container'
454     },
455     onRender : function(ct, position)
456     {
457        /* Roo.log("Roo.bootstrap.Body - onRender");
458         if (this.cls && this.cls.length) {
459             Roo.get(document.body).addClass(this.cls);
460         }
461         // style??? xttr???
462         */
463     }
464
465
466
467
468 });
469 /*
470  * - LGPL
471  *
472  * button group
473  * 
474  */
475
476
477 /**
478  * @class Roo.bootstrap.ButtonGroup
479  * @extends Roo.bootstrap.Component
480  * Bootstrap ButtonGroup class
481  * @cfg {String} size lg | sm | xs (default empty normal)
482  * @cfg {String} align vertical | justified  (default none)
483  * @cfg {String} direction up | down (default down)
484  * @cfg {Boolean} toolbar false | true
485  * @cfg {Boolean} btn true | false
486  * 
487  * 
488  * @constructor
489  * Create a new Input
490  * @param {Object} config The config object
491  */
492
493 Roo.bootstrap.ButtonGroup = function(config){
494     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
495 };
496
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
498     
499     size: '',
500     align: '',
501     direction: '',
502     toolbar: false,
503     btn: true,
504
505     getAutoCreate : function(){
506         var cfg = {
507             cls: 'btn-group',
508             html : null
509         };
510         
511         cfg.html = this.html || cfg.html;
512         
513         if (this.toolbar) {
514             cfg = {
515                 cls: 'btn-toolbar',
516                 html: null
517             };
518             
519             return cfg;
520         }
521         
522         if (['vertical','justified'].indexOf(this.align)!==-1) {
523             cfg.cls = 'btn-group-' + this.align;
524             
525             if (this.align == 'justified') {
526                 console.log(this.items);
527             }
528         }
529         
530         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531             cfg.cls += ' btn-group-' + this.size;
532         }
533         
534         if (this.direction == 'up') {
535             cfg.cls += ' dropup' ;
536         }
537         
538         return cfg;
539     }
540    
541 });
542
543  /*
544  * - LGPL
545  *
546  * button
547  * 
548  */
549
550 /**
551  * @class Roo.bootstrap.Button
552  * @extends Roo.bootstrap.Component
553  * Bootstrap Button class
554  * @cfg {String} html The button content
555  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
556  * @cfg {String} size ( lg | sm | xs)
557  * @cfg {String} tag ( a | input | submit)
558  * @cfg {String} href empty or href
559  * @cfg {Boolean} disabled default false;
560  * @cfg {Boolean} isClose default false;
561  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
562  * @cfg {String} badge text for badge
563  * @cfg {String} theme (default|glow)  
564  * @cfg {Boolean} inverse dark themed version
565  * @cfg {Boolean} toggle is it a slidy toggle button
566  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567  * @cfg {String} ontext text for on slidy toggle state
568  * @cfg {String} offtext text for off slidy toggle state
569  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
570  * @cfg {Boolean} removeClass remove the standard class..
571  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
572  * 
573  * @constructor
574  * Create a new button
575  * @param {Object} config The config object
576  */
577
578
579 Roo.bootstrap.Button = function(config){
580     Roo.bootstrap.Button.superclass.constructor.call(this, config);
581     this.weightClass = ["btn-default", 
582                        "btn-primary", 
583                        "btn-success", 
584                        "btn-info", 
585                        "btn-warning",
586                        "btn-danger",
587                        "btn-link"
588                       ],  
589     this.addEvents({
590         // raw events
591         /**
592          * @event click
593          * When a butotn is pressed
594          * @param {Roo.bootstrap.Button} btn
595          * @param {Roo.EventObject} e
596          */
597         "click" : true,
598          /**
599          * @event toggle
600          * After the button has been toggles
601          * @param {Roo.bootstrap.Button} btn
602          * @param {Roo.EventObject} e
603          * @param {boolean} pressed (also available as button.pressed)
604          */
605         "toggle" : true
606     });
607 };
608
609 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
610     html: false,
611     active: false,
612     weight: '',
613     size: '',
614     tag: 'button',
615     href: '',
616     disabled: false,
617     isClose: false,
618     glyphicon: '',
619     badge: '',
620     theme: 'default',
621     inverse: false,
622     
623     toggle: false,
624     ontext: 'ON',
625     offtext: 'OFF',
626     defaulton: true,
627     preventDefault: true,
628     removeClass: false,
629     name: false,
630     target: false,
631      
632     pressed : null,
633      
634     
635     getAutoCreate : function(){
636         
637         var cfg = {
638             tag : 'button',
639             cls : 'roo-button',
640             html: ''
641         };
642         
643         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
645             this.tag = 'button';
646         } else {
647             cfg.tag = this.tag;
648         }
649         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
650         
651         if (this.toggle == true) {
652             cfg={
653                 tag: 'div',
654                 cls: 'slider-frame roo-button',
655                 cn: [
656                     {
657                         tag: 'span',
658                         'data-on-text':'ON',
659                         'data-off-text':'OFF',
660                         cls: 'slider-button',
661                         html: this.offtext
662                     }
663                 ]
664             };
665             
666             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667                 cfg.cls += ' '+this.weight;
668             }
669             
670             return cfg;
671         }
672         
673         if (this.isClose) {
674             cfg.cls += ' close';
675             
676             cfg["aria-hidden"] = true;
677             
678             cfg.html = "&times;";
679             
680             return cfg;
681         }
682         
683          
684         if (this.theme==='default') {
685             cfg.cls = 'btn roo-button';
686             
687             //if (this.parentType != 'Navbar') {
688             this.weight = this.weight.length ?  this.weight : 'default';
689             //}
690             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
691                 
692                 cfg.cls += ' btn-' + this.weight;
693             }
694         } else if (this.theme==='glow') {
695             
696             cfg.tag = 'a';
697             cfg.cls = 'btn-glow roo-button';
698             
699             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
700                 
701                 cfg.cls += ' ' + this.weight;
702             }
703         }
704    
705         
706         if (this.inverse) {
707             this.cls += ' inverse';
708         }
709         
710         
711         if (this.active || this.pressed === true) {
712             cfg.cls += ' active';
713         }
714         
715         if (this.disabled) {
716             cfg.disabled = 'disabled';
717         }
718         
719         if (this.items) {
720             Roo.log('changing to ul' );
721             cfg.tag = 'ul';
722             this.glyphicon = 'caret';
723         }
724         
725         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
726          
727         //gsRoo.log(this.parentType);
728         if (this.parentType === 'Navbar' && !this.parent().bar) {
729             Roo.log('changing to li?');
730             
731             cfg.tag = 'li';
732             
733             cfg.cls = '';
734             cfg.cn =  [{
735                 tag : 'a',
736                 cls : 'roo-button',
737                 html : this.html,
738                 href : this.href || '#'
739             }];
740             if (this.menu) {
741                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
742                 cfg.cls += ' dropdown';
743             }   
744             
745             delete cfg.html;
746             
747         }
748         
749        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
750         
751         if (this.glyphicon) {
752             cfg.html = ' ' + cfg.html;
753             
754             cfg.cn = [
755                 {
756                     tag: 'span',
757                     cls: 'glyphicon glyphicon-' + this.glyphicon
758                 }
759             ];
760         }
761         
762         if (this.badge) {
763             cfg.html += ' ';
764             
765             cfg.tag = 'a';
766             
767 //            cfg.cls='btn roo-button';
768             
769             cfg.href=this.href;
770             
771             var value = cfg.html;
772             
773             if(this.glyphicon){
774                 value = {
775                             tag: 'span',
776                             cls: 'glyphicon glyphicon-' + this.glyphicon,
777                             html: this.html
778                         };
779                 
780             }
781             
782             cfg.cn = [
783                 value,
784                 {
785                     tag: 'span',
786                     cls: 'badge',
787                     html: this.badge
788                 }
789             ];
790             
791             cfg.html='';
792         }
793         
794         if (this.menu) {
795             cfg.cls += ' dropdown';
796             cfg.html = typeof(cfg.html) != 'undefined' ?
797                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
798         }
799         
800         if (cfg.tag !== 'a' && this.href !== '') {
801             throw "Tag must be a to set href.";
802         } else if (this.href.length > 0) {
803             cfg.href = this.href;
804         }
805         
806         if(this.removeClass){
807             cfg.cls = '';
808         }
809         
810         if(this.target){
811             cfg.target = this.target;
812         }
813         
814         return cfg;
815     },
816     initEvents: function() {
817        // Roo.log('init events?');
818 //        Roo.log(this.el.dom);
819         // add the menu...
820         
821         if (typeof (this.menu) != 'undefined') {
822             this.menu.parentType = this.xtype;
823             this.menu.triggerEl = this.el;
824             this.addxtype(Roo.apply({}, this.menu));
825         }
826
827
828        if (this.el.hasClass('roo-button')) {
829             this.el.on('click', this.onClick, this);
830        } else {
831             this.el.select('.roo-button').on('click', this.onClick, this);
832        }
833        
834        if(this.removeClass){
835            this.el.on('click', this.onClick, this);
836        }
837        
838        this.el.enableDisplayMode();
839         
840     },
841     onClick : function(e)
842     {
843         if (this.disabled) {
844             return;
845         }
846         
847         Roo.log('button on click ');
848         if(this.preventDefault){
849             e.preventDefault();
850         }
851         
852         if (this.pressed === true || this.pressed === false) {
853             this.toggleActive(e);
854         }
855         
856         
857         this.fireEvent('click', this, e);
858     },
859     
860     /**
861      * Enables this button
862      */
863     enable : function()
864     {
865         this.disabled = false;
866         this.el.removeClass('disabled');
867     },
868     
869     /**
870      * Disable this button
871      */
872     disable : function()
873     {
874         this.disabled = true;
875         this.el.addClass('disabled');
876     },
877      /**
878      * sets the active state on/off, 
879      * @param {Boolean} state (optional) Force a particular state
880      */
881     setActive : function(v) {
882         
883         this.el[v ? 'addClass' : 'removeClass']('active');
884         this.pressed = v;
885     },
886      /**
887      * toggles the current active state 
888      */
889     toggleActive : function(e)
890     {
891         this.setActive(!this.pressed);
892         this.fireEvent('toggle', this, e, !this.pressed);
893     },
894      /**
895      * get the current active state
896      * @return {boolean} true if it's active
897      */
898     isActive : function()
899     {
900         return this.el.hasClass('active');
901     },
902     /**
903      * set the text of the first selected button
904      */
905     setText : function(str)
906     {
907         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
908     },
909     /**
910      * get the text of the first selected button
911      */
912     getText : function()
913     {
914         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
915     },
916     hide: function() {
917        
918      
919         this.el.hide();   
920     },
921     show: function() {
922        
923         this.el.show();   
924     },
925     setWeight : function(str)
926     {
927         this.el.removeClass(this.weightClass);
928         this.el.addClass('btn-' + str);        
929     }
930     
931     
932 });
933
934  /*
935  * - LGPL
936  *
937  * column
938  * 
939  */
940
941 /**
942  * @class Roo.bootstrap.Column
943  * @extends Roo.bootstrap.Component
944  * Bootstrap Column class
945  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
949  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
953  *
954  * 
955  * @cfg {Boolean} hidden (true|false) hide the element
956  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
957  * @cfg {String} fa (ban|check|...) font awesome icon
958  * @cfg {Number} fasize (1|2|....) font awsome size
959
960  * @cfg {String} icon (info-sign|check|...) glyphicon name
961
962  * @cfg {String} html content of column.
963  * 
964  * @constructor
965  * Create a new Column
966  * @param {Object} config The config object
967  */
968
969 Roo.bootstrap.Column = function(config){
970     Roo.bootstrap.Column.superclass.constructor.call(this, config);
971 };
972
973 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
974     
975     xs: false,
976     sm: false,
977     md: false,
978     lg: false,
979     xsoff: false,
980     smoff: false,
981     mdoff: false,
982     lgoff: false,
983     html: '',
984     offset: 0,
985     alert: false,
986     fa: false,
987     icon : false,
988     hidden : false,
989     fasize : 1,
990     
991     getAutoCreate : function(){
992         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
993         
994         cfg = {
995             tag: 'div',
996             cls: 'column'
997         };
998         
999         var settings=this;
1000         ['xs','sm','md','lg'].map(function(size){
1001             //Roo.log( size + ':' + settings[size]);
1002             
1003             if (settings[size+'off'] !== false) {
1004                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1005             }
1006             
1007             if (settings[size] === false) {
1008                 return;
1009             }
1010             
1011             if (!settings[size]) { // 0 = hidden
1012                 cfg.cls += ' hidden-' + size;
1013                 return;
1014             }
1015             cfg.cls += ' col-' + size + '-' + settings[size];
1016             
1017         });
1018         
1019         if (this.hidden) {
1020             cfg.cls += ' hidden';
1021         }
1022         
1023         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024             cfg.cls +=' alert alert-' + this.alert;
1025         }
1026         
1027         
1028         if (this.html.length) {
1029             cfg.html = this.html;
1030         }
1031         if (this.fa) {
1032             var fasize = '';
1033             if (this.fasize > 1) {
1034                 fasize = ' fa-' + this.fasize + 'x';
1035             }
1036             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1037             
1038             
1039         }
1040         if (this.icon) {
1041             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1042         }
1043         
1044         return cfg;
1045     }
1046    
1047 });
1048
1049  
1050
1051  /*
1052  * - LGPL
1053  *
1054  * page container.
1055  * 
1056  */
1057
1058
1059 /**
1060  * @class Roo.bootstrap.Container
1061  * @extends Roo.bootstrap.Component
1062  * Bootstrap Container class
1063  * @cfg {Boolean} jumbotron is it a jumbotron element
1064  * @cfg {String} html content of element
1065  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1066  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1067  * @cfg {String} header content of header (for panel)
1068  * @cfg {String} footer content of footer (for panel)
1069  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1070  * @cfg {String} tag (header|aside|section) type of HTML tag.
1071  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1072  * @cfg {String} fa font awesome icon
1073  * @cfg {String} icon (info-sign|check|...) glyphicon name
1074  * @cfg {Boolean} hidden (true|false) hide the element
1075  * @cfg {Boolean} expandable (true|false) default false
1076  * @cfg {Boolean} expanded (true|false) default true
1077  * @cfg {String} rheader contet on the right of header
1078  * @cfg {Boolean} clickable (true|false) default false
1079
1080  *     
1081  * @constructor
1082  * Create a new Container
1083  * @param {Object} config The config object
1084  */
1085
1086 Roo.bootstrap.Container = function(config){
1087     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1088     
1089     this.addEvents({
1090         // raw events
1091          /**
1092          * @event expand
1093          * After the panel has been expand
1094          * 
1095          * @param {Roo.bootstrap.Container} this
1096          */
1097         "expand" : true,
1098         /**
1099          * @event collapse
1100          * After the panel has been collapsed
1101          * 
1102          * @param {Roo.bootstrap.Container} this
1103          */
1104         "collapse" : true,
1105         /**
1106          * @event click
1107          * When a element is chick
1108          * @param {Roo.bootstrap.Container} this
1109          * @param {Roo.EventObject} e
1110          */
1111         "click" : true
1112     });
1113 };
1114
1115 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1116     
1117     jumbotron : false,
1118     well: '',
1119     panel : '',
1120     header: '',
1121     footer : '',
1122     sticky: '',
1123     tag : false,
1124     alert : false,
1125     fa: false,
1126     icon : false,
1127     expandable : false,
1128     rheader : '',
1129     expanded : true,
1130     clickable: false,
1131   
1132      
1133     getChildContainer : function() {
1134         
1135         if(!this.el){
1136             return false;
1137         }
1138         
1139         if (this.panel.length) {
1140             return this.el.select('.panel-body',true).first();
1141         }
1142         
1143         return this.el;
1144     },
1145     
1146     
1147     getAutoCreate : function(){
1148         
1149         var cfg = {
1150             tag : this.tag || 'div',
1151             html : '',
1152             cls : ''
1153         };
1154         if (this.jumbotron) {
1155             cfg.cls = 'jumbotron';
1156         }
1157         
1158         
1159         
1160         // - this is applied by the parent..
1161         //if (this.cls) {
1162         //    cfg.cls = this.cls + '';
1163         //}
1164         
1165         if (this.sticky.length) {
1166             
1167             var bd = Roo.get(document.body);
1168             if (!bd.hasClass('bootstrap-sticky')) {
1169                 bd.addClass('bootstrap-sticky');
1170                 Roo.select('html',true).setStyle('height', '100%');
1171             }
1172              
1173             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1174         }
1175         
1176         
1177         if (this.well.length) {
1178             switch (this.well) {
1179                 case 'lg':
1180                 case 'sm':
1181                     cfg.cls +=' well well-' +this.well;
1182                     break;
1183                 default:
1184                     cfg.cls +=' well';
1185                     break;
1186             }
1187         }
1188         
1189         if (this.hidden) {
1190             cfg.cls += ' hidden';
1191         }
1192         
1193         
1194         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1195             cfg.cls +=' alert alert-' + this.alert;
1196         }
1197         
1198         var body = cfg;
1199         
1200         if (this.panel.length) {
1201             cfg.cls += ' panel panel-' + this.panel;
1202             cfg.cn = [];
1203             if (this.header.length) {
1204                 
1205                 var h = [];
1206                 
1207                 if(this.expandable){
1208                     
1209                     cfg.cls = cfg.cls + ' expandable';
1210                     
1211                     h.push({
1212                         tag: 'i',
1213                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1214                     });
1215                     
1216                 }
1217                 
1218                 h.push(
1219                     {
1220                         tag: 'span',
1221                         cls : 'panel-title',
1222                         html : (this.expandable ? '&nbsp;' : '') + this.header
1223                     },
1224                     {
1225                         tag: 'span',
1226                         cls: 'panel-header-right',
1227                         html: this.rheader
1228                     }
1229                 );
1230                 
1231                 cfg.cn.push({
1232                     cls : 'panel-heading',
1233                     style : this.expandable ? 'cursor: pointer' : '',
1234                     cn : h
1235                 });
1236                 
1237             }
1238             
1239             body = false;
1240             cfg.cn.push({
1241                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1242                 html : this.html
1243             });
1244             
1245             
1246             if (this.footer.length) {
1247                 cfg.cn.push({
1248                     cls : 'panel-footer',
1249                     html : this.footer
1250                     
1251                 });
1252             }
1253             
1254         }
1255         
1256         if (body) {
1257             body.html = this.html || cfg.html;
1258             // prefix with the icons..
1259             if (this.fa) {
1260                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1261             }
1262             if (this.icon) {
1263                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1264             }
1265             
1266             
1267         }
1268         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1269             cfg.cls =  'container';
1270         }
1271         
1272         return cfg;
1273     },
1274     
1275     initEvents: function() 
1276     {
1277         if(this.expandable){
1278             var headerEl = this.headerEl();
1279         
1280             if(headerEl){
1281                 headerEl.on('click', this.onToggleClick, this);
1282             }
1283         }
1284         
1285         if(this.clickable){
1286             this.el.on('click', this.onClick, this);
1287         }
1288         
1289     },
1290     
1291     onToggleClick : function()
1292     {
1293         var headerEl = this.headerEl();
1294         
1295         if(!headerEl){
1296             return;
1297         }
1298         
1299         if(this.expanded){
1300             this.collapse();
1301             return;
1302         }
1303         
1304         this.expand();
1305     },
1306     
1307     expand : function()
1308     {
1309         if(this.fireEvent('expand', this)) {
1310             
1311             this.expanded = true;
1312             
1313             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1314             
1315             this.el.select('.panel-body',true).first().removeClass('hide');
1316             
1317             var toggleEl = this.toggleEl();
1318
1319             if(!toggleEl){
1320                 return;
1321             }
1322
1323             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1324         }
1325         
1326     },
1327     
1328     collapse : function()
1329     {
1330         if(this.fireEvent('collapse', this)) {
1331             
1332             this.expanded = false;
1333             
1334             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1335             this.el.select('.panel-body',true).first().addClass('hide');
1336         
1337             var toggleEl = this.toggleEl();
1338
1339             if(!toggleEl){
1340                 return;
1341             }
1342
1343             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1344         }
1345     },
1346     
1347     toggleEl : function()
1348     {
1349         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1350             return;
1351         }
1352         
1353         return this.el.select('.panel-heading .fa',true).first();
1354     },
1355     
1356     headerEl : function()
1357     {
1358         if(!this.el || !this.panel.length || !this.header.length){
1359             return;
1360         }
1361         
1362         return this.el.select('.panel-heading',true).first()
1363     },
1364     
1365     bodyEl : function()
1366     {
1367         if(!this.el || !this.panel.length){
1368             return;
1369         }
1370         
1371         return this.el.select('.panel-body',true).first()
1372     },
1373     
1374     titleEl : function()
1375     {
1376         if(!this.el || !this.panel.length || !this.header.length){
1377             return;
1378         }
1379         
1380         return this.el.select('.panel-title',true).first();
1381     },
1382     
1383     setTitle : function(v)
1384     {
1385         var titleEl = this.titleEl();
1386         
1387         if(!titleEl){
1388             return;
1389         }
1390         
1391         titleEl.dom.innerHTML = v;
1392     },
1393     
1394     getTitle : function()
1395     {
1396         
1397         var titleEl = this.titleEl();
1398         
1399         if(!titleEl){
1400             return '';
1401         }
1402         
1403         return titleEl.dom.innerHTML;
1404     },
1405     
1406     setRightTitle : function(v)
1407     {
1408         var t = this.el.select('.panel-header-right',true).first();
1409         
1410         if(!t){
1411             return;
1412         }
1413         
1414         t.dom.innerHTML = v;
1415     },
1416     
1417     onClick : function(e)
1418     {
1419         e.preventDefault();
1420         
1421         this.fireEvent('click', this, e);
1422     }
1423 });
1424
1425  /*
1426  * - LGPL
1427  *
1428  * image
1429  * 
1430  */
1431
1432
1433 /**
1434  * @class Roo.bootstrap.Img
1435  * @extends Roo.bootstrap.Component
1436  * Bootstrap Img class
1437  * @cfg {Boolean} imgResponsive false | true
1438  * @cfg {String} border rounded | circle | thumbnail
1439  * @cfg {String} src image source
1440  * @cfg {String} alt image alternative text
1441  * @cfg {String} href a tag href
1442  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1443  * @cfg {String} xsUrl xs image source
1444  * @cfg {String} smUrl sm image source
1445  * @cfg {String} mdUrl md image source
1446  * @cfg {String} lgUrl lg image source
1447  * 
1448  * @constructor
1449  * Create a new Input
1450  * @param {Object} config The config object
1451  */
1452
1453 Roo.bootstrap.Img = function(config){
1454     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1455     
1456     this.addEvents({
1457         // img events
1458         /**
1459          * @event click
1460          * The img click event for the img.
1461          * @param {Roo.EventObject} e
1462          */
1463         "click" : true
1464     });
1465 };
1466
1467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1468     
1469     imgResponsive: true,
1470     border: '',
1471     src: 'about:blank',
1472     href: false,
1473     target: false,
1474     xsUrl: '',
1475     smUrl: '',
1476     mdUrl: '',
1477     lgUrl: '',
1478
1479     getAutoCreate : function()
1480     {   
1481         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1482             return this.createSingleImg();
1483         }
1484         
1485         var cfg = {
1486             tag: 'div',
1487             cls: 'roo-image-responsive-group',
1488             cn: []
1489         };
1490         var _this = this;
1491         
1492         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1493             
1494             if(!_this[size + 'Url']){
1495                 return;
1496             }
1497             
1498             var img = {
1499                 tag: 'img',
1500                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1501                 html: _this.html || cfg.html,
1502                 src: _this[size + 'Url']
1503             };
1504             
1505             img.cls += ' roo-image-responsive-' + size;
1506             
1507             var s = ['xs', 'sm', 'md', 'lg'];
1508             
1509             s.splice(s.indexOf(size), 1);
1510             
1511             Roo.each(s, function(ss){
1512                 img.cls += ' hidden-' + ss;
1513             });
1514             
1515             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1516                 cfg.cls += ' img-' + _this.border;
1517             }
1518             
1519             if(_this.alt){
1520                 cfg.alt = _this.alt;
1521             }
1522             
1523             if(_this.href){
1524                 var a = {
1525                     tag: 'a',
1526                     href: _this.href,
1527                     cn: [
1528                         img
1529                     ]
1530                 };
1531
1532                 if(this.target){
1533                     a.target = _this.target;
1534                 }
1535             }
1536             
1537             cfg.cn.push((_this.href) ? a : img);
1538             
1539         });
1540         
1541         return cfg;
1542     },
1543     
1544     createSingleImg : function()
1545     {
1546         var cfg = {
1547             tag: 'img',
1548             cls: (this.imgResponsive) ? 'img-responsive' : '',
1549             html : null,
1550             src : 'about:blank'  // just incase src get's set to undefined?!?
1551         };
1552         
1553         cfg.html = this.html || cfg.html;
1554         
1555         cfg.src = this.src || cfg.src;
1556         
1557         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1558             cfg.cls += ' img-' + this.border;
1559         }
1560         
1561         if(this.alt){
1562             cfg.alt = this.alt;
1563         }
1564         
1565         if(this.href){
1566             var a = {
1567                 tag: 'a',
1568                 href: this.href,
1569                 cn: [
1570                     cfg
1571                 ]
1572             };
1573             
1574             if(this.target){
1575                 a.target = this.target;
1576             }
1577             
1578         }
1579         
1580         return (this.href) ? a : cfg;
1581     },
1582     
1583     initEvents: function() 
1584     {
1585         if(!this.href){
1586             this.el.on('click', this.onClick, this);
1587         }
1588         
1589     },
1590     
1591     onClick : function(e)
1592     {
1593         Roo.log('img onclick');
1594         this.fireEvent('click', this, e);
1595     },
1596     /**
1597      * Sets the url of the image - used to update it
1598      * @param {String} url the url of the image
1599      */
1600     
1601     setSrc : function(url)
1602     {
1603         this.src =  url;
1604         
1605         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1606             this.el.dom.src =  url;
1607             return;
1608         }
1609         
1610         this.el.select('img', true).first().dom.src =  url;
1611     }
1612     
1613     
1614    
1615 });
1616
1617  /*
1618  * - LGPL
1619  *
1620  * image
1621  * 
1622  */
1623
1624
1625 /**
1626  * @class Roo.bootstrap.Link
1627  * @extends Roo.bootstrap.Component
1628  * Bootstrap Link Class
1629  * @cfg {String} alt image alternative text
1630  * @cfg {String} href a tag href
1631  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1632  * @cfg {String} html the content of the link.
1633  * @cfg {String} anchor name for the anchor link
1634  * @cfg {String} fa - favicon
1635
1636  * @cfg {Boolean} preventDefault (true | false) default false
1637
1638  * 
1639  * @constructor
1640  * Create a new Input
1641  * @param {Object} config The config object
1642  */
1643
1644 Roo.bootstrap.Link = function(config){
1645     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1646     
1647     this.addEvents({
1648         // img events
1649         /**
1650          * @event click
1651          * The img click event for the img.
1652          * @param {Roo.EventObject} e
1653          */
1654         "click" : true
1655     });
1656 };
1657
1658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1659     
1660     href: false,
1661     target: false,
1662     preventDefault: false,
1663     anchor : false,
1664     alt : false,
1665     fa: false,
1666
1667
1668     getAutoCreate : function()
1669     {
1670         var html = this.html || '';
1671         
1672         if (this.fa !== false) {
1673             html = '<i class="fa fa-' + this.fa + '"></i>';
1674         }
1675         var cfg = {
1676             tag: 'a'
1677         };
1678         // anchor's do not require html/href...
1679         if (this.anchor === false) {
1680             cfg.html = html;
1681             cfg.href = this.href || '#';
1682         } else {
1683             cfg.name = this.anchor;
1684             if (this.html !== false || this.fa !== false) {
1685                 cfg.html = html;
1686             }
1687             if (this.href !== false) {
1688                 cfg.href = this.href;
1689             }
1690         }
1691         
1692         if(this.alt !== false){
1693             cfg.alt = this.alt;
1694         }
1695         
1696         
1697         if(this.target !== false) {
1698             cfg.target = this.target;
1699         }
1700         
1701         return cfg;
1702     },
1703     
1704     initEvents: function() {
1705         
1706         if(!this.href || this.preventDefault){
1707             this.el.on('click', this.onClick, this);
1708         }
1709     },
1710     
1711     onClick : function(e)
1712     {
1713         if(this.preventDefault){
1714             e.preventDefault();
1715         }
1716         //Roo.log('img onclick');
1717         this.fireEvent('click', this, e);
1718     }
1719    
1720 });
1721
1722  /*
1723  * - LGPL
1724  *
1725  * header
1726  * 
1727  */
1728
1729 /**
1730  * @class Roo.bootstrap.Header
1731  * @extends Roo.bootstrap.Component
1732  * Bootstrap Header class
1733  * @cfg {String} html content of header
1734  * @cfg {Number} level (1|2|3|4|5|6) default 1
1735  * 
1736  * @constructor
1737  * Create a new Header
1738  * @param {Object} config The config object
1739  */
1740
1741
1742 Roo.bootstrap.Header  = function(config){
1743     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1744 };
1745
1746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1747     
1748     //href : false,
1749     html : false,
1750     level : 1,
1751     
1752     
1753     
1754     getAutoCreate : function(){
1755         
1756         
1757         
1758         var cfg = {
1759             tag: 'h' + (1 *this.level),
1760             html: this.html || ''
1761         } ;
1762         
1763         return cfg;
1764     }
1765    
1766 });
1767
1768  
1769
1770  /*
1771  * Based on:
1772  * Ext JS Library 1.1.1
1773  * Copyright(c) 2006-2007, Ext JS, LLC.
1774  *
1775  * Originally Released Under LGPL - original licence link has changed is not relivant.
1776  *
1777  * Fork - LGPL
1778  * <script type="text/javascript">
1779  */
1780  
1781 /**
1782  * @class Roo.bootstrap.MenuMgr
1783  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1784  * @singleton
1785  */
1786 Roo.bootstrap.MenuMgr = function(){
1787    var menus, active, groups = {}, attached = false, lastShow = new Date();
1788
1789    // private - called when first menu is created
1790    function init(){
1791        menus = {};
1792        active = new Roo.util.MixedCollection();
1793        Roo.get(document).addKeyListener(27, function(){
1794            if(active.length > 0){
1795                hideAll();
1796            }
1797        });
1798    }
1799
1800    // private
1801    function hideAll(){
1802        if(active && active.length > 0){
1803            var c = active.clone();
1804            c.each(function(m){
1805                m.hide();
1806            });
1807        }
1808    }
1809
1810    // private
1811    function onHide(m){
1812        active.remove(m);
1813        if(active.length < 1){
1814            Roo.get(document).un("mouseup", onMouseDown);
1815             
1816            attached = false;
1817        }
1818    }
1819
1820    // private
1821    function onShow(m){
1822        var last = active.last();
1823        lastShow = new Date();
1824        active.add(m);
1825        if(!attached){
1826           Roo.get(document).on("mouseup", onMouseDown);
1827            
1828            attached = true;
1829        }
1830        if(m.parentMenu){
1831           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1832           m.parentMenu.activeChild = m;
1833        }else if(last && last.isVisible()){
1834           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1835        }
1836    }
1837
1838    // private
1839    function onBeforeHide(m){
1840        if(m.activeChild){
1841            m.activeChild.hide();
1842        }
1843        if(m.autoHideTimer){
1844            clearTimeout(m.autoHideTimer);
1845            delete m.autoHideTimer;
1846        }
1847    }
1848
1849    // private
1850    function onBeforeShow(m){
1851        var pm = m.parentMenu;
1852        if(!pm && !m.allowOtherMenus){
1853            hideAll();
1854        }else if(pm && pm.activeChild && active != m){
1855            pm.activeChild.hide();
1856        }
1857    }
1858
1859    // private this should really trigger on mouseup..
1860    function onMouseDown(e){
1861         Roo.log("on Mouse Up");
1862         
1863         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1864             Roo.log("MenuManager hideAll");
1865             hideAll();
1866             e.stopEvent();
1867         }
1868         
1869         
1870    }
1871
1872    // private
1873    function onBeforeCheck(mi, state){
1874        if(state){
1875            var g = groups[mi.group];
1876            for(var i = 0, l = g.length; i < l; i++){
1877                if(g[i] != mi){
1878                    g[i].setChecked(false);
1879                }
1880            }
1881        }
1882    }
1883
1884    return {
1885
1886        /**
1887         * Hides all menus that are currently visible
1888         */
1889        hideAll : function(){
1890             hideAll();  
1891        },
1892
1893        // private
1894        register : function(menu){
1895            if(!menus){
1896                init();
1897            }
1898            menus[menu.id] = menu;
1899            menu.on("beforehide", onBeforeHide);
1900            menu.on("hide", onHide);
1901            menu.on("beforeshow", onBeforeShow);
1902            menu.on("show", onShow);
1903            var g = menu.group;
1904            if(g && menu.events["checkchange"]){
1905                if(!groups[g]){
1906                    groups[g] = [];
1907                }
1908                groups[g].push(menu);
1909                menu.on("checkchange", onCheck);
1910            }
1911        },
1912
1913         /**
1914          * Returns a {@link Roo.menu.Menu} object
1915          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1916          * be used to generate and return a new Menu instance.
1917          */
1918        get : function(menu){
1919            if(typeof menu == "string"){ // menu id
1920                return menus[menu];
1921            }else if(menu.events){  // menu instance
1922                return menu;
1923            }
1924            /*else if(typeof menu.length == 'number'){ // array of menu items?
1925                return new Roo.bootstrap.Menu({items:menu});
1926            }else{ // otherwise, must be a config
1927                return new Roo.bootstrap.Menu(menu);
1928            }
1929            */
1930            return false;
1931        },
1932
1933        // private
1934        unregister : function(menu){
1935            delete menus[menu.id];
1936            menu.un("beforehide", onBeforeHide);
1937            menu.un("hide", onHide);
1938            menu.un("beforeshow", onBeforeShow);
1939            menu.un("show", onShow);
1940            var g = menu.group;
1941            if(g && menu.events["checkchange"]){
1942                groups[g].remove(menu);
1943                menu.un("checkchange", onCheck);
1944            }
1945        },
1946
1947        // private
1948        registerCheckable : function(menuItem){
1949            var g = menuItem.group;
1950            if(g){
1951                if(!groups[g]){
1952                    groups[g] = [];
1953                }
1954                groups[g].push(menuItem);
1955                menuItem.on("beforecheckchange", onBeforeCheck);
1956            }
1957        },
1958
1959        // private
1960        unregisterCheckable : function(menuItem){
1961            var g = menuItem.group;
1962            if(g){
1963                groups[g].remove(menuItem);
1964                menuItem.un("beforecheckchange", onBeforeCheck);
1965            }
1966        }
1967    };
1968 }();/*
1969  * - LGPL
1970  *
1971  * menu
1972  * 
1973  */
1974
1975 /**
1976  * @class Roo.bootstrap.Menu
1977  * @extends Roo.bootstrap.Component
1978  * Bootstrap Menu class - container for MenuItems
1979  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1980  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1981  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1982  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1983  * 
1984  * @constructor
1985  * Create a new Menu
1986  * @param {Object} config The config object
1987  */
1988
1989
1990 Roo.bootstrap.Menu = function(config){
1991     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1992     if (this.registerMenu && this.type != 'treeview')  {
1993         Roo.bootstrap.MenuMgr.register(this);
1994     }
1995     this.addEvents({
1996         /**
1997          * @event beforeshow
1998          * Fires before this menu is displayed
1999          * @param {Roo.menu.Menu} this
2000          */
2001         beforeshow : true,
2002         /**
2003          * @event beforehide
2004          * Fires before this menu is hidden
2005          * @param {Roo.menu.Menu} this
2006          */
2007         beforehide : true,
2008         /**
2009          * @event show
2010          * Fires after this menu is displayed
2011          * @param {Roo.menu.Menu} this
2012          */
2013         show : true,
2014         /**
2015          * @event hide
2016          * Fires after this menu is hidden
2017          * @param {Roo.menu.Menu} this
2018          */
2019         hide : true,
2020         /**
2021          * @event click
2022          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2023          * @param {Roo.menu.Menu} this
2024          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2025          * @param {Roo.EventObject} e
2026          */
2027         click : true,
2028         /**
2029          * @event mouseover
2030          * Fires when the mouse is hovering over this menu
2031          * @param {Roo.menu.Menu} this
2032          * @param {Roo.EventObject} e
2033          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034          */
2035         mouseover : true,
2036         /**
2037          * @event mouseout
2038          * Fires when the mouse exits this menu
2039          * @param {Roo.menu.Menu} this
2040          * @param {Roo.EventObject} e
2041          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042          */
2043         mouseout : true,
2044         /**
2045          * @event itemclick
2046          * Fires when a menu item contained in this menu is clicked
2047          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2048          * @param {Roo.EventObject} e
2049          */
2050         itemclick: true
2051     });
2052     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2053 };
2054
2055 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2056     
2057    /// html : false,
2058     //align : '',
2059     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2060     type: false,
2061     /**
2062      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2063      */
2064     registerMenu : true,
2065     
2066     menuItems :false, // stores the menu items..
2067     
2068     hidden:true,
2069         
2070     parentMenu : false,
2071     
2072     stopEvent : true,
2073     
2074     isLink : false,
2075     
2076     getChildContainer : function() {
2077         return this.el;  
2078     },
2079     
2080     getAutoCreate : function(){
2081          
2082         //if (['right'].indexOf(this.align)!==-1) {
2083         //    cfg.cn[1].cls += ' pull-right'
2084         //}
2085         
2086         
2087         var cfg = {
2088             tag : 'ul',
2089             cls : 'dropdown-menu' ,
2090             style : 'z-index:1000'
2091             
2092         };
2093         
2094         if (this.type === 'submenu') {
2095             cfg.cls = 'submenu active';
2096         }
2097         if (this.type === 'treeview') {
2098             cfg.cls = 'treeview-menu';
2099         }
2100         
2101         return cfg;
2102     },
2103     initEvents : function() {
2104         
2105        // Roo.log("ADD event");
2106        // Roo.log(this.triggerEl.dom);
2107         
2108         this.triggerEl.on('click', this.onTriggerClick, this);
2109         
2110         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2111         
2112         this.triggerEl.addClass('dropdown-toggle');
2113         
2114         if (Roo.isTouch) {
2115             this.el.on('touchstart'  , this.onTouch, this);
2116         }
2117         this.el.on('click' , this.onClick, this);
2118
2119         this.el.on("mouseover", this.onMouseOver, this);
2120         this.el.on("mouseout", this.onMouseOut, this);
2121         
2122     },
2123     
2124     findTargetItem : function(e)
2125     {
2126         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2127         if(!t){
2128             return false;
2129         }
2130         //Roo.log(t);         Roo.log(t.id);
2131         if(t && t.id){
2132             //Roo.log(this.menuitems);
2133             return this.menuitems.get(t.id);
2134             
2135             //return this.items.get(t.menuItemId);
2136         }
2137         
2138         return false;
2139     },
2140     
2141     onTouch : function(e) 
2142     {
2143         Roo.log("menu.onTouch");
2144         //e.stopEvent(); this make the user popdown broken
2145         this.onClick(e);
2146     },
2147     
2148     onClick : function(e)
2149     {
2150         Roo.log("menu.onClick");
2151         
2152         var t = this.findTargetItem(e);
2153         if(!t || t.isContainer){
2154             return;
2155         }
2156         Roo.log(e);
2157         /*
2158         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2159             if(t == this.activeItem && t.shouldDeactivate(e)){
2160                 this.activeItem.deactivate();
2161                 delete this.activeItem;
2162                 return;
2163             }
2164             if(t.canActivate){
2165                 this.setActiveItem(t, true);
2166             }
2167             return;
2168             
2169             
2170         }
2171         */
2172        
2173         Roo.log('pass click event');
2174         
2175         t.onClick(e);
2176         
2177         this.fireEvent("click", this, t, e);
2178         
2179         var _this = this;
2180         
2181         if(!t.href.length || t.href == '#'){
2182             (function() { _this.hide(); }).defer(100);
2183         }
2184         
2185     },
2186     
2187     onMouseOver : function(e){
2188         var t  = this.findTargetItem(e);
2189         //Roo.log(t);
2190         //if(t){
2191         //    if(t.canActivate && !t.disabled){
2192         //        this.setActiveItem(t, true);
2193         //    }
2194         //}
2195         
2196         this.fireEvent("mouseover", this, e, t);
2197     },
2198     isVisible : function(){
2199         return !this.hidden;
2200     },
2201      onMouseOut : function(e){
2202         var t  = this.findTargetItem(e);
2203         
2204         //if(t ){
2205         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2206         //        this.activeItem.deactivate();
2207         //        delete this.activeItem;
2208         //    }
2209         //}
2210         this.fireEvent("mouseout", this, e, t);
2211     },
2212     
2213     
2214     /**
2215      * Displays this menu relative to another element
2216      * @param {String/HTMLElement/Roo.Element} element The element to align to
2217      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2218      * the element (defaults to this.defaultAlign)
2219      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2220      */
2221     show : function(el, pos, parentMenu){
2222         this.parentMenu = parentMenu;
2223         if(!this.el){
2224             this.render();
2225         }
2226         this.fireEvent("beforeshow", this);
2227         
2228         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2229     },
2230      /**
2231      * Displays this menu at a specific xy position
2232      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2233      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2234      */
2235     showAt : function(xy, parentMenu, /* private: */_e){
2236         this.parentMenu = parentMenu;
2237         if(!this.el){
2238             this.render();
2239         }
2240         if(_e !== false){
2241             this.fireEvent("beforeshow", this);
2242             // xy = this.el.adjustForConstraints(xy);
2243         }
2244         
2245         //this.el.show();
2246         this.hideMenuItems();
2247         this.hidden = false;
2248         this.triggerEl.addClass('open');
2249         
2250         xy = this.el.getAlignToXY(this.triggerEl, '?');
2251         
2252         // if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2253         //     xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2254         // }
2255         
2256         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2257             this.el.setXY(xy);
2258         }
2259         
2260         this.focus();
2261         this.fireEvent("show", this);
2262     },
2263     
2264     focus : function(){
2265         return;
2266         if(!this.hidden){
2267             this.doFocus.defer(50, this);
2268         }
2269     },
2270
2271     doFocus : function(){
2272         if(!this.hidden){
2273             this.focusEl.focus();
2274         }
2275     },
2276
2277     /**
2278      * Hides this menu and optionally all parent menus
2279      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2280      */
2281     hide : function(deep)
2282     {
2283         
2284         this.hideMenuItems();
2285         if(this.el && this.isVisible()){
2286             this.fireEvent("beforehide", this);
2287             if(this.activeItem){
2288                 this.activeItem.deactivate();
2289                 this.activeItem = null;
2290             }
2291             this.triggerEl.removeClass('open');;
2292             this.hidden = true;
2293             this.fireEvent("hide", this);
2294         }
2295         if(deep === true && this.parentMenu){
2296             this.parentMenu.hide(true);
2297         }
2298     },
2299     
2300     onTriggerClick : function(e)
2301     {
2302         Roo.log('trigger click');
2303         
2304         var target = e.getTarget();
2305         
2306         Roo.log(target.nodeName.toLowerCase());
2307         
2308         if(target.nodeName.toLowerCase() === 'i'){
2309             e.preventDefault();
2310         }
2311         
2312     },
2313     
2314     onTriggerPress  : function(e)
2315     {
2316         Roo.log('trigger press');
2317         //Roo.log(e.getTarget());
2318        // Roo.log(this.triggerEl.dom);
2319        
2320         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2321         var pel = Roo.get(e.getTarget());
2322         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2323             Roo.log('is treeview or dropdown?');
2324             return;
2325         }
2326         
2327         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2328             return;
2329         }
2330         
2331         if (this.isVisible()) {
2332             Roo.log('hide');
2333             this.hide();
2334         } else {
2335             Roo.log('show');
2336             this.show(this.triggerEl, false, false);
2337         }
2338         
2339         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2340             e.stopEvent();
2341         }
2342         
2343     },
2344        
2345     
2346     hideMenuItems : function()
2347     {
2348         Roo.log("hide Menu Items");
2349         if (!this.el) { 
2350             return;
2351         }
2352         //$(backdrop).remove()
2353         this.el.select('.open',true).each(function(aa) {
2354             
2355             aa.removeClass('open');
2356           //var parent = getParent($(this))
2357           //var relatedTarget = { relatedTarget: this }
2358           
2359            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2360           //if (e.isDefaultPrevented()) return
2361            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2362         });
2363     },
2364     addxtypeChild : function (tree, cntr) {
2365         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2366           
2367         this.menuitems.add(comp);
2368         return comp;
2369
2370     },
2371     getEl : function()
2372     {
2373         Roo.log(this.el);
2374         return this.el;
2375     },
2376     
2377     clear : function()
2378     {
2379         this.getEl().dom.innerHTML = '';
2380         this.menuitems.clear();
2381     }
2382 });
2383
2384  
2385  /*
2386  * - LGPL
2387  *
2388  * menu item
2389  * 
2390  */
2391
2392
2393 /**
2394  * @class Roo.bootstrap.MenuItem
2395  * @extends Roo.bootstrap.Component
2396  * Bootstrap MenuItem class
2397  * @cfg {String} html the menu label
2398  * @cfg {String} href the link
2399  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2400  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2401  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2402  * @cfg {String} fa favicon to show on left of menu item.
2403  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2404  * 
2405  * 
2406  * @constructor
2407  * Create a new MenuItem
2408  * @param {Object} config The config object
2409  */
2410
2411
2412 Roo.bootstrap.MenuItem = function(config){
2413     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2414     this.addEvents({
2415         // raw events
2416         /**
2417          * @event click
2418          * The raw click event for the entire grid.
2419          * @param {Roo.bootstrap.MenuItem} this
2420          * @param {Roo.EventObject} e
2421          */
2422         "click" : true
2423     });
2424 };
2425
2426 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2427     
2428     href : false,
2429     html : false,
2430     preventDefault: false,
2431     isContainer : false,
2432     active : false,
2433     fa: false,
2434     
2435     getAutoCreate : function(){
2436         
2437         if(this.isContainer){
2438             return {
2439                 tag: 'li',
2440                 cls: 'dropdown-menu-item'
2441             };
2442         }
2443         var ctag = {
2444             tag: 'span',
2445             html: 'Link'
2446         };
2447         
2448         var anc = {
2449             tag : 'a',
2450             href : '#',
2451             cn : [  ]
2452         };
2453         
2454         if (this.fa !== false) {
2455             anc.cn.push({
2456                 tag : 'i',
2457                 cls : 'fa fa-' + this.fa
2458             });
2459         }
2460         
2461         anc.cn.push(ctag);
2462         
2463         
2464         var cfg= {
2465             tag: 'li',
2466             cls: 'dropdown-menu-item',
2467             cn: [ anc ]
2468         };
2469         if (this.parent().type == 'treeview') {
2470             cfg.cls = 'treeview-menu';
2471         }
2472         if (this.active) {
2473             cfg.cls += ' active';
2474         }
2475         
2476         
2477         
2478         anc.href = this.href || cfg.cn[0].href ;
2479         ctag.html = this.html || cfg.cn[0].html ;
2480         return cfg;
2481     },
2482     
2483     initEvents: function()
2484     {
2485         if (this.parent().type == 'treeview') {
2486             this.el.select('a').on('click', this.onClick, this);
2487         }
2488         
2489         if (this.menu) {
2490             this.menu.parentType = this.xtype;
2491             this.menu.triggerEl = this.el;
2492             this.menu = this.addxtype(Roo.apply({}, this.menu));
2493         }
2494         
2495     },
2496     onClick : function(e)
2497     {
2498         Roo.log('item on click ');
2499         
2500         if(this.preventDefault){
2501             e.preventDefault();
2502         }
2503         //this.parent().hideMenuItems();
2504         
2505         this.fireEvent('click', this, e);
2506     },
2507     getEl : function()
2508     {
2509         return this.el;
2510     } 
2511 });
2512
2513  
2514
2515  /*
2516  * - LGPL
2517  *
2518  * menu separator
2519  * 
2520  */
2521
2522
2523 /**
2524  * @class Roo.bootstrap.MenuSeparator
2525  * @extends Roo.bootstrap.Component
2526  * Bootstrap MenuSeparator class
2527  * 
2528  * @constructor
2529  * Create a new MenuItem
2530  * @param {Object} config The config object
2531  */
2532
2533
2534 Roo.bootstrap.MenuSeparator = function(config){
2535     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2536 };
2537
2538 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2539     
2540     getAutoCreate : function(){
2541         var cfg = {
2542             cls: 'divider',
2543             tag : 'li'
2544         };
2545         
2546         return cfg;
2547     }
2548    
2549 });
2550
2551  
2552
2553  
2554 /*
2555 * Licence: LGPL
2556 */
2557
2558 /**
2559  * @class Roo.bootstrap.Modal
2560  * @extends Roo.bootstrap.Component
2561  * Bootstrap Modal class
2562  * @cfg {String} title Title of dialog
2563  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2564  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2565  * @cfg {Boolean} specificTitle default false
2566  * @cfg {Array} buttons Array of buttons or standard button set..
2567  * @cfg {String} buttonPosition (left|right|center) default right
2568  * @cfg {Boolean} animate default true
2569  * @cfg {Boolean} allow_close default true
2570  * @cfg {Boolean} fitwindow default false
2571  * @cfg {String} size (sm|lg) default empty
2572  *
2573  *
2574  * @constructor
2575  * Create a new Modal Dialog
2576  * @param {Object} config The config object
2577  */
2578
2579 Roo.bootstrap.Modal = function(config){
2580     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2581     this.addEvents({
2582         // raw events
2583         /**
2584          * @event btnclick
2585          * The raw btnclick event for the button
2586          * @param {Roo.EventObject} e
2587          */
2588         "btnclick" : true,
2589         /**
2590          * @event resize
2591          * Fire when dialog resize
2592          * @param {Roo.bootstrap.Modal} this
2593          * @param {Roo.EventObject} e
2594          */
2595         "resize" : true
2596     });
2597     this.buttons = this.buttons || [];
2598
2599     if (this.tmpl) {
2600         this.tmpl = Roo.factory(this.tmpl);
2601     }
2602
2603 };
2604
2605 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2606
2607     title : 'test dialog',
2608
2609     buttons : false,
2610
2611     // set on load...
2612
2613     html: false,
2614
2615     tmp: false,
2616
2617     specificTitle: false,
2618
2619     buttonPosition: 'right',
2620
2621     allow_close : true,
2622
2623     animate : true,
2624
2625     fitwindow: false,
2626
2627
2628      // private
2629     dialogEl: false,
2630     bodyEl:  false,
2631     footerEl:  false,
2632     titleEl:  false,
2633     closeEl:  false,
2634
2635     size: '',
2636
2637
2638     onRender : function(ct, position)
2639     {
2640         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2641
2642         if(!this.el){
2643             var cfg = Roo.apply({},  this.getAutoCreate());
2644             cfg.id = Roo.id();
2645             //if(!cfg.name){
2646             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2647             //}
2648             //if (!cfg.name.length) {
2649             //    delete cfg.name;
2650            // }
2651             if (this.cls) {
2652                 cfg.cls += ' ' + this.cls;
2653             }
2654             if (this.style) {
2655                 cfg.style = this.style;
2656             }
2657             this.el = Roo.get(document.body).createChild(cfg, position);
2658         }
2659         //var type = this.el.dom.type;
2660
2661
2662         if(this.tabIndex !== undefined){
2663             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2664         }
2665
2666         this.dialogEl = this.el.select('.modal-dialog',true).first();
2667         this.bodyEl = this.el.select('.modal-body',true).first();
2668         this.closeEl = this.el.select('.modal-header .close', true).first();
2669         this.headerEl = this.el.select('.modal-header',true).first();
2670         this.titleEl = this.el.select('.modal-title',true).first();
2671         this.footerEl = this.el.select('.modal-footer',true).first();
2672
2673         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2674         
2675         //this.el.addClass("x-dlg-modal");
2676
2677         if (this.buttons.length) {
2678             Roo.each(this.buttons, function(bb) {
2679                 var b = Roo.apply({}, bb);
2680                 b.xns = b.xns || Roo.bootstrap;
2681                 b.xtype = b.xtype || 'Button';
2682                 if (typeof(b.listeners) == 'undefined') {
2683                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2684                 }
2685
2686                 var btn = Roo.factory(b);
2687
2688                 btn.render(this.el.select('.modal-footer div').first());
2689
2690             },this);
2691         }
2692         // render the children.
2693         var nitems = [];
2694
2695         if(typeof(this.items) != 'undefined'){
2696             var items = this.items;
2697             delete this.items;
2698
2699             for(var i =0;i < items.length;i++) {
2700                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2701             }
2702         }
2703
2704         this.items = nitems;
2705
2706         // where are these used - they used to be body/close/footer
2707
2708
2709         this.initEvents();
2710         //this.el.addClass([this.fieldClass, this.cls]);
2711
2712     },
2713
2714     getAutoCreate : function(){
2715
2716
2717         var bdy = {
2718                 cls : 'modal-body',
2719                 html : this.html || ''
2720         };
2721
2722         var title = {
2723             tag: 'h4',
2724             cls : 'modal-title',
2725             html : this.title
2726         };
2727
2728         if(this.specificTitle){
2729             title = this.title;
2730
2731         };
2732
2733         var header = [];
2734         if (this.allow_close) {
2735             header.push({
2736                 tag: 'button',
2737                 cls : 'close',
2738                 html : '&times'
2739             });
2740         }
2741
2742         header.push(title);
2743
2744         var size = '';
2745
2746         if(this.size.length){
2747             size = 'modal-' + this.size;
2748         }
2749
2750         var modal = {
2751             cls: "modal",
2752              cn : [
2753                 {
2754                     cls: "modal-dialog " + size,
2755                     cn : [
2756                         {
2757                             cls : "modal-content",
2758                             cn : [
2759                                 {
2760                                     cls : 'modal-header',
2761                                     cn : header
2762                                 },
2763                                 bdy,
2764                                 {
2765                                     cls : 'modal-footer',
2766                                     cn : [
2767                                         {
2768                                             tag: 'div',
2769                                             cls: 'btn-' + this.buttonPosition
2770                                         }
2771                                     ]
2772
2773                                 }
2774
2775
2776                             ]
2777
2778                         }
2779                     ]
2780
2781                 }
2782             ]
2783         };
2784
2785         if(this.animate){
2786             modal.cls += ' fade';
2787         }
2788
2789         return modal;
2790
2791     },
2792     getChildContainer : function() {
2793
2794          return this.bodyEl;
2795
2796     },
2797     getButtonContainer : function() {
2798          return this.el.select('.modal-footer div',true).first();
2799
2800     },
2801     initEvents : function()
2802     {
2803         if (this.allow_close) {
2804             this.closeEl.on('click', this.hide, this);
2805         }
2806         Roo.EventManager.onWindowResize(this.resize, this, true);
2807
2808
2809     },
2810
2811     resize : function()
2812     {
2813         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2814         if (this.fitwindow) {
2815             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2816             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2817             this.setSize(w,h);
2818         }
2819     },
2820
2821     setSize : function(w,h)
2822     {
2823         if (!w && !h) {
2824             return;
2825         }
2826         this.resizeTo(w,h);
2827     },
2828
2829     show : function() {
2830
2831         if (!this.rendered) {
2832             this.render();
2833         }
2834
2835         //this.el.setStyle('display', 'block');
2836         this.el.removeClass('hideing');        
2837         this.el.addClass('show');
2838  
2839         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2840             var _this = this;
2841             (function(){
2842                 this.el.addClass('in');
2843             }).defer(50, this);
2844         }else{
2845             this.el.addClass('in');
2846
2847         }
2848
2849         // not sure how we can show data in here..
2850         //if (this.tmpl) {
2851         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2852         //}
2853
2854         Roo.get(document.body).addClass("x-body-masked");
2855         
2856         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2857         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2858         this.maskEl.addClass('show');
2859         
2860         this.resize();
2861         
2862         this.fireEvent('show', this);
2863
2864         // set zindex here - otherwise it appears to be ignored...
2865         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2866
2867         (function () {
2868             this.items.forEach( function(e) {
2869                 e.layout ? e.layout() : false;
2870
2871             });
2872         }).defer(100,this);
2873
2874     },
2875     hide : function()
2876     {
2877         if(this.fireEvent("beforehide", this) !== false){
2878             this.maskEl.removeClass('show');
2879             Roo.get(document.body).removeClass("x-body-masked");
2880             this.el.removeClass('in');
2881             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2882
2883             if(this.animate){ // why
2884                 this.el.addClass('hideing');
2885                 (function(){
2886                     if (!this.el.hasClass('hideing')) {
2887                         return; // it's been shown again...
2888                     }
2889                     this.el.removeClass('show');
2890                     this.el.removeClass('hideing');
2891                 }).defer(150,this);
2892                 
2893             }else{
2894                  this.el.removeClass('show');
2895             }
2896             this.fireEvent('hide', this);
2897         }
2898     },
2899     isVisible : function()
2900     {
2901         
2902         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2903         
2904     },
2905
2906     addButton : function(str, cb)
2907     {
2908
2909
2910         var b = Roo.apply({}, { html : str } );
2911         b.xns = b.xns || Roo.bootstrap;
2912         b.xtype = b.xtype || 'Button';
2913         if (typeof(b.listeners) == 'undefined') {
2914             b.listeners = { click : cb.createDelegate(this)  };
2915         }
2916
2917         var btn = Roo.factory(b);
2918
2919         btn.render(this.el.select('.modal-footer div').first());
2920
2921         return btn;
2922
2923     },
2924
2925     setDefaultButton : function(btn)
2926     {
2927         //this.el.select('.modal-footer').()
2928     },
2929     diff : false,
2930
2931     resizeTo: function(w,h)
2932     {
2933         // skip.. ?? why??
2934
2935         this.dialogEl.setWidth(w);
2936         if (this.diff === false) {
2937             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2938         }
2939
2940         this.bodyEl.setHeight(h-this.diff);
2941
2942         this.fireEvent('resize', this);
2943
2944     },
2945     setContentSize  : function(w, h)
2946     {
2947
2948     },
2949     onButtonClick: function(btn,e)
2950     {
2951         //Roo.log([a,b,c]);
2952         this.fireEvent('btnclick', btn.name, e);
2953     },
2954      /**
2955      * Set the title of the Dialog
2956      * @param {String} str new Title
2957      */
2958     setTitle: function(str) {
2959         this.titleEl.dom.innerHTML = str;
2960     },
2961     /**
2962      * Set the body of the Dialog
2963      * @param {String} str new Title
2964      */
2965     setBody: function(str) {
2966         this.bodyEl.dom.innerHTML = str;
2967     },
2968     /**
2969      * Set the body of the Dialog using the template
2970      * @param {Obj} data - apply this data to the template and replace the body contents.
2971      */
2972     applyBody: function(obj)
2973     {
2974         if (!this.tmpl) {
2975             Roo.log("Error - using apply Body without a template");
2976             //code
2977         }
2978         this.tmpl.overwrite(this.bodyEl, obj);
2979     }
2980
2981 });
2982
2983
2984 Roo.apply(Roo.bootstrap.Modal,  {
2985     /**
2986          * Button config that displays a single OK button
2987          * @type Object
2988          */
2989         OK :  [{
2990             name : 'ok',
2991             weight : 'primary',
2992             html : 'OK'
2993         }],
2994         /**
2995          * Button config that displays Yes and No buttons
2996          * @type Object
2997          */
2998         YESNO : [
2999             {
3000                 name  : 'no',
3001                 html : 'No'
3002             },
3003             {
3004                 name  :'yes',
3005                 weight : 'primary',
3006                 html : 'Yes'
3007             }
3008         ],
3009
3010         /**
3011          * Button config that displays OK and Cancel buttons
3012          * @type Object
3013          */
3014         OKCANCEL : [
3015             {
3016                name : 'cancel',
3017                 html : 'Cancel'
3018             },
3019             {
3020                 name : 'ok',
3021                 weight : 'primary',
3022                 html : 'OK'
3023             }
3024         ],
3025         /**
3026          * Button config that displays Yes, No and Cancel buttons
3027          * @type Object
3028          */
3029         YESNOCANCEL : [
3030             {
3031                 name : 'yes',
3032                 weight : 'primary',
3033                 html : 'Yes'
3034             },
3035             {
3036                 name : 'no',
3037                 html : 'No'
3038             },
3039             {
3040                 name : 'cancel',
3041                 html : 'Cancel'
3042             }
3043         ],
3044         
3045         zIndex : 10001
3046 });
3047 /*
3048  * - LGPL
3049  *
3050  * messagebox - can be used as a replace
3051  * 
3052  */
3053 /**
3054  * @class Roo.MessageBox
3055  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3056  * Example usage:
3057  *<pre><code>
3058 // Basic alert:
3059 Roo.Msg.alert('Status', 'Changes saved successfully.');
3060
3061 // Prompt for user data:
3062 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3063     if (btn == 'ok'){
3064         // process text value...
3065     }
3066 });
3067
3068 // Show a dialog using config options:
3069 Roo.Msg.show({
3070    title:'Save Changes?',
3071    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3072    buttons: Roo.Msg.YESNOCANCEL,
3073    fn: processResult,
3074    animEl: 'elId'
3075 });
3076 </code></pre>
3077  * @singleton
3078  */
3079 Roo.bootstrap.MessageBox = function(){
3080     var dlg, opt, mask, waitTimer;
3081     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3082     var buttons, activeTextEl, bwidth;
3083
3084     
3085     // private
3086     var handleButton = function(button){
3087         dlg.hide();
3088         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3089     };
3090
3091     // private
3092     var handleHide = function(){
3093         if(opt && opt.cls){
3094             dlg.el.removeClass(opt.cls);
3095         }
3096         //if(waitTimer){
3097         //    Roo.TaskMgr.stop(waitTimer);
3098         //    waitTimer = null;
3099         //}
3100     };
3101
3102     // private
3103     var updateButtons = function(b){
3104         var width = 0;
3105         if(!b){
3106             buttons["ok"].hide();
3107             buttons["cancel"].hide();
3108             buttons["yes"].hide();
3109             buttons["no"].hide();
3110             //dlg.footer.dom.style.display = 'none';
3111             return width;
3112         }
3113         dlg.footerEl.dom.style.display = '';
3114         for(var k in buttons){
3115             if(typeof buttons[k] != "function"){
3116                 if(b[k]){
3117                     buttons[k].show();
3118                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3119                     width += buttons[k].el.getWidth()+15;
3120                 }else{
3121                     buttons[k].hide();
3122                 }
3123             }
3124         }
3125         return width;
3126     };
3127
3128     // private
3129     var handleEsc = function(d, k, e){
3130         if(opt && opt.closable !== false){
3131             dlg.hide();
3132         }
3133         if(e){
3134             e.stopEvent();
3135         }
3136     };
3137
3138     return {
3139         /**
3140          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3141          * @return {Roo.BasicDialog} The BasicDialog element
3142          */
3143         getDialog : function(){
3144            if(!dlg){
3145                 dlg = new Roo.bootstrap.Modal( {
3146                     //draggable: true,
3147                     //resizable:false,
3148                     //constraintoviewport:false,
3149                     //fixedcenter:true,
3150                     //collapsible : false,
3151                     //shim:true,
3152                     //modal: true,
3153                 //    width: 'auto',
3154                   //  height:100,
3155                     //buttonAlign:"center",
3156                     closeClick : function(){
3157                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3158                             handleButton("no");
3159                         }else{
3160                             handleButton("cancel");
3161                         }
3162                     }
3163                 });
3164                 dlg.render();
3165                 dlg.on("hide", handleHide);
3166                 mask = dlg.mask;
3167                 //dlg.addKeyListener(27, handleEsc);
3168                 buttons = {};
3169                 this.buttons = buttons;
3170                 var bt = this.buttonText;
3171                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3172                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3173                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3174                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3175                 //Roo.log(buttons);
3176                 bodyEl = dlg.bodyEl.createChild({
3177
3178                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3179                         '<textarea class="roo-mb-textarea"></textarea>' +
3180                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3181                 });
3182                 msgEl = bodyEl.dom.firstChild;
3183                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3184                 textboxEl.enableDisplayMode();
3185                 textboxEl.addKeyListener([10,13], function(){
3186                     if(dlg.isVisible() && opt && opt.buttons){
3187                         if(opt.buttons.ok){
3188                             handleButton("ok");
3189                         }else if(opt.buttons.yes){
3190                             handleButton("yes");
3191                         }
3192                     }
3193                 });
3194                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3195                 textareaEl.enableDisplayMode();
3196                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3197                 progressEl.enableDisplayMode();
3198                 
3199                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3200                 var pf = progressEl.dom.firstChild;
3201                 if (pf) {
3202                     pp = Roo.get(pf.firstChild);
3203                     pp.setHeight(pf.offsetHeight);
3204                 }
3205                 
3206             }
3207             return dlg;
3208         },
3209
3210         /**
3211          * Updates the message box body text
3212          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3213          * the XHTML-compliant non-breaking space character '&amp;#160;')
3214          * @return {Roo.MessageBox} This message box
3215          */
3216         updateText : function(text)
3217         {
3218             if(!dlg.isVisible() && !opt.width){
3219                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3220                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3221             }
3222             msgEl.innerHTML = text || '&#160;';
3223       
3224             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3225             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3226             var w = Math.max(
3227                     Math.min(opt.width || cw , this.maxWidth), 
3228                     Math.max(opt.minWidth || this.minWidth, bwidth)
3229             );
3230             if(opt.prompt){
3231                 activeTextEl.setWidth(w);
3232             }
3233             if(dlg.isVisible()){
3234                 dlg.fixedcenter = false;
3235             }
3236             // to big, make it scroll. = But as usual stupid IE does not support
3237             // !important..
3238             
3239             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3240                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3241                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3242             } else {
3243                 bodyEl.dom.style.height = '';
3244                 bodyEl.dom.style.overflowY = '';
3245             }
3246             if (cw > w) {
3247                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3248             } else {
3249                 bodyEl.dom.style.overflowX = '';
3250             }
3251             
3252             dlg.setContentSize(w, bodyEl.getHeight());
3253             if(dlg.isVisible()){
3254                 dlg.fixedcenter = true;
3255             }
3256             return this;
3257         },
3258
3259         /**
3260          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3261          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3262          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3263          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3264          * @return {Roo.MessageBox} This message box
3265          */
3266         updateProgress : function(value, text){
3267             if(text){
3268                 this.updateText(text);
3269             }
3270             
3271             if (pp) { // weird bug on my firefox - for some reason this is not defined
3272                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3273                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3274             }
3275             return this;
3276         },        
3277
3278         /**
3279          * Returns true if the message box is currently displayed
3280          * @return {Boolean} True if the message box is visible, else false
3281          */
3282         isVisible : function(){
3283             return dlg && dlg.isVisible();  
3284         },
3285
3286         /**
3287          * Hides the message box if it is displayed
3288          */
3289         hide : function(){
3290             if(this.isVisible()){
3291                 dlg.hide();
3292             }  
3293         },
3294
3295         /**
3296          * Displays a new message box, or reinitializes an existing message box, based on the config options
3297          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3298          * The following config object properties are supported:
3299          * <pre>
3300 Property    Type             Description
3301 ----------  ---------------  ------------------------------------------------------------------------------------
3302 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3303                                    closes (defaults to undefined)
3304 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3305                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3306 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3307                                    progress and wait dialogs will ignore this property and always hide the
3308                                    close button as they can only be closed programmatically.
3309 cls               String           A custom CSS class to apply to the message box element
3310 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3311                                    displayed (defaults to 75)
3312 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3313                                    function will be btn (the name of the button that was clicked, if applicable,
3314                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3315                                    Progress and wait dialogs will ignore this option since they do not respond to
3316                                    user actions and can only be closed programmatically, so any required function
3317                                    should be called by the same code after it closes the dialog.
3318 icon              String           A CSS class that provides a background image to be used as an icon for
3319                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3320 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3321 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3322 modal             Boolean          False to allow user interaction with the page while the message box is
3323                                    displayed (defaults to true)
3324 msg               String           A string that will replace the existing message box body text (defaults
3325                                    to the XHTML-compliant non-breaking space character '&#160;')
3326 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3327 progress          Boolean          True to display a progress bar (defaults to false)
3328 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3329 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3330 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3331 title             String           The title text
3332 value             String           The string value to set into the active textbox element if displayed
3333 wait              Boolean          True to display a progress bar (defaults to false)
3334 width             Number           The width of the dialog in pixels
3335 </pre>
3336          *
3337          * Example usage:
3338          * <pre><code>
3339 Roo.Msg.show({
3340    title: 'Address',
3341    msg: 'Please enter your address:',
3342    width: 300,
3343    buttons: Roo.MessageBox.OKCANCEL,
3344    multiline: true,
3345    fn: saveAddress,
3346    animEl: 'addAddressBtn'
3347 });
3348 </code></pre>
3349          * @param {Object} config Configuration options
3350          * @return {Roo.MessageBox} This message box
3351          */
3352         show : function(options)
3353         {
3354             
3355             // this causes nightmares if you show one dialog after another
3356             // especially on callbacks..
3357              
3358             if(this.isVisible()){
3359                 
3360                 this.hide();
3361                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3362                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3363                 Roo.log("New Dialog Message:" +  options.msg )
3364                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3365                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3366                 
3367             }
3368             var d = this.getDialog();
3369             opt = options;
3370             d.setTitle(opt.title || "&#160;");
3371             d.closeEl.setDisplayed(opt.closable !== false);
3372             activeTextEl = textboxEl;
3373             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3374             if(opt.prompt){
3375                 if(opt.multiline){
3376                     textboxEl.hide();
3377                     textareaEl.show();
3378                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3379                         opt.multiline : this.defaultTextHeight);
3380                     activeTextEl = textareaEl;
3381                 }else{
3382                     textboxEl.show();
3383                     textareaEl.hide();
3384                 }
3385             }else{
3386                 textboxEl.hide();
3387                 textareaEl.hide();
3388             }
3389             progressEl.setDisplayed(opt.progress === true);
3390             this.updateProgress(0);
3391             activeTextEl.dom.value = opt.value || "";
3392             if(opt.prompt){
3393                 dlg.setDefaultButton(activeTextEl);
3394             }else{
3395                 var bs = opt.buttons;
3396                 var db = null;
3397                 if(bs && bs.ok){
3398                     db = buttons["ok"];
3399                 }else if(bs && bs.yes){
3400                     db = buttons["yes"];
3401                 }
3402                 dlg.setDefaultButton(db);
3403             }
3404             bwidth = updateButtons(opt.buttons);
3405             this.updateText(opt.msg);
3406             if(opt.cls){
3407                 d.el.addClass(opt.cls);
3408             }
3409             d.proxyDrag = opt.proxyDrag === true;
3410             d.modal = opt.modal !== false;
3411             d.mask = opt.modal !== false ? mask : false;
3412             if(!d.isVisible()){
3413                 // force it to the end of the z-index stack so it gets a cursor in FF
3414                 document.body.appendChild(dlg.el.dom);
3415                 d.animateTarget = null;
3416                 d.show(options.animEl);
3417             }
3418             return this;
3419         },
3420
3421         /**
3422          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3423          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3424          * and closing the message box when the process is complete.
3425          * @param {String} title The title bar text
3426          * @param {String} msg The message box body text
3427          * @return {Roo.MessageBox} This message box
3428          */
3429         progress : function(title, msg){
3430             this.show({
3431                 title : title,
3432                 msg : msg,
3433                 buttons: false,
3434                 progress:true,
3435                 closable:false,
3436                 minWidth: this.minProgressWidth,
3437                 modal : true
3438             });
3439             return this;
3440         },
3441
3442         /**
3443          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3444          * If a callback function is passed it will be called after the user clicks the button, and the
3445          * id of the button that was clicked will be passed as the only parameter to the callback
3446          * (could also be the top-right close button).
3447          * @param {String} title The title bar text
3448          * @param {String} msg The message box body text
3449          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3450          * @param {Object} scope (optional) The scope of the callback function
3451          * @return {Roo.MessageBox} This message box
3452          */
3453         alert : function(title, msg, fn, scope)
3454         {
3455             this.show({
3456                 title : title,
3457                 msg : msg,
3458                 buttons: this.OK,
3459                 fn: fn,
3460                 closable : false,
3461                 scope : scope,
3462                 modal : true
3463             });
3464             return this;
3465         },
3466
3467         /**
3468          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3469          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3470          * You are responsible for closing the message box when the process is complete.
3471          * @param {String} msg The message box body text
3472          * @param {String} title (optional) The title bar text
3473          * @return {Roo.MessageBox} This message box
3474          */
3475         wait : function(msg, title){
3476             this.show({
3477                 title : title,
3478                 msg : msg,
3479                 buttons: false,
3480                 closable:false,
3481                 progress:true,
3482                 modal:true,
3483                 width:300,
3484                 wait:true
3485             });
3486             waitTimer = Roo.TaskMgr.start({
3487                 run: function(i){
3488                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3489                 },
3490                 interval: 1000
3491             });
3492             return this;
3493         },
3494
3495         /**
3496          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3497          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3498          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3499          * @param {String} title The title bar text
3500          * @param {String} msg The message box body text
3501          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3502          * @param {Object} scope (optional) The scope of the callback function
3503          * @return {Roo.MessageBox} This message box
3504          */
3505         confirm : function(title, msg, fn, scope){
3506             this.show({
3507                 title : title,
3508                 msg : msg,
3509                 buttons: this.YESNO,
3510                 fn: fn,
3511                 scope : scope,
3512                 modal : true
3513             });
3514             return this;
3515         },
3516
3517         /**
3518          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3519          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3520          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3521          * (could also be the top-right close button) and the text that was entered will be passed as the two
3522          * parameters to the callback.
3523          * @param {String} title The title bar text
3524          * @param {String} msg The message box body text
3525          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3526          * @param {Object} scope (optional) The scope of the callback function
3527          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3528          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3529          * @return {Roo.MessageBox} This message box
3530          */
3531         prompt : function(title, msg, fn, scope, multiline){
3532             this.show({
3533                 title : title,
3534                 msg : msg,
3535                 buttons: this.OKCANCEL,
3536                 fn: fn,
3537                 minWidth:250,
3538                 scope : scope,
3539                 prompt:true,
3540                 multiline: multiline,
3541                 modal : true
3542             });
3543             return this;
3544         },
3545
3546         /**
3547          * Button config that displays a single OK button
3548          * @type Object
3549          */
3550         OK : {ok:true},
3551         /**
3552          * Button config that displays Yes and No buttons
3553          * @type Object
3554          */
3555         YESNO : {yes:true, no:true},
3556         /**
3557          * Button config that displays OK and Cancel buttons
3558          * @type Object
3559          */
3560         OKCANCEL : {ok:true, cancel:true},
3561         /**
3562          * Button config that displays Yes, No and Cancel buttons
3563          * @type Object
3564          */
3565         YESNOCANCEL : {yes:true, no:true, cancel:true},
3566
3567         /**
3568          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3569          * @type Number
3570          */
3571         defaultTextHeight : 75,
3572         /**
3573          * The maximum width in pixels of the message box (defaults to 600)
3574          * @type Number
3575          */
3576         maxWidth : 600,
3577         /**
3578          * The minimum width in pixels of the message box (defaults to 100)
3579          * @type Number
3580          */
3581         minWidth : 100,
3582         /**
3583          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3584          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3585          * @type Number
3586          */
3587         minProgressWidth : 250,
3588         /**
3589          * An object containing the default button text strings that can be overriden for localized language support.
3590          * Supported properties are: ok, cancel, yes and no.
3591          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3592          * @type Object
3593          */
3594         buttonText : {
3595             ok : "OK",
3596             cancel : "Cancel",
3597             yes : "Yes",
3598             no : "No"
3599         }
3600     };
3601 }();
3602
3603 /**
3604  * Shorthand for {@link Roo.MessageBox}
3605  */
3606 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3607 Roo.Msg = Roo.Msg || Roo.MessageBox;
3608 /*
3609  * - LGPL
3610  *
3611  * navbar
3612  * 
3613  */
3614
3615 /**
3616  * @class Roo.bootstrap.Navbar
3617  * @extends Roo.bootstrap.Component
3618  * Bootstrap Navbar class
3619
3620  * @constructor
3621  * Create a new Navbar
3622  * @param {Object} config The config object
3623  */
3624
3625
3626 Roo.bootstrap.Navbar = function(config){
3627     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3628     this.addEvents({
3629         // raw events
3630         /**
3631          * @event beforetoggle
3632          * Fire before toggle the menu
3633          * @param {Roo.EventObject} e
3634          */
3635         "beforetoggle" : true
3636     });
3637 };
3638
3639 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3640     
3641     
3642    
3643     // private
3644     navItems : false,
3645     loadMask : false,
3646     
3647     
3648     getAutoCreate : function(){
3649         
3650         
3651         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3652         
3653     },
3654     
3655     initEvents :function ()
3656     {
3657         //Roo.log(this.el.select('.navbar-toggle',true));
3658         this.el.select('.navbar-toggle',true).on('click', function() {
3659             if(this.fireEvent('beforetoggle', this) !== false){
3660                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3661             }
3662             
3663         }, this);
3664         
3665         var mark = {
3666             tag: "div",
3667             cls:"x-dlg-mask"
3668         };
3669         
3670         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3671         
3672         var size = this.el.getSize();
3673         this.maskEl.setSize(size.width, size.height);
3674         this.maskEl.enableDisplayMode("block");
3675         this.maskEl.hide();
3676         
3677         if(this.loadMask){
3678             this.maskEl.show();
3679         }
3680     },
3681     
3682     
3683     getChildContainer : function()
3684     {
3685         if (this.el.select('.collapse').getCount()) {
3686             return this.el.select('.collapse',true).first();
3687         }
3688         
3689         return this.el;
3690     },
3691     
3692     mask : function()
3693     {
3694         this.maskEl.show();
3695     },
3696     
3697     unmask : function()
3698     {
3699         this.maskEl.hide();
3700     } 
3701     
3702     
3703     
3704     
3705 });
3706
3707
3708
3709  
3710
3711  /*
3712  * - LGPL
3713  *
3714  * navbar
3715  * 
3716  */
3717
3718 /**
3719  * @class Roo.bootstrap.NavSimplebar
3720  * @extends Roo.bootstrap.Navbar
3721  * Bootstrap Sidebar class
3722  *
3723  * @cfg {Boolean} inverse is inverted color
3724  * 
3725  * @cfg {String} type (nav | pills | tabs)
3726  * @cfg {Boolean} arrangement stacked | justified
3727  * @cfg {String} align (left | right) alignment
3728  * 
3729  * @cfg {Boolean} main (true|false) main nav bar? default false
3730  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3731  * 
3732  * @cfg {String} tag (header|footer|nav|div) default is nav 
3733
3734  * 
3735  * 
3736  * 
3737  * @constructor
3738  * Create a new Sidebar
3739  * @param {Object} config The config object
3740  */
3741
3742
3743 Roo.bootstrap.NavSimplebar = function(config){
3744     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3745 };
3746
3747 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3748     
3749     inverse: false,
3750     
3751     type: false,
3752     arrangement: '',
3753     align : false,
3754     
3755     
3756     
3757     main : false,
3758     
3759     
3760     tag : false,
3761     
3762     
3763     getAutoCreate : function(){
3764         
3765         
3766         var cfg = {
3767             tag : this.tag || 'div',
3768             cls : 'navbar'
3769         };
3770           
3771         
3772         cfg.cn = [
3773             {
3774                 cls: 'nav',
3775                 tag : 'ul'
3776             }
3777         ];
3778         
3779          
3780         this.type = this.type || 'nav';
3781         if (['tabs','pills'].indexOf(this.type)!==-1) {
3782             cfg.cn[0].cls += ' nav-' + this.type
3783         
3784         
3785         } else {
3786             if (this.type!=='nav') {
3787                 Roo.log('nav type must be nav/tabs/pills')
3788             }
3789             cfg.cn[0].cls += ' navbar-nav'
3790         }
3791         
3792         
3793         
3794         
3795         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3796             cfg.cn[0].cls += ' nav-' + this.arrangement;
3797         }
3798         
3799         
3800         if (this.align === 'right') {
3801             cfg.cn[0].cls += ' navbar-right';
3802         }
3803         
3804         if (this.inverse) {
3805             cfg.cls += ' navbar-inverse';
3806             
3807         }
3808         
3809         
3810         return cfg;
3811     
3812         
3813     }
3814     
3815     
3816     
3817 });
3818
3819
3820
3821  
3822
3823  
3824        /*
3825  * - LGPL
3826  *
3827  * navbar
3828  * 
3829  */
3830
3831 /**
3832  * @class Roo.bootstrap.NavHeaderbar
3833  * @extends Roo.bootstrap.NavSimplebar
3834  * Bootstrap Sidebar class
3835  *
3836  * @cfg {String} brand what is brand
3837  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3838  * @cfg {String} brand_href href of the brand
3839  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3840  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3841  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3842  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3843  * 
3844  * @constructor
3845  * Create a new Sidebar
3846  * @param {Object} config The config object
3847  */
3848
3849
3850 Roo.bootstrap.NavHeaderbar = function(config){
3851     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3852       
3853 };
3854
3855 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3856     
3857     position: '',
3858     brand: '',
3859     brand_href: false,
3860     srButton : true,
3861     autohide : false,
3862     desktopCenter : false,
3863    
3864     
3865     getAutoCreate : function(){
3866         
3867         var   cfg = {
3868             tag: this.nav || 'nav',
3869             cls: 'navbar',
3870             role: 'navigation',
3871             cn: []
3872         };
3873         
3874         var cn = cfg.cn;
3875         if (this.desktopCenter) {
3876             cn.push({cls : 'container', cn : []});
3877             cn = cn[0].cn;
3878         }
3879         
3880         if(this.srButton){
3881             cn.push({
3882                 tag: 'div',
3883                 cls: 'navbar-header',
3884                 cn: [
3885                     {
3886                         tag: 'button',
3887                         type: 'button',
3888                         cls: 'navbar-toggle',
3889                         'data-toggle': 'collapse',
3890                         cn: [
3891                             {
3892                                 tag: 'span',
3893                                 cls: 'sr-only',
3894                                 html: 'Toggle navigation'
3895                             },
3896                             {
3897                                 tag: 'span',
3898                                 cls: 'icon-bar'
3899                             },
3900                             {
3901                                 tag: 'span',
3902                                 cls: 'icon-bar'
3903                             },
3904                             {
3905                                 tag: 'span',
3906                                 cls: 'icon-bar'
3907                             }
3908                         ]
3909                     }
3910                 ]
3911             });
3912         }
3913         
3914         cn.push({
3915             tag: 'div',
3916             cls: 'collapse navbar-collapse',
3917             cn : []
3918         });
3919         
3920         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3921         
3922         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3923             cfg.cls += ' navbar-' + this.position;
3924             
3925             // tag can override this..
3926             
3927             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3928         }
3929         
3930         if (this.brand !== '') {
3931             cn[0].cn.push({
3932                 tag: 'a',
3933                 href: this.brand_href ? this.brand_href : '#',
3934                 cls: 'navbar-brand',
3935                 cn: [
3936                 this.brand
3937                 ]
3938             });
3939         }
3940         
3941         if(this.main){
3942             cfg.cls += ' main-nav';
3943         }
3944         
3945         
3946         return cfg;
3947
3948         
3949     },
3950     getHeaderChildContainer : function()
3951     {
3952         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3953             return this.el.select('.navbar-header',true).first();
3954         }
3955         
3956         return this.getChildContainer();
3957     },
3958     
3959     
3960     initEvents : function()
3961     {
3962         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3963         
3964         if (this.autohide) {
3965             
3966             var prevScroll = 0;
3967             var ft = this.el;
3968             
3969             Roo.get(document).on('scroll',function(e) {
3970                 var ns = Roo.get(document).getScroll().top;
3971                 var os = prevScroll;
3972                 prevScroll = ns;
3973                 
3974                 if(ns > os){
3975                     ft.removeClass('slideDown');
3976                     ft.addClass('slideUp');
3977                     return;
3978                 }
3979                 ft.removeClass('slideUp');
3980                 ft.addClass('slideDown');
3981                  
3982               
3983           },this);
3984         }
3985     }    
3986     
3987 });
3988
3989
3990
3991  
3992
3993  /*
3994  * - LGPL
3995  *
3996  * navbar
3997  * 
3998  */
3999
4000 /**
4001  * @class Roo.bootstrap.NavSidebar
4002  * @extends Roo.bootstrap.Navbar
4003  * Bootstrap Sidebar class
4004  * 
4005  * @constructor
4006  * Create a new Sidebar
4007  * @param {Object} config The config object
4008  */
4009
4010
4011 Roo.bootstrap.NavSidebar = function(config){
4012     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4013 };
4014
4015 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4016     
4017     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4018     
4019     getAutoCreate : function(){
4020         
4021         
4022         return  {
4023             tag: 'div',
4024             cls: 'sidebar sidebar-nav'
4025         };
4026     
4027         
4028     }
4029     
4030     
4031     
4032 });
4033
4034
4035
4036  
4037
4038  /*
4039  * - LGPL
4040  *
4041  * nav group
4042  * 
4043  */
4044
4045 /**
4046  * @class Roo.bootstrap.NavGroup
4047  * @extends Roo.bootstrap.Component
4048  * Bootstrap NavGroup class
4049  * @cfg {String} align (left|right)
4050  * @cfg {Boolean} inverse
4051  * @cfg {String} type (nav|pills|tab) default nav
4052  * @cfg {String} navId - reference Id for navbar.
4053
4054  * 
4055  * @constructor
4056  * Create a new nav group
4057  * @param {Object} config The config object
4058  */
4059
4060 Roo.bootstrap.NavGroup = function(config){
4061     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4062     this.navItems = [];
4063    
4064     Roo.bootstrap.NavGroup.register(this);
4065      this.addEvents({
4066         /**
4067              * @event changed
4068              * Fires when the active item changes
4069              * @param {Roo.bootstrap.NavGroup} this
4070              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4071              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4072          */
4073         'changed': true
4074      });
4075     
4076 };
4077
4078 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4079     
4080     align: '',
4081     inverse: false,
4082     form: false,
4083     type: 'nav',
4084     navId : '',
4085     // private
4086     
4087     navItems : false, 
4088     
4089     getAutoCreate : function()
4090     {
4091         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4092         
4093         cfg = {
4094             tag : 'ul',
4095             cls: 'nav' 
4096         };
4097         
4098         if (['tabs','pills'].indexOf(this.type)!==-1) {
4099             cfg.cls += ' nav-' + this.type
4100         } else {
4101             if (this.type!=='nav') {
4102                 Roo.log('nav type must be nav/tabs/pills')
4103             }
4104             cfg.cls += ' navbar-nav'
4105         }
4106         
4107         if (this.parent() && this.parent().sidebar) {
4108             cfg = {
4109                 tag: 'ul',
4110                 cls: 'dashboard-menu sidebar-menu'
4111             };
4112             
4113             return cfg;
4114         }
4115         
4116         if (this.form === true) {
4117             cfg = {
4118                 tag: 'form',
4119                 cls: 'navbar-form'
4120             };
4121             
4122             if (this.align === 'right') {
4123                 cfg.cls += ' navbar-right';
4124             } else {
4125                 cfg.cls += ' navbar-left';
4126             }
4127         }
4128         
4129         if (this.align === 'right') {
4130             cfg.cls += ' navbar-right';
4131         }
4132         
4133         if (this.inverse) {
4134             cfg.cls += ' navbar-inverse';
4135             
4136         }
4137         
4138         
4139         return cfg;
4140     },
4141     /**
4142     * sets the active Navigation item
4143     * @param {Roo.bootstrap.NavItem} the new current navitem
4144     */
4145     setActiveItem : function(item)
4146     {
4147         var prev = false;
4148         Roo.each(this.navItems, function(v){
4149             if (v == item) {
4150                 return ;
4151             }
4152             if (v.isActive()) {
4153                 v.setActive(false, true);
4154                 prev = v;
4155                 
4156             }
4157             
4158         });
4159
4160         item.setActive(true, true);
4161         this.fireEvent('changed', this, item, prev);
4162         
4163         
4164     },
4165     /**
4166     * gets the active Navigation item
4167     * @return {Roo.bootstrap.NavItem} the current navitem
4168     */
4169     getActive : function()
4170     {
4171         
4172         var prev = false;
4173         Roo.each(this.navItems, function(v){
4174             
4175             if (v.isActive()) {
4176                 prev = v;
4177                 
4178             }
4179             
4180         });
4181         return prev;
4182     },
4183     
4184     indexOfNav : function()
4185     {
4186         
4187         var prev = false;
4188         Roo.each(this.navItems, function(v,i){
4189             
4190             if (v.isActive()) {
4191                 prev = i;
4192                 
4193             }
4194             
4195         });
4196         return prev;
4197     },
4198     /**
4199     * adds a Navigation item
4200     * @param {Roo.bootstrap.NavItem} the navitem to add
4201     */
4202     addItem : function(cfg)
4203     {
4204         var cn = new Roo.bootstrap.NavItem(cfg);
4205         this.register(cn);
4206         cn.parentId = this.id;
4207         cn.onRender(this.el, null);
4208         return cn;
4209     },
4210     /**
4211     * register a Navigation item
4212     * @param {Roo.bootstrap.NavItem} the navitem to add
4213     */
4214     register : function(item)
4215     {
4216         this.navItems.push( item);
4217         item.navId = this.navId;
4218     
4219     },
4220     
4221     /**
4222     * clear all the Navigation item
4223     */
4224    
4225     clearAll : function()
4226     {
4227         this.navItems = [];
4228         this.el.dom.innerHTML = '';
4229     },
4230     
4231     getNavItem: function(tabId)
4232     {
4233         var ret = false;
4234         Roo.each(this.navItems, function(e) {
4235             if (e.tabId == tabId) {
4236                ret =  e;
4237                return false;
4238             }
4239             return true;
4240             
4241         });
4242         return ret;
4243     },
4244     
4245     setActiveNext : function()
4246     {
4247         var i = this.indexOfNav(this.getActive());
4248         if (i > this.navItems.length) {
4249             return;
4250         }
4251         this.setActiveItem(this.navItems[i+1]);
4252     },
4253     setActivePrev : function()
4254     {
4255         var i = this.indexOfNav(this.getActive());
4256         if (i  < 1) {
4257             return;
4258         }
4259         this.setActiveItem(this.navItems[i-1]);
4260     },
4261     clearWasActive : function(except) {
4262         Roo.each(this.navItems, function(e) {
4263             if (e.tabId != except.tabId && e.was_active) {
4264                e.was_active = false;
4265                return false;
4266             }
4267             return true;
4268             
4269         });
4270     },
4271     getWasActive : function ()
4272     {
4273         var r = false;
4274         Roo.each(this.navItems, function(e) {
4275             if (e.was_active) {
4276                r = e;
4277                return false;
4278             }
4279             return true;
4280             
4281         });
4282         return r;
4283     }
4284     
4285     
4286 });
4287
4288  
4289 Roo.apply(Roo.bootstrap.NavGroup, {
4290     
4291     groups: {},
4292      /**
4293     * register a Navigation Group
4294     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4295     */
4296     register : function(navgrp)
4297     {
4298         this.groups[navgrp.navId] = navgrp;
4299         
4300     },
4301     /**
4302     * fetch a Navigation Group based on the navigation ID
4303     * @param {string} the navgroup to add
4304     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4305     */
4306     get: function(navId) {
4307         if (typeof(this.groups[navId]) == 'undefined') {
4308             return false;
4309             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4310         }
4311         return this.groups[navId] ;
4312     }
4313     
4314     
4315     
4316 });
4317
4318  /*
4319  * - LGPL
4320  *
4321  * row
4322  * 
4323  */
4324
4325 /**
4326  * @class Roo.bootstrap.NavItem
4327  * @extends Roo.bootstrap.Component
4328  * Bootstrap Navbar.NavItem class
4329  * @cfg {String} href  link to
4330  * @cfg {String} html content of button
4331  * @cfg {String} badge text inside badge
4332  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4333  * @cfg {String} glyphicon name of glyphicon
4334  * @cfg {String} icon name of font awesome icon
4335  * @cfg {Boolean} active Is item active
4336  * @cfg {Boolean} disabled Is item disabled
4337  
4338  * @cfg {Boolean} preventDefault (true | false) default false
4339  * @cfg {String} tabId the tab that this item activates.
4340  * @cfg {String} tagtype (a|span) render as a href or span?
4341  * @cfg {Boolean} animateRef (true|false) link to element default false  
4342   
4343  * @constructor
4344  * Create a new Navbar Item
4345  * @param {Object} config The config object
4346  */
4347 Roo.bootstrap.NavItem = function(config){
4348     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4349     this.addEvents({
4350         // raw events
4351         /**
4352          * @event click
4353          * The raw click event for the entire grid.
4354          * @param {Roo.EventObject} e
4355          */
4356         "click" : true,
4357          /**
4358             * @event changed
4359             * Fires when the active item active state changes
4360             * @param {Roo.bootstrap.NavItem} this
4361             * @param {boolean} state the new state
4362              
4363          */
4364         'changed': true,
4365         /**
4366             * @event scrollto
4367             * Fires when scroll to element
4368             * @param {Roo.bootstrap.NavItem} this
4369             * @param {Object} options
4370             * @param {Roo.EventObject} e
4371              
4372          */
4373         'scrollto': true
4374     });
4375    
4376 };
4377
4378 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4379     
4380     href: false,
4381     html: '',
4382     badge: '',
4383     icon: false,
4384     glyphicon: false,
4385     active: false,
4386     preventDefault : false,
4387     tabId : false,
4388     tagtype : 'a',
4389     disabled : false,
4390     animateRef : false,
4391     was_active : false,
4392     
4393     getAutoCreate : function(){
4394          
4395         var cfg = {
4396             tag: 'li',
4397             cls: 'nav-item'
4398             
4399         };
4400         
4401         if (this.active) {
4402             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4403         }
4404         if (this.disabled) {
4405             cfg.cls += ' disabled';
4406         }
4407         
4408         if (this.href || this.html || this.glyphicon || this.icon) {
4409             cfg.cn = [
4410                 {
4411                     tag: this.tagtype,
4412                     href : this.href || "#",
4413                     html: this.html || ''
4414                 }
4415             ];
4416             
4417             if (this.icon) {
4418                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4419             }
4420
4421             if(this.glyphicon) {
4422                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4423             }
4424             
4425             if (this.menu) {
4426                 
4427                 cfg.cn[0].html += " <span class='caret'></span>";
4428              
4429             }
4430             
4431             if (this.badge !== '') {
4432                  
4433                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4434             }
4435         }
4436         
4437         
4438         
4439         return cfg;
4440     },
4441     initEvents: function() 
4442     {
4443         if (typeof (this.menu) != 'undefined') {
4444             this.menu.parentType = this.xtype;
4445             this.menu.triggerEl = this.el;
4446             this.menu = this.addxtype(Roo.apply({}, this.menu));
4447         }
4448         
4449         this.el.select('a',true).on('click', this.onClick, this);
4450         
4451         if(this.tagtype == 'span'){
4452             this.el.select('span',true).on('click', this.onClick, this);
4453         }
4454        
4455         // at this point parent should be available..
4456         this.parent().register(this);
4457     },
4458     
4459     onClick : function(e)
4460     {
4461         if (e.getTarget('.dropdown-menu-item')) {
4462             // did you click on a menu itemm.... - then don't trigger onclick..
4463             return;
4464         }
4465         
4466         if(
4467                 this.preventDefault || 
4468                 this.href == '#' 
4469         ){
4470             Roo.log("NavItem - prevent Default?");
4471             e.preventDefault();
4472         }
4473         
4474         if (this.disabled) {
4475             return;
4476         }
4477         
4478         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4479         if (tg && tg.transition) {
4480             Roo.log("waiting for the transitionend");
4481             return;
4482         }
4483         
4484         
4485         
4486         //Roo.log("fire event clicked");
4487         if(this.fireEvent('click', this, e) === false){
4488             return;
4489         };
4490         
4491         if(this.tagtype == 'span'){
4492             return;
4493         }
4494         
4495         //Roo.log(this.href);
4496         var ael = this.el.select('a',true).first();
4497         //Roo.log(ael);
4498         
4499         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4500             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4501             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4502                 return; // ignore... - it's a 'hash' to another page.
4503             }
4504             Roo.log("NavItem - prevent Default?");
4505             e.preventDefault();
4506             this.scrollToElement(e);
4507         }
4508         
4509         
4510         var p =  this.parent();
4511    
4512         if (['tabs','pills'].indexOf(p.type)!==-1) {
4513             if (typeof(p.setActiveItem) !== 'undefined') {
4514                 p.setActiveItem(this);
4515             }
4516         }
4517         
4518         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4519         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4520             // remove the collapsed menu expand...
4521             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4522         }
4523     },
4524     
4525     isActive: function () {
4526         return this.active
4527     },
4528     setActive : function(state, fire, is_was_active)
4529     {
4530         if (this.active && !state && this.navId) {
4531             this.was_active = true;
4532             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4533             if (nv) {
4534                 nv.clearWasActive(this);
4535             }
4536             
4537         }
4538         this.active = state;
4539         
4540         if (!state ) {
4541             this.el.removeClass('active');
4542         } else if (!this.el.hasClass('active')) {
4543             this.el.addClass('active');
4544         }
4545         if (fire) {
4546             this.fireEvent('changed', this, state);
4547         }
4548         
4549         // show a panel if it's registered and related..
4550         
4551         if (!this.navId || !this.tabId || !state || is_was_active) {
4552             return;
4553         }
4554         
4555         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4556         if (!tg) {
4557             return;
4558         }
4559         var pan = tg.getPanelByName(this.tabId);
4560         if (!pan) {
4561             return;
4562         }
4563         // if we can not flip to new panel - go back to old nav highlight..
4564         if (false == tg.showPanel(pan)) {
4565             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4566             if (nv) {
4567                 var onav = nv.getWasActive();
4568                 if (onav) {
4569                     onav.setActive(true, false, true);
4570                 }
4571             }
4572             
4573         }
4574         
4575         
4576         
4577     },
4578      // this should not be here...
4579     setDisabled : function(state)
4580     {
4581         this.disabled = state;
4582         if (!state ) {
4583             this.el.removeClass('disabled');
4584         } else if (!this.el.hasClass('disabled')) {
4585             this.el.addClass('disabled');
4586         }
4587         
4588     },
4589     
4590     /**
4591      * Fetch the element to display the tooltip on.
4592      * @return {Roo.Element} defaults to this.el
4593      */
4594     tooltipEl : function()
4595     {
4596         return this.el.select('' + this.tagtype + '', true).first();
4597     },
4598     
4599     scrollToElement : function(e)
4600     {
4601         var c = document.body;
4602         
4603         /*
4604          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4605          */
4606         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4607             c = document.documentElement;
4608         }
4609         
4610         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4611         
4612         if(!target){
4613             return;
4614         }
4615
4616         var o = target.calcOffsetsTo(c);
4617         
4618         var options = {
4619             target : target,
4620             value : o[1]
4621         };
4622         
4623         this.fireEvent('scrollto', this, options, e);
4624         
4625         Roo.get(c).scrollTo('top', options.value, true);
4626         
4627         return;
4628     }
4629 });
4630  
4631
4632  /*
4633  * - LGPL
4634  *
4635  * sidebar item
4636  *
4637  *  li
4638  *    <span> icon </span>
4639  *    <span> text </span>
4640  *    <span>badge </span>
4641  */
4642
4643 /**
4644  * @class Roo.bootstrap.NavSidebarItem
4645  * @extends Roo.bootstrap.NavItem
4646  * Bootstrap Navbar.NavSidebarItem class
4647  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4648  * {Boolean} open is the menu open
4649  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4650  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4651  * {String} buttonSize (sm|md|lg)the extra classes for the button
4652  * {Boolean} showArrow show arrow next to the text (default true)
4653  * @constructor
4654  * Create a new Navbar Button
4655  * @param {Object} config The config object
4656  */
4657 Roo.bootstrap.NavSidebarItem = function(config){
4658     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4659     this.addEvents({
4660         // raw events
4661         /**
4662          * @event click
4663          * The raw click event for the entire grid.
4664          * @param {Roo.EventObject} e
4665          */
4666         "click" : true,
4667          /**
4668             * @event changed
4669             * Fires when the active item active state changes
4670             * @param {Roo.bootstrap.NavSidebarItem} this
4671             * @param {boolean} state the new state
4672              
4673          */
4674         'changed': true
4675     });
4676    
4677 };
4678
4679 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4680     
4681     badgeWeight : 'default',
4682     
4683     open: false,
4684     
4685     buttonView : false,
4686     
4687     buttonWeight : 'default',
4688     
4689     buttonSize : 'md',
4690     
4691     showArrow : true,
4692     
4693     getAutoCreate : function(){
4694         
4695         
4696         var a = {
4697                 tag: 'a',
4698                 href : this.href || '#',
4699                 cls: '',
4700                 html : '',
4701                 cn : []
4702         };
4703         
4704         if(this.buttonView){
4705             a = {
4706                 tag: 'button',
4707                 href : this.href || '#',
4708                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4709                 html : this.html,
4710                 cn : []
4711             };
4712         }
4713         
4714         var cfg = {
4715             tag: 'li',
4716             cls: '',
4717             cn: [ a ]
4718         };
4719         
4720         if (this.active) {
4721             cfg.cls += ' active';
4722         }
4723         
4724         if (this.disabled) {
4725             cfg.cls += ' disabled';
4726         }
4727         if (this.open) {
4728             cfg.cls += ' open x-open';
4729         }
4730         // left icon..
4731         if (this.glyphicon || this.icon) {
4732             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4733             a.cn.push({ tag : 'i', cls : c }) ;
4734         }
4735         
4736         if(!this.buttonView){
4737             var span = {
4738                 tag: 'span',
4739                 html : this.html || ''
4740             };
4741
4742             a.cn.push(span);
4743             
4744         }
4745         
4746         if (this.badge !== '') {
4747             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4748         }
4749         
4750         if (this.menu) {
4751             
4752             if(this.showArrow){
4753                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4754             }
4755             
4756             a.cls += ' dropdown-toggle treeview' ;
4757         }
4758         
4759         return cfg;
4760     },
4761     
4762     initEvents : function()
4763     { 
4764         if (typeof (this.menu) != 'undefined') {
4765             this.menu.parentType = this.xtype;
4766             this.menu.triggerEl = this.el;
4767             this.menu = this.addxtype(Roo.apply({}, this.menu));
4768         }
4769         
4770         this.el.on('click', this.onClick, this);
4771         
4772         if(this.badge !== ''){
4773             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4774         }
4775         
4776     },
4777     
4778     onClick : function(e)
4779     {
4780         if(this.disabled){
4781             e.preventDefault();
4782             return;
4783         }
4784         
4785         if(this.preventDefault){
4786             e.preventDefault();
4787         }
4788         
4789         this.fireEvent('click', this);
4790     },
4791     
4792     disable : function()
4793     {
4794         this.setDisabled(true);
4795     },
4796     
4797     enable : function()
4798     {
4799         this.setDisabled(false);
4800     },
4801     
4802     setDisabled : function(state)
4803     {
4804         if(this.disabled == state){
4805             return;
4806         }
4807         
4808         this.disabled = state;
4809         
4810         if (state) {
4811             this.el.addClass('disabled');
4812             return;
4813         }
4814         
4815         this.el.removeClass('disabled');
4816         
4817         return;
4818     },
4819     
4820     setActive : function(state)
4821     {
4822         if(this.active == state){
4823             return;
4824         }
4825         
4826         this.active = state;
4827         
4828         if (state) {
4829             this.el.addClass('active');
4830             return;
4831         }
4832         
4833         this.el.removeClass('active');
4834         
4835         return;
4836     },
4837     
4838     isActive: function () 
4839     {
4840         return this.active;
4841     },
4842     
4843     setBadge : function(str)
4844     {
4845         if(!this.badgeEl){
4846             return;
4847         }
4848         
4849         this.badgeEl.dom.innerHTML = str;
4850     }
4851     
4852    
4853      
4854  
4855 });
4856  
4857
4858  /*
4859  * - LGPL
4860  *
4861  * row
4862  * 
4863  */
4864
4865 /**
4866  * @class Roo.bootstrap.Row
4867  * @extends Roo.bootstrap.Component
4868  * Bootstrap Row class (contains columns...)
4869  * 
4870  * @constructor
4871  * Create a new Row
4872  * @param {Object} config The config object
4873  */
4874
4875 Roo.bootstrap.Row = function(config){
4876     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4877 };
4878
4879 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4880     
4881     getAutoCreate : function(){
4882        return {
4883             cls: 'row clearfix'
4884        };
4885     }
4886     
4887     
4888 });
4889
4890  
4891
4892  /*
4893  * - LGPL
4894  *
4895  * element
4896  * 
4897  */
4898
4899 /**
4900  * @class Roo.bootstrap.Element
4901  * @extends Roo.bootstrap.Component
4902  * Bootstrap Element class
4903  * @cfg {String} html contents of the element
4904  * @cfg {String} tag tag of the element
4905  * @cfg {String} cls class of the element
4906  * @cfg {Boolean} preventDefault (true|false) default false
4907  * @cfg {Boolean} clickable (true|false) default false
4908  * 
4909  * @constructor
4910  * Create a new Element
4911  * @param {Object} config The config object
4912  */
4913
4914 Roo.bootstrap.Element = function(config){
4915     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4916     
4917     this.addEvents({
4918         // raw events
4919         /**
4920          * @event click
4921          * When a element is chick
4922          * @param {Roo.bootstrap.Element} this
4923          * @param {Roo.EventObject} e
4924          */
4925         "click" : true
4926     });
4927 };
4928
4929 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4930     
4931     tag: 'div',
4932     cls: '',
4933     html: '',
4934     preventDefault: false, 
4935     clickable: false,
4936     
4937     getAutoCreate : function(){
4938         
4939         var cfg = {
4940             tag: this.tag,
4941             // cls: this.cls, double assign in parent class Component.js :: onRender
4942             html: this.html
4943         };
4944         
4945         return cfg;
4946     },
4947     
4948     initEvents: function() 
4949     {
4950         Roo.bootstrap.Element.superclass.initEvents.call(this);
4951         
4952         if(this.clickable){
4953             this.el.on('click', this.onClick, this);
4954         }
4955         
4956     },
4957     
4958     onClick : function(e)
4959     {
4960         if(this.preventDefault){
4961             e.preventDefault();
4962         }
4963         
4964         this.fireEvent('click', this, e);
4965     },
4966     
4967     getValue : function()
4968     {
4969         return this.el.dom.innerHTML;
4970     },
4971     
4972     setValue : function(value)
4973     {
4974         this.el.dom.innerHTML = value;
4975     }
4976    
4977 });
4978
4979  
4980
4981  /*
4982  * - LGPL
4983  *
4984  * pagination
4985  * 
4986  */
4987
4988 /**
4989  * @class Roo.bootstrap.Pagination
4990  * @extends Roo.bootstrap.Component
4991  * Bootstrap Pagination class
4992  * @cfg {String} size xs | sm | md | lg
4993  * @cfg {Boolean} inverse false | true
4994  * 
4995  * @constructor
4996  * Create a new Pagination
4997  * @param {Object} config The config object
4998  */
4999
5000 Roo.bootstrap.Pagination = function(config){
5001     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5002 };
5003
5004 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5005     
5006     cls: false,
5007     size: false,
5008     inverse: false,
5009     
5010     getAutoCreate : function(){
5011         var cfg = {
5012             tag: 'ul',
5013                 cls: 'pagination'
5014         };
5015         if (this.inverse) {
5016             cfg.cls += ' inverse';
5017         }
5018         if (this.html) {
5019             cfg.html=this.html;
5020         }
5021         if (this.cls) {
5022             cfg.cls += " " + this.cls;
5023         }
5024         return cfg;
5025     }
5026    
5027 });
5028
5029  
5030
5031  /*
5032  * - LGPL
5033  *
5034  * Pagination item
5035  * 
5036  */
5037
5038
5039 /**
5040  * @class Roo.bootstrap.PaginationItem
5041  * @extends Roo.bootstrap.Component
5042  * Bootstrap PaginationItem class
5043  * @cfg {String} html text
5044  * @cfg {String} href the link
5045  * @cfg {Boolean} preventDefault (true | false) default true
5046  * @cfg {Boolean} active (true | false) default false
5047  * @cfg {Boolean} disabled default false
5048  * 
5049  * 
5050  * @constructor
5051  * Create a new PaginationItem
5052  * @param {Object} config The config object
5053  */
5054
5055
5056 Roo.bootstrap.PaginationItem = function(config){
5057     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5058     this.addEvents({
5059         // raw events
5060         /**
5061          * @event click
5062          * The raw click event for the entire grid.
5063          * @param {Roo.EventObject} e
5064          */
5065         "click" : true
5066     });
5067 };
5068
5069 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5070     
5071     href : false,
5072     html : false,
5073     preventDefault: true,
5074     active : false,
5075     cls : false,
5076     disabled: false,
5077     
5078     getAutoCreate : function(){
5079         var cfg= {
5080             tag: 'li',
5081             cn: [
5082                 {
5083                     tag : 'a',
5084                     href : this.href ? this.href : '#',
5085                     html : this.html ? this.html : ''
5086                 }
5087             ]
5088         };
5089         
5090         if(this.cls){
5091             cfg.cls = this.cls;
5092         }
5093         
5094         if(this.disabled){
5095             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5096         }
5097         
5098         if(this.active){
5099             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5100         }
5101         
5102         return cfg;
5103     },
5104     
5105     initEvents: function() {
5106         
5107         this.el.on('click', this.onClick, this);
5108         
5109     },
5110     onClick : function(e)
5111     {
5112         Roo.log('PaginationItem on click ');
5113         if(this.preventDefault){
5114             e.preventDefault();
5115         }
5116         
5117         if(this.disabled){
5118             return;
5119         }
5120         
5121         this.fireEvent('click', this, e);
5122     }
5123    
5124 });
5125
5126  
5127
5128  /*
5129  * - LGPL
5130  *
5131  * slider
5132  * 
5133  */
5134
5135
5136 /**
5137  * @class Roo.bootstrap.Slider
5138  * @extends Roo.bootstrap.Component
5139  * Bootstrap Slider class
5140  *    
5141  * @constructor
5142  * Create a new Slider
5143  * @param {Object} config The config object
5144  */
5145
5146 Roo.bootstrap.Slider = function(config){
5147     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5148 };
5149
5150 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5151     
5152     getAutoCreate : function(){
5153         
5154         var cfg = {
5155             tag: 'div',
5156             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5157             cn: [
5158                 {
5159                     tag: 'a',
5160                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5161                 }
5162             ]
5163         };
5164         
5165         return cfg;
5166     }
5167    
5168 });
5169
5170  /*
5171  * Based on:
5172  * Ext JS Library 1.1.1
5173  * Copyright(c) 2006-2007, Ext JS, LLC.
5174  *
5175  * Originally Released Under LGPL - original licence link has changed is not relivant.
5176  *
5177  * Fork - LGPL
5178  * <script type="text/javascript">
5179  */
5180  
5181
5182 /**
5183  * @class Roo.grid.ColumnModel
5184  * @extends Roo.util.Observable
5185  * This is the default implementation of a ColumnModel used by the Grid. It defines
5186  * the columns in the grid.
5187  * <br>Usage:<br>
5188  <pre><code>
5189  var colModel = new Roo.grid.ColumnModel([
5190         {header: "Ticker", width: 60, sortable: true, locked: true},
5191         {header: "Company Name", width: 150, sortable: true},
5192         {header: "Market Cap.", width: 100, sortable: true},
5193         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5194         {header: "Employees", width: 100, sortable: true, resizable: false}
5195  ]);
5196  </code></pre>
5197  * <p>
5198  
5199  * The config options listed for this class are options which may appear in each
5200  * individual column definition.
5201  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5202  * @constructor
5203  * @param {Object} config An Array of column config objects. See this class's
5204  * config objects for details.
5205 */
5206 Roo.grid.ColumnModel = function(config){
5207         /**
5208      * The config passed into the constructor
5209      */
5210     this.config = config;
5211     this.lookup = {};
5212
5213     // if no id, create one
5214     // if the column does not have a dataIndex mapping,
5215     // map it to the order it is in the config
5216     for(var i = 0, len = config.length; i < len; i++){
5217         var c = config[i];
5218         if(typeof c.dataIndex == "undefined"){
5219             c.dataIndex = i;
5220         }
5221         if(typeof c.renderer == "string"){
5222             c.renderer = Roo.util.Format[c.renderer];
5223         }
5224         if(typeof c.id == "undefined"){
5225             c.id = Roo.id();
5226         }
5227         if(c.editor && c.editor.xtype){
5228             c.editor  = Roo.factory(c.editor, Roo.grid);
5229         }
5230         if(c.editor && c.editor.isFormField){
5231             c.editor = new Roo.grid.GridEditor(c.editor);
5232         }
5233         this.lookup[c.id] = c;
5234     }
5235
5236     /**
5237      * The width of columns which have no width specified (defaults to 100)
5238      * @type Number
5239      */
5240     this.defaultWidth = 100;
5241
5242     /**
5243      * Default sortable of columns which have no sortable specified (defaults to false)
5244      * @type Boolean
5245      */
5246     this.defaultSortable = false;
5247
5248     this.addEvents({
5249         /**
5250              * @event widthchange
5251              * Fires when the width of a column changes.
5252              * @param {ColumnModel} this
5253              * @param {Number} columnIndex The column index
5254              * @param {Number} newWidth The new width
5255              */
5256             "widthchange": true,
5257         /**
5258              * @event headerchange
5259              * Fires when the text of a header changes.
5260              * @param {ColumnModel} this
5261              * @param {Number} columnIndex The column index
5262              * @param {Number} newText The new header text
5263              */
5264             "headerchange": true,
5265         /**
5266              * @event hiddenchange
5267              * Fires when a column is hidden or "unhidden".
5268              * @param {ColumnModel} this
5269              * @param {Number} columnIndex The column index
5270              * @param {Boolean} hidden true if hidden, false otherwise
5271              */
5272             "hiddenchange": true,
5273             /**
5274          * @event columnmoved
5275          * Fires when a column is moved.
5276          * @param {ColumnModel} this
5277          * @param {Number} oldIndex
5278          * @param {Number} newIndex
5279          */
5280         "columnmoved" : true,
5281         /**
5282          * @event columlockchange
5283          * Fires when a column's locked state is changed
5284          * @param {ColumnModel} this
5285          * @param {Number} colIndex
5286          * @param {Boolean} locked true if locked
5287          */
5288         "columnlockchange" : true
5289     });
5290     Roo.grid.ColumnModel.superclass.constructor.call(this);
5291 };
5292 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5293     /**
5294      * @cfg {String} header The header text to display in the Grid view.
5295      */
5296     /**
5297      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5298      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5299      * specified, the column's index is used as an index into the Record's data Array.
5300      */
5301     /**
5302      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5303      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5304      */
5305     /**
5306      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5307      * Defaults to the value of the {@link #defaultSortable} property.
5308      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5309      */
5310     /**
5311      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5312      */
5313     /**
5314      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5315      */
5316     /**
5317      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5318      */
5319     /**
5320      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5321      */
5322     /**
5323      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5324      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5325      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5326      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5327      */
5328        /**
5329      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5330      */
5331     /**
5332      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5333      */
5334     /**
5335      * @cfg {String} cursor (Optional)
5336      */
5337     /**
5338      * @cfg {String} tooltip (Optional)
5339      */
5340     /**
5341      * @cfg {Number} xs (Optional)
5342      */
5343     /**
5344      * @cfg {Number} sm (Optional)
5345      */
5346     /**
5347      * @cfg {Number} md (Optional)
5348      */
5349     /**
5350      * @cfg {Number} lg (Optional)
5351      */
5352     /**
5353      * Returns the id of the column at the specified index.
5354      * @param {Number} index The column index
5355      * @return {String} the id
5356      */
5357     getColumnId : function(index){
5358         return this.config[index].id;
5359     },
5360
5361     /**
5362      * Returns the column for a specified id.
5363      * @param {String} id The column id
5364      * @return {Object} the column
5365      */
5366     getColumnById : function(id){
5367         return this.lookup[id];
5368     },
5369
5370     
5371     /**
5372      * Returns the column for a specified dataIndex.
5373      * @param {String} dataIndex The column dataIndex
5374      * @return {Object|Boolean} the column or false if not found
5375      */
5376     getColumnByDataIndex: function(dataIndex){
5377         var index = this.findColumnIndex(dataIndex);
5378         return index > -1 ? this.config[index] : false;
5379     },
5380     
5381     /**
5382      * Returns the index for a specified column id.
5383      * @param {String} id The column id
5384      * @return {Number} the index, or -1 if not found
5385      */
5386     getIndexById : function(id){
5387         for(var i = 0, len = this.config.length; i < len; i++){
5388             if(this.config[i].id == id){
5389                 return i;
5390             }
5391         }
5392         return -1;
5393     },
5394     
5395     /**
5396      * Returns the index for a specified column dataIndex.
5397      * @param {String} dataIndex The column dataIndex
5398      * @return {Number} the index, or -1 if not found
5399      */
5400     
5401     findColumnIndex : function(dataIndex){
5402         for(var i = 0, len = this.config.length; i < len; i++){
5403             if(this.config[i].dataIndex == dataIndex){
5404                 return i;
5405             }
5406         }
5407         return -1;
5408     },
5409     
5410     
5411     moveColumn : function(oldIndex, newIndex){
5412         var c = this.config[oldIndex];
5413         this.config.splice(oldIndex, 1);
5414         this.config.splice(newIndex, 0, c);
5415         this.dataMap = null;
5416         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5417     },
5418
5419     isLocked : function(colIndex){
5420         return this.config[colIndex].locked === true;
5421     },
5422
5423     setLocked : function(colIndex, value, suppressEvent){
5424         if(this.isLocked(colIndex) == value){
5425             return;
5426         }
5427         this.config[colIndex].locked = value;
5428         if(!suppressEvent){
5429             this.fireEvent("columnlockchange", this, colIndex, value);
5430         }
5431     },
5432
5433     getTotalLockedWidth : function(){
5434         var totalWidth = 0;
5435         for(var i = 0; i < this.config.length; i++){
5436             if(this.isLocked(i) && !this.isHidden(i)){
5437                 this.totalWidth += this.getColumnWidth(i);
5438             }
5439         }
5440         return totalWidth;
5441     },
5442
5443     getLockedCount : function(){
5444         for(var i = 0, len = this.config.length; i < len; i++){
5445             if(!this.isLocked(i)){
5446                 return i;
5447             }
5448         }
5449         
5450         return this.config.length;
5451     },
5452
5453     /**
5454      * Returns the number of columns.
5455      * @return {Number}
5456      */
5457     getColumnCount : function(visibleOnly){
5458         if(visibleOnly === true){
5459             var c = 0;
5460             for(var i = 0, len = this.config.length; i < len; i++){
5461                 if(!this.isHidden(i)){
5462                     c++;
5463                 }
5464             }
5465             return c;
5466         }
5467         return this.config.length;
5468     },
5469
5470     /**
5471      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5472      * @param {Function} fn
5473      * @param {Object} scope (optional)
5474      * @return {Array} result
5475      */
5476     getColumnsBy : function(fn, scope){
5477         var r = [];
5478         for(var i = 0, len = this.config.length; i < len; i++){
5479             var c = this.config[i];
5480             if(fn.call(scope||this, c, i) === true){
5481                 r[r.length] = c;
5482             }
5483         }
5484         return r;
5485     },
5486
5487     /**
5488      * Returns true if the specified column is sortable.
5489      * @param {Number} col The column index
5490      * @return {Boolean}
5491      */
5492     isSortable : function(col){
5493         if(typeof this.config[col].sortable == "undefined"){
5494             return this.defaultSortable;
5495         }
5496         return this.config[col].sortable;
5497     },
5498
5499     /**
5500      * Returns the rendering (formatting) function defined for the column.
5501      * @param {Number} col The column index.
5502      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5503      */
5504     getRenderer : function(col){
5505         if(!this.config[col].renderer){
5506             return Roo.grid.ColumnModel.defaultRenderer;
5507         }
5508         return this.config[col].renderer;
5509     },
5510
5511     /**
5512      * Sets the rendering (formatting) function for a column.
5513      * @param {Number} col The column index
5514      * @param {Function} fn The function to use to process the cell's raw data
5515      * to return HTML markup for the grid view. The render function is called with
5516      * the following parameters:<ul>
5517      * <li>Data value.</li>
5518      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5519      * <li>css A CSS style string to apply to the table cell.</li>
5520      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5521      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5522      * <li>Row index</li>
5523      * <li>Column index</li>
5524      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5525      */
5526     setRenderer : function(col, fn){
5527         this.config[col].renderer = fn;
5528     },
5529
5530     /**
5531      * Returns the width for the specified column.
5532      * @param {Number} col The column index
5533      * @return {Number}
5534      */
5535     getColumnWidth : function(col){
5536         return this.config[col].width * 1 || this.defaultWidth;
5537     },
5538
5539     /**
5540      * Sets the width for a column.
5541      * @param {Number} col The column index
5542      * @param {Number} width The new width
5543      */
5544     setColumnWidth : function(col, width, suppressEvent){
5545         this.config[col].width = width;
5546         this.totalWidth = null;
5547         if(!suppressEvent){
5548              this.fireEvent("widthchange", this, col, width);
5549         }
5550     },
5551
5552     /**
5553      * Returns the total width of all columns.
5554      * @param {Boolean} includeHidden True to include hidden column widths
5555      * @return {Number}
5556      */
5557     getTotalWidth : function(includeHidden){
5558         if(!this.totalWidth){
5559             this.totalWidth = 0;
5560             for(var i = 0, len = this.config.length; i < len; i++){
5561                 if(includeHidden || !this.isHidden(i)){
5562                     this.totalWidth += this.getColumnWidth(i);
5563                 }
5564             }
5565         }
5566         return this.totalWidth;
5567     },
5568
5569     /**
5570      * Returns the header for the specified column.
5571      * @param {Number} col The column index
5572      * @return {String}
5573      */
5574     getColumnHeader : function(col){
5575         return this.config[col].header;
5576     },
5577
5578     /**
5579      * Sets the header for a column.
5580      * @param {Number} col The column index
5581      * @param {String} header The new header
5582      */
5583     setColumnHeader : function(col, header){
5584         this.config[col].header = header;
5585         this.fireEvent("headerchange", this, col, header);
5586     },
5587
5588     /**
5589      * Returns the tooltip for the specified column.
5590      * @param {Number} col The column index
5591      * @return {String}
5592      */
5593     getColumnTooltip : function(col){
5594             return this.config[col].tooltip;
5595     },
5596     /**
5597      * Sets the tooltip for a column.
5598      * @param {Number} col The column index
5599      * @param {String} tooltip The new tooltip
5600      */
5601     setColumnTooltip : function(col, tooltip){
5602             this.config[col].tooltip = tooltip;
5603     },
5604
5605     /**
5606      * Returns the dataIndex for the specified column.
5607      * @param {Number} col The column index
5608      * @return {Number}
5609      */
5610     getDataIndex : function(col){
5611         return this.config[col].dataIndex;
5612     },
5613
5614     /**
5615      * Sets the dataIndex for a column.
5616      * @param {Number} col The column index
5617      * @param {Number} dataIndex The new dataIndex
5618      */
5619     setDataIndex : function(col, dataIndex){
5620         this.config[col].dataIndex = dataIndex;
5621     },
5622
5623     
5624     
5625     /**
5626      * Returns true if the cell is editable.
5627      * @param {Number} colIndex The column index
5628      * @param {Number} rowIndex The row index - this is nto actually used..?
5629      * @return {Boolean}
5630      */
5631     isCellEditable : function(colIndex, rowIndex){
5632         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5633     },
5634
5635     /**
5636      * Returns the editor defined for the cell/column.
5637      * return false or null to disable editing.
5638      * @param {Number} colIndex The column index
5639      * @param {Number} rowIndex The row index
5640      * @return {Object}
5641      */
5642     getCellEditor : function(colIndex, rowIndex){
5643         return this.config[colIndex].editor;
5644     },
5645
5646     /**
5647      * Sets if a column is editable.
5648      * @param {Number} col The column index
5649      * @param {Boolean} editable True if the column is editable
5650      */
5651     setEditable : function(col, editable){
5652         this.config[col].editable = editable;
5653     },
5654
5655
5656     /**
5657      * Returns true if the column is hidden.
5658      * @param {Number} colIndex The column index
5659      * @return {Boolean}
5660      */
5661     isHidden : function(colIndex){
5662         return this.config[colIndex].hidden;
5663     },
5664
5665
5666     /**
5667      * Returns true if the column width cannot be changed
5668      */
5669     isFixed : function(colIndex){
5670         return this.config[colIndex].fixed;
5671     },
5672
5673     /**
5674      * Returns true if the column can be resized
5675      * @return {Boolean}
5676      */
5677     isResizable : function(colIndex){
5678         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5679     },
5680     /**
5681      * Sets if a column is hidden.
5682      * @param {Number} colIndex The column index
5683      * @param {Boolean} hidden True if the column is hidden
5684      */
5685     setHidden : function(colIndex, hidden){
5686         this.config[colIndex].hidden = hidden;
5687         this.totalWidth = null;
5688         this.fireEvent("hiddenchange", this, colIndex, hidden);
5689     },
5690
5691     /**
5692      * Sets the editor for a column.
5693      * @param {Number} col The column index
5694      * @param {Object} editor The editor object
5695      */
5696     setEditor : function(col, editor){
5697         this.config[col].editor = editor;
5698     }
5699 });
5700
5701 Roo.grid.ColumnModel.defaultRenderer = function(value)
5702 {
5703     if(typeof value == "object") {
5704         return value;
5705     }
5706         if(typeof value == "string" && value.length < 1){
5707             return "&#160;";
5708         }
5709     
5710         return String.format("{0}", value);
5711 };
5712
5713 // Alias for backwards compatibility
5714 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725  
5726 /**
5727  * @class Roo.LoadMask
5728  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5729  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5730  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5731  * element's UpdateManager load indicator and will be destroyed after the initial load.
5732  * @constructor
5733  * Create a new LoadMask
5734  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5735  * @param {Object} config The config object
5736  */
5737 Roo.LoadMask = function(el, config){
5738     this.el = Roo.get(el);
5739     Roo.apply(this, config);
5740     if(this.store){
5741         this.store.on('beforeload', this.onBeforeLoad, this);
5742         this.store.on('load', this.onLoad, this);
5743         this.store.on('loadexception', this.onLoadException, this);
5744         this.removeMask = false;
5745     }else{
5746         var um = this.el.getUpdateManager();
5747         um.showLoadIndicator = false; // disable the default indicator
5748         um.on('beforeupdate', this.onBeforeLoad, this);
5749         um.on('update', this.onLoad, this);
5750         um.on('failure', this.onLoad, this);
5751         this.removeMask = true;
5752     }
5753 };
5754
5755 Roo.LoadMask.prototype = {
5756     /**
5757      * @cfg {Boolean} removeMask
5758      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5759      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5760      */
5761     /**
5762      * @cfg {String} msg
5763      * The text to display in a centered loading message box (defaults to 'Loading...')
5764      */
5765     msg : 'Loading...',
5766     /**
5767      * @cfg {String} msgCls
5768      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5769      */
5770     msgCls : 'x-mask-loading',
5771
5772     /**
5773      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5774      * @type Boolean
5775      */
5776     disabled: false,
5777
5778     /**
5779      * Disables the mask to prevent it from being displayed
5780      */
5781     disable : function(){
5782        this.disabled = true;
5783     },
5784
5785     /**
5786      * Enables the mask so that it can be displayed
5787      */
5788     enable : function(){
5789         this.disabled = false;
5790     },
5791     
5792     onLoadException : function()
5793     {
5794         Roo.log(arguments);
5795         
5796         if (typeof(arguments[3]) != 'undefined') {
5797             Roo.MessageBox.alert("Error loading",arguments[3]);
5798         } 
5799         /*
5800         try {
5801             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5802                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5803             }   
5804         } catch(e) {
5805             
5806         }
5807         */
5808     
5809         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5810     },
5811     // private
5812     onLoad : function()
5813     {
5814         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5815     },
5816
5817     // private
5818     onBeforeLoad : function(){
5819         if(!this.disabled){
5820             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5821         }
5822     },
5823
5824     // private
5825     destroy : function(){
5826         if(this.store){
5827             this.store.un('beforeload', this.onBeforeLoad, this);
5828             this.store.un('load', this.onLoad, this);
5829             this.store.un('loadexception', this.onLoadException, this);
5830         }else{
5831             var um = this.el.getUpdateManager();
5832             um.un('beforeupdate', this.onBeforeLoad, this);
5833             um.un('update', this.onLoad, this);
5834             um.un('failure', this.onLoad, this);
5835         }
5836     }
5837 };/*
5838  * - LGPL
5839  *
5840  * table
5841  * 
5842  */
5843
5844 /**
5845  * @class Roo.bootstrap.Table
5846  * @extends Roo.bootstrap.Component
5847  * Bootstrap Table class
5848  * @cfg {String} cls table class
5849  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5850  * @cfg {String} bgcolor Specifies the background color for a table
5851  * @cfg {Number} border Specifies whether the table cells should have borders or not
5852  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5853  * @cfg {Number} cellspacing Specifies the space between cells
5854  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5855  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5856  * @cfg {String} sortable Specifies that the table should be sortable
5857  * @cfg {String} summary Specifies a summary of the content of a table
5858  * @cfg {Number} width Specifies the width of a table
5859  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5860  * 
5861  * @cfg {boolean} striped Should the rows be alternative striped
5862  * @cfg {boolean} bordered Add borders to the table
5863  * @cfg {boolean} hover Add hover highlighting
5864  * @cfg {boolean} condensed Format condensed
5865  * @cfg {boolean} responsive Format condensed
5866  * @cfg {Boolean} loadMask (true|false) default false
5867  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5868  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5869  * @cfg {Boolean} rowSelection (true|false) default false
5870  * @cfg {Boolean} cellSelection (true|false) default false
5871  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5872  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5873  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5874  
5875  * 
5876  * @constructor
5877  * Create a new Table
5878  * @param {Object} config The config object
5879  */
5880
5881 Roo.bootstrap.Table = function(config){
5882     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5883     
5884   
5885     
5886     // BC...
5887     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5888     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5889     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5890     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5891     
5892     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5893     if (this.sm) {
5894         this.sm.grid = this;
5895         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5896         this.sm = this.selModel;
5897         this.sm.xmodule = this.xmodule || false;
5898     }
5899     
5900     if (this.cm && typeof(this.cm.config) == 'undefined') {
5901         this.colModel = new Roo.grid.ColumnModel(this.cm);
5902         this.cm = this.colModel;
5903         this.cm.xmodule = this.xmodule || false;
5904     }
5905     if (this.store) {
5906         this.store= Roo.factory(this.store, Roo.data);
5907         this.ds = this.store;
5908         this.ds.xmodule = this.xmodule || false;
5909          
5910     }
5911     if (this.footer && this.store) {
5912         this.footer.dataSource = this.ds;
5913         this.footer = Roo.factory(this.footer);
5914     }
5915     
5916     /** @private */
5917     this.addEvents({
5918         /**
5919          * @event cellclick
5920          * Fires when a cell is clicked
5921          * @param {Roo.bootstrap.Table} this
5922          * @param {Roo.Element} el
5923          * @param {Number} rowIndex
5924          * @param {Number} columnIndex
5925          * @param {Roo.EventObject} e
5926          */
5927         "cellclick" : true,
5928         /**
5929          * @event celldblclick
5930          * Fires when a cell is double clicked
5931          * @param {Roo.bootstrap.Table} this
5932          * @param {Roo.Element} el
5933          * @param {Number} rowIndex
5934          * @param {Number} columnIndex
5935          * @param {Roo.EventObject} e
5936          */
5937         "celldblclick" : true,
5938         /**
5939          * @event rowclick
5940          * Fires when a row is clicked
5941          * @param {Roo.bootstrap.Table} this
5942          * @param {Roo.Element} el
5943          * @param {Number} rowIndex
5944          * @param {Roo.EventObject} e
5945          */
5946         "rowclick" : true,
5947         /**
5948          * @event rowdblclick
5949          * Fires when a row is double clicked
5950          * @param {Roo.bootstrap.Table} this
5951          * @param {Roo.Element} el
5952          * @param {Number} rowIndex
5953          * @param {Roo.EventObject} e
5954          */
5955         "rowdblclick" : true,
5956         /**
5957          * @event mouseover
5958          * Fires when a mouseover occur
5959          * @param {Roo.bootstrap.Table} this
5960          * @param {Roo.Element} el
5961          * @param {Number} rowIndex
5962          * @param {Number} columnIndex
5963          * @param {Roo.EventObject} e
5964          */
5965         "mouseover" : true,
5966         /**
5967          * @event mouseout
5968          * Fires when a mouseout occur
5969          * @param {Roo.bootstrap.Table} this
5970          * @param {Roo.Element} el
5971          * @param {Number} rowIndex
5972          * @param {Number} columnIndex
5973          * @param {Roo.EventObject} e
5974          */
5975         "mouseout" : true,
5976         /**
5977          * @event rowclass
5978          * Fires when a row is rendered, so you can change add a style to it.
5979          * @param {Roo.bootstrap.Table} this
5980          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5981          */
5982         'rowclass' : true,
5983           /**
5984          * @event rowsrendered
5985          * Fires when all the  rows have been rendered
5986          * @param {Roo.bootstrap.Table} this
5987          */
5988         'rowsrendered' : true,
5989         /**
5990          * @event contextmenu
5991          * The raw contextmenu event for the entire grid.
5992          * @param {Roo.EventObject} e
5993          */
5994         "contextmenu" : true,
5995         /**
5996          * @event rowcontextmenu
5997          * Fires when a row is right clicked
5998          * @param {Roo.bootstrap.Table} this
5999          * @param {Number} rowIndex
6000          * @param {Roo.EventObject} e
6001          */
6002         "rowcontextmenu" : true,
6003         /**
6004          * @event cellcontextmenu
6005          * Fires when a cell is right clicked
6006          * @param {Roo.bootstrap.Table} this
6007          * @param {Number} rowIndex
6008          * @param {Number} cellIndex
6009          * @param {Roo.EventObject} e
6010          */
6011          "cellcontextmenu" : true,
6012          /**
6013          * @event headercontextmenu
6014          * Fires when a header is right clicked
6015          * @param {Roo.bootstrap.Table} this
6016          * @param {Number} columnIndex
6017          * @param {Roo.EventObject} e
6018          */
6019         "headercontextmenu" : true
6020     });
6021 };
6022
6023 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6024     
6025     cls: false,
6026     align: false,
6027     bgcolor: false,
6028     border: false,
6029     cellpadding: false,
6030     cellspacing: false,
6031     frame: false,
6032     rules: false,
6033     sortable: false,
6034     summary: false,
6035     width: false,
6036     striped : false,
6037     scrollBody : false,
6038     bordered: false,
6039     hover:  false,
6040     condensed : false,
6041     responsive : false,
6042     sm : false,
6043     cm : false,
6044     store : false,
6045     loadMask : false,
6046     footerShow : true,
6047     headerShow : true,
6048   
6049     rowSelection : false,
6050     cellSelection : false,
6051     layout : false,
6052     
6053     // Roo.Element - the tbody
6054     mainBody: false,
6055     // Roo.Element - thead element
6056     mainHead: false,
6057     
6058     container: false, // used by gridpanel...
6059     
6060     lazyLoad : false,
6061     
6062     CSS : Roo.util.CSS,
6063     
6064     getAutoCreate : function()
6065     {
6066         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6067         
6068         cfg = {
6069             tag: 'table',
6070             cls : 'table',
6071             cn : []
6072         };
6073         if (this.scrollBody) {
6074             cfg.cls += ' table-body-fixed';
6075         }    
6076         if (this.striped) {
6077             cfg.cls += ' table-striped';
6078         }
6079         
6080         if (this.hover) {
6081             cfg.cls += ' table-hover';
6082         }
6083         if (this.bordered) {
6084             cfg.cls += ' table-bordered';
6085         }
6086         if (this.condensed) {
6087             cfg.cls += ' table-condensed';
6088         }
6089         if (this.responsive) {
6090             cfg.cls += ' table-responsive';
6091         }
6092         
6093         if (this.cls) {
6094             cfg.cls+=  ' ' +this.cls;
6095         }
6096         
6097         // this lot should be simplifed...
6098         
6099         if (this.align) {
6100             cfg.align=this.align;
6101         }
6102         if (this.bgcolor) {
6103             cfg.bgcolor=this.bgcolor;
6104         }
6105         if (this.border) {
6106             cfg.border=this.border;
6107         }
6108         if (this.cellpadding) {
6109             cfg.cellpadding=this.cellpadding;
6110         }
6111         if (this.cellspacing) {
6112             cfg.cellspacing=this.cellspacing;
6113         }
6114         if (this.frame) {
6115             cfg.frame=this.frame;
6116         }
6117         if (this.rules) {
6118             cfg.rules=this.rules;
6119         }
6120         if (this.sortable) {
6121             cfg.sortable=this.sortable;
6122         }
6123         if (this.summary) {
6124             cfg.summary=this.summary;
6125         }
6126         if (this.width) {
6127             cfg.width=this.width;
6128         }
6129         if (this.layout) {
6130             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6131         }
6132         
6133         if(this.store || this.cm){
6134             if(this.headerShow){
6135                 cfg.cn.push(this.renderHeader());
6136             }
6137             
6138             cfg.cn.push(this.renderBody());
6139             
6140             if(this.footerShow){
6141                 cfg.cn.push(this.renderFooter());
6142             }
6143             // where does this come from?
6144             //cfg.cls+=  ' TableGrid';
6145         }
6146         
6147         return { cn : [ cfg ] };
6148     },
6149     
6150     initEvents : function()
6151     {   
6152         if(!this.store || !this.cm){
6153             return;
6154         }
6155         if (this.selModel) {
6156             this.selModel.initEvents();
6157         }
6158         
6159         
6160         //Roo.log('initEvents with ds!!!!');
6161         
6162         this.mainBody = this.el.select('tbody', true).first();
6163         this.mainHead = this.el.select('thead', true).first();
6164         
6165         
6166         
6167         
6168         var _this = this;
6169         
6170         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6171             e.on('click', _this.sort, _this);
6172         });
6173         
6174         this.mainBody.on("click", this.onClick, this);
6175         this.mainBody.on("dblclick", this.onDblClick, this);
6176         
6177         // why is this done????? = it breaks dialogs??
6178         //this.parent().el.setStyle('position', 'relative');
6179         
6180         
6181         if (this.footer) {
6182             this.footer.parentId = this.id;
6183             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6184             
6185             if(this.lazyLoad){
6186                 this.el.select('tfoot tr td').first().addClass('hide');
6187             }
6188         } 
6189         
6190         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6191         
6192         this.store.on('load', this.onLoad, this);
6193         this.store.on('beforeload', this.onBeforeLoad, this);
6194         this.store.on('update', this.onUpdate, this);
6195         this.store.on('add', this.onAdd, this);
6196         this.store.on("clear", this.clear, this);
6197         
6198         this.el.on("contextmenu", this.onContextMenu, this);
6199         
6200         this.mainBody.on('scroll', this.onBodyScroll, this);
6201         
6202         this.cm.on("headerchange", this.onHeaderChange, this);
6203         
6204         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6205         
6206     },
6207     
6208     onContextMenu : function(e, t)
6209     {
6210         this.processEvent("contextmenu", e);
6211     },
6212     
6213     processEvent : function(name, e)
6214     {
6215         if (name != 'touchstart' ) {
6216             this.fireEvent(name, e);    
6217         }
6218         
6219         var t = e.getTarget();
6220         
6221         var cell = Roo.get(t);
6222         
6223         if(!cell){
6224             return;
6225         }
6226         
6227         if(cell.findParent('tfoot', false, true)){
6228             return;
6229         }
6230         
6231         if(cell.findParent('thead', false, true)){
6232             
6233             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6234                 cell = Roo.get(t).findParent('th', false, true);
6235                 if (!cell) {
6236                     Roo.log("failed to find th in thead?");
6237                     Roo.log(e.getTarget());
6238                     return;
6239                 }
6240             }
6241             
6242             var cellIndex = cell.dom.cellIndex;
6243             
6244             var ename = name == 'touchstart' ? 'click' : name;
6245             this.fireEvent("header" + ename, this, cellIndex, e);
6246             
6247             return;
6248         }
6249         
6250         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6251             cell = Roo.get(t).findParent('td', false, true);
6252             if (!cell) {
6253                 Roo.log("failed to find th in tbody?");
6254                 Roo.log(e.getTarget());
6255                 return;
6256             }
6257         }
6258         
6259         var row = cell.findParent('tr', false, true);
6260         var cellIndex = cell.dom.cellIndex;
6261         var rowIndex = row.dom.rowIndex - 1;
6262         
6263         if(row !== false){
6264             
6265             this.fireEvent("row" + name, this, rowIndex, e);
6266             
6267             if(cell !== false){
6268             
6269                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6270             }
6271         }
6272         
6273     },
6274     
6275     onMouseover : function(e, el)
6276     {
6277         var cell = Roo.get(el);
6278         
6279         if(!cell){
6280             return;
6281         }
6282         
6283         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6284             cell = cell.findParent('td', false, true);
6285         }
6286         
6287         var row = cell.findParent('tr', false, true);
6288         var cellIndex = cell.dom.cellIndex;
6289         var rowIndex = row.dom.rowIndex - 1; // start from 0
6290         
6291         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6292         
6293     },
6294     
6295     onMouseout : function(e, el)
6296     {
6297         var cell = Roo.get(el);
6298         
6299         if(!cell){
6300             return;
6301         }
6302         
6303         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6304             cell = cell.findParent('td', false, true);
6305         }
6306         
6307         var row = cell.findParent('tr', false, true);
6308         var cellIndex = cell.dom.cellIndex;
6309         var rowIndex = row.dom.rowIndex - 1; // start from 0
6310         
6311         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6312         
6313     },
6314     
6315     onClick : function(e, el)
6316     {
6317         var cell = Roo.get(el);
6318         
6319         if(!cell || (!this.cellSelection && !this.rowSelection)){
6320             return;
6321         }
6322         
6323         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6324             cell = cell.findParent('td', false, true);
6325         }
6326         
6327         if(!cell || typeof(cell) == 'undefined'){
6328             return;
6329         }
6330         
6331         var row = cell.findParent('tr', false, true);
6332         
6333         if(!row || typeof(row) == 'undefined'){
6334             return;
6335         }
6336         
6337         var cellIndex = cell.dom.cellIndex;
6338         var rowIndex = this.getRowIndex(row);
6339         
6340         // why??? - should these not be based on SelectionModel?
6341         if(this.cellSelection){
6342             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6343         }
6344         
6345         if(this.rowSelection){
6346             this.fireEvent('rowclick', this, row, rowIndex, e);
6347         }
6348         
6349         
6350     },
6351         
6352     onDblClick : function(e,el)
6353     {
6354         var cell = Roo.get(el);
6355         
6356         if(!cell || (!this.cellSelection && !this.rowSelection)){
6357             return;
6358         }
6359         
6360         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6361             cell = cell.findParent('td', false, true);
6362         }
6363         
6364         if(!cell || typeof(cell) == 'undefined'){
6365             return;
6366         }
6367         
6368         var row = cell.findParent('tr', false, true);
6369         
6370         if(!row || typeof(row) == 'undefined'){
6371             return;
6372         }
6373         
6374         var cellIndex = cell.dom.cellIndex;
6375         var rowIndex = this.getRowIndex(row);
6376         
6377         if(this.cellSelection){
6378             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6379         }
6380         
6381         if(this.rowSelection){
6382             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6383         }
6384     },
6385     
6386     sort : function(e,el)
6387     {
6388         var col = Roo.get(el);
6389         
6390         if(!col.hasClass('sortable')){
6391             return;
6392         }
6393         
6394         var sort = col.attr('sort');
6395         var dir = 'ASC';
6396         
6397         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6398             dir = 'DESC';
6399         }
6400         
6401         this.store.sortInfo = {field : sort, direction : dir};
6402         
6403         if (this.footer) {
6404             Roo.log("calling footer first");
6405             this.footer.onClick('first');
6406         } else {
6407         
6408             this.store.load({ params : { start : 0 } });
6409         }
6410     },
6411     
6412     renderHeader : function()
6413     {
6414         var header = {
6415             tag: 'thead',
6416             cn : []
6417         };
6418         
6419         var cm = this.cm;
6420         this.totalWidth = 0;
6421         
6422         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6423             
6424             var config = cm.config[i];
6425             
6426             var c = {
6427                 tag: 'th',
6428                 cls : 'x-hcol-' + i,
6429                 style : '',
6430                 html: cm.getColumnHeader(i)
6431             };
6432             
6433             var hh = '';
6434             
6435             if(typeof(config.sortable) != 'undefined' && config.sortable){
6436                 c.cls = 'sortable';
6437                 c.html = '<i class="glyphicon"></i>' + c.html;
6438             }
6439             
6440             if(typeof(config.lgHeader) != 'undefined'){
6441                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6442             }
6443             
6444             if(typeof(config.mdHeader) != 'undefined'){
6445                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6446             }
6447             
6448             if(typeof(config.smHeader) != 'undefined'){
6449                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6450             }
6451             
6452             if(typeof(config.xsHeader) != 'undefined'){
6453                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6454             }
6455             
6456             if(hh.length){
6457                 c.html = hh;
6458             }
6459             
6460             if(typeof(config.tooltip) != 'undefined'){
6461                 c.tooltip = config.tooltip;
6462             }
6463             
6464             if(typeof(config.colspan) != 'undefined'){
6465                 c.colspan = config.colspan;
6466             }
6467             
6468             if(typeof(config.hidden) != 'undefined' && config.hidden){
6469                 c.style += ' display:none;';
6470             }
6471             
6472             if(typeof(config.dataIndex) != 'undefined'){
6473                 c.sort = config.dataIndex;
6474             }
6475             
6476            
6477             
6478             if(typeof(config.align) != 'undefined' && config.align.length){
6479                 c.style += ' text-align:' + config.align + ';';
6480             }
6481             
6482             if(typeof(config.width) != 'undefined'){
6483                 c.style += ' width:' + config.width + 'px;';
6484                 this.totalWidth += config.width;
6485             } else {
6486                 this.totalWidth += 100; // assume minimum of 100 per column?
6487             }
6488             
6489             if(typeof(config.cls) != 'undefined'){
6490                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6491             }
6492             
6493             ['xs','sm','md','lg'].map(function(size){
6494                 
6495                 if(typeof(config[size]) == 'undefined'){
6496                     return;
6497                 }
6498                 
6499                 if (!config[size]) { // 0 = hidden
6500                     c.cls += ' hidden-' + size;
6501                     return;
6502                 }
6503                 
6504                 c.cls += ' col-' + size + '-' + config[size];
6505
6506             });
6507             
6508             header.cn.push(c)
6509         }
6510         
6511         return header;
6512     },
6513     
6514     renderBody : function()
6515     {
6516         var body = {
6517             tag: 'tbody',
6518             cn : [
6519                 {
6520                     tag: 'tr',
6521                     cn : [
6522                         {
6523                             tag : 'td',
6524                             colspan :  this.cm.getColumnCount()
6525                         }
6526                     ]
6527                 }
6528             ]
6529         };
6530         
6531         return body;
6532     },
6533     
6534     renderFooter : function()
6535     {
6536         var footer = {
6537             tag: 'tfoot',
6538             cn : [
6539                 {
6540                     tag: 'tr',
6541                     cn : [
6542                         {
6543                             tag : 'td',
6544                             colspan :  this.cm.getColumnCount()
6545                         }
6546                     ]
6547                 }
6548             ]
6549         };
6550         
6551         return footer;
6552     },
6553     
6554     
6555     
6556     onLoad : function()
6557     {
6558 //        Roo.log('ds onload');
6559         this.clear();
6560         
6561         var _this = this;
6562         var cm = this.cm;
6563         var ds = this.store;
6564         
6565         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6566             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6567             if (_this.store.sortInfo) {
6568                     
6569                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6570                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6571                 }
6572                 
6573                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6574                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6575                 }
6576             }
6577         });
6578         
6579         var tbody =  this.mainBody;
6580               
6581         if(ds.getCount() > 0){
6582             ds.data.each(function(d,rowIndex){
6583                 var row =  this.renderRow(cm, ds, rowIndex);
6584                 
6585                 tbody.createChild(row);
6586                 
6587                 var _this = this;
6588                 
6589                 if(row.cellObjects.length){
6590                     Roo.each(row.cellObjects, function(r){
6591                         _this.renderCellObject(r);
6592                     })
6593                 }
6594                 
6595             }, this);
6596         }
6597         
6598         Roo.each(this.el.select('tbody td', true).elements, function(e){
6599             e.on('mouseover', _this.onMouseover, _this);
6600         });
6601         
6602         Roo.each(this.el.select('tbody td', true).elements, function(e){
6603             e.on('mouseout', _this.onMouseout, _this);
6604         });
6605         this.fireEvent('rowsrendered', this);
6606         //if(this.loadMask){
6607         //    this.maskEl.hide();
6608         //}
6609         
6610         this.autoSize();
6611     },
6612     
6613     
6614     onUpdate : function(ds,record)
6615     {
6616         this.refreshRow(record);
6617         this.autoSize();
6618     },
6619     
6620     onRemove : function(ds, record, index, isUpdate){
6621         if(isUpdate !== true){
6622             this.fireEvent("beforerowremoved", this, index, record);
6623         }
6624         var bt = this.mainBody.dom;
6625         
6626         var rows = this.el.select('tbody > tr', true).elements;
6627         
6628         if(typeof(rows[index]) != 'undefined'){
6629             bt.removeChild(rows[index].dom);
6630         }
6631         
6632 //        if(bt.rows[index]){
6633 //            bt.removeChild(bt.rows[index]);
6634 //        }
6635         
6636         if(isUpdate !== true){
6637             //this.stripeRows(index);
6638             //this.syncRowHeights(index, index);
6639             //this.layout();
6640             this.fireEvent("rowremoved", this, index, record);
6641         }
6642     },
6643     
6644     onAdd : function(ds, records, rowIndex)
6645     {
6646         //Roo.log('on Add called');
6647         // - note this does not handle multiple adding very well..
6648         var bt = this.mainBody.dom;
6649         for (var i =0 ; i < records.length;i++) {
6650             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6651             //Roo.log(records[i]);
6652             //Roo.log(this.store.getAt(rowIndex+i));
6653             this.insertRow(this.store, rowIndex + i, false);
6654             return;
6655         }
6656         
6657     },
6658     
6659     
6660     refreshRow : function(record){
6661         var ds = this.store, index;
6662         if(typeof record == 'number'){
6663             index = record;
6664             record = ds.getAt(index);
6665         }else{
6666             index = ds.indexOf(record);
6667         }
6668         this.insertRow(ds, index, true);
6669         this.autoSize();
6670         this.onRemove(ds, record, index+1, true);
6671         this.autoSize();
6672         //this.syncRowHeights(index, index);
6673         //this.layout();
6674         this.fireEvent("rowupdated", this, index, record);
6675     },
6676     
6677     insertRow : function(dm, rowIndex, isUpdate){
6678         
6679         if(!isUpdate){
6680             this.fireEvent("beforerowsinserted", this, rowIndex);
6681         }
6682             //var s = this.getScrollState();
6683         var row = this.renderRow(this.cm, this.store, rowIndex);
6684         // insert before rowIndex..
6685         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6686         
6687         var _this = this;
6688                 
6689         if(row.cellObjects.length){
6690             Roo.each(row.cellObjects, function(r){
6691                 _this.renderCellObject(r);
6692             })
6693         }
6694             
6695         if(!isUpdate){
6696             this.fireEvent("rowsinserted", this, rowIndex);
6697             //this.syncRowHeights(firstRow, lastRow);
6698             //this.stripeRows(firstRow);
6699             //this.layout();
6700         }
6701         
6702     },
6703     
6704     
6705     getRowDom : function(rowIndex)
6706     {
6707         var rows = this.el.select('tbody > tr', true).elements;
6708         
6709         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6710         
6711     },
6712     // returns the object tree for a tr..
6713   
6714     
6715     renderRow : function(cm, ds, rowIndex) 
6716     {
6717         var d = ds.getAt(rowIndex);
6718         
6719         var row = {
6720             tag : 'tr',
6721             cls : 'x-row-' + rowIndex,
6722             cn : []
6723         };
6724             
6725         var cellObjects = [];
6726         
6727         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6728             var config = cm.config[i];
6729             
6730             var renderer = cm.getRenderer(i);
6731             var value = '';
6732             var id = false;
6733             
6734             if(typeof(renderer) !== 'undefined'){
6735                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6736             }
6737             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6738             // and are rendered into the cells after the row is rendered - using the id for the element.
6739             
6740             if(typeof(value) === 'object'){
6741                 id = Roo.id();
6742                 cellObjects.push({
6743                     container : id,
6744                     cfg : value 
6745                 })
6746             }
6747             
6748             var rowcfg = {
6749                 record: d,
6750                 rowIndex : rowIndex,
6751                 colIndex : i,
6752                 rowClass : ''
6753             };
6754
6755             this.fireEvent('rowclass', this, rowcfg);
6756             
6757             var td = {
6758                 tag: 'td',
6759                 cls : rowcfg.rowClass + ' x-col-' + i,
6760                 style: '',
6761                 html: (typeof(value) === 'object') ? '' : value
6762             };
6763             
6764             if (id) {
6765                 td.id = id;
6766             }
6767             
6768             if(typeof(config.colspan) != 'undefined'){
6769                 td.colspan = config.colspan;
6770             }
6771             
6772             if(typeof(config.hidden) != 'undefined' && config.hidden){
6773                 td.style += ' display:none;';
6774             }
6775             
6776             if(typeof(config.align) != 'undefined' && config.align.length){
6777                 td.style += ' text-align:' + config.align + ';';
6778             }
6779             
6780             if(typeof(config.width) != 'undefined'){
6781                 td.style += ' width:' +  config.width + 'px;';
6782             }
6783             
6784             if(typeof(config.cursor) != 'undefined'){
6785                 td.style += ' cursor:' +  config.cursor + ';';
6786             }
6787             
6788             if(typeof(config.cls) != 'undefined'){
6789                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6790             }
6791             
6792             ['xs','sm','md','lg'].map(function(size){
6793                 
6794                 if(typeof(config[size]) == 'undefined'){
6795                     return;
6796                 }
6797                 
6798                 if (!config[size]) { // 0 = hidden
6799                     td.cls += ' hidden-' + size;
6800                     return;
6801                 }
6802                 
6803                 td.cls += ' col-' + size + '-' + config[size];
6804
6805             });
6806             
6807             row.cn.push(td);
6808            
6809         }
6810         
6811         row.cellObjects = cellObjects;
6812         
6813         return row;
6814           
6815     },
6816     
6817     
6818     
6819     onBeforeLoad : function()
6820     {
6821         //Roo.log('ds onBeforeLoad');
6822         
6823         //this.clear();
6824         
6825         //if(this.loadMask){
6826         //    this.maskEl.show();
6827         //}
6828     },
6829      /**
6830      * Remove all rows
6831      */
6832     clear : function()
6833     {
6834         this.el.select('tbody', true).first().dom.innerHTML = '';
6835     },
6836     /**
6837      * Show or hide a row.
6838      * @param {Number} rowIndex to show or hide
6839      * @param {Boolean} state hide
6840      */
6841     setRowVisibility : function(rowIndex, state)
6842     {
6843         var bt = this.mainBody.dom;
6844         
6845         var rows = this.el.select('tbody > tr', true).elements;
6846         
6847         if(typeof(rows[rowIndex]) == 'undefined'){
6848             return;
6849         }
6850         rows[rowIndex].dom.style.display = state ? '' : 'none';
6851     },
6852     
6853     
6854     getSelectionModel : function(){
6855         if(!this.selModel){
6856             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6857         }
6858         return this.selModel;
6859     },
6860     /*
6861      * Render the Roo.bootstrap object from renderder
6862      */
6863     renderCellObject : function(r)
6864     {
6865         var _this = this;
6866         
6867         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6868         
6869         var t = r.cfg.render(r.container);
6870         
6871         if(r.cfg.cn){
6872             Roo.each(r.cfg.cn, function(c){
6873                 var child = {
6874                     container: t.getChildContainer(),
6875                     cfg: c
6876                 };
6877                 _this.renderCellObject(child);
6878             })
6879         }
6880     },
6881     
6882     getRowIndex : function(row)
6883     {
6884         var rowIndex = -1;
6885         
6886         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6887             if(el != row){
6888                 return;
6889             }
6890             
6891             rowIndex = index;
6892         });
6893         
6894         return rowIndex;
6895     },
6896      /**
6897      * Returns the grid's underlying element = used by panel.Grid
6898      * @return {Element} The element
6899      */
6900     getGridEl : function(){
6901         return this.el;
6902     },
6903      /**
6904      * Forces a resize - used by panel.Grid
6905      * @return {Element} The element
6906      */
6907     autoSize : function()
6908     {
6909         //var ctr = Roo.get(this.container.dom.parentElement);
6910         var ctr = Roo.get(this.el.dom);
6911         
6912         var thd = this.getGridEl().select('thead',true).first();
6913         var tbd = this.getGridEl().select('tbody', true).first();
6914         var tfd = this.getGridEl().select('tfoot', true).first();
6915         
6916         var cw = ctr.getWidth();
6917         
6918         if (tbd) {
6919             
6920             tbd.setSize(ctr.getWidth(),
6921                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6922             );
6923             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6924             cw -= barsize;
6925         }
6926         cw = Math.max(cw, this.totalWidth);
6927         this.getGridEl().select('tr',true).setWidth(cw);
6928         // resize 'expandable coloumn?
6929         
6930         return; // we doe not have a view in this design..
6931         
6932     },
6933     onBodyScroll: function()
6934     {
6935         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6936         if(this.mainHead){
6937             this.mainHead.setStyle({
6938                 'position' : 'relative',
6939                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6940             });
6941         }
6942         
6943         if(this.lazyLoad){
6944             
6945             var scrollHeight = this.mainBody.dom.scrollHeight;
6946             
6947             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6948             
6949             var height = this.mainBody.getHeight();
6950             
6951             if(scrollHeight - height == scrollTop) {
6952                 
6953                 var total = this.ds.getTotalCount();
6954                 
6955                 if(this.footer.cursor + this.footer.pageSize < total){
6956                     
6957                     this.footer.ds.load({
6958                         params : {
6959                             start : this.footer.cursor + this.footer.pageSize,
6960                             limit : this.footer.pageSize
6961                         },
6962                         add : true
6963                     });
6964                 }
6965             }
6966             
6967         }
6968     },
6969     
6970     onHeaderChange : function()
6971     {
6972         var header = this.renderHeader();
6973         var table = this.el.select('table', true).first();
6974         
6975         this.mainHead.remove();
6976         this.mainHead = table.createChild(header, this.mainBody, false);
6977     },
6978     
6979     onHiddenChange : function(colModel, colIndex, hidden)
6980     {
6981         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6982         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6983         
6984         this.CSS.updateRule(thSelector, "display", "");
6985         this.CSS.updateRule(tdSelector, "display", "");
6986         
6987         if(hidden){
6988             this.CSS.updateRule(thSelector, "display", "none");
6989             this.CSS.updateRule(tdSelector, "display", "none");
6990         }
6991         
6992         this.onHeaderChange();
6993         this.onLoad();
6994         
6995     }
6996     
6997 });
6998
6999  
7000
7001  /*
7002  * - LGPL
7003  *
7004  * table cell
7005  * 
7006  */
7007
7008 /**
7009  * @class Roo.bootstrap.TableCell
7010  * @extends Roo.bootstrap.Component
7011  * Bootstrap TableCell class
7012  * @cfg {String} html cell contain text
7013  * @cfg {String} cls cell class
7014  * @cfg {String} tag cell tag (td|th) default td
7015  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7016  * @cfg {String} align Aligns the content in a cell
7017  * @cfg {String} axis Categorizes cells
7018  * @cfg {String} bgcolor Specifies the background color of a cell
7019  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7020  * @cfg {Number} colspan Specifies the number of columns a cell should span
7021  * @cfg {String} headers Specifies one or more header cells a cell is related to
7022  * @cfg {Number} height Sets the height of a cell
7023  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7024  * @cfg {Number} rowspan Sets the number of rows a cell should span
7025  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7026  * @cfg {String} valign Vertical aligns the content in a cell
7027  * @cfg {Number} width Specifies the width of a cell
7028  * 
7029  * @constructor
7030  * Create a new TableCell
7031  * @param {Object} config The config object
7032  */
7033
7034 Roo.bootstrap.TableCell = function(config){
7035     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7036 };
7037
7038 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7039     
7040     html: false,
7041     cls: false,
7042     tag: false,
7043     abbr: false,
7044     align: false,
7045     axis: false,
7046     bgcolor: false,
7047     charoff: false,
7048     colspan: false,
7049     headers: false,
7050     height: false,
7051     nowrap: false,
7052     rowspan: false,
7053     scope: false,
7054     valign: false,
7055     width: false,
7056     
7057     
7058     getAutoCreate : function(){
7059         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7060         
7061         cfg = {
7062             tag: 'td'
7063         };
7064         
7065         if(this.tag){
7066             cfg.tag = this.tag;
7067         }
7068         
7069         if (this.html) {
7070             cfg.html=this.html
7071         }
7072         if (this.cls) {
7073             cfg.cls=this.cls
7074         }
7075         if (this.abbr) {
7076             cfg.abbr=this.abbr
7077         }
7078         if (this.align) {
7079             cfg.align=this.align
7080         }
7081         if (this.axis) {
7082             cfg.axis=this.axis
7083         }
7084         if (this.bgcolor) {
7085             cfg.bgcolor=this.bgcolor
7086         }
7087         if (this.charoff) {
7088             cfg.charoff=this.charoff
7089         }
7090         if (this.colspan) {
7091             cfg.colspan=this.colspan
7092         }
7093         if (this.headers) {
7094             cfg.headers=this.headers
7095         }
7096         if (this.height) {
7097             cfg.height=this.height
7098         }
7099         if (this.nowrap) {
7100             cfg.nowrap=this.nowrap
7101         }
7102         if (this.rowspan) {
7103             cfg.rowspan=this.rowspan
7104         }
7105         if (this.scope) {
7106             cfg.scope=this.scope
7107         }
7108         if (this.valign) {
7109             cfg.valign=this.valign
7110         }
7111         if (this.width) {
7112             cfg.width=this.width
7113         }
7114         
7115         
7116         return cfg;
7117     }
7118    
7119 });
7120
7121  
7122
7123  /*
7124  * - LGPL
7125  *
7126  * table row
7127  * 
7128  */
7129
7130 /**
7131  * @class Roo.bootstrap.TableRow
7132  * @extends Roo.bootstrap.Component
7133  * Bootstrap TableRow class
7134  * @cfg {String} cls row class
7135  * @cfg {String} align Aligns the content in a table row
7136  * @cfg {String} bgcolor Specifies a background color for a table row
7137  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7138  * @cfg {String} valign Vertical aligns the content in a table row
7139  * 
7140  * @constructor
7141  * Create a new TableRow
7142  * @param {Object} config The config object
7143  */
7144
7145 Roo.bootstrap.TableRow = function(config){
7146     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7147 };
7148
7149 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7150     
7151     cls: false,
7152     align: false,
7153     bgcolor: false,
7154     charoff: false,
7155     valign: false,
7156     
7157     getAutoCreate : function(){
7158         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7159         
7160         cfg = {
7161             tag: 'tr'
7162         };
7163             
7164         if(this.cls){
7165             cfg.cls = this.cls;
7166         }
7167         if(this.align){
7168             cfg.align = this.align;
7169         }
7170         if(this.bgcolor){
7171             cfg.bgcolor = this.bgcolor;
7172         }
7173         if(this.charoff){
7174             cfg.charoff = this.charoff;
7175         }
7176         if(this.valign){
7177             cfg.valign = this.valign;
7178         }
7179         
7180         return cfg;
7181     }
7182    
7183 });
7184
7185  
7186
7187  /*
7188  * - LGPL
7189  *
7190  * table body
7191  * 
7192  */
7193
7194 /**
7195  * @class Roo.bootstrap.TableBody
7196  * @extends Roo.bootstrap.Component
7197  * Bootstrap TableBody class
7198  * @cfg {String} cls element class
7199  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7200  * @cfg {String} align Aligns the content inside the element
7201  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7202  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7203  * 
7204  * @constructor
7205  * Create a new TableBody
7206  * @param {Object} config The config object
7207  */
7208
7209 Roo.bootstrap.TableBody = function(config){
7210     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7211 };
7212
7213 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7214     
7215     cls: false,
7216     tag: false,
7217     align: false,
7218     charoff: false,
7219     valign: false,
7220     
7221     getAutoCreate : function(){
7222         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7223         
7224         cfg = {
7225             tag: 'tbody'
7226         };
7227             
7228         if (this.cls) {
7229             cfg.cls=this.cls
7230         }
7231         if(this.tag){
7232             cfg.tag = this.tag;
7233         }
7234         
7235         if(this.align){
7236             cfg.align = this.align;
7237         }
7238         if(this.charoff){
7239             cfg.charoff = this.charoff;
7240         }
7241         if(this.valign){
7242             cfg.valign = this.valign;
7243         }
7244         
7245         return cfg;
7246     }
7247     
7248     
7249 //    initEvents : function()
7250 //    {
7251 //        
7252 //        if(!this.store){
7253 //            return;
7254 //        }
7255 //        
7256 //        this.store = Roo.factory(this.store, Roo.data);
7257 //        this.store.on('load', this.onLoad, this);
7258 //        
7259 //        this.store.load();
7260 //        
7261 //    },
7262 //    
7263 //    onLoad: function () 
7264 //    {   
7265 //        this.fireEvent('load', this);
7266 //    }
7267 //    
7268 //   
7269 });
7270
7271  
7272
7273  /*
7274  * Based on:
7275  * Ext JS Library 1.1.1
7276  * Copyright(c) 2006-2007, Ext JS, LLC.
7277  *
7278  * Originally Released Under LGPL - original licence link has changed is not relivant.
7279  *
7280  * Fork - LGPL
7281  * <script type="text/javascript">
7282  */
7283
7284 // as we use this in bootstrap.
7285 Roo.namespace('Roo.form');
7286  /**
7287  * @class Roo.form.Action
7288  * Internal Class used to handle form actions
7289  * @constructor
7290  * @param {Roo.form.BasicForm} el The form element or its id
7291  * @param {Object} config Configuration options
7292  */
7293
7294  
7295  
7296 // define the action interface
7297 Roo.form.Action = function(form, options){
7298     this.form = form;
7299     this.options = options || {};
7300 };
7301 /**
7302  * Client Validation Failed
7303  * @const 
7304  */
7305 Roo.form.Action.CLIENT_INVALID = 'client';
7306 /**
7307  * Server Validation Failed
7308  * @const 
7309  */
7310 Roo.form.Action.SERVER_INVALID = 'server';
7311  /**
7312  * Connect to Server Failed
7313  * @const 
7314  */
7315 Roo.form.Action.CONNECT_FAILURE = 'connect';
7316 /**
7317  * Reading Data from Server Failed
7318  * @const 
7319  */
7320 Roo.form.Action.LOAD_FAILURE = 'load';
7321
7322 Roo.form.Action.prototype = {
7323     type : 'default',
7324     failureType : undefined,
7325     response : undefined,
7326     result : undefined,
7327
7328     // interface method
7329     run : function(options){
7330
7331     },
7332
7333     // interface method
7334     success : function(response){
7335
7336     },
7337
7338     // interface method
7339     handleResponse : function(response){
7340
7341     },
7342
7343     // default connection failure
7344     failure : function(response){
7345         
7346         this.response = response;
7347         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7348         this.form.afterAction(this, false);
7349     },
7350
7351     processResponse : function(response){
7352         this.response = response;
7353         if(!response.responseText){
7354             return true;
7355         }
7356         this.result = this.handleResponse(response);
7357         return this.result;
7358     },
7359
7360     // utility functions used internally
7361     getUrl : function(appendParams){
7362         var url = this.options.url || this.form.url || this.form.el.dom.action;
7363         if(appendParams){
7364             var p = this.getParams();
7365             if(p){
7366                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7367             }
7368         }
7369         return url;
7370     },
7371
7372     getMethod : function(){
7373         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7374     },
7375
7376     getParams : function(){
7377         var bp = this.form.baseParams;
7378         var p = this.options.params;
7379         if(p){
7380             if(typeof p == "object"){
7381                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7382             }else if(typeof p == 'string' && bp){
7383                 p += '&' + Roo.urlEncode(bp);
7384             }
7385         }else if(bp){
7386             p = Roo.urlEncode(bp);
7387         }
7388         return p;
7389     },
7390
7391     createCallback : function(){
7392         return {
7393             success: this.success,
7394             failure: this.failure,
7395             scope: this,
7396             timeout: (this.form.timeout*1000),
7397             upload: this.form.fileUpload ? this.success : undefined
7398         };
7399     }
7400 };
7401
7402 Roo.form.Action.Submit = function(form, options){
7403     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7404 };
7405
7406 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7407     type : 'submit',
7408
7409     haveProgress : false,
7410     uploadComplete : false,
7411     
7412     // uploadProgress indicator.
7413     uploadProgress : function()
7414     {
7415         if (!this.form.progressUrl) {
7416             return;
7417         }
7418         
7419         if (!this.haveProgress) {
7420             Roo.MessageBox.progress("Uploading", "Uploading");
7421         }
7422         if (this.uploadComplete) {
7423            Roo.MessageBox.hide();
7424            return;
7425         }
7426         
7427         this.haveProgress = true;
7428    
7429         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7430         
7431         var c = new Roo.data.Connection();
7432         c.request({
7433             url : this.form.progressUrl,
7434             params: {
7435                 id : uid
7436             },
7437             method: 'GET',
7438             success : function(req){
7439                //console.log(data);
7440                 var rdata = false;
7441                 var edata;
7442                 try  {
7443                    rdata = Roo.decode(req.responseText)
7444                 } catch (e) {
7445                     Roo.log("Invalid data from server..");
7446                     Roo.log(edata);
7447                     return;
7448                 }
7449                 if (!rdata || !rdata.success) {
7450                     Roo.log(rdata);
7451                     Roo.MessageBox.alert(Roo.encode(rdata));
7452                     return;
7453                 }
7454                 var data = rdata.data;
7455                 
7456                 if (this.uploadComplete) {
7457                    Roo.MessageBox.hide();
7458                    return;
7459                 }
7460                    
7461                 if (data){
7462                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7463                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7464                     );
7465                 }
7466                 this.uploadProgress.defer(2000,this);
7467             },
7468        
7469             failure: function(data) {
7470                 Roo.log('progress url failed ');
7471                 Roo.log(data);
7472             },
7473             scope : this
7474         });
7475            
7476     },
7477     
7478     
7479     run : function()
7480     {
7481         // run get Values on the form, so it syncs any secondary forms.
7482         this.form.getValues();
7483         
7484         var o = this.options;
7485         var method = this.getMethod();
7486         var isPost = method == 'POST';
7487         if(o.clientValidation === false || this.form.isValid()){
7488             
7489             if (this.form.progressUrl) {
7490                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7491                     (new Date() * 1) + '' + Math.random());
7492                     
7493             } 
7494             
7495             
7496             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7497                 form:this.form.el.dom,
7498                 url:this.getUrl(!isPost),
7499                 method: method,
7500                 params:isPost ? this.getParams() : null,
7501                 isUpload: this.form.fileUpload
7502             }));
7503             
7504             this.uploadProgress();
7505
7506         }else if (o.clientValidation !== false){ // client validation failed
7507             this.failureType = Roo.form.Action.CLIENT_INVALID;
7508             this.form.afterAction(this, false);
7509         }
7510     },
7511
7512     success : function(response)
7513     {
7514         this.uploadComplete= true;
7515         if (this.haveProgress) {
7516             Roo.MessageBox.hide();
7517         }
7518         
7519         
7520         var result = this.processResponse(response);
7521         if(result === true || result.success){
7522             this.form.afterAction(this, true);
7523             return;
7524         }
7525         if(result.errors){
7526             this.form.markInvalid(result.errors);
7527             this.failureType = Roo.form.Action.SERVER_INVALID;
7528         }
7529         this.form.afterAction(this, false);
7530     },
7531     failure : function(response)
7532     {
7533         this.uploadComplete= true;
7534         if (this.haveProgress) {
7535             Roo.MessageBox.hide();
7536         }
7537         
7538         this.response = response;
7539         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7540         this.form.afterAction(this, false);
7541     },
7542     
7543     handleResponse : function(response){
7544         if(this.form.errorReader){
7545             var rs = this.form.errorReader.read(response);
7546             var errors = [];
7547             if(rs.records){
7548                 for(var i = 0, len = rs.records.length; i < len; i++) {
7549                     var r = rs.records[i];
7550                     errors[i] = r.data;
7551                 }
7552             }
7553             if(errors.length < 1){
7554                 errors = null;
7555             }
7556             return {
7557                 success : rs.success,
7558                 errors : errors
7559             };
7560         }
7561         var ret = false;
7562         try {
7563             ret = Roo.decode(response.responseText);
7564         } catch (e) {
7565             ret = {
7566                 success: false,
7567                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7568                 errors : []
7569             };
7570         }
7571         return ret;
7572         
7573     }
7574 });
7575
7576
7577 Roo.form.Action.Load = function(form, options){
7578     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7579     this.reader = this.form.reader;
7580 };
7581
7582 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7583     type : 'load',
7584
7585     run : function(){
7586         
7587         Roo.Ajax.request(Roo.apply(
7588                 this.createCallback(), {
7589                     method:this.getMethod(),
7590                     url:this.getUrl(false),
7591                     params:this.getParams()
7592         }));
7593     },
7594
7595     success : function(response){
7596         
7597         var result = this.processResponse(response);
7598         if(result === true || !result.success || !result.data){
7599             this.failureType = Roo.form.Action.LOAD_FAILURE;
7600             this.form.afterAction(this, false);
7601             return;
7602         }
7603         this.form.clearInvalid();
7604         this.form.setValues(result.data);
7605         this.form.afterAction(this, true);
7606     },
7607
7608     handleResponse : function(response){
7609         if(this.form.reader){
7610             var rs = this.form.reader.read(response);
7611             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7612             return {
7613                 success : rs.success,
7614                 data : data
7615             };
7616         }
7617         return Roo.decode(response.responseText);
7618     }
7619 });
7620
7621 Roo.form.Action.ACTION_TYPES = {
7622     'load' : Roo.form.Action.Load,
7623     'submit' : Roo.form.Action.Submit
7624 };/*
7625  * - LGPL
7626  *
7627  * form
7628  *
7629  */
7630
7631 /**
7632  * @class Roo.bootstrap.Form
7633  * @extends Roo.bootstrap.Component
7634  * Bootstrap Form class
7635  * @cfg {String} method  GET | POST (default POST)
7636  * @cfg {String} labelAlign top | left (default top)
7637  * @cfg {String} align left  | right - for navbars
7638  * @cfg {Boolean} loadMask load mask when submit (default true)
7639
7640  *
7641  * @constructor
7642  * Create a new Form
7643  * @param {Object} config The config object
7644  */
7645
7646
7647 Roo.bootstrap.Form = function(config){
7648     
7649     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7650     
7651     Roo.bootstrap.Form.popover.apply();
7652     
7653     this.addEvents({
7654         /**
7655          * @event clientvalidation
7656          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7657          * @param {Form} this
7658          * @param {Boolean} valid true if the form has passed client-side validation
7659          */
7660         clientvalidation: true,
7661         /**
7662          * @event beforeaction
7663          * Fires before any action is performed. Return false to cancel the action.
7664          * @param {Form} this
7665          * @param {Action} action The action to be performed
7666          */
7667         beforeaction: true,
7668         /**
7669          * @event actionfailed
7670          * Fires when an action fails.
7671          * @param {Form} this
7672          * @param {Action} action The action that failed
7673          */
7674         actionfailed : true,
7675         /**
7676          * @event actioncomplete
7677          * Fires when an action is completed.
7678          * @param {Form} this
7679          * @param {Action} action The action that completed
7680          */
7681         actioncomplete : true
7682     });
7683 };
7684
7685 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7686
7687      /**
7688      * @cfg {String} method
7689      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7690      */
7691     method : 'POST',
7692     /**
7693      * @cfg {String} url
7694      * The URL to use for form actions if one isn't supplied in the action options.
7695      */
7696     /**
7697      * @cfg {Boolean} fileUpload
7698      * Set to true if this form is a file upload.
7699      */
7700
7701     /**
7702      * @cfg {Object} baseParams
7703      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7704      */
7705
7706     /**
7707      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7708      */
7709     timeout: 30,
7710     /**
7711      * @cfg {Sting} align (left|right) for navbar forms
7712      */
7713     align : 'left',
7714
7715     // private
7716     activeAction : null,
7717
7718     /**
7719      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7720      * element by passing it or its id or mask the form itself by passing in true.
7721      * @type Mixed
7722      */
7723     waitMsgTarget : false,
7724
7725     loadMask : true,
7726     
7727     /**
7728      * @cfg {Boolean} errorMask (true|false) default false
7729      */
7730     errorMask : false,
7731     
7732     /**
7733      * @cfg {Number} maskOffset Default 100
7734      */
7735     maskOffset : 100,
7736     
7737     /**
7738      * @cfg {Boolean} maskBody
7739      */
7740     maskBody : false,
7741
7742     getAutoCreate : function(){
7743
7744         var cfg = {
7745             tag: 'form',
7746             method : this.method || 'POST',
7747             id : this.id || Roo.id(),
7748             cls : ''
7749         };
7750         if (this.parent().xtype.match(/^Nav/)) {
7751             cfg.cls = 'navbar-form navbar-' + this.align;
7752
7753         }
7754
7755         if (this.labelAlign == 'left' ) {
7756             cfg.cls += ' form-horizontal';
7757         }
7758
7759
7760         return cfg;
7761     },
7762     initEvents : function()
7763     {
7764         this.el.on('submit', this.onSubmit, this);
7765         // this was added as random key presses on the form where triggering form submit.
7766         this.el.on('keypress', function(e) {
7767             if (e.getCharCode() != 13) {
7768                 return true;
7769             }
7770             // we might need to allow it for textareas.. and some other items.
7771             // check e.getTarget().
7772
7773             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7774                 return true;
7775             }
7776
7777             Roo.log("keypress blocked");
7778
7779             e.preventDefault();
7780             return false;
7781         });
7782         
7783     },
7784     // private
7785     onSubmit : function(e){
7786         e.stopEvent();
7787     },
7788
7789      /**
7790      * Returns true if client-side validation on the form is successful.
7791      * @return Boolean
7792      */
7793     isValid : function(){
7794         var items = this.getItems();
7795         var valid = true;
7796         var target = false;
7797         
7798         items.each(function(f){
7799             
7800             if(f.validate()){
7801                 return;
7802             }
7803             valid = false;
7804
7805             if(!target && f.el.isVisible(true)){
7806                 target = f;
7807             }
7808            
7809         });
7810         
7811         if(this.errorMask && !valid){
7812             Roo.bootstrap.Form.popover.mask(this, target);
7813         }
7814         
7815         return valid;
7816     },
7817     
7818     /**
7819      * Returns true if any fields in this form have changed since their original load.
7820      * @return Boolean
7821      */
7822     isDirty : function(){
7823         var dirty = false;
7824         var items = this.getItems();
7825         items.each(function(f){
7826            if(f.isDirty()){
7827                dirty = true;
7828                return false;
7829            }
7830            return true;
7831         });
7832         return dirty;
7833     },
7834      /**
7835      * Performs a predefined action (submit or load) or custom actions you define on this form.
7836      * @param {String} actionName The name of the action type
7837      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7838      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7839      * accept other config options):
7840      * <pre>
7841 Property          Type             Description
7842 ----------------  ---------------  ----------------------------------------------------------------------------------
7843 url               String           The url for the action (defaults to the form's url)
7844 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7845 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7846 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7847                                    validate the form on the client (defaults to false)
7848      * </pre>
7849      * @return {BasicForm} this
7850      */
7851     doAction : function(action, options){
7852         if(typeof action == 'string'){
7853             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7854         }
7855         if(this.fireEvent('beforeaction', this, action) !== false){
7856             this.beforeAction(action);
7857             action.run.defer(100, action);
7858         }
7859         return this;
7860     },
7861
7862     // private
7863     beforeAction : function(action){
7864         var o = action.options;
7865         
7866         if(this.loadMask){
7867             
7868             if(this.maskBody){
7869                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7870             } else {
7871                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7872             }
7873         }
7874         // not really supported yet.. ??
7875
7876         //if(this.waitMsgTarget === true){
7877         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7878         //}else if(this.waitMsgTarget){
7879         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7880         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7881         //}else {
7882         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7883        // }
7884
7885     },
7886
7887     // private
7888     afterAction : function(action, success){
7889         this.activeAction = null;
7890         var o = action.options;
7891
7892         if(this.loadMask){
7893             
7894             if(this.maskBody){
7895                 Roo.get(document.body).unmask();
7896             } else {
7897                 this.el.unmask();
7898             }
7899         }
7900         
7901         //if(this.waitMsgTarget === true){
7902 //            this.el.unmask();
7903         //}else if(this.waitMsgTarget){
7904         //    this.waitMsgTarget.unmask();
7905         //}else{
7906         //    Roo.MessageBox.updateProgress(1);
7907         //    Roo.MessageBox.hide();
7908        // }
7909         //
7910         if(success){
7911             if(o.reset){
7912                 this.reset();
7913             }
7914             Roo.callback(o.success, o.scope, [this, action]);
7915             this.fireEvent('actioncomplete', this, action);
7916
7917         }else{
7918
7919             // failure condition..
7920             // we have a scenario where updates need confirming.
7921             // eg. if a locking scenario exists..
7922             // we look for { errors : { needs_confirm : true }} in the response.
7923             if (
7924                 (typeof(action.result) != 'undefined')  &&
7925                 (typeof(action.result.errors) != 'undefined')  &&
7926                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7927            ){
7928                 var _t = this;
7929                 Roo.log("not supported yet");
7930                  /*
7931
7932                 Roo.MessageBox.confirm(
7933                     "Change requires confirmation",
7934                     action.result.errorMsg,
7935                     function(r) {
7936                         if (r != 'yes') {
7937                             return;
7938                         }
7939                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7940                     }
7941
7942                 );
7943                 */
7944
7945
7946                 return;
7947             }
7948
7949             Roo.callback(o.failure, o.scope, [this, action]);
7950             // show an error message if no failed handler is set..
7951             if (!this.hasListener('actionfailed')) {
7952                 Roo.log("need to add dialog support");
7953                 /*
7954                 Roo.MessageBox.alert("Error",
7955                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7956                         action.result.errorMsg :
7957                         "Saving Failed, please check your entries or try again"
7958                 );
7959                 */
7960             }
7961
7962             this.fireEvent('actionfailed', this, action);
7963         }
7964
7965     },
7966     /**
7967      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7968      * @param {String} id The value to search for
7969      * @return Field
7970      */
7971     findField : function(id){
7972         var items = this.getItems();
7973         var field = items.get(id);
7974         if(!field){
7975              items.each(function(f){
7976                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7977                     field = f;
7978                     return false;
7979                 }
7980                 return true;
7981             });
7982         }
7983         return field || null;
7984     },
7985      /**
7986      * Mark fields in this form invalid in bulk.
7987      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7988      * @return {BasicForm} this
7989      */
7990     markInvalid : function(errors){
7991         if(errors instanceof Array){
7992             for(var i = 0, len = errors.length; i < len; i++){
7993                 var fieldError = errors[i];
7994                 var f = this.findField(fieldError.id);
7995                 if(f){
7996                     f.markInvalid(fieldError.msg);
7997                 }
7998             }
7999         }else{
8000             var field, id;
8001             for(id in errors){
8002                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8003                     field.markInvalid(errors[id]);
8004                 }
8005             }
8006         }
8007         //Roo.each(this.childForms || [], function (f) {
8008         //    f.markInvalid(errors);
8009         //});
8010
8011         return this;
8012     },
8013
8014     /**
8015      * Set values for fields in this form in bulk.
8016      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8017      * @return {BasicForm} this
8018      */
8019     setValues : function(values){
8020         if(values instanceof Array){ // array of objects
8021             for(var i = 0, len = values.length; i < len; i++){
8022                 var v = values[i];
8023                 var f = this.findField(v.id);
8024                 if(f){
8025                     f.setValue(v.value);
8026                     if(this.trackResetOnLoad){
8027                         f.originalValue = f.getValue();
8028                     }
8029                 }
8030             }
8031         }else{ // object hash
8032             var field, id;
8033             for(id in values){
8034                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8035
8036                     if (field.setFromData &&
8037                         field.valueField &&
8038                         field.displayField &&
8039                         // combos' with local stores can
8040                         // be queried via setValue()
8041                         // to set their value..
8042                         (field.store && !field.store.isLocal)
8043                         ) {
8044                         // it's a combo
8045                         var sd = { };
8046                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8047                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8048                         field.setFromData(sd);
8049
8050                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8051                         
8052                         field.setFromData(values);
8053                         
8054                     } else {
8055                         field.setValue(values[id]);
8056                     }
8057
8058
8059                     if(this.trackResetOnLoad){
8060                         field.originalValue = field.getValue();
8061                     }
8062                 }
8063             }
8064         }
8065
8066         //Roo.each(this.childForms || [], function (f) {
8067         //    f.setValues(values);
8068         //});
8069
8070         return this;
8071     },
8072
8073     /**
8074      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8075      * they are returned as an array.
8076      * @param {Boolean} asString
8077      * @return {Object}
8078      */
8079     getValues : function(asString){
8080         //if (this.childForms) {
8081             // copy values from the child forms
8082         //    Roo.each(this.childForms, function (f) {
8083         //        this.setValues(f.getValues());
8084         //    }, this);
8085         //}
8086
8087
8088
8089         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8090         if(asString === true){
8091             return fs;
8092         }
8093         return Roo.urlDecode(fs);
8094     },
8095
8096     /**
8097      * Returns the fields in this form as an object with key/value pairs.
8098      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8099      * @return {Object}
8100      */
8101     getFieldValues : function(with_hidden)
8102     {
8103         var items = this.getItems();
8104         var ret = {};
8105         items.each(function(f){
8106             
8107             if (!f.getName()) {
8108                 return;
8109             }
8110             
8111             var v = f.getValue();
8112             
8113             if (f.inputType =='radio') {
8114                 if (typeof(ret[f.getName()]) == 'undefined') {
8115                     ret[f.getName()] = ''; // empty..
8116                 }
8117
8118                 if (!f.el.dom.checked) {
8119                     return;
8120
8121                 }
8122                 v = f.el.dom.value;
8123
8124             }
8125             
8126             if(f.xtype == 'MoneyField'){
8127                 ret[f.currencyName] = f.getCurrency();
8128             }
8129
8130             // not sure if this supported any more..
8131             if ((typeof(v) == 'object') && f.getRawValue) {
8132                 v = f.getRawValue() ; // dates..
8133             }
8134             // combo boxes where name != hiddenName...
8135             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8136                 ret[f.name] = f.getRawValue();
8137             }
8138             ret[f.getName()] = v;
8139         });
8140
8141         return ret;
8142     },
8143
8144     /**
8145      * Clears all invalid messages in this form.
8146      * @return {BasicForm} this
8147      */
8148     clearInvalid : function(){
8149         var items = this.getItems();
8150
8151         items.each(function(f){
8152            f.clearInvalid();
8153         });
8154
8155         return this;
8156     },
8157
8158     /**
8159      * Resets this form.
8160      * @return {BasicForm} this
8161      */
8162     reset : function(){
8163         var items = this.getItems();
8164         items.each(function(f){
8165             f.reset();
8166         });
8167
8168         Roo.each(this.childForms || [], function (f) {
8169             f.reset();
8170         });
8171
8172
8173         return this;
8174     },
8175     
8176     getItems : function()
8177     {
8178         var r=new Roo.util.MixedCollection(false, function(o){
8179             return o.id || (o.id = Roo.id());
8180         });
8181         var iter = function(el) {
8182             if (el.inputEl) {
8183                 r.add(el);
8184             }
8185             if (!el.items) {
8186                 return;
8187             }
8188             Roo.each(el.items,function(e) {
8189                 iter(e);
8190             });
8191         };
8192
8193         iter(this);
8194         return r;
8195     },
8196     
8197     hideFields : function(items)
8198     {
8199         Roo.each(items, function(i){
8200             
8201             var f = this.findField(i);
8202             
8203             if(!f){
8204                 return;
8205             }
8206             
8207             if(f.xtype == 'DateField'){
8208                 f.setVisible(false);
8209                 return;
8210             }
8211             
8212             f.hide();
8213             
8214         }, this);
8215     },
8216     
8217     showFields : function(items)
8218     {
8219         Roo.each(items, function(i){
8220             
8221             var f = this.findField(i);
8222             
8223             if(!f){
8224                 return;
8225             }
8226             
8227             if(f.xtype == 'DateField'){
8228                 f.setVisible(true);
8229                 return;
8230             }
8231             
8232             f.show();
8233             
8234         }, this);
8235     }
8236
8237 });
8238
8239 Roo.apply(Roo.bootstrap.Form, {
8240     
8241     popover : {
8242         
8243         padding : 5,
8244         
8245         isApplied : false,
8246         
8247         isMasked : false,
8248         
8249         form : false,
8250         
8251         target : false,
8252         
8253         toolTip : false,
8254         
8255         intervalID : false,
8256         
8257         maskEl : false,
8258         
8259         apply : function()
8260         {
8261             if(this.isApplied){
8262                 return;
8263             }
8264             
8265             this.maskEl = {
8266                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8267                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8268                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8269                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8270             };
8271             
8272             this.maskEl.top.enableDisplayMode("block");
8273             this.maskEl.left.enableDisplayMode("block");
8274             this.maskEl.bottom.enableDisplayMode("block");
8275             this.maskEl.right.enableDisplayMode("block");
8276             
8277             this.toolTip = new Roo.bootstrap.Tooltip({
8278                 cls : 'roo-form-error-popover',
8279                 alignment : {
8280                     'left' : ['r-l', [-2,0], 'right'],
8281                     'right' : ['l-r', [2,0], 'left'],
8282                     'bottom' : ['tl-bl', [0,2], 'top'],
8283                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8284                 }
8285             });
8286             
8287             this.toolTip.render(Roo.get(document.body));
8288
8289             this.toolTip.el.enableDisplayMode("block");
8290             
8291             Roo.get(document.body).on('click', function(){
8292                 this.unmask();
8293             }, this);
8294             
8295             Roo.get(document.body).on('touchstart', function(){
8296                 this.unmask();
8297             }, this);
8298             
8299             this.isApplied = true
8300         },
8301         
8302         mask : function(form, target)
8303         {
8304             this.form = form;
8305             
8306             this.target = target;
8307             
8308             if(!this.form.errorMask || !target.el){
8309                 return;
8310             }
8311             
8312             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8313             
8314             Roo.log(scrollable);
8315             
8316             var ot = this.target.el.calcOffsetsTo(scrollable);
8317             
8318             var scrollTo = ot[1] - this.form.maskOffset;
8319             
8320             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8321             
8322             scrollable.scrollTo('top', scrollTo);
8323             
8324             var box = this.target.el.getBox();
8325             Roo.log(box);
8326             var zIndex = Roo.bootstrap.Modal.zIndex++;
8327
8328             
8329             this.maskEl.top.setStyle('position', 'absolute');
8330             this.maskEl.top.setStyle('z-index', zIndex);
8331             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8332             this.maskEl.top.setLeft(0);
8333             this.maskEl.top.setTop(0);
8334             this.maskEl.top.show();
8335             
8336             this.maskEl.left.setStyle('position', 'absolute');
8337             this.maskEl.left.setStyle('z-index', zIndex);
8338             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8339             this.maskEl.left.setLeft(0);
8340             this.maskEl.left.setTop(box.y - this.padding);
8341             this.maskEl.left.show();
8342
8343             this.maskEl.bottom.setStyle('position', 'absolute');
8344             this.maskEl.bottom.setStyle('z-index', zIndex);
8345             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8346             this.maskEl.bottom.setLeft(0);
8347             this.maskEl.bottom.setTop(box.bottom + this.padding);
8348             this.maskEl.bottom.show();
8349
8350             this.maskEl.right.setStyle('position', 'absolute');
8351             this.maskEl.right.setStyle('z-index', zIndex);
8352             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8353             this.maskEl.right.setLeft(box.right + this.padding);
8354             this.maskEl.right.setTop(box.y - this.padding);
8355             this.maskEl.right.show();
8356
8357             this.toolTip.bindEl = this.target.el;
8358
8359             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8360
8361             var tip = this.target.blankText;
8362
8363             if(this.target.getValue() !== '' ) {
8364                 
8365                 if (this.target.invalidText.length) {
8366                     tip = this.target.invalidText;
8367                 } else if (this.target.regexText.length){
8368                     tip = this.target.regexText;
8369                 }
8370             }
8371
8372             this.toolTip.show(tip);
8373
8374             this.intervalID = window.setInterval(function() {
8375                 Roo.bootstrap.Form.popover.unmask();
8376             }, 10000);
8377
8378             window.onwheel = function(){ return false;};
8379             
8380             (function(){ this.isMasked = true; }).defer(500, this);
8381             
8382         },
8383         
8384         unmask : function()
8385         {
8386             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8387                 return;
8388             }
8389             
8390             this.maskEl.top.setStyle('position', 'absolute');
8391             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8392             this.maskEl.top.hide();
8393
8394             this.maskEl.left.setStyle('position', 'absolute');
8395             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8396             this.maskEl.left.hide();
8397
8398             this.maskEl.bottom.setStyle('position', 'absolute');
8399             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8400             this.maskEl.bottom.hide();
8401
8402             this.maskEl.right.setStyle('position', 'absolute');
8403             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8404             this.maskEl.right.hide();
8405             
8406             this.toolTip.hide();
8407             
8408             this.toolTip.el.hide();
8409             
8410             window.onwheel = function(){ return true;};
8411             
8412             if(this.intervalID){
8413                 window.clearInterval(this.intervalID);
8414                 this.intervalID = false;
8415             }
8416             
8417             this.isMasked = false;
8418             
8419         }
8420         
8421     }
8422     
8423 });
8424
8425 /*
8426  * Based on:
8427  * Ext JS Library 1.1.1
8428  * Copyright(c) 2006-2007, Ext JS, LLC.
8429  *
8430  * Originally Released Under LGPL - original licence link has changed is not relivant.
8431  *
8432  * Fork - LGPL
8433  * <script type="text/javascript">
8434  */
8435 /**
8436  * @class Roo.form.VTypes
8437  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8438  * @singleton
8439  */
8440 Roo.form.VTypes = function(){
8441     // closure these in so they are only created once.
8442     var alpha = /^[a-zA-Z_]+$/;
8443     var alphanum = /^[a-zA-Z0-9_]+$/;
8444     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8445     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8446
8447     // All these messages and functions are configurable
8448     return {
8449         /**
8450          * The function used to validate email addresses
8451          * @param {String} value The email address
8452          */
8453         'email' : function(v){
8454             return email.test(v);
8455         },
8456         /**
8457          * The error text to display when the email validation function returns false
8458          * @type String
8459          */
8460         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8461         /**
8462          * The keystroke filter mask to be applied on email input
8463          * @type RegExp
8464          */
8465         'emailMask' : /[a-z0-9_\.\-@]/i,
8466
8467         /**
8468          * The function used to validate URLs
8469          * @param {String} value The URL
8470          */
8471         'url' : function(v){
8472             return url.test(v);
8473         },
8474         /**
8475          * The error text to display when the url validation function returns false
8476          * @type String
8477          */
8478         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8479         
8480         /**
8481          * The function used to validate alpha values
8482          * @param {String} value The value
8483          */
8484         'alpha' : function(v){
8485             return alpha.test(v);
8486         },
8487         /**
8488          * The error text to display when the alpha validation function returns false
8489          * @type String
8490          */
8491         'alphaText' : 'This field should only contain letters and _',
8492         /**
8493          * The keystroke filter mask to be applied on alpha input
8494          * @type RegExp
8495          */
8496         'alphaMask' : /[a-z_]/i,
8497
8498         /**
8499          * The function used to validate alphanumeric values
8500          * @param {String} value The value
8501          */
8502         'alphanum' : function(v){
8503             return alphanum.test(v);
8504         },
8505         /**
8506          * The error text to display when the alphanumeric validation function returns false
8507          * @type String
8508          */
8509         'alphanumText' : 'This field should only contain letters, numbers and _',
8510         /**
8511          * The keystroke filter mask to be applied on alphanumeric input
8512          * @type RegExp
8513          */
8514         'alphanumMask' : /[a-z0-9_]/i
8515     };
8516 }();/*
8517  * - LGPL
8518  *
8519  * Input
8520  * 
8521  */
8522
8523 /**
8524  * @class Roo.bootstrap.Input
8525  * @extends Roo.bootstrap.Component
8526  * Bootstrap Input class
8527  * @cfg {Boolean} disabled is it disabled
8528  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8529  * @cfg {String} name name of the input
8530  * @cfg {string} fieldLabel - the label associated
8531  * @cfg {string} placeholder - placeholder to put in text.
8532  * @cfg {string}  before - input group add on before
8533  * @cfg {string} after - input group add on after
8534  * @cfg {string} size - (lg|sm) or leave empty..
8535  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8536  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8537  * @cfg {Number} md colspan out of 12 for computer-sized screens
8538  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8539  * @cfg {string} value default value of the input
8540  * @cfg {Number} labelWidth set the width of label 
8541  * @cfg {Number} labellg set the width of label (1-12)
8542  * @cfg {Number} labelmd set the width of label (1-12)
8543  * @cfg {Number} labelsm set the width of label (1-12)
8544  * @cfg {Number} labelxs set the width of label (1-12)
8545  * @cfg {String} labelAlign (top|left)
8546  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8547  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8548  * @cfg {String} indicatorpos (left|right) default left
8549  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8550  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8551
8552  * @cfg {String} align (left|center|right) Default left
8553  * @cfg {Boolean} forceFeedback (true|false) Default false
8554  * 
8555  * @constructor
8556  * Create a new Input
8557  * @param {Object} config The config object
8558  */
8559
8560 Roo.bootstrap.Input = function(config){
8561     
8562     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8563     
8564     this.addEvents({
8565         /**
8566          * @event focus
8567          * Fires when this field receives input focus.
8568          * @param {Roo.form.Field} this
8569          */
8570         focus : true,
8571         /**
8572          * @event blur
8573          * Fires when this field loses input focus.
8574          * @param {Roo.form.Field} this
8575          */
8576         blur : true,
8577         /**
8578          * @event specialkey
8579          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8580          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8581          * @param {Roo.form.Field} this
8582          * @param {Roo.EventObject} e The event object
8583          */
8584         specialkey : true,
8585         /**
8586          * @event change
8587          * Fires just before the field blurs if the field value has changed.
8588          * @param {Roo.form.Field} this
8589          * @param {Mixed} newValue The new value
8590          * @param {Mixed} oldValue The original value
8591          */
8592         change : true,
8593         /**
8594          * @event invalid
8595          * Fires after the field has been marked as invalid.
8596          * @param {Roo.form.Field} this
8597          * @param {String} msg The validation message
8598          */
8599         invalid : true,
8600         /**
8601          * @event valid
8602          * Fires after the field has been validated with no errors.
8603          * @param {Roo.form.Field} this
8604          */
8605         valid : true,
8606          /**
8607          * @event keyup
8608          * Fires after the key up
8609          * @param {Roo.form.Field} this
8610          * @param {Roo.EventObject}  e The event Object
8611          */
8612         keyup : true
8613     });
8614 };
8615
8616 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8617      /**
8618      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8619       automatic validation (defaults to "keyup").
8620      */
8621     validationEvent : "keyup",
8622      /**
8623      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8624      */
8625     validateOnBlur : true,
8626     /**
8627      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8628      */
8629     validationDelay : 250,
8630      /**
8631      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8632      */
8633     focusClass : "x-form-focus",  // not needed???
8634     
8635        
8636     /**
8637      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8638      */
8639     invalidClass : "has-warning",
8640     
8641     /**
8642      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8643      */
8644     validClass : "has-success",
8645     
8646     /**
8647      * @cfg {Boolean} hasFeedback (true|false) default true
8648      */
8649     hasFeedback : true,
8650     
8651     /**
8652      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8653      */
8654     invalidFeedbackClass : "glyphicon-warning-sign",
8655     
8656     /**
8657      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8658      */
8659     validFeedbackClass : "glyphicon-ok",
8660     
8661     /**
8662      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8663      */
8664     selectOnFocus : false,
8665     
8666      /**
8667      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8668      */
8669     maskRe : null,
8670        /**
8671      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8672      */
8673     vtype : null,
8674     
8675       /**
8676      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8677      */
8678     disableKeyFilter : false,
8679     
8680        /**
8681      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8682      */
8683     disabled : false,
8684      /**
8685      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8686      */
8687     allowBlank : true,
8688     /**
8689      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8690      */
8691     blankText : "Please complete this mandatory field",
8692     
8693      /**
8694      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8695      */
8696     minLength : 0,
8697     /**
8698      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8699      */
8700     maxLength : Number.MAX_VALUE,
8701     /**
8702      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8703      */
8704     minLengthText : "The minimum length for this field is {0}",
8705     /**
8706      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8707      */
8708     maxLengthText : "The maximum length for this field is {0}",
8709   
8710     
8711     /**
8712      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8713      * If available, this function will be called only after the basic validators all return true, and will be passed the
8714      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8715      */
8716     validator : null,
8717     /**
8718      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8719      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8720      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8721      */
8722     regex : null,
8723     /**
8724      * @cfg {String} regexText -- Depricated - use Invalid Text
8725      */
8726     regexText : "",
8727     
8728     /**
8729      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8730      */
8731     invalidText : "",
8732     
8733     
8734     
8735     autocomplete: false,
8736     
8737     
8738     fieldLabel : '',
8739     inputType : 'text',
8740     
8741     name : false,
8742     placeholder: false,
8743     before : false,
8744     after : false,
8745     size : false,
8746     hasFocus : false,
8747     preventMark: false,
8748     isFormField : true,
8749     value : '',
8750     labelWidth : 2,
8751     labelAlign : false,
8752     readOnly : false,
8753     align : false,
8754     formatedValue : false,
8755     forceFeedback : false,
8756     
8757     indicatorpos : 'left',
8758     
8759     labellg : 0,
8760     labelmd : 0,
8761     labelsm : 0,
8762     labelxs : 0,
8763     
8764     capture : '',
8765     accept : '',
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.accept.length){
8807             input.accept = this.accept + "/*";
8808         }
8809         
8810         if(this.align){
8811             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8812         }
8813         
8814         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8815             input.maxLength = this.maxLength;
8816         }
8817         
8818         if (this.disabled) {
8819             input.disabled=true;
8820         }
8821         
8822         if (this.readOnly) {
8823             input.readonly=true;
8824         }
8825         
8826         if (this.name) {
8827             input.name = this.name;
8828         }
8829         
8830         if (this.size) {
8831             input.cls += ' input-' + this.size;
8832         }
8833         
8834         var settings=this;
8835         ['xs','sm','md','lg'].map(function(size){
8836             if (settings[size]) {
8837                 cfg.cls += ' col-' + size + '-' + settings[size];
8838             }
8839         });
8840         
8841         var inputblock = input;
8842         
8843         var feedback = {
8844             tag: 'span',
8845             cls: 'glyphicon form-control-feedback'
8846         };
8847             
8848         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8849             
8850             inputblock = {
8851                 cls : 'has-feedback',
8852                 cn :  [
8853                     input,
8854                     feedback
8855                 ] 
8856             };  
8857         }
8858         
8859         if (this.before || this.after) {
8860             
8861             inputblock = {
8862                 cls : 'input-group',
8863                 cn :  [] 
8864             };
8865             
8866             if (this.before && typeof(this.before) == 'string') {
8867                 
8868                 inputblock.cn.push({
8869                     tag :'span',
8870                     cls : 'roo-input-before input-group-addon',
8871                     html : this.before
8872                 });
8873             }
8874             if (this.before && typeof(this.before) == 'object') {
8875                 this.before = Roo.factory(this.before);
8876                 
8877                 inputblock.cn.push({
8878                     tag :'span',
8879                     cls : 'roo-input-before input-group-' +
8880                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8881                 });
8882             }
8883             
8884             inputblock.cn.push(input);
8885             
8886             if (this.after && typeof(this.after) == 'string') {
8887                 inputblock.cn.push({
8888                     tag :'span',
8889                     cls : 'roo-input-after input-group-addon',
8890                     html : this.after
8891                 });
8892             }
8893             if (this.after && typeof(this.after) == 'object') {
8894                 this.after = Roo.factory(this.after);
8895                 
8896                 inputblock.cn.push({
8897                     tag :'span',
8898                     cls : 'roo-input-after input-group-' +
8899                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8900                 });
8901             }
8902             
8903             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8904                 inputblock.cls += ' has-feedback';
8905                 inputblock.cn.push(feedback);
8906             }
8907         };
8908         
8909         if (align ==='left' && this.fieldLabel.length) {
8910             
8911             cfg.cls += ' roo-form-group-label-left';
8912             
8913             cfg.cn = [
8914                 {
8915                     tag : 'i',
8916                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8917                     tooltip : 'This field is required'
8918                 },
8919                 {
8920                     tag: 'label',
8921                     'for' :  id,
8922                     cls : 'control-label',
8923                     html : this.fieldLabel
8924
8925                 },
8926                 {
8927                     cls : "", 
8928                     cn: [
8929                         inputblock
8930                     ]
8931                 }
8932             ];
8933             
8934             var labelCfg = cfg.cn[1];
8935             var contentCfg = cfg.cn[2];
8936             
8937             if(this.indicatorpos == 'right'){
8938                 cfg.cn = [
8939                     {
8940                         tag: 'label',
8941                         'for' :  id,
8942                         cls : 'control-label',
8943                         cn : [
8944                             {
8945                                 tag : 'span',
8946                                 html : this.fieldLabel
8947                             },
8948                             {
8949                                 tag : 'i',
8950                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8951                                 tooltip : 'This field is required'
8952                             }
8953                         ]
8954                     },
8955                     {
8956                         cls : "",
8957                         cn: [
8958                             inputblock
8959                         ]
8960                     }
8961
8962                 ];
8963                 
8964                 labelCfg = cfg.cn[0];
8965                 contentCfg = cfg.cn[1];
8966             
8967             }
8968             
8969             if(this.labelWidth > 12){
8970                 labelCfg.style = "width: " + this.labelWidth + 'px';
8971             }
8972             
8973             if(this.labelWidth < 13 && this.labelmd == 0){
8974                 this.labelmd = this.labelWidth;
8975             }
8976             
8977             if(this.labellg > 0){
8978                 labelCfg.cls += ' col-lg-' + this.labellg;
8979                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8980             }
8981             
8982             if(this.labelmd > 0){
8983                 labelCfg.cls += ' col-md-' + this.labelmd;
8984                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8985             }
8986             
8987             if(this.labelsm > 0){
8988                 labelCfg.cls += ' col-sm-' + this.labelsm;
8989                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8990             }
8991             
8992             if(this.labelxs > 0){
8993                 labelCfg.cls += ' col-xs-' + this.labelxs;
8994                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8995             }
8996             
8997             
8998         } else if ( this.fieldLabel.length) {
8999                 
9000             cfg.cn = [
9001                 {
9002                     tag : 'i',
9003                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9004                     tooltip : 'This field is required'
9005                 },
9006                 {
9007                     tag: 'label',
9008                    //cls : 'input-group-addon',
9009                     html : this.fieldLabel
9010
9011                 },
9012
9013                inputblock
9014
9015            ];
9016            
9017            if(this.indicatorpos == 'right'){
9018                 
9019                 cfg.cn = [
9020                     {
9021                         tag: 'label',
9022                        //cls : 'input-group-addon',
9023                         html : this.fieldLabel
9024
9025                     },
9026                     {
9027                         tag : 'i',
9028                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9029                         tooltip : 'This field is required'
9030                     },
9031
9032                    inputblock
9033
9034                ];
9035
9036             }
9037
9038         } else {
9039             
9040             cfg.cn = [
9041
9042                     inputblock
9043
9044             ];
9045                 
9046                 
9047         };
9048         
9049         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9050            cfg.cls += ' navbar-form';
9051         }
9052         
9053         if (this.parentType === 'NavGroup') {
9054            cfg.cls += ' navbar-form';
9055            cfg.tag = 'li';
9056         }
9057         
9058         return cfg;
9059         
9060     },
9061     /**
9062      * return the real input element.
9063      */
9064     inputEl: function ()
9065     {
9066         return this.el.select('input.form-control',true).first();
9067     },
9068     
9069     tooltipEl : function()
9070     {
9071         return this.inputEl();
9072     },
9073     
9074     indicatorEl : function()
9075     {
9076         var indicator = this.el.select('i.roo-required-indicator',true).first();
9077         
9078         if(!indicator){
9079             return false;
9080         }
9081         
9082         return indicator;
9083         
9084     },
9085     
9086     setDisabled : function(v)
9087     {
9088         var i  = this.inputEl().dom;
9089         if (!v) {
9090             i.removeAttribute('disabled');
9091             return;
9092             
9093         }
9094         i.setAttribute('disabled','true');
9095     },
9096     initEvents : function()
9097     {
9098           
9099         this.inputEl().on("keydown" , this.fireKey,  this);
9100         this.inputEl().on("focus", this.onFocus,  this);
9101         this.inputEl().on("blur", this.onBlur,  this);
9102         
9103         this.inputEl().relayEvent('keyup', this);
9104         
9105         this.indicator = this.indicatorEl();
9106         
9107         if(this.indicator){
9108             this.indicator.addClass('invisible');
9109         }
9110  
9111         // reference to original value for reset
9112         this.originalValue = this.getValue();
9113         //Roo.form.TextField.superclass.initEvents.call(this);
9114         if(this.validationEvent == 'keyup'){
9115             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9116             this.inputEl().on('keyup', this.filterValidation, this);
9117         }
9118         else if(this.validationEvent !== false){
9119             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9120         }
9121         
9122         if(this.selectOnFocus){
9123             this.on("focus", this.preFocus, this);
9124             
9125         }
9126         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9127             this.inputEl().on("keypress", this.filterKeys, this);
9128         } else {
9129             this.inputEl().relayEvent('keypress', this);
9130         }
9131        /* if(this.grow){
9132             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9133             this.el.on("click", this.autoSize,  this);
9134         }
9135         */
9136         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9137             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9138         }
9139         
9140         if (typeof(this.before) == 'object') {
9141             this.before.render(this.el.select('.roo-input-before',true).first());
9142         }
9143         if (typeof(this.after) == 'object') {
9144             this.after.render(this.el.select('.roo-input-after',true).first());
9145         }
9146         
9147         this.inputEl().on('change', this.onChange, this);
9148         
9149     },
9150     filterValidation : function(e){
9151         if(!e.isNavKeyPress()){
9152             this.validationTask.delay(this.validationDelay);
9153         }
9154     },
9155      /**
9156      * Validates the field value
9157      * @return {Boolean} True if the value is valid, else false
9158      */
9159     validate : function(){
9160         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9161         if(this.disabled || this.validateValue(this.getRawValue())){
9162             this.markValid();
9163             return true;
9164         }
9165         
9166         this.markInvalid();
9167         return false;
9168     },
9169     
9170     
9171     /**
9172      * Validates a value according to the field's validation rules and marks the field as invalid
9173      * if the validation fails
9174      * @param {Mixed} value The value to validate
9175      * @return {Boolean} True if the value is valid, else false
9176      */
9177     validateValue : function(value)
9178     {
9179         if(this.getVisibilityEl().hasClass('hidden')){
9180             return true;
9181         }
9182         
9183         if(value.length < 1)  { // if it's blank
9184             if(this.allowBlank){
9185                 return true;
9186             }
9187             return false;
9188         }
9189         
9190         if(value.length < this.minLength){
9191             return false;
9192         }
9193         if(value.length > this.maxLength){
9194             return false;
9195         }
9196         if(this.vtype){
9197             var vt = Roo.form.VTypes;
9198             if(!vt[this.vtype](value, this)){
9199                 return false;
9200             }
9201         }
9202         if(typeof this.validator == "function"){
9203             var msg = this.validator(value);
9204             if(msg !== true){
9205                 return false;
9206             }
9207             if (typeof(msg) == 'string') {
9208                 this.invalidText = msg;
9209             }
9210         }
9211         
9212         if(this.regex && !this.regex.test(value)){
9213             return false;
9214         }
9215         
9216         return true;
9217     },
9218     
9219      // private
9220     fireKey : function(e){
9221         //Roo.log('field ' + e.getKey());
9222         if(e.isNavKeyPress()){
9223             this.fireEvent("specialkey", this, e);
9224         }
9225     },
9226     focus : function (selectText){
9227         if(this.rendered){
9228             this.inputEl().focus();
9229             if(selectText === true){
9230                 this.inputEl().dom.select();
9231             }
9232         }
9233         return this;
9234     } ,
9235     
9236     onFocus : function(){
9237         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9238            // this.el.addClass(this.focusClass);
9239         }
9240         if(!this.hasFocus){
9241             this.hasFocus = true;
9242             this.startValue = this.getValue();
9243             this.fireEvent("focus", this);
9244         }
9245     },
9246     
9247     beforeBlur : Roo.emptyFn,
9248
9249     
9250     // private
9251     onBlur : function(){
9252         this.beforeBlur();
9253         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9254             //this.el.removeClass(this.focusClass);
9255         }
9256         this.hasFocus = false;
9257         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9258             this.validate();
9259         }
9260         var v = this.getValue();
9261         if(String(v) !== String(this.startValue)){
9262             this.fireEvent('change', this, v, this.startValue);
9263         }
9264         this.fireEvent("blur", this);
9265     },
9266     
9267     onChange : function(e)
9268     {
9269         var v = this.getValue();
9270         if(String(v) !== String(this.startValue)){
9271             this.fireEvent('change', this, v, this.startValue);
9272         }
9273         
9274     },
9275     
9276     /**
9277      * Resets the current field value to the originally loaded value and clears any validation messages
9278      */
9279     reset : function(){
9280         this.setValue(this.originalValue);
9281         this.validate();
9282     },
9283      /**
9284      * Returns the name of the field
9285      * @return {Mixed} name The name field
9286      */
9287     getName: function(){
9288         return this.name;
9289     },
9290      /**
9291      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9292      * @return {Mixed} value The field value
9293      */
9294     getValue : function(){
9295         
9296         var v = this.inputEl().getValue();
9297         
9298         return v;
9299     },
9300     /**
9301      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9302      * @return {Mixed} value The field value
9303      */
9304     getRawValue : function(){
9305         var v = this.inputEl().getValue();
9306         
9307         return v;
9308     },
9309     
9310     /**
9311      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9312      * @param {Mixed} value The value to set
9313      */
9314     setRawValue : function(v){
9315         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9316     },
9317     
9318     selectText : function(start, end){
9319         var v = this.getRawValue();
9320         if(v.length > 0){
9321             start = start === undefined ? 0 : start;
9322             end = end === undefined ? v.length : end;
9323             var d = this.inputEl().dom;
9324             if(d.setSelectionRange){
9325                 d.setSelectionRange(start, end);
9326             }else if(d.createTextRange){
9327                 var range = d.createTextRange();
9328                 range.moveStart("character", start);
9329                 range.moveEnd("character", v.length-end);
9330                 range.select();
9331             }
9332         }
9333     },
9334     
9335     /**
9336      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9337      * @param {Mixed} value The value to set
9338      */
9339     setValue : function(v){
9340         this.value = v;
9341         if(this.rendered){
9342             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9343             this.validate();
9344         }
9345     },
9346     
9347     /*
9348     processValue : function(value){
9349         if(this.stripCharsRe){
9350             var newValue = value.replace(this.stripCharsRe, '');
9351             if(newValue !== value){
9352                 this.setRawValue(newValue);
9353                 return newValue;
9354             }
9355         }
9356         return value;
9357     },
9358   */
9359     preFocus : function(){
9360         
9361         if(this.selectOnFocus){
9362             this.inputEl().dom.select();
9363         }
9364     },
9365     filterKeys : function(e){
9366         var k = e.getKey();
9367         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9368             return;
9369         }
9370         var c = e.getCharCode(), cc = String.fromCharCode(c);
9371         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9372             return;
9373         }
9374         if(!this.maskRe.test(cc)){
9375             e.stopEvent();
9376         }
9377     },
9378      /**
9379      * Clear any invalid styles/messages for this field
9380      */
9381     clearInvalid : function(){
9382         
9383         if(!this.el || this.preventMark){ // not rendered
9384             return;
9385         }
9386         
9387      
9388         this.el.removeClass(this.invalidClass);
9389         
9390         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9391             
9392             var feedback = this.el.select('.form-control-feedback', true).first();
9393             
9394             if(feedback){
9395                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9396             }
9397             
9398         }
9399         
9400         this.fireEvent('valid', this);
9401     },
9402     
9403      /**
9404      * Mark this field as valid
9405      */
9406     markValid : function()
9407     {
9408         if(!this.el  || this.preventMark){ // not rendered...
9409             return;
9410         }
9411         
9412         this.el.removeClass([this.invalidClass, this.validClass]);
9413         
9414         var feedback = this.el.select('.form-control-feedback', true).first();
9415             
9416         if(feedback){
9417             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9418         }
9419         
9420         if(this.indicator){
9421             this.indicator.removeClass('visible');
9422             this.indicator.addClass('invisible');
9423         }
9424         
9425         if(this.disabled){
9426             return;
9427         }
9428         
9429         if(this.allowBlank && !this.getRawValue().length){
9430             return;
9431         }
9432         
9433         this.el.addClass(this.validClass);
9434         
9435         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9436             
9437             var feedback = this.el.select('.form-control-feedback', true).first();
9438             
9439             if(feedback){
9440                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9441                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9442             }
9443             
9444         }
9445         
9446         this.fireEvent('valid', this);
9447     },
9448     
9449      /**
9450      * Mark this field as invalid
9451      * @param {String} msg The validation message
9452      */
9453     markInvalid : function(msg)
9454     {
9455         if(!this.el  || this.preventMark){ // not rendered
9456             return;
9457         }
9458         
9459         this.el.removeClass([this.invalidClass, this.validClass]);
9460         
9461         var feedback = this.el.select('.form-control-feedback', true).first();
9462             
9463         if(feedback){
9464             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9465         }
9466
9467         if(this.disabled){
9468             return;
9469         }
9470         
9471         if(this.allowBlank && !this.getRawValue().length){
9472             return;
9473         }
9474         
9475         if(this.indicator){
9476             this.indicator.removeClass('invisible');
9477             this.indicator.addClass('visible');
9478         }
9479         
9480         this.el.addClass(this.invalidClass);
9481         
9482         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9483             
9484             var feedback = this.el.select('.form-control-feedback', true).first();
9485             
9486             if(feedback){
9487                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9488                 
9489                 if(this.getValue().length || this.forceFeedback){
9490                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9491                 }
9492                 
9493             }
9494             
9495         }
9496         
9497         this.fireEvent('invalid', this, msg);
9498     },
9499     // private
9500     SafariOnKeyDown : function(event)
9501     {
9502         // this is a workaround for a password hang bug on chrome/ webkit.
9503         if (this.inputEl().dom.type != 'password') {
9504             return;
9505         }
9506         
9507         var isSelectAll = false;
9508         
9509         if(this.inputEl().dom.selectionEnd > 0){
9510             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9511         }
9512         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9513             event.preventDefault();
9514             this.setValue('');
9515             return;
9516         }
9517         
9518         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9519             
9520             event.preventDefault();
9521             // this is very hacky as keydown always get's upper case.
9522             //
9523             var cc = String.fromCharCode(event.getCharCode());
9524             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9525             
9526         }
9527     },
9528     adjustWidth : function(tag, w){
9529         tag = tag.toLowerCase();
9530         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9531             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9532                 if(tag == 'input'){
9533                     return w + 2;
9534                 }
9535                 if(tag == 'textarea'){
9536                     return w-2;
9537                 }
9538             }else if(Roo.isOpera){
9539                 if(tag == 'input'){
9540                     return w + 2;
9541                 }
9542                 if(tag == 'textarea'){
9543                     return w-2;
9544                 }
9545             }
9546         }
9547         return w;
9548     },
9549     
9550     setFieldLabel : function(v)
9551     {
9552         if(!this.rendered){
9553             return;
9554         }
9555         
9556         if(this.indicator){
9557             var ar = this.el.select('label > span',true);
9558             
9559             if (ar.elements.length) {
9560                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9561                 this.fieldLabel = v;
9562                 return;
9563             }
9564             
9565             var br = this.el.select('label',true);
9566             
9567             if(br.elements.length) {
9568                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9569                 this.fieldLabel = v;
9570                 return;
9571             }
9572             
9573             Roo.log('Cannot Found any of label > span || label in input');
9574             return;
9575         }
9576         
9577         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9578         this.fieldLabel = v;
9579         
9580         
9581     }
9582 });
9583
9584  
9585 /*
9586  * - LGPL
9587  *
9588  * Input
9589  * 
9590  */
9591
9592 /**
9593  * @class Roo.bootstrap.TextArea
9594  * @extends Roo.bootstrap.Input
9595  * Bootstrap TextArea class
9596  * @cfg {Number} cols Specifies the visible width of a text area
9597  * @cfg {Number} rows Specifies the visible number of lines in a text area
9598  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9599  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9600  * @cfg {string} html text
9601  * 
9602  * @constructor
9603  * Create a new TextArea
9604  * @param {Object} config The config object
9605  */
9606
9607 Roo.bootstrap.TextArea = function(config){
9608     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9609    
9610 };
9611
9612 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9613      
9614     cols : false,
9615     rows : 5,
9616     readOnly : false,
9617     warp : 'soft',
9618     resize : false,
9619     value: false,
9620     html: false,
9621     
9622     getAutoCreate : function(){
9623         
9624         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9625         
9626         var id = Roo.id();
9627         
9628         var cfg = {};
9629         
9630         if(this.inputType != 'hidden'){
9631             cfg.cls = 'form-group' //input-group
9632         }
9633         
9634         var input =  {
9635             tag: 'textarea',
9636             id : id,
9637             warp : this.warp,
9638             rows : this.rows,
9639             value : this.value || '',
9640             html: this.html || '',
9641             cls : 'form-control',
9642             placeholder : this.placeholder || '' 
9643             
9644         };
9645         
9646         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9647             input.maxLength = this.maxLength;
9648         }
9649         
9650         if(this.resize){
9651             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9652         }
9653         
9654         if(this.cols){
9655             input.cols = this.cols;
9656         }
9657         
9658         if (this.readOnly) {
9659             input.readonly = true;
9660         }
9661         
9662         if (this.name) {
9663             input.name = this.name;
9664         }
9665         
9666         if (this.size) {
9667             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9668         }
9669         
9670         var settings=this;
9671         ['xs','sm','md','lg'].map(function(size){
9672             if (settings[size]) {
9673                 cfg.cls += ' col-' + size + '-' + settings[size];
9674             }
9675         });
9676         
9677         var inputblock = input;
9678         
9679         if(this.hasFeedback && !this.allowBlank){
9680             
9681             var feedback = {
9682                 tag: 'span',
9683                 cls: 'glyphicon form-control-feedback'
9684             };
9685
9686             inputblock = {
9687                 cls : 'has-feedback',
9688                 cn :  [
9689                     input,
9690                     feedback
9691                 ] 
9692             };  
9693         }
9694         
9695         
9696         if (this.before || this.after) {
9697             
9698             inputblock = {
9699                 cls : 'input-group',
9700                 cn :  [] 
9701             };
9702             if (this.before) {
9703                 inputblock.cn.push({
9704                     tag :'span',
9705                     cls : 'input-group-addon',
9706                     html : this.before
9707                 });
9708             }
9709             
9710             inputblock.cn.push(input);
9711             
9712             if(this.hasFeedback && !this.allowBlank){
9713                 inputblock.cls += ' has-feedback';
9714                 inputblock.cn.push(feedback);
9715             }
9716             
9717             if (this.after) {
9718                 inputblock.cn.push({
9719                     tag :'span',
9720                     cls : 'input-group-addon',
9721                     html : this.after
9722                 });
9723             }
9724             
9725         }
9726         
9727         if (align ==='left' && this.fieldLabel.length) {
9728             cfg.cn = [
9729                 {
9730                     tag: 'label',
9731                     'for' :  id,
9732                     cls : 'control-label',
9733                     html : this.fieldLabel
9734                 },
9735                 {
9736                     cls : "",
9737                     cn: [
9738                         inputblock
9739                     ]
9740                 }
9741
9742             ];
9743             
9744             if(this.labelWidth > 12){
9745                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9746             }
9747
9748             if(this.labelWidth < 13 && this.labelmd == 0){
9749                 this.labelmd = this.labelWidth;
9750             }
9751
9752             if(this.labellg > 0){
9753                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9754                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9755             }
9756
9757             if(this.labelmd > 0){
9758                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9759                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9760             }
9761
9762             if(this.labelsm > 0){
9763                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9764                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9765             }
9766
9767             if(this.labelxs > 0){
9768                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9769                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9770             }
9771             
9772         } else if ( this.fieldLabel.length) {
9773             cfg.cn = [
9774
9775                {
9776                    tag: 'label',
9777                    //cls : 'input-group-addon',
9778                    html : this.fieldLabel
9779
9780                },
9781
9782                inputblock
9783
9784            ];
9785
9786         } else {
9787
9788             cfg.cn = [
9789
9790                 inputblock
9791
9792             ];
9793                 
9794         }
9795         
9796         if (this.disabled) {
9797             input.disabled=true;
9798         }
9799         
9800         return cfg;
9801         
9802     },
9803     /**
9804      * return the real textarea element.
9805      */
9806     inputEl: function ()
9807     {
9808         return this.el.select('textarea.form-control',true).first();
9809     },
9810     
9811     /**
9812      * Clear any invalid styles/messages for this field
9813      */
9814     clearInvalid : function()
9815     {
9816         
9817         if(!this.el || this.preventMark){ // not rendered
9818             return;
9819         }
9820         
9821         var label = this.el.select('label', true).first();
9822         var icon = this.el.select('i.fa-star', true).first();
9823         
9824         if(label && icon){
9825             icon.remove();
9826         }
9827         
9828         this.el.removeClass(this.invalidClass);
9829         
9830         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9831             
9832             var feedback = this.el.select('.form-control-feedback', true).first();
9833             
9834             if(feedback){
9835                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9836             }
9837             
9838         }
9839         
9840         this.fireEvent('valid', this);
9841     },
9842     
9843      /**
9844      * Mark this field as valid
9845      */
9846     markValid : function()
9847     {
9848         if(!this.el  || this.preventMark){ // not rendered
9849             return;
9850         }
9851         
9852         this.el.removeClass([this.invalidClass, this.validClass]);
9853         
9854         var feedback = this.el.select('.form-control-feedback', true).first();
9855             
9856         if(feedback){
9857             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9858         }
9859
9860         if(this.disabled || this.allowBlank){
9861             return;
9862         }
9863         
9864         var label = this.el.select('label', true).first();
9865         var icon = this.el.select('i.fa-star', true).first();
9866         
9867         if(label && icon){
9868             icon.remove();
9869         }
9870         
9871         this.el.addClass(this.validClass);
9872         
9873         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9874             
9875             var feedback = this.el.select('.form-control-feedback', true).first();
9876             
9877             if(feedback){
9878                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9879                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9880             }
9881             
9882         }
9883         
9884         this.fireEvent('valid', this);
9885     },
9886     
9887      /**
9888      * Mark this field as invalid
9889      * @param {String} msg The validation message
9890      */
9891     markInvalid : function(msg)
9892     {
9893         if(!this.el  || this.preventMark){ // not rendered
9894             return;
9895         }
9896         
9897         this.el.removeClass([this.invalidClass, this.validClass]);
9898         
9899         var feedback = this.el.select('.form-control-feedback', true).first();
9900             
9901         if(feedback){
9902             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9903         }
9904
9905         if(this.disabled || this.allowBlank){
9906             return;
9907         }
9908         
9909         var label = this.el.select('label', true).first();
9910         var icon = this.el.select('i.fa-star', true).first();
9911         
9912         if(!this.getValue().length && label && !icon){
9913             this.el.createChild({
9914                 tag : 'i',
9915                 cls : 'text-danger fa fa-lg fa-star',
9916                 tooltip : 'This field is required',
9917                 style : 'margin-right:5px;'
9918             }, label, true);
9919         }
9920
9921         this.el.addClass(this.invalidClass);
9922         
9923         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9924             
9925             var feedback = this.el.select('.form-control-feedback', true).first();
9926             
9927             if(feedback){
9928                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9929                 
9930                 if(this.getValue().length || this.forceFeedback){
9931                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9932                 }
9933                 
9934             }
9935             
9936         }
9937         
9938         this.fireEvent('invalid', this, msg);
9939     }
9940 });
9941
9942  
9943 /*
9944  * - LGPL
9945  *
9946  * trigger field - base class for combo..
9947  * 
9948  */
9949  
9950 /**
9951  * @class Roo.bootstrap.TriggerField
9952  * @extends Roo.bootstrap.Input
9953  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9954  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9955  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9956  * for which you can provide a custom implementation.  For example:
9957  * <pre><code>
9958 var trigger = new Roo.bootstrap.TriggerField();
9959 trigger.onTriggerClick = myTriggerFn;
9960 trigger.applyTo('my-field');
9961 </code></pre>
9962  *
9963  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9964  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9965  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9966  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9967  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9968
9969  * @constructor
9970  * Create a new TriggerField.
9971  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9972  * to the base TextField)
9973  */
9974 Roo.bootstrap.TriggerField = function(config){
9975     this.mimicing = false;
9976     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9977 };
9978
9979 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9980     /**
9981      * @cfg {String} triggerClass A CSS class to apply to the trigger
9982      */
9983      /**
9984      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9985      */
9986     hideTrigger:false,
9987
9988     /**
9989      * @cfg {Boolean} removable (true|false) special filter default false
9990      */
9991     removable : false,
9992     
9993     /** @cfg {Boolean} grow @hide */
9994     /** @cfg {Number} growMin @hide */
9995     /** @cfg {Number} growMax @hide */
9996
9997     /**
9998      * @hide 
9999      * @method
10000      */
10001     autoSize: Roo.emptyFn,
10002     // private
10003     monitorTab : true,
10004     // private
10005     deferHeight : true,
10006
10007     
10008     actionMode : 'wrap',
10009     
10010     caret : false,
10011     
10012     
10013     getAutoCreate : function(){
10014        
10015         var align = this.labelAlign || this.parentLabelAlign();
10016         
10017         var id = Roo.id();
10018         
10019         var cfg = {
10020             cls: 'form-group' //input-group
10021         };
10022         
10023         
10024         var input =  {
10025             tag: 'input',
10026             id : id,
10027             type : this.inputType,
10028             cls : 'form-control',
10029             autocomplete: 'new-password',
10030             placeholder : this.placeholder || '' 
10031             
10032         };
10033         if (this.name) {
10034             input.name = this.name;
10035         }
10036         if (this.size) {
10037             input.cls += ' input-' + this.size;
10038         }
10039         
10040         if (this.disabled) {
10041             input.disabled=true;
10042         }
10043         
10044         var inputblock = input;
10045         
10046         if(this.hasFeedback && !this.allowBlank){
10047             
10048             var feedback = {
10049                 tag: 'span',
10050                 cls: 'glyphicon form-control-feedback'
10051             };
10052             
10053             if(this.removable && !this.editable && !this.tickable){
10054                 inputblock = {
10055                     cls : 'has-feedback',
10056                     cn :  [
10057                         inputblock,
10058                         {
10059                             tag: 'button',
10060                             html : 'x',
10061                             cls : 'roo-combo-removable-btn close'
10062                         },
10063                         feedback
10064                     ] 
10065                 };
10066             } else {
10067                 inputblock = {
10068                     cls : 'has-feedback',
10069                     cn :  [
10070                         inputblock,
10071                         feedback
10072                     ] 
10073                 };
10074             }
10075
10076         } else {
10077             if(this.removable && !this.editable && !this.tickable){
10078                 inputblock = {
10079                     cls : 'roo-removable',
10080                     cn :  [
10081                         inputblock,
10082                         {
10083                             tag: 'button',
10084                             html : 'x',
10085                             cls : 'roo-combo-removable-btn close'
10086                         }
10087                     ] 
10088                 };
10089             }
10090         }
10091         
10092         if (this.before || this.after) {
10093             
10094             inputblock = {
10095                 cls : 'input-group',
10096                 cn :  [] 
10097             };
10098             if (this.before) {
10099                 inputblock.cn.push({
10100                     tag :'span',
10101                     cls : 'input-group-addon',
10102                     html : this.before
10103                 });
10104             }
10105             
10106             inputblock.cn.push(input);
10107             
10108             if(this.hasFeedback && !this.allowBlank){
10109                 inputblock.cls += ' has-feedback';
10110                 inputblock.cn.push(feedback);
10111             }
10112             
10113             if (this.after) {
10114                 inputblock.cn.push({
10115                     tag :'span',
10116                     cls : 'input-group-addon',
10117                     html : this.after
10118                 });
10119             }
10120             
10121         };
10122         
10123         var box = {
10124             tag: 'div',
10125             cn: [
10126                 {
10127                     tag: 'input',
10128                     type : 'hidden',
10129                     cls: 'form-hidden-field'
10130                 },
10131                 inputblock
10132             ]
10133             
10134         };
10135         
10136         if(this.multiple){
10137             box = {
10138                 tag: 'div',
10139                 cn: [
10140                     {
10141                         tag: 'input',
10142                         type : 'hidden',
10143                         cls: 'form-hidden-field'
10144                     },
10145                     {
10146                         tag: 'ul',
10147                         cls: 'roo-select2-choices',
10148                         cn:[
10149                             {
10150                                 tag: 'li',
10151                                 cls: 'roo-select2-search-field',
10152                                 cn: [
10153
10154                                     inputblock
10155                                 ]
10156                             }
10157                         ]
10158                     }
10159                 ]
10160             }
10161         };
10162         
10163         var combobox = {
10164             cls: 'roo-select2-container input-group',
10165             cn: [
10166                 box
10167 //                {
10168 //                    tag: 'ul',
10169 //                    cls: 'typeahead typeahead-long dropdown-menu',
10170 //                    style: 'display:none'
10171 //                }
10172             ]
10173         };
10174         
10175         if(!this.multiple && this.showToggleBtn){
10176             
10177             var caret = {
10178                         tag: 'span',
10179                         cls: 'caret'
10180              };
10181             if (this.caret != false) {
10182                 caret = {
10183                      tag: 'i',
10184                      cls: 'fa fa-' + this.caret
10185                 };
10186                 
10187             }
10188             
10189             combobox.cn.push({
10190                 tag :'span',
10191                 cls : 'input-group-addon btn dropdown-toggle',
10192                 cn : [
10193                     caret,
10194                     {
10195                         tag: 'span',
10196                         cls: 'combobox-clear',
10197                         cn  : [
10198                             {
10199                                 tag : 'i',
10200                                 cls: 'icon-remove'
10201                             }
10202                         ]
10203                     }
10204                 ]
10205
10206             })
10207         }
10208         
10209         if(this.multiple){
10210             combobox.cls += ' roo-select2-container-multi';
10211         }
10212         
10213         if (align ==='left' && this.fieldLabel.length) {
10214             
10215             cfg.cls += ' roo-form-group-label-left';
10216
10217             cfg.cn = [
10218                 {
10219                     tag : 'i',
10220                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10221                     tooltip : 'This field is required'
10222                 },
10223                 {
10224                     tag: 'label',
10225                     'for' :  id,
10226                     cls : 'control-label',
10227                     html : this.fieldLabel
10228
10229                 },
10230                 {
10231                     cls : "", 
10232                     cn: [
10233                         combobox
10234                     ]
10235                 }
10236
10237             ];
10238             
10239             var labelCfg = cfg.cn[1];
10240             var contentCfg = cfg.cn[2];
10241             
10242             if(this.indicatorpos == 'right'){
10243                 cfg.cn = [
10244                     {
10245                         tag: 'label',
10246                         'for' :  id,
10247                         cls : 'control-label',
10248                         cn : [
10249                             {
10250                                 tag : 'span',
10251                                 html : this.fieldLabel
10252                             },
10253                             {
10254                                 tag : 'i',
10255                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10256                                 tooltip : 'This field is required'
10257                             }
10258                         ]
10259                     },
10260                     {
10261                         cls : "", 
10262                         cn: [
10263                             combobox
10264                         ]
10265                     }
10266
10267                 ];
10268                 
10269                 labelCfg = cfg.cn[0];
10270                 contentCfg = cfg.cn[1];
10271             }
10272             
10273             if(this.labelWidth > 12){
10274                 labelCfg.style = "width: " + this.labelWidth + 'px';
10275             }
10276             
10277             if(this.labelWidth < 13 && this.labelmd == 0){
10278                 this.labelmd = this.labelWidth;
10279             }
10280             
10281             if(this.labellg > 0){
10282                 labelCfg.cls += ' col-lg-' + this.labellg;
10283                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10284             }
10285             
10286             if(this.labelmd > 0){
10287                 labelCfg.cls += ' col-md-' + this.labelmd;
10288                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10289             }
10290             
10291             if(this.labelsm > 0){
10292                 labelCfg.cls += ' col-sm-' + this.labelsm;
10293                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10294             }
10295             
10296             if(this.labelxs > 0){
10297                 labelCfg.cls += ' col-xs-' + this.labelxs;
10298                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10299             }
10300             
10301         } else if ( this.fieldLabel.length) {
10302 //                Roo.log(" label");
10303             cfg.cn = [
10304                 {
10305                    tag : 'i',
10306                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10307                    tooltip : 'This field is required'
10308                },
10309                {
10310                    tag: 'label',
10311                    //cls : 'input-group-addon',
10312                    html : this.fieldLabel
10313
10314                },
10315
10316                combobox
10317
10318             ];
10319             
10320             if(this.indicatorpos == 'right'){
10321                 
10322                 cfg.cn = [
10323                     {
10324                        tag: 'label',
10325                        cn : [
10326                            {
10327                                tag : 'span',
10328                                html : this.fieldLabel
10329                            },
10330                            {
10331                               tag : 'i',
10332                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10333                               tooltip : 'This field is required'
10334                            }
10335                        ]
10336
10337                     },
10338                     combobox
10339
10340                 ];
10341
10342             }
10343
10344         } else {
10345             
10346 //                Roo.log(" no label && no align");
10347                 cfg = combobox
10348                      
10349                 
10350         }
10351         
10352         var settings=this;
10353         ['xs','sm','md','lg'].map(function(size){
10354             if (settings[size]) {
10355                 cfg.cls += ' col-' + size + '-' + settings[size];
10356             }
10357         });
10358         
10359         return cfg;
10360         
10361     },
10362     
10363     
10364     
10365     // private
10366     onResize : function(w, h){
10367 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10368 //        if(typeof w == 'number'){
10369 //            var x = w - this.trigger.getWidth();
10370 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10371 //            this.trigger.setStyle('left', x+'px');
10372 //        }
10373     },
10374
10375     // private
10376     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10377
10378     // private
10379     getResizeEl : function(){
10380         return this.inputEl();
10381     },
10382
10383     // private
10384     getPositionEl : function(){
10385         return this.inputEl();
10386     },
10387
10388     // private
10389     alignErrorIcon : function(){
10390         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10391     },
10392
10393     // private
10394     initEvents : function(){
10395         
10396         this.createList();
10397         
10398         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10399         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10400         if(!this.multiple && this.showToggleBtn){
10401             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10402             if(this.hideTrigger){
10403                 this.trigger.setDisplayed(false);
10404             }
10405             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10406         }
10407         
10408         if(this.multiple){
10409             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10410         }
10411         
10412         if(this.removable && !this.editable && !this.tickable){
10413             var close = this.closeTriggerEl();
10414             
10415             if(close){
10416                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10417                 close.on('click', this.removeBtnClick, this, close);
10418             }
10419         }
10420         
10421         //this.trigger.addClassOnOver('x-form-trigger-over');
10422         //this.trigger.addClassOnClick('x-form-trigger-click');
10423         
10424         //if(!this.width){
10425         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10426         //}
10427     },
10428     
10429     closeTriggerEl : function()
10430     {
10431         var close = this.el.select('.roo-combo-removable-btn', true).first();
10432         return close ? close : false;
10433     },
10434     
10435     removeBtnClick : function(e, h, el)
10436     {
10437         e.preventDefault();
10438         
10439         if(this.fireEvent("remove", this) !== false){
10440             this.reset();
10441             this.fireEvent("afterremove", this)
10442         }
10443     },
10444     
10445     createList : function()
10446     {
10447         this.list = Roo.get(document.body).createChild({
10448             tag: 'ul',
10449             cls: 'typeahead typeahead-long dropdown-menu',
10450             style: 'display:none'
10451         });
10452         
10453         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10454         
10455     },
10456
10457     // private
10458     initTrigger : function(){
10459        
10460     },
10461
10462     // private
10463     onDestroy : function(){
10464         if(this.trigger){
10465             this.trigger.removeAllListeners();
10466           //  this.trigger.remove();
10467         }
10468         //if(this.wrap){
10469         //    this.wrap.remove();
10470         //}
10471         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10472     },
10473
10474     // private
10475     onFocus : function(){
10476         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10477         /*
10478         if(!this.mimicing){
10479             this.wrap.addClass('x-trigger-wrap-focus');
10480             this.mimicing = true;
10481             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10482             if(this.monitorTab){
10483                 this.el.on("keydown", this.checkTab, this);
10484             }
10485         }
10486         */
10487     },
10488
10489     // private
10490     checkTab : function(e){
10491         if(e.getKey() == e.TAB){
10492             this.triggerBlur();
10493         }
10494     },
10495
10496     // private
10497     onBlur : function(){
10498         // do nothing
10499     },
10500
10501     // private
10502     mimicBlur : function(e, t){
10503         /*
10504         if(!this.wrap.contains(t) && this.validateBlur()){
10505             this.triggerBlur();
10506         }
10507         */
10508     },
10509
10510     // private
10511     triggerBlur : function(){
10512         this.mimicing = false;
10513         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10514         if(this.monitorTab){
10515             this.el.un("keydown", this.checkTab, this);
10516         }
10517         //this.wrap.removeClass('x-trigger-wrap-focus');
10518         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10519     },
10520
10521     // private
10522     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10523     validateBlur : function(e, t){
10524         return true;
10525     },
10526
10527     // private
10528     onDisable : function(){
10529         this.inputEl().dom.disabled = true;
10530         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10531         //if(this.wrap){
10532         //    this.wrap.addClass('x-item-disabled');
10533         //}
10534     },
10535
10536     // private
10537     onEnable : function(){
10538         this.inputEl().dom.disabled = false;
10539         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10540         //if(this.wrap){
10541         //    this.el.removeClass('x-item-disabled');
10542         //}
10543     },
10544
10545     // private
10546     onShow : function(){
10547         var ae = this.getActionEl();
10548         
10549         if(ae){
10550             ae.dom.style.display = '';
10551             ae.dom.style.visibility = 'visible';
10552         }
10553     },
10554
10555     // private
10556     
10557     onHide : function(){
10558         var ae = this.getActionEl();
10559         ae.dom.style.display = 'none';
10560     },
10561
10562     /**
10563      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10564      * by an implementing function.
10565      * @method
10566      * @param {EventObject} e
10567      */
10568     onTriggerClick : Roo.emptyFn
10569 });
10570  /*
10571  * Based on:
10572  * Ext JS Library 1.1.1
10573  * Copyright(c) 2006-2007, Ext JS, LLC.
10574  *
10575  * Originally Released Under LGPL - original licence link has changed is not relivant.
10576  *
10577  * Fork - LGPL
10578  * <script type="text/javascript">
10579  */
10580
10581
10582 /**
10583  * @class Roo.data.SortTypes
10584  * @singleton
10585  * Defines the default sorting (casting?) comparison functions used when sorting data.
10586  */
10587 Roo.data.SortTypes = {
10588     /**
10589      * Default sort that does nothing
10590      * @param {Mixed} s The value being converted
10591      * @return {Mixed} The comparison value
10592      */
10593     none : function(s){
10594         return s;
10595     },
10596     
10597     /**
10598      * The regular expression used to strip tags
10599      * @type {RegExp}
10600      * @property
10601      */
10602     stripTagsRE : /<\/?[^>]+>/gi,
10603     
10604     /**
10605      * Strips all HTML tags to sort on text only
10606      * @param {Mixed} s The value being converted
10607      * @return {String} The comparison value
10608      */
10609     asText : function(s){
10610         return String(s).replace(this.stripTagsRE, "");
10611     },
10612     
10613     /**
10614      * Strips all HTML tags to sort on text only - Case insensitive
10615      * @param {Mixed} s The value being converted
10616      * @return {String} The comparison value
10617      */
10618     asUCText : function(s){
10619         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10620     },
10621     
10622     /**
10623      * Case insensitive string
10624      * @param {Mixed} s The value being converted
10625      * @return {String} The comparison value
10626      */
10627     asUCString : function(s) {
10628         return String(s).toUpperCase();
10629     },
10630     
10631     /**
10632      * Date sorting
10633      * @param {Mixed} s The value being converted
10634      * @return {Number} The comparison value
10635      */
10636     asDate : function(s) {
10637         if(!s){
10638             return 0;
10639         }
10640         if(s instanceof Date){
10641             return s.getTime();
10642         }
10643         return Date.parse(String(s));
10644     },
10645     
10646     /**
10647      * Float sorting
10648      * @param {Mixed} s The value being converted
10649      * @return {Float} The comparison value
10650      */
10651     asFloat : function(s) {
10652         var val = parseFloat(String(s).replace(/,/g, ""));
10653         if(isNaN(val)) {
10654             val = 0;
10655         }
10656         return val;
10657     },
10658     
10659     /**
10660      * Integer sorting
10661      * @param {Mixed} s The value being converted
10662      * @return {Number} The comparison value
10663      */
10664     asInt : function(s) {
10665         var val = parseInt(String(s).replace(/,/g, ""));
10666         if(isNaN(val)) {
10667             val = 0;
10668         }
10669         return val;
10670     }
10671 };/*
10672  * Based on:
10673  * Ext JS Library 1.1.1
10674  * Copyright(c) 2006-2007, Ext JS, LLC.
10675  *
10676  * Originally Released Under LGPL - original licence link has changed is not relivant.
10677  *
10678  * Fork - LGPL
10679  * <script type="text/javascript">
10680  */
10681
10682 /**
10683 * @class Roo.data.Record
10684  * Instances of this class encapsulate both record <em>definition</em> information, and record
10685  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10686  * to access Records cached in an {@link Roo.data.Store} object.<br>
10687  * <p>
10688  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10689  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10690  * objects.<br>
10691  * <p>
10692  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10693  * @constructor
10694  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10695  * {@link #create}. The parameters are the same.
10696  * @param {Array} data An associative Array of data values keyed by the field name.
10697  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10698  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10699  * not specified an integer id is generated.
10700  */
10701 Roo.data.Record = function(data, id){
10702     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10703     this.data = data;
10704 };
10705
10706 /**
10707  * Generate a constructor for a specific record layout.
10708  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10709  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10710  * Each field definition object may contain the following properties: <ul>
10711  * <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,
10712  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10713  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10714  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10715  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10716  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10717  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10718  * this may be omitted.</p></li>
10719  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10720  * <ul><li>auto (Default, implies no conversion)</li>
10721  * <li>string</li>
10722  * <li>int</li>
10723  * <li>float</li>
10724  * <li>boolean</li>
10725  * <li>date</li></ul></p></li>
10726  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10727  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10728  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10729  * by the Reader into an object that will be stored in the Record. It is passed the
10730  * following parameters:<ul>
10731  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10732  * </ul></p></li>
10733  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10734  * </ul>
10735  * <br>usage:<br><pre><code>
10736 var TopicRecord = Roo.data.Record.create(
10737     {name: 'title', mapping: 'topic_title'},
10738     {name: 'author', mapping: 'username'},
10739     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10740     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10741     {name: 'lastPoster', mapping: 'user2'},
10742     {name: 'excerpt', mapping: 'post_text'}
10743 );
10744
10745 var myNewRecord = new TopicRecord({
10746     title: 'Do my job please',
10747     author: 'noobie',
10748     totalPosts: 1,
10749     lastPost: new Date(),
10750     lastPoster: 'Animal',
10751     excerpt: 'No way dude!'
10752 });
10753 myStore.add(myNewRecord);
10754 </code></pre>
10755  * @method create
10756  * @static
10757  */
10758 Roo.data.Record.create = function(o){
10759     var f = function(){
10760         f.superclass.constructor.apply(this, arguments);
10761     };
10762     Roo.extend(f, Roo.data.Record);
10763     var p = f.prototype;
10764     p.fields = new Roo.util.MixedCollection(false, function(field){
10765         return field.name;
10766     });
10767     for(var i = 0, len = o.length; i < len; i++){
10768         p.fields.add(new Roo.data.Field(o[i]));
10769     }
10770     f.getField = function(name){
10771         return p.fields.get(name);  
10772     };
10773     return f;
10774 };
10775
10776 Roo.data.Record.AUTO_ID = 1000;
10777 Roo.data.Record.EDIT = 'edit';
10778 Roo.data.Record.REJECT = 'reject';
10779 Roo.data.Record.COMMIT = 'commit';
10780
10781 Roo.data.Record.prototype = {
10782     /**
10783      * Readonly flag - true if this record has been modified.
10784      * @type Boolean
10785      */
10786     dirty : false,
10787     editing : false,
10788     error: null,
10789     modified: null,
10790
10791     // private
10792     join : function(store){
10793         this.store = store;
10794     },
10795
10796     /**
10797      * Set the named field to the specified value.
10798      * @param {String} name The name of the field to set.
10799      * @param {Object} value The value to set the field to.
10800      */
10801     set : function(name, value){
10802         if(this.data[name] == value){
10803             return;
10804         }
10805         this.dirty = true;
10806         if(!this.modified){
10807             this.modified = {};
10808         }
10809         if(typeof this.modified[name] == 'undefined'){
10810             this.modified[name] = this.data[name];
10811         }
10812         this.data[name] = value;
10813         if(!this.editing && this.store){
10814             this.store.afterEdit(this);
10815         }       
10816     },
10817
10818     /**
10819      * Get the value of the named field.
10820      * @param {String} name The name of the field to get the value of.
10821      * @return {Object} The value of the field.
10822      */
10823     get : function(name){
10824         return this.data[name]; 
10825     },
10826
10827     // private
10828     beginEdit : function(){
10829         this.editing = true;
10830         this.modified = {}; 
10831     },
10832
10833     // private
10834     cancelEdit : function(){
10835         this.editing = false;
10836         delete this.modified;
10837     },
10838
10839     // private
10840     endEdit : function(){
10841         this.editing = false;
10842         if(this.dirty && this.store){
10843             this.store.afterEdit(this);
10844         }
10845     },
10846
10847     /**
10848      * Usually called by the {@link Roo.data.Store} which owns the Record.
10849      * Rejects all changes made to the Record since either creation, or the last commit operation.
10850      * Modified fields are reverted to their original values.
10851      * <p>
10852      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10853      * of reject operations.
10854      */
10855     reject : function(){
10856         var m = this.modified;
10857         for(var n in m){
10858             if(typeof m[n] != "function"){
10859                 this.data[n] = m[n];
10860             }
10861         }
10862         this.dirty = false;
10863         delete this.modified;
10864         this.editing = false;
10865         if(this.store){
10866             this.store.afterReject(this);
10867         }
10868     },
10869
10870     /**
10871      * Usually called by the {@link Roo.data.Store} which owns the Record.
10872      * Commits all changes made to the Record since either creation, or the last commit operation.
10873      * <p>
10874      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10875      * of commit operations.
10876      */
10877     commit : function(){
10878         this.dirty = false;
10879         delete this.modified;
10880         this.editing = false;
10881         if(this.store){
10882             this.store.afterCommit(this);
10883         }
10884     },
10885
10886     // private
10887     hasError : function(){
10888         return this.error != null;
10889     },
10890
10891     // private
10892     clearError : function(){
10893         this.error = null;
10894     },
10895
10896     /**
10897      * Creates a copy of this record.
10898      * @param {String} id (optional) A new record id if you don't want to use this record's id
10899      * @return {Record}
10900      */
10901     copy : function(newId) {
10902         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10903     }
10904 };/*
10905  * Based on:
10906  * Ext JS Library 1.1.1
10907  * Copyright(c) 2006-2007, Ext JS, LLC.
10908  *
10909  * Originally Released Under LGPL - original licence link has changed is not relivant.
10910  *
10911  * Fork - LGPL
10912  * <script type="text/javascript">
10913  */
10914
10915
10916
10917 /**
10918  * @class Roo.data.Store
10919  * @extends Roo.util.Observable
10920  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10921  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10922  * <p>
10923  * 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
10924  * has no knowledge of the format of the data returned by the Proxy.<br>
10925  * <p>
10926  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10927  * instances from the data object. These records are cached and made available through accessor functions.
10928  * @constructor
10929  * Creates a new Store.
10930  * @param {Object} config A config object containing the objects needed for the Store to access data,
10931  * and read the data into Records.
10932  */
10933 Roo.data.Store = function(config){
10934     this.data = new Roo.util.MixedCollection(false);
10935     this.data.getKey = function(o){
10936         return o.id;
10937     };
10938     this.baseParams = {};
10939     // private
10940     this.paramNames = {
10941         "start" : "start",
10942         "limit" : "limit",
10943         "sort" : "sort",
10944         "dir" : "dir",
10945         "multisort" : "_multisort"
10946     };
10947
10948     if(config && config.data){
10949         this.inlineData = config.data;
10950         delete config.data;
10951     }
10952
10953     Roo.apply(this, config);
10954     
10955     if(this.reader){ // reader passed
10956         this.reader = Roo.factory(this.reader, Roo.data);
10957         this.reader.xmodule = this.xmodule || false;
10958         if(!this.recordType){
10959             this.recordType = this.reader.recordType;
10960         }
10961         if(this.reader.onMetaChange){
10962             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10963         }
10964     }
10965
10966     if(this.recordType){
10967         this.fields = this.recordType.prototype.fields;
10968     }
10969     this.modified = [];
10970
10971     this.addEvents({
10972         /**
10973          * @event datachanged
10974          * Fires when the data cache has changed, and a widget which is using this Store
10975          * as a Record cache should refresh its view.
10976          * @param {Store} this
10977          */
10978         datachanged : true,
10979         /**
10980          * @event metachange
10981          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10982          * @param {Store} this
10983          * @param {Object} meta The JSON metadata
10984          */
10985         metachange : true,
10986         /**
10987          * @event add
10988          * Fires when Records have been added to the Store
10989          * @param {Store} this
10990          * @param {Roo.data.Record[]} records The array of Records added
10991          * @param {Number} index The index at which the record(s) were added
10992          */
10993         add : true,
10994         /**
10995          * @event remove
10996          * Fires when a Record has been removed from the Store
10997          * @param {Store} this
10998          * @param {Roo.data.Record} record The Record that was removed
10999          * @param {Number} index The index at which the record was removed
11000          */
11001         remove : true,
11002         /**
11003          * @event update
11004          * Fires when a Record has been updated
11005          * @param {Store} this
11006          * @param {Roo.data.Record} record The Record that was updated
11007          * @param {String} operation The update operation being performed.  Value may be one of:
11008          * <pre><code>
11009  Roo.data.Record.EDIT
11010  Roo.data.Record.REJECT
11011  Roo.data.Record.COMMIT
11012          * </code></pre>
11013          */
11014         update : true,
11015         /**
11016          * @event clear
11017          * Fires when the data cache has been cleared.
11018          * @param {Store} this
11019          */
11020         clear : true,
11021         /**
11022          * @event beforeload
11023          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11024          * the load action will be canceled.
11025          * @param {Store} this
11026          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11027          */
11028         beforeload : true,
11029         /**
11030          * @event beforeloadadd
11031          * Fires after a new set of Records has been loaded.
11032          * @param {Store} this
11033          * @param {Roo.data.Record[]} records The Records that were loaded
11034          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11035          */
11036         beforeloadadd : true,
11037         /**
11038          * @event load
11039          * Fires after a new set of Records has been loaded, before they are added to the store.
11040          * @param {Store} this
11041          * @param {Roo.data.Record[]} records The Records that were loaded
11042          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11043          * @params {Object} return from reader
11044          */
11045         load : true,
11046         /**
11047          * @event loadexception
11048          * Fires if an exception occurs in the Proxy during loading.
11049          * Called with the signature of the Proxy's "loadexception" event.
11050          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11051          * 
11052          * @param {Proxy} 
11053          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11054          * @param {Object} load options 
11055          * @param {Object} jsonData from your request (normally this contains the Exception)
11056          */
11057         loadexception : true
11058     });
11059     
11060     if(this.proxy){
11061         this.proxy = Roo.factory(this.proxy, Roo.data);
11062         this.proxy.xmodule = this.xmodule || false;
11063         this.relayEvents(this.proxy,  ["loadexception"]);
11064     }
11065     this.sortToggle = {};
11066     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11067
11068     Roo.data.Store.superclass.constructor.call(this);
11069
11070     if(this.inlineData){
11071         this.loadData(this.inlineData);
11072         delete this.inlineData;
11073     }
11074 };
11075
11076 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11077      /**
11078     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11079     * without a remote query - used by combo/forms at present.
11080     */
11081     
11082     /**
11083     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11084     */
11085     /**
11086     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11087     */
11088     /**
11089     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11090     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11091     */
11092     /**
11093     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11094     * on any HTTP request
11095     */
11096     /**
11097     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11098     */
11099     /**
11100     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11101     */
11102     multiSort: false,
11103     /**
11104     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11105     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11106     */
11107     remoteSort : false,
11108
11109     /**
11110     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11111      * loaded or when a record is removed. (defaults to false).
11112     */
11113     pruneModifiedRecords : false,
11114
11115     // private
11116     lastOptions : null,
11117
11118     /**
11119      * Add Records to the Store and fires the add event.
11120      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11121      */
11122     add : function(records){
11123         records = [].concat(records);
11124         for(var i = 0, len = records.length; i < len; i++){
11125             records[i].join(this);
11126         }
11127         var index = this.data.length;
11128         this.data.addAll(records);
11129         this.fireEvent("add", this, records, index);
11130     },
11131
11132     /**
11133      * Remove a Record from the Store and fires the remove event.
11134      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11135      */
11136     remove : function(record){
11137         var index = this.data.indexOf(record);
11138         this.data.removeAt(index);
11139  
11140         if(this.pruneModifiedRecords){
11141             this.modified.remove(record);
11142         }
11143         this.fireEvent("remove", this, record, index);
11144     },
11145
11146     /**
11147      * Remove all Records from the Store and fires the clear event.
11148      */
11149     removeAll : function(){
11150         this.data.clear();
11151         if(this.pruneModifiedRecords){
11152             this.modified = [];
11153         }
11154         this.fireEvent("clear", this);
11155     },
11156
11157     /**
11158      * Inserts Records to the Store at the given index and fires the add event.
11159      * @param {Number} index The start index at which to insert the passed Records.
11160      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11161      */
11162     insert : function(index, records){
11163         records = [].concat(records);
11164         for(var i = 0, len = records.length; i < len; i++){
11165             this.data.insert(index, records[i]);
11166             records[i].join(this);
11167         }
11168         this.fireEvent("add", this, records, index);
11169     },
11170
11171     /**
11172      * Get the index within the cache of the passed Record.
11173      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11174      * @return {Number} The index of the passed Record. Returns -1 if not found.
11175      */
11176     indexOf : function(record){
11177         return this.data.indexOf(record);
11178     },
11179
11180     /**
11181      * Get the index within the cache of the Record with the passed id.
11182      * @param {String} id The id of the Record to find.
11183      * @return {Number} The index of the Record. Returns -1 if not found.
11184      */
11185     indexOfId : function(id){
11186         return this.data.indexOfKey(id);
11187     },
11188
11189     /**
11190      * Get the Record with the specified id.
11191      * @param {String} id The id of the Record to find.
11192      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11193      */
11194     getById : function(id){
11195         return this.data.key(id);
11196     },
11197
11198     /**
11199      * Get the Record at the specified index.
11200      * @param {Number} index The index of the Record to find.
11201      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11202      */
11203     getAt : function(index){
11204         return this.data.itemAt(index);
11205     },
11206
11207     /**
11208      * Returns a range of Records between specified indices.
11209      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11210      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11211      * @return {Roo.data.Record[]} An array of Records
11212      */
11213     getRange : function(start, end){
11214         return this.data.getRange(start, end);
11215     },
11216
11217     // private
11218     storeOptions : function(o){
11219         o = Roo.apply({}, o);
11220         delete o.callback;
11221         delete o.scope;
11222         this.lastOptions = o;
11223     },
11224
11225     /**
11226      * Loads the Record cache from the configured Proxy using the configured Reader.
11227      * <p>
11228      * If using remote paging, then the first load call must specify the <em>start</em>
11229      * and <em>limit</em> properties in the options.params property to establish the initial
11230      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11231      * <p>
11232      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11233      * and this call will return before the new data has been loaded. Perform any post-processing
11234      * in a callback function, or in a "load" event handler.</strong>
11235      * <p>
11236      * @param {Object} options An object containing properties which control loading options:<ul>
11237      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11238      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11239      * passed the following arguments:<ul>
11240      * <li>r : Roo.data.Record[]</li>
11241      * <li>options: Options object from the load call</li>
11242      * <li>success: Boolean success indicator</li></ul></li>
11243      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11244      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11245      * </ul>
11246      */
11247     load : function(options){
11248         options = options || {};
11249         if(this.fireEvent("beforeload", this, options) !== false){
11250             this.storeOptions(options);
11251             var p = Roo.apply(options.params || {}, this.baseParams);
11252             // if meta was not loaded from remote source.. try requesting it.
11253             if (!this.reader.metaFromRemote) {
11254                 p._requestMeta = 1;
11255             }
11256             if(this.sortInfo && this.remoteSort){
11257                 var pn = this.paramNames;
11258                 p[pn["sort"]] = this.sortInfo.field;
11259                 p[pn["dir"]] = this.sortInfo.direction;
11260             }
11261             if (this.multiSort) {
11262                 var pn = this.paramNames;
11263                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11264             }
11265             
11266             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11267         }
11268     },
11269
11270     /**
11271      * Reloads the Record cache from the configured Proxy using the configured Reader and
11272      * the options from the last load operation performed.
11273      * @param {Object} options (optional) An object containing properties which may override the options
11274      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11275      * the most recently used options are reused).
11276      */
11277     reload : function(options){
11278         this.load(Roo.applyIf(options||{}, this.lastOptions));
11279     },
11280
11281     // private
11282     // Called as a callback by the Reader during a load operation.
11283     loadRecords : function(o, options, success){
11284         if(!o || success === false){
11285             if(success !== false){
11286                 this.fireEvent("load", this, [], options, o);
11287             }
11288             if(options.callback){
11289                 options.callback.call(options.scope || this, [], options, false);
11290             }
11291             return;
11292         }
11293         // if data returned failure - throw an exception.
11294         if (o.success === false) {
11295             // show a message if no listener is registered.
11296             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11297                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11298             }
11299             // loadmask wil be hooked into this..
11300             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11301             return;
11302         }
11303         var r = o.records, t = o.totalRecords || r.length;
11304         
11305         this.fireEvent("beforeloadadd", this, r, options, o);
11306         
11307         if(!options || options.add !== true){
11308             if(this.pruneModifiedRecords){
11309                 this.modified = [];
11310             }
11311             for(var i = 0, len = r.length; i < len; i++){
11312                 r[i].join(this);
11313             }
11314             if(this.snapshot){
11315                 this.data = this.snapshot;
11316                 delete this.snapshot;
11317             }
11318             this.data.clear();
11319             this.data.addAll(r);
11320             this.totalLength = t;
11321             this.applySort();
11322             this.fireEvent("datachanged", this);
11323         }else{
11324             this.totalLength = Math.max(t, this.data.length+r.length);
11325             this.add(r);
11326         }
11327         
11328         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11329                 
11330             var e = new Roo.data.Record({});
11331
11332             e.set(this.parent.displayField, this.parent.emptyTitle);
11333             e.set(this.parent.valueField, '');
11334
11335             this.insert(0, e);
11336         }
11337             
11338         this.fireEvent("load", this, r, options, o);
11339         if(options.callback){
11340             options.callback.call(options.scope || this, r, options, true);
11341         }
11342     },
11343
11344
11345     /**
11346      * Loads data from a passed data block. A Reader which understands the format of the data
11347      * must have been configured in the constructor.
11348      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11349      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11350      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11351      */
11352     loadData : function(o, append){
11353         var r = this.reader.readRecords(o);
11354         this.loadRecords(r, {add: append}, true);
11355     },
11356
11357     /**
11358      * Gets the number of cached records.
11359      * <p>
11360      * <em>If using paging, this may not be the total size of the dataset. If the data object
11361      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11362      * the data set size</em>
11363      */
11364     getCount : function(){
11365         return this.data.length || 0;
11366     },
11367
11368     /**
11369      * Gets the total number of records in the dataset as returned by the server.
11370      * <p>
11371      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11372      * the dataset size</em>
11373      */
11374     getTotalCount : function(){
11375         return this.totalLength || 0;
11376     },
11377
11378     /**
11379      * Returns the sort state of the Store as an object with two properties:
11380      * <pre><code>
11381  field {String} The name of the field by which the Records are sorted
11382  direction {String} The sort order, "ASC" or "DESC"
11383      * </code></pre>
11384      */
11385     getSortState : function(){
11386         return this.sortInfo;
11387     },
11388
11389     // private
11390     applySort : function(){
11391         if(this.sortInfo && !this.remoteSort){
11392             var s = this.sortInfo, f = s.field;
11393             var st = this.fields.get(f).sortType;
11394             var fn = function(r1, r2){
11395                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11396                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11397             };
11398             this.data.sort(s.direction, fn);
11399             if(this.snapshot && this.snapshot != this.data){
11400                 this.snapshot.sort(s.direction, fn);
11401             }
11402         }
11403     },
11404
11405     /**
11406      * Sets the default sort column and order to be used by the next load operation.
11407      * @param {String} fieldName The name of the field to sort by.
11408      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11409      */
11410     setDefaultSort : function(field, dir){
11411         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11412     },
11413
11414     /**
11415      * Sort the Records.
11416      * If remote sorting is used, the sort is performed on the server, and the cache is
11417      * reloaded. If local sorting is used, the cache is sorted internally.
11418      * @param {String} fieldName The name of the field to sort by.
11419      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11420      */
11421     sort : function(fieldName, dir){
11422         var f = this.fields.get(fieldName);
11423         if(!dir){
11424             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11425             
11426             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11427                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11428             }else{
11429                 dir = f.sortDir;
11430             }
11431         }
11432         this.sortToggle[f.name] = dir;
11433         this.sortInfo = {field: f.name, direction: dir};
11434         if(!this.remoteSort){
11435             this.applySort();
11436             this.fireEvent("datachanged", this);
11437         }else{
11438             this.load(this.lastOptions);
11439         }
11440     },
11441
11442     /**
11443      * Calls the specified function for each of the Records in the cache.
11444      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11445      * Returning <em>false</em> aborts and exits the iteration.
11446      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11447      */
11448     each : function(fn, scope){
11449         this.data.each(fn, scope);
11450     },
11451
11452     /**
11453      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11454      * (e.g., during paging).
11455      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11456      */
11457     getModifiedRecords : function(){
11458         return this.modified;
11459     },
11460
11461     // private
11462     createFilterFn : function(property, value, anyMatch){
11463         if(!value.exec){ // not a regex
11464             value = String(value);
11465             if(value.length == 0){
11466                 return false;
11467             }
11468             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11469         }
11470         return function(r){
11471             return value.test(r.data[property]);
11472         };
11473     },
11474
11475     /**
11476      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11477      * @param {String} property A field on your records
11478      * @param {Number} start The record index to start at (defaults to 0)
11479      * @param {Number} end The last record index to include (defaults to length - 1)
11480      * @return {Number} The sum
11481      */
11482     sum : function(property, start, end){
11483         var rs = this.data.items, v = 0;
11484         start = start || 0;
11485         end = (end || end === 0) ? end : rs.length-1;
11486
11487         for(var i = start; i <= end; i++){
11488             v += (rs[i].data[property] || 0);
11489         }
11490         return v;
11491     },
11492
11493     /**
11494      * Filter the records by a specified property.
11495      * @param {String} field A field on your records
11496      * @param {String/RegExp} value Either a string that the field
11497      * should start with or a RegExp to test against the field
11498      * @param {Boolean} anyMatch True to match any part not just the beginning
11499      */
11500     filter : function(property, value, anyMatch){
11501         var fn = this.createFilterFn(property, value, anyMatch);
11502         return fn ? this.filterBy(fn) : this.clearFilter();
11503     },
11504
11505     /**
11506      * Filter by a function. The specified function will be called with each
11507      * record in this data source. If the function returns true the record is included,
11508      * otherwise it is filtered.
11509      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11510      * @param {Object} scope (optional) The scope of the function (defaults to this)
11511      */
11512     filterBy : function(fn, scope){
11513         this.snapshot = this.snapshot || this.data;
11514         this.data = this.queryBy(fn, scope||this);
11515         this.fireEvent("datachanged", this);
11516     },
11517
11518     /**
11519      * Query the records by a specified property.
11520      * @param {String} field A field on your records
11521      * @param {String/RegExp} value Either a string that the field
11522      * should start with or a RegExp to test against the field
11523      * @param {Boolean} anyMatch True to match any part not just the beginning
11524      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11525      */
11526     query : function(property, value, anyMatch){
11527         var fn = this.createFilterFn(property, value, anyMatch);
11528         return fn ? this.queryBy(fn) : this.data.clone();
11529     },
11530
11531     /**
11532      * Query by a function. The specified function will be called with each
11533      * record in this data source. If the function returns true the record is included
11534      * in the results.
11535      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11536      * @param {Object} scope (optional) The scope of the function (defaults to this)
11537       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11538      **/
11539     queryBy : function(fn, scope){
11540         var data = this.snapshot || this.data;
11541         return data.filterBy(fn, scope||this);
11542     },
11543
11544     /**
11545      * Collects unique values for a particular dataIndex from this store.
11546      * @param {String} dataIndex The property to collect
11547      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11548      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11549      * @return {Array} An array of the unique values
11550      **/
11551     collect : function(dataIndex, allowNull, bypassFilter){
11552         var d = (bypassFilter === true && this.snapshot) ?
11553                 this.snapshot.items : this.data.items;
11554         var v, sv, r = [], l = {};
11555         for(var i = 0, len = d.length; i < len; i++){
11556             v = d[i].data[dataIndex];
11557             sv = String(v);
11558             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11559                 l[sv] = true;
11560                 r[r.length] = v;
11561             }
11562         }
11563         return r;
11564     },
11565
11566     /**
11567      * Revert to a view of the Record cache with no filtering applied.
11568      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11569      */
11570     clearFilter : function(suppressEvent){
11571         if(this.snapshot && this.snapshot != this.data){
11572             this.data = this.snapshot;
11573             delete this.snapshot;
11574             if(suppressEvent !== true){
11575                 this.fireEvent("datachanged", this);
11576             }
11577         }
11578     },
11579
11580     // private
11581     afterEdit : function(record){
11582         if(this.modified.indexOf(record) == -1){
11583             this.modified.push(record);
11584         }
11585         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11586     },
11587     
11588     // private
11589     afterReject : function(record){
11590         this.modified.remove(record);
11591         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11592     },
11593
11594     // private
11595     afterCommit : function(record){
11596         this.modified.remove(record);
11597         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11598     },
11599
11600     /**
11601      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11602      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11603      */
11604     commitChanges : function(){
11605         var m = this.modified.slice(0);
11606         this.modified = [];
11607         for(var i = 0, len = m.length; i < len; i++){
11608             m[i].commit();
11609         }
11610     },
11611
11612     /**
11613      * Cancel outstanding changes on all changed records.
11614      */
11615     rejectChanges : function(){
11616         var m = this.modified.slice(0);
11617         this.modified = [];
11618         for(var i = 0, len = m.length; i < len; i++){
11619             m[i].reject();
11620         }
11621     },
11622
11623     onMetaChange : function(meta, rtype, o){
11624         this.recordType = rtype;
11625         this.fields = rtype.prototype.fields;
11626         delete this.snapshot;
11627         this.sortInfo = meta.sortInfo || this.sortInfo;
11628         this.modified = [];
11629         this.fireEvent('metachange', this, this.reader.meta);
11630     },
11631     
11632     moveIndex : function(data, type)
11633     {
11634         var index = this.indexOf(data);
11635         
11636         var newIndex = index + type;
11637         
11638         this.remove(data);
11639         
11640         this.insert(newIndex, data);
11641         
11642     }
11643 });/*
11644  * Based on:
11645  * Ext JS Library 1.1.1
11646  * Copyright(c) 2006-2007, Ext JS, LLC.
11647  *
11648  * Originally Released Under LGPL - original licence link has changed is not relivant.
11649  *
11650  * Fork - LGPL
11651  * <script type="text/javascript">
11652  */
11653
11654 /**
11655  * @class Roo.data.SimpleStore
11656  * @extends Roo.data.Store
11657  * Small helper class to make creating Stores from Array data easier.
11658  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11659  * @cfg {Array} fields An array of field definition objects, or field name strings.
11660  * @cfg {Array} data The multi-dimensional array of data
11661  * @constructor
11662  * @param {Object} config
11663  */
11664 Roo.data.SimpleStore = function(config){
11665     Roo.data.SimpleStore.superclass.constructor.call(this, {
11666         isLocal : true,
11667         reader: new Roo.data.ArrayReader({
11668                 id: config.id
11669             },
11670             Roo.data.Record.create(config.fields)
11671         ),
11672         proxy : new Roo.data.MemoryProxy(config.data)
11673     });
11674     this.load();
11675 };
11676 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11677  * Based on:
11678  * Ext JS Library 1.1.1
11679  * Copyright(c) 2006-2007, Ext JS, LLC.
11680  *
11681  * Originally Released Under LGPL - original licence link has changed is not relivant.
11682  *
11683  * Fork - LGPL
11684  * <script type="text/javascript">
11685  */
11686
11687 /**
11688 /**
11689  * @extends Roo.data.Store
11690  * @class Roo.data.JsonStore
11691  * Small helper class to make creating Stores for JSON data easier. <br/>
11692 <pre><code>
11693 var store = new Roo.data.JsonStore({
11694     url: 'get-images.php',
11695     root: 'images',
11696     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11697 });
11698 </code></pre>
11699  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11700  * JsonReader and HttpProxy (unless inline data is provided).</b>
11701  * @cfg {Array} fields An array of field definition objects, or field name strings.
11702  * @constructor
11703  * @param {Object} config
11704  */
11705 Roo.data.JsonStore = function(c){
11706     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11707         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11708         reader: new Roo.data.JsonReader(c, c.fields)
11709     }));
11710 };
11711 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11712  * Based on:
11713  * Ext JS Library 1.1.1
11714  * Copyright(c) 2006-2007, Ext JS, LLC.
11715  *
11716  * Originally Released Under LGPL - original licence link has changed is not relivant.
11717  *
11718  * Fork - LGPL
11719  * <script type="text/javascript">
11720  */
11721
11722  
11723 Roo.data.Field = function(config){
11724     if(typeof config == "string"){
11725         config = {name: config};
11726     }
11727     Roo.apply(this, config);
11728     
11729     if(!this.type){
11730         this.type = "auto";
11731     }
11732     
11733     var st = Roo.data.SortTypes;
11734     // named sortTypes are supported, here we look them up
11735     if(typeof this.sortType == "string"){
11736         this.sortType = st[this.sortType];
11737     }
11738     
11739     // set default sortType for strings and dates
11740     if(!this.sortType){
11741         switch(this.type){
11742             case "string":
11743                 this.sortType = st.asUCString;
11744                 break;
11745             case "date":
11746                 this.sortType = st.asDate;
11747                 break;
11748             default:
11749                 this.sortType = st.none;
11750         }
11751     }
11752
11753     // define once
11754     var stripRe = /[\$,%]/g;
11755
11756     // prebuilt conversion function for this field, instead of
11757     // switching every time we're reading a value
11758     if(!this.convert){
11759         var cv, dateFormat = this.dateFormat;
11760         switch(this.type){
11761             case "":
11762             case "auto":
11763             case undefined:
11764                 cv = function(v){ return v; };
11765                 break;
11766             case "string":
11767                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11768                 break;
11769             case "int":
11770                 cv = function(v){
11771                     return v !== undefined && v !== null && v !== '' ?
11772                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11773                     };
11774                 break;
11775             case "float":
11776                 cv = function(v){
11777                     return v !== undefined && v !== null && v !== '' ?
11778                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11779                     };
11780                 break;
11781             case "bool":
11782             case "boolean":
11783                 cv = function(v){ return v === true || v === "true" || v == 1; };
11784                 break;
11785             case "date":
11786                 cv = function(v){
11787                     if(!v){
11788                         return '';
11789                     }
11790                     if(v instanceof Date){
11791                         return v;
11792                     }
11793                     if(dateFormat){
11794                         if(dateFormat == "timestamp"){
11795                             return new Date(v*1000);
11796                         }
11797                         return Date.parseDate(v, dateFormat);
11798                     }
11799                     var parsed = Date.parse(v);
11800                     return parsed ? new Date(parsed) : null;
11801                 };
11802              break;
11803             
11804         }
11805         this.convert = cv;
11806     }
11807 };
11808
11809 Roo.data.Field.prototype = {
11810     dateFormat: null,
11811     defaultValue: "",
11812     mapping: null,
11813     sortType : null,
11814     sortDir : "ASC"
11815 };/*
11816  * Based on:
11817  * Ext JS Library 1.1.1
11818  * Copyright(c) 2006-2007, Ext JS, LLC.
11819  *
11820  * Originally Released Under LGPL - original licence link has changed is not relivant.
11821  *
11822  * Fork - LGPL
11823  * <script type="text/javascript">
11824  */
11825  
11826 // Base class for reading structured data from a data source.  This class is intended to be
11827 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11828
11829 /**
11830  * @class Roo.data.DataReader
11831  * Base class for reading structured data from a data source.  This class is intended to be
11832  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11833  */
11834
11835 Roo.data.DataReader = function(meta, recordType){
11836     
11837     this.meta = meta;
11838     
11839     this.recordType = recordType instanceof Array ? 
11840         Roo.data.Record.create(recordType) : recordType;
11841 };
11842
11843 Roo.data.DataReader.prototype = {
11844      /**
11845      * Create an empty record
11846      * @param {Object} data (optional) - overlay some values
11847      * @return {Roo.data.Record} record created.
11848      */
11849     newRow :  function(d) {
11850         var da =  {};
11851         this.recordType.prototype.fields.each(function(c) {
11852             switch( c.type) {
11853                 case 'int' : da[c.name] = 0; break;
11854                 case 'date' : da[c.name] = new Date(); break;
11855                 case 'float' : da[c.name] = 0.0; break;
11856                 case 'boolean' : da[c.name] = false; break;
11857                 default : da[c.name] = ""; break;
11858             }
11859             
11860         });
11861         return new this.recordType(Roo.apply(da, d));
11862     }
11863     
11864 };/*
11865  * Based on:
11866  * Ext JS Library 1.1.1
11867  * Copyright(c) 2006-2007, Ext JS, LLC.
11868  *
11869  * Originally Released Under LGPL - original licence link has changed is not relivant.
11870  *
11871  * Fork - LGPL
11872  * <script type="text/javascript">
11873  */
11874
11875 /**
11876  * @class Roo.data.DataProxy
11877  * @extends Roo.data.Observable
11878  * This class is an abstract base class for implementations which provide retrieval of
11879  * unformatted data objects.<br>
11880  * <p>
11881  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11882  * (of the appropriate type which knows how to parse the data object) to provide a block of
11883  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11884  * <p>
11885  * Custom implementations must implement the load method as described in
11886  * {@link Roo.data.HttpProxy#load}.
11887  */
11888 Roo.data.DataProxy = function(){
11889     this.addEvents({
11890         /**
11891          * @event beforeload
11892          * Fires before a network request is made to retrieve a data object.
11893          * @param {Object} This DataProxy object.
11894          * @param {Object} params The params parameter to the load function.
11895          */
11896         beforeload : true,
11897         /**
11898          * @event load
11899          * Fires before the load method's callback is called.
11900          * @param {Object} This DataProxy object.
11901          * @param {Object} o The data object.
11902          * @param {Object} arg The callback argument object passed to the load function.
11903          */
11904         load : true,
11905         /**
11906          * @event loadexception
11907          * Fires if an Exception occurs during data retrieval.
11908          * @param {Object} This DataProxy object.
11909          * @param {Object} o The data object.
11910          * @param {Object} arg The callback argument object passed to the load function.
11911          * @param {Object} e The Exception.
11912          */
11913         loadexception : true
11914     });
11915     Roo.data.DataProxy.superclass.constructor.call(this);
11916 };
11917
11918 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11919
11920     /**
11921      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11922      */
11923 /*
11924  * Based on:
11925  * Ext JS Library 1.1.1
11926  * Copyright(c) 2006-2007, Ext JS, LLC.
11927  *
11928  * Originally Released Under LGPL - original licence link has changed is not relivant.
11929  *
11930  * Fork - LGPL
11931  * <script type="text/javascript">
11932  */
11933 /**
11934  * @class Roo.data.MemoryProxy
11935  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11936  * to the Reader when its load method is called.
11937  * @constructor
11938  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11939  */
11940 Roo.data.MemoryProxy = function(data){
11941     if (data.data) {
11942         data = data.data;
11943     }
11944     Roo.data.MemoryProxy.superclass.constructor.call(this);
11945     this.data = data;
11946 };
11947
11948 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11949     
11950     /**
11951      * Load data from the requested source (in this case an in-memory
11952      * data object passed to the constructor), read the data object into
11953      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11954      * process that block using the passed callback.
11955      * @param {Object} params This parameter is not used by the MemoryProxy class.
11956      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11957      * object into a block of Roo.data.Records.
11958      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11959      * The function must be passed <ul>
11960      * <li>The Record block object</li>
11961      * <li>The "arg" argument from the load function</li>
11962      * <li>A boolean success indicator</li>
11963      * </ul>
11964      * @param {Object} scope The scope in which to call the callback
11965      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11966      */
11967     load : function(params, reader, callback, scope, arg){
11968         params = params || {};
11969         var result;
11970         try {
11971             result = reader.readRecords(this.data);
11972         }catch(e){
11973             this.fireEvent("loadexception", this, arg, null, e);
11974             callback.call(scope, null, arg, false);
11975             return;
11976         }
11977         callback.call(scope, result, arg, true);
11978     },
11979     
11980     // private
11981     update : function(params, records){
11982         
11983     }
11984 });/*
11985  * Based on:
11986  * Ext JS Library 1.1.1
11987  * Copyright(c) 2006-2007, Ext JS, LLC.
11988  *
11989  * Originally Released Under LGPL - original licence link has changed is not relivant.
11990  *
11991  * Fork - LGPL
11992  * <script type="text/javascript">
11993  */
11994 /**
11995  * @class Roo.data.HttpProxy
11996  * @extends Roo.data.DataProxy
11997  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11998  * configured to reference a certain URL.<br><br>
11999  * <p>
12000  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12001  * from which the running page was served.<br><br>
12002  * <p>
12003  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12004  * <p>
12005  * Be aware that to enable the browser to parse an XML document, the server must set
12006  * the Content-Type header in the HTTP response to "text/xml".
12007  * @constructor
12008  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12009  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12010  * will be used to make the request.
12011  */
12012 Roo.data.HttpProxy = function(conn){
12013     Roo.data.HttpProxy.superclass.constructor.call(this);
12014     // is conn a conn config or a real conn?
12015     this.conn = conn;
12016     this.useAjax = !conn || !conn.events;
12017   
12018 };
12019
12020 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12021     // thse are take from connection...
12022     
12023     /**
12024      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12025      */
12026     /**
12027      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12028      * extra parameters to each request made by this object. (defaults to undefined)
12029      */
12030     /**
12031      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12032      *  to each request made by this object. (defaults to undefined)
12033      */
12034     /**
12035      * @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)
12036      */
12037     /**
12038      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12039      */
12040      /**
12041      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12042      * @type Boolean
12043      */
12044   
12045
12046     /**
12047      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12048      * @type Boolean
12049      */
12050     /**
12051      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12052      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12053      * a finer-grained basis than the DataProxy events.
12054      */
12055     getConnection : function(){
12056         return this.useAjax ? Roo.Ajax : this.conn;
12057     },
12058
12059     /**
12060      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12061      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12062      * process that block using the passed callback.
12063      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12064      * for the request to the remote server.
12065      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12066      * object into a block of Roo.data.Records.
12067      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12068      * The function must be passed <ul>
12069      * <li>The Record block object</li>
12070      * <li>The "arg" argument from the load function</li>
12071      * <li>A boolean success indicator</li>
12072      * </ul>
12073      * @param {Object} scope The scope in which to call the callback
12074      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12075      */
12076     load : function(params, reader, callback, scope, arg){
12077         if(this.fireEvent("beforeload", this, params) !== false){
12078             var  o = {
12079                 params : params || {},
12080                 request: {
12081                     callback : callback,
12082                     scope : scope,
12083                     arg : arg
12084                 },
12085                 reader: reader,
12086                 callback : this.loadResponse,
12087                 scope: this
12088             };
12089             if(this.useAjax){
12090                 Roo.applyIf(o, this.conn);
12091                 if(this.activeRequest){
12092                     Roo.Ajax.abort(this.activeRequest);
12093                 }
12094                 this.activeRequest = Roo.Ajax.request(o);
12095             }else{
12096                 this.conn.request(o);
12097             }
12098         }else{
12099             callback.call(scope||this, null, arg, false);
12100         }
12101     },
12102
12103     // private
12104     loadResponse : function(o, success, response){
12105         delete this.activeRequest;
12106         if(!success){
12107             this.fireEvent("loadexception", this, o, response);
12108             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12109             return;
12110         }
12111         var result;
12112         try {
12113             result = o.reader.read(response);
12114         }catch(e){
12115             this.fireEvent("loadexception", this, o, response, e);
12116             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12117             return;
12118         }
12119         
12120         this.fireEvent("load", this, o, o.request.arg);
12121         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12122     },
12123
12124     // private
12125     update : function(dataSet){
12126
12127     },
12128
12129     // private
12130     updateResponse : function(dataSet){
12131
12132     }
12133 });/*
12134  * Based on:
12135  * Ext JS Library 1.1.1
12136  * Copyright(c) 2006-2007, Ext JS, LLC.
12137  *
12138  * Originally Released Under LGPL - original licence link has changed is not relivant.
12139  *
12140  * Fork - LGPL
12141  * <script type="text/javascript">
12142  */
12143
12144 /**
12145  * @class Roo.data.ScriptTagProxy
12146  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12147  * other than the originating domain of the running page.<br><br>
12148  * <p>
12149  * <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
12150  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12151  * <p>
12152  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12153  * source code that is used as the source inside a &lt;script> tag.<br><br>
12154  * <p>
12155  * In order for the browser to process the returned data, the server must wrap the data object
12156  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12157  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12158  * depending on whether the callback name was passed:
12159  * <p>
12160  * <pre><code>
12161 boolean scriptTag = false;
12162 String cb = request.getParameter("callback");
12163 if (cb != null) {
12164     scriptTag = true;
12165     response.setContentType("text/javascript");
12166 } else {
12167     response.setContentType("application/x-json");
12168 }
12169 Writer out = response.getWriter();
12170 if (scriptTag) {
12171     out.write(cb + "(");
12172 }
12173 out.print(dataBlock.toJsonString());
12174 if (scriptTag) {
12175     out.write(");");
12176 }
12177 </pre></code>
12178  *
12179  * @constructor
12180  * @param {Object} config A configuration object.
12181  */
12182 Roo.data.ScriptTagProxy = function(config){
12183     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12184     Roo.apply(this, config);
12185     this.head = document.getElementsByTagName("head")[0];
12186 };
12187
12188 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12189
12190 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12191     /**
12192      * @cfg {String} url The URL from which to request the data object.
12193      */
12194     /**
12195      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12196      */
12197     timeout : 30000,
12198     /**
12199      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12200      * the server the name of the callback function set up by the load call to process the returned data object.
12201      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12202      * javascript output which calls this named function passing the data object as its only parameter.
12203      */
12204     callbackParam : "callback",
12205     /**
12206      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12207      * name to the request.
12208      */
12209     nocache : true,
12210
12211     /**
12212      * Load data from the configured URL, read the data object into
12213      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12214      * process that block using the passed callback.
12215      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12216      * for the request to the remote server.
12217      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12218      * object into a block of Roo.data.Records.
12219      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12220      * The function must be passed <ul>
12221      * <li>The Record block object</li>
12222      * <li>The "arg" argument from the load function</li>
12223      * <li>A boolean success indicator</li>
12224      * </ul>
12225      * @param {Object} scope The scope in which to call the callback
12226      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12227      */
12228     load : function(params, reader, callback, scope, arg){
12229         if(this.fireEvent("beforeload", this, params) !== false){
12230
12231             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12232
12233             var url = this.url;
12234             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12235             if(this.nocache){
12236                 url += "&_dc=" + (new Date().getTime());
12237             }
12238             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12239             var trans = {
12240                 id : transId,
12241                 cb : "stcCallback"+transId,
12242                 scriptId : "stcScript"+transId,
12243                 params : params,
12244                 arg : arg,
12245                 url : url,
12246                 callback : callback,
12247                 scope : scope,
12248                 reader : reader
12249             };
12250             var conn = this;
12251
12252             window[trans.cb] = function(o){
12253                 conn.handleResponse(o, trans);
12254             };
12255
12256             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12257
12258             if(this.autoAbort !== false){
12259                 this.abort();
12260             }
12261
12262             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12263
12264             var script = document.createElement("script");
12265             script.setAttribute("src", url);
12266             script.setAttribute("type", "text/javascript");
12267             script.setAttribute("id", trans.scriptId);
12268             this.head.appendChild(script);
12269
12270             this.trans = trans;
12271         }else{
12272             callback.call(scope||this, null, arg, false);
12273         }
12274     },
12275
12276     // private
12277     isLoading : function(){
12278         return this.trans ? true : false;
12279     },
12280
12281     /**
12282      * Abort the current server request.
12283      */
12284     abort : function(){
12285         if(this.isLoading()){
12286             this.destroyTrans(this.trans);
12287         }
12288     },
12289
12290     // private
12291     destroyTrans : function(trans, isLoaded){
12292         this.head.removeChild(document.getElementById(trans.scriptId));
12293         clearTimeout(trans.timeoutId);
12294         if(isLoaded){
12295             window[trans.cb] = undefined;
12296             try{
12297                 delete window[trans.cb];
12298             }catch(e){}
12299         }else{
12300             // if hasn't been loaded, wait for load to remove it to prevent script error
12301             window[trans.cb] = function(){
12302                 window[trans.cb] = undefined;
12303                 try{
12304                     delete window[trans.cb];
12305                 }catch(e){}
12306             };
12307         }
12308     },
12309
12310     // private
12311     handleResponse : function(o, trans){
12312         this.trans = false;
12313         this.destroyTrans(trans, true);
12314         var result;
12315         try {
12316             result = trans.reader.readRecords(o);
12317         }catch(e){
12318             this.fireEvent("loadexception", this, o, trans.arg, e);
12319             trans.callback.call(trans.scope||window, null, trans.arg, false);
12320             return;
12321         }
12322         this.fireEvent("load", this, o, trans.arg);
12323         trans.callback.call(trans.scope||window, result, trans.arg, true);
12324     },
12325
12326     // private
12327     handleFailure : function(trans){
12328         this.trans = false;
12329         this.destroyTrans(trans, false);
12330         this.fireEvent("loadexception", this, null, trans.arg);
12331         trans.callback.call(trans.scope||window, null, trans.arg, false);
12332     }
12333 });/*
12334  * Based on:
12335  * Ext JS Library 1.1.1
12336  * Copyright(c) 2006-2007, Ext JS, LLC.
12337  *
12338  * Originally Released Under LGPL - original licence link has changed is not relivant.
12339  *
12340  * Fork - LGPL
12341  * <script type="text/javascript">
12342  */
12343
12344 /**
12345  * @class Roo.data.JsonReader
12346  * @extends Roo.data.DataReader
12347  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12348  * based on mappings in a provided Roo.data.Record constructor.
12349  * 
12350  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12351  * in the reply previously. 
12352  * 
12353  * <p>
12354  * Example code:
12355  * <pre><code>
12356 var RecordDef = Roo.data.Record.create([
12357     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12358     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12359 ]);
12360 var myReader = new Roo.data.JsonReader({
12361     totalProperty: "results",    // The property which contains the total dataset size (optional)
12362     root: "rows",                // The property which contains an Array of row objects
12363     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12364 }, RecordDef);
12365 </code></pre>
12366  * <p>
12367  * This would consume a JSON file like this:
12368  * <pre><code>
12369 { 'results': 2, 'rows': [
12370     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12371     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12372 }
12373 </code></pre>
12374  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12375  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12376  * paged from the remote server.
12377  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12378  * @cfg {String} root name of the property which contains the Array of row objects.
12379  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12380  * @cfg {Array} fields Array of field definition objects
12381  * @constructor
12382  * Create a new JsonReader
12383  * @param {Object} meta Metadata configuration options
12384  * @param {Object} recordType Either an Array of field definition objects,
12385  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12386  */
12387 Roo.data.JsonReader = function(meta, recordType){
12388     
12389     meta = meta || {};
12390     // set some defaults:
12391     Roo.applyIf(meta, {
12392         totalProperty: 'total',
12393         successProperty : 'success',
12394         root : 'data',
12395         id : 'id'
12396     });
12397     
12398     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12399 };
12400 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12401     
12402     /**
12403      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12404      * Used by Store query builder to append _requestMeta to params.
12405      * 
12406      */
12407     metaFromRemote : false,
12408     /**
12409      * This method is only used by a DataProxy which has retrieved data from a remote server.
12410      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12411      * @return {Object} data A data block which is used by an Roo.data.Store object as
12412      * a cache of Roo.data.Records.
12413      */
12414     read : function(response){
12415         var json = response.responseText;
12416        
12417         var o = /* eval:var:o */ eval("("+json+")");
12418         if(!o) {
12419             throw {message: "JsonReader.read: Json object not found"};
12420         }
12421         
12422         if(o.metaData){
12423             
12424             delete this.ef;
12425             this.metaFromRemote = true;
12426             this.meta = o.metaData;
12427             this.recordType = Roo.data.Record.create(o.metaData.fields);
12428             this.onMetaChange(this.meta, this.recordType, o);
12429         }
12430         return this.readRecords(o);
12431     },
12432
12433     // private function a store will implement
12434     onMetaChange : function(meta, recordType, o){
12435
12436     },
12437
12438     /**
12439          * @ignore
12440          */
12441     simpleAccess: function(obj, subsc) {
12442         return obj[subsc];
12443     },
12444
12445         /**
12446          * @ignore
12447          */
12448     getJsonAccessor: function(){
12449         var re = /[\[\.]/;
12450         return function(expr) {
12451             try {
12452                 return(re.test(expr))
12453                     ? new Function("obj", "return obj." + expr)
12454                     : function(obj){
12455                         return obj[expr];
12456                     };
12457             } catch(e){}
12458             return Roo.emptyFn;
12459         };
12460     }(),
12461
12462     /**
12463      * Create a data block containing Roo.data.Records from an XML document.
12464      * @param {Object} o An object which contains an Array of row objects in the property specified
12465      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12466      * which contains the total size of the dataset.
12467      * @return {Object} data A data block which is used by an Roo.data.Store object as
12468      * a cache of Roo.data.Records.
12469      */
12470     readRecords : function(o){
12471         /**
12472          * After any data loads, the raw JSON data is available for further custom processing.
12473          * @type Object
12474          */
12475         this.o = o;
12476         var s = this.meta, Record = this.recordType,
12477             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12478
12479 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12480         if (!this.ef) {
12481             if(s.totalProperty) {
12482                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12483                 }
12484                 if(s.successProperty) {
12485                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12486                 }
12487                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12488                 if (s.id) {
12489                         var g = this.getJsonAccessor(s.id);
12490                         this.getId = function(rec) {
12491                                 var r = g(rec);  
12492                                 return (r === undefined || r === "") ? null : r;
12493                         };
12494                 } else {
12495                         this.getId = function(){return null;};
12496                 }
12497             this.ef = [];
12498             for(var jj = 0; jj < fl; jj++){
12499                 f = fi[jj];
12500                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12501                 this.ef[jj] = this.getJsonAccessor(map);
12502             }
12503         }
12504
12505         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12506         if(s.totalProperty){
12507             var vt = parseInt(this.getTotal(o), 10);
12508             if(!isNaN(vt)){
12509                 totalRecords = vt;
12510             }
12511         }
12512         if(s.successProperty){
12513             var vs = this.getSuccess(o);
12514             if(vs === false || vs === 'false'){
12515                 success = false;
12516             }
12517         }
12518         var records = [];
12519         for(var i = 0; i < c; i++){
12520                 var n = root[i];
12521             var values = {};
12522             var id = this.getId(n);
12523             for(var j = 0; j < fl; j++){
12524                 f = fi[j];
12525             var v = this.ef[j](n);
12526             if (!f.convert) {
12527                 Roo.log('missing convert for ' + f.name);
12528                 Roo.log(f);
12529                 continue;
12530             }
12531             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12532             }
12533             var record = new Record(values, id);
12534             record.json = n;
12535             records[i] = record;
12536         }
12537         return {
12538             raw : o,
12539             success : success,
12540             records : records,
12541             totalRecords : totalRecords
12542         };
12543     }
12544 });/*
12545  * Based on:
12546  * Ext JS Library 1.1.1
12547  * Copyright(c) 2006-2007, Ext JS, LLC.
12548  *
12549  * Originally Released Under LGPL - original licence link has changed is not relivant.
12550  *
12551  * Fork - LGPL
12552  * <script type="text/javascript">
12553  */
12554
12555 /**
12556  * @class Roo.data.ArrayReader
12557  * @extends Roo.data.DataReader
12558  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12559  * Each element of that Array represents a row of data fields. The
12560  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12561  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12562  * <p>
12563  * Example code:.
12564  * <pre><code>
12565 var RecordDef = Roo.data.Record.create([
12566     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12567     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12568 ]);
12569 var myReader = new Roo.data.ArrayReader({
12570     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12571 }, RecordDef);
12572 </code></pre>
12573  * <p>
12574  * This would consume an Array like this:
12575  * <pre><code>
12576 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12577   </code></pre>
12578  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12579  * @constructor
12580  * Create a new JsonReader
12581  * @param {Object} meta Metadata configuration options.
12582  * @param {Object} recordType Either an Array of field definition objects
12583  * as specified to {@link Roo.data.Record#create},
12584  * or an {@link Roo.data.Record} object
12585  * created using {@link Roo.data.Record#create}.
12586  */
12587 Roo.data.ArrayReader = function(meta, recordType){
12588     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12589 };
12590
12591 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12592     /**
12593      * Create a data block containing Roo.data.Records from an XML document.
12594      * @param {Object} o An Array of row objects which represents the dataset.
12595      * @return {Object} data A data block which is used by an Roo.data.Store object as
12596      * a cache of Roo.data.Records.
12597      */
12598     readRecords : function(o){
12599         var sid = this.meta ? this.meta.id : null;
12600         var recordType = this.recordType, fields = recordType.prototype.fields;
12601         var records = [];
12602         var root = o;
12603             for(var i = 0; i < root.length; i++){
12604                     var n = root[i];
12605                 var values = {};
12606                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12607                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12608                 var f = fields.items[j];
12609                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12610                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12611                 v = f.convert(v);
12612                 values[f.name] = v;
12613             }
12614                 var record = new recordType(values, id);
12615                 record.json = n;
12616                 records[records.length] = record;
12617             }
12618             return {
12619                 records : records,
12620                 totalRecords : records.length
12621             };
12622     }
12623 });/*
12624  * - LGPL
12625  * * 
12626  */
12627
12628 /**
12629  * @class Roo.bootstrap.ComboBox
12630  * @extends Roo.bootstrap.TriggerField
12631  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12632  * @cfg {Boolean} append (true|false) default false
12633  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12634  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12635  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12636  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12637  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12638  * @cfg {Boolean} animate default true
12639  * @cfg {Boolean} emptyResultText only for touch device
12640  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12641  * @cfg {String} emptyTitle default ''
12642  * @constructor
12643  * Create a new ComboBox.
12644  * @param {Object} config Configuration options
12645  */
12646 Roo.bootstrap.ComboBox = function(config){
12647     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12648     this.addEvents({
12649         /**
12650          * @event expand
12651          * Fires when the dropdown list is expanded
12652         * @param {Roo.bootstrap.ComboBox} combo This combo box
12653         */
12654         'expand' : true,
12655         /**
12656          * @event collapse
12657          * Fires when the dropdown list is collapsed
12658         * @param {Roo.bootstrap.ComboBox} combo This combo box
12659         */
12660         'collapse' : true,
12661         /**
12662          * @event beforeselect
12663          * Fires before a list item is selected. Return false to cancel the selection.
12664         * @param {Roo.bootstrap.ComboBox} combo This combo box
12665         * @param {Roo.data.Record} record The data record returned from the underlying store
12666         * @param {Number} index The index of the selected item in the dropdown list
12667         */
12668         'beforeselect' : true,
12669         /**
12670          * @event select
12671          * Fires when a list item is selected
12672         * @param {Roo.bootstrap.ComboBox} combo This combo box
12673         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12674         * @param {Number} index The index of the selected item in the dropdown list
12675         */
12676         'select' : true,
12677         /**
12678          * @event beforequery
12679          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12680          * The event object passed has these properties:
12681         * @param {Roo.bootstrap.ComboBox} combo This combo box
12682         * @param {String} query The query
12683         * @param {Boolean} forceAll true to force "all" query
12684         * @param {Boolean} cancel true to cancel the query
12685         * @param {Object} e The query event object
12686         */
12687         'beforequery': true,
12688          /**
12689          * @event add
12690          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12691         * @param {Roo.bootstrap.ComboBox} combo This combo box
12692         */
12693         'add' : true,
12694         /**
12695          * @event edit
12696          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12697         * @param {Roo.bootstrap.ComboBox} combo This combo box
12698         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12699         */
12700         'edit' : true,
12701         /**
12702          * @event remove
12703          * Fires when the remove value from the combobox array
12704         * @param {Roo.bootstrap.ComboBox} combo This combo box
12705         */
12706         'remove' : true,
12707         /**
12708          * @event afterremove
12709          * Fires when the remove value from the combobox array
12710         * @param {Roo.bootstrap.ComboBox} combo This combo box
12711         */
12712         'afterremove' : true,
12713         /**
12714          * @event specialfilter
12715          * Fires when specialfilter
12716             * @param {Roo.bootstrap.ComboBox} combo This combo box
12717             */
12718         'specialfilter' : true,
12719         /**
12720          * @event tick
12721          * Fires when tick the element
12722             * @param {Roo.bootstrap.ComboBox} combo This combo box
12723             */
12724         'tick' : true,
12725         /**
12726          * @event touchviewdisplay
12727          * Fires when touch view require special display (default is using displayField)
12728             * @param {Roo.bootstrap.ComboBox} combo This combo box
12729             * @param {Object} cfg set html .
12730             */
12731         'touchviewdisplay' : true
12732         
12733     });
12734     
12735     this.item = [];
12736     this.tickItems = [];
12737     
12738     this.selectedIndex = -1;
12739     if(this.mode == 'local'){
12740         if(config.queryDelay === undefined){
12741             this.queryDelay = 10;
12742         }
12743         if(config.minChars === undefined){
12744             this.minChars = 0;
12745         }
12746     }
12747 };
12748
12749 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12750      
12751     /**
12752      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12753      * rendering into an Roo.Editor, defaults to false)
12754      */
12755     /**
12756      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12757      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12758      */
12759     /**
12760      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12761      */
12762     /**
12763      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12764      * the dropdown list (defaults to undefined, with no header element)
12765      */
12766
12767      /**
12768      * @cfg {String/Roo.Template} tpl The template to use to render the output
12769      */
12770      
12771      /**
12772      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12773      */
12774     listWidth: undefined,
12775     /**
12776      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12777      * mode = 'remote' or 'text' if mode = 'local')
12778      */
12779     displayField: undefined,
12780     
12781     /**
12782      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12783      * mode = 'remote' or 'value' if mode = 'local'). 
12784      * Note: use of a valueField requires the user make a selection
12785      * in order for a value to be mapped.
12786      */
12787     valueField: undefined,
12788     /**
12789      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12790      */
12791     modalTitle : '',
12792     
12793     /**
12794      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12795      * field's data value (defaults to the underlying DOM element's name)
12796      */
12797     hiddenName: undefined,
12798     /**
12799      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12800      */
12801     listClass: '',
12802     /**
12803      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12804      */
12805     selectedClass: 'active',
12806     
12807     /**
12808      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12809      */
12810     shadow:'sides',
12811     /**
12812      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12813      * anchor positions (defaults to 'tl-bl')
12814      */
12815     listAlign: 'tl-bl?',
12816     /**
12817      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12818      */
12819     maxHeight: 300,
12820     /**
12821      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12822      * query specified by the allQuery config option (defaults to 'query')
12823      */
12824     triggerAction: 'query',
12825     /**
12826      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12827      * (defaults to 4, does not apply if editable = false)
12828      */
12829     minChars : 4,
12830     /**
12831      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12832      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12833      */
12834     typeAhead: false,
12835     /**
12836      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12837      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12838      */
12839     queryDelay: 500,
12840     /**
12841      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12842      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12843      */
12844     pageSize: 0,
12845     /**
12846      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12847      * when editable = true (defaults to false)
12848      */
12849     selectOnFocus:false,
12850     /**
12851      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12852      */
12853     queryParam: 'query',
12854     /**
12855      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12856      * when mode = 'remote' (defaults to 'Loading...')
12857      */
12858     loadingText: 'Loading...',
12859     /**
12860      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12861      */
12862     resizable: false,
12863     /**
12864      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12865      */
12866     handleHeight : 8,
12867     /**
12868      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12869      * traditional select (defaults to true)
12870      */
12871     editable: true,
12872     /**
12873      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12874      */
12875     allQuery: '',
12876     /**
12877      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12878      */
12879     mode: 'remote',
12880     /**
12881      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12882      * listWidth has a higher value)
12883      */
12884     minListWidth : 70,
12885     /**
12886      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12887      * allow the user to set arbitrary text into the field (defaults to false)
12888      */
12889     forceSelection:false,
12890     /**
12891      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12892      * if typeAhead = true (defaults to 250)
12893      */
12894     typeAheadDelay : 250,
12895     /**
12896      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12897      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12898      */
12899     valueNotFoundText : undefined,
12900     /**
12901      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12902      */
12903     blockFocus : false,
12904     
12905     /**
12906      * @cfg {Boolean} disableClear Disable showing of clear button.
12907      */
12908     disableClear : false,
12909     /**
12910      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12911      */
12912     alwaysQuery : false,
12913     
12914     /**
12915      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12916      */
12917     multiple : false,
12918     
12919     /**
12920      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12921      */
12922     invalidClass : "has-warning",
12923     
12924     /**
12925      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12926      */
12927     validClass : "has-success",
12928     
12929     /**
12930      * @cfg {Boolean} specialFilter (true|false) special filter default false
12931      */
12932     specialFilter : false,
12933     
12934     /**
12935      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12936      */
12937     mobileTouchView : true,
12938     
12939     /**
12940      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12941      */
12942     useNativeIOS : false,
12943     
12944     ios_options : false,
12945     
12946     //private
12947     addicon : false,
12948     editicon: false,
12949     
12950     page: 0,
12951     hasQuery: false,
12952     append: false,
12953     loadNext: false,
12954     autoFocus : true,
12955     tickable : false,
12956     btnPosition : 'right',
12957     triggerList : true,
12958     showToggleBtn : true,
12959     animate : true,
12960     emptyResultText: 'Empty',
12961     triggerText : 'Select',
12962     emptyTitle : '',
12963     
12964     // element that contains real text value.. (when hidden is used..)
12965     
12966     getAutoCreate : function()
12967     {   
12968         var cfg = false;
12969         //render
12970         /*
12971          * Render classic select for iso
12972          */
12973         
12974         if(Roo.isIOS && this.useNativeIOS){
12975             cfg = this.getAutoCreateNativeIOS();
12976             return cfg;
12977         }
12978         
12979         /*
12980          * Touch Devices
12981          */
12982         
12983         if(Roo.isTouch && this.mobileTouchView){
12984             cfg = this.getAutoCreateTouchView();
12985             return cfg;;
12986         }
12987         
12988         /*
12989          *  Normal ComboBox
12990          */
12991         if(!this.tickable){
12992             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12993             return cfg;
12994         }
12995         
12996         /*
12997          *  ComboBox with tickable selections
12998          */
12999              
13000         var align = this.labelAlign || this.parentLabelAlign();
13001         
13002         cfg = {
13003             cls : 'form-group roo-combobox-tickable' //input-group
13004         };
13005         
13006         var btn_text_select = '';
13007         var btn_text_done = '';
13008         var btn_text_cancel = '';
13009         
13010         if (this.btn_text_show) {
13011             btn_text_select = 'Select';
13012             btn_text_done = 'Done';
13013             btn_text_cancel = 'Cancel'; 
13014         }
13015         
13016         var buttons = {
13017             tag : 'div',
13018             cls : 'tickable-buttons',
13019             cn : [
13020                 {
13021                     tag : 'button',
13022                     type : 'button',
13023                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13024                     //html : this.triggerText
13025                     html: btn_text_select
13026                 },
13027                 {
13028                     tag : 'button',
13029                     type : 'button',
13030                     name : 'ok',
13031                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13032                     //html : 'Done'
13033                     html: btn_text_done
13034                 },
13035                 {
13036                     tag : 'button',
13037                     type : 'button',
13038                     name : 'cancel',
13039                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13040                     //html : 'Cancel'
13041                     html: btn_text_cancel
13042                 }
13043             ]
13044         };
13045         
13046         if(this.editable){
13047             buttons.cn.unshift({
13048                 tag: 'input',
13049                 cls: 'roo-select2-search-field-input'
13050             });
13051         }
13052         
13053         var _this = this;
13054         
13055         Roo.each(buttons.cn, function(c){
13056             if (_this.size) {
13057                 c.cls += ' btn-' + _this.size;
13058             }
13059
13060             if (_this.disabled) {
13061                 c.disabled = true;
13062             }
13063         });
13064         
13065         var box = {
13066             tag: 'div',
13067             cn: [
13068                 {
13069                     tag: 'input',
13070                     type : 'hidden',
13071                     cls: 'form-hidden-field'
13072                 },
13073                 {
13074                     tag: 'ul',
13075                     cls: 'roo-select2-choices',
13076                     cn:[
13077                         {
13078                             tag: 'li',
13079                             cls: 'roo-select2-search-field',
13080                             cn: [
13081                                 buttons
13082                             ]
13083                         }
13084                     ]
13085                 }
13086             ]
13087         };
13088         
13089         var combobox = {
13090             cls: 'roo-select2-container input-group roo-select2-container-multi',
13091             cn: [
13092                 box
13093 //                {
13094 //                    tag: 'ul',
13095 //                    cls: 'typeahead typeahead-long dropdown-menu',
13096 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13097 //                }
13098             ]
13099         };
13100         
13101         if(this.hasFeedback && !this.allowBlank){
13102             
13103             var feedback = {
13104                 tag: 'span',
13105                 cls: 'glyphicon form-control-feedback'
13106             };
13107
13108             combobox.cn.push(feedback);
13109         }
13110         
13111         
13112         if (align ==='left' && this.fieldLabel.length) {
13113             
13114             cfg.cls += ' roo-form-group-label-left';
13115             
13116             cfg.cn = [
13117                 {
13118                     tag : 'i',
13119                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13120                     tooltip : 'This field is required'
13121                 },
13122                 {
13123                     tag: 'label',
13124                     'for' :  id,
13125                     cls : 'control-label',
13126                     html : this.fieldLabel
13127
13128                 },
13129                 {
13130                     cls : "", 
13131                     cn: [
13132                         combobox
13133                     ]
13134                 }
13135
13136             ];
13137             
13138             var labelCfg = cfg.cn[1];
13139             var contentCfg = cfg.cn[2];
13140             
13141
13142             if(this.indicatorpos == 'right'){
13143                 
13144                 cfg.cn = [
13145                     {
13146                         tag: 'label',
13147                         'for' :  id,
13148                         cls : 'control-label',
13149                         cn : [
13150                             {
13151                                 tag : 'span',
13152                                 html : this.fieldLabel
13153                             },
13154                             {
13155                                 tag : 'i',
13156                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13157                                 tooltip : 'This field is required'
13158                             }
13159                         ]
13160                     },
13161                     {
13162                         cls : "",
13163                         cn: [
13164                             combobox
13165                         ]
13166                     }
13167
13168                 ];
13169                 
13170                 
13171                 
13172                 labelCfg = cfg.cn[0];
13173                 contentCfg = cfg.cn[1];
13174             
13175             }
13176             
13177             if(this.labelWidth > 12){
13178                 labelCfg.style = "width: " + this.labelWidth + 'px';
13179             }
13180             
13181             if(this.labelWidth < 13 && this.labelmd == 0){
13182                 this.labelmd = this.labelWidth;
13183             }
13184             
13185             if(this.labellg > 0){
13186                 labelCfg.cls += ' col-lg-' + this.labellg;
13187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13188             }
13189             
13190             if(this.labelmd > 0){
13191                 labelCfg.cls += ' col-md-' + this.labelmd;
13192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13193             }
13194             
13195             if(this.labelsm > 0){
13196                 labelCfg.cls += ' col-sm-' + this.labelsm;
13197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13198             }
13199             
13200             if(this.labelxs > 0){
13201                 labelCfg.cls += ' col-xs-' + this.labelxs;
13202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13203             }
13204                 
13205                 
13206         } else if ( this.fieldLabel.length) {
13207 //                Roo.log(" label");
13208                  cfg.cn = [
13209                     {
13210                         tag : 'i',
13211                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13212                         tooltip : 'This field is required'
13213                     },
13214                     {
13215                         tag: 'label',
13216                         //cls : 'input-group-addon',
13217                         html : this.fieldLabel
13218                     },
13219                     combobox
13220                 ];
13221                 
13222                 if(this.indicatorpos == 'right'){
13223                     cfg.cn = [
13224                         {
13225                             tag: 'label',
13226                             //cls : 'input-group-addon',
13227                             html : this.fieldLabel
13228                         },
13229                         {
13230                             tag : 'i',
13231                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13232                             tooltip : 'This field is required'
13233                         },
13234                         combobox
13235                     ];
13236                     
13237                 }
13238
13239         } else {
13240             
13241 //                Roo.log(" no label && no align");
13242                 cfg = combobox
13243                      
13244                 
13245         }
13246          
13247         var settings=this;
13248         ['xs','sm','md','lg'].map(function(size){
13249             if (settings[size]) {
13250                 cfg.cls += ' col-' + size + '-' + settings[size];
13251             }
13252         });
13253         
13254         return cfg;
13255         
13256     },
13257     
13258     _initEventsCalled : false,
13259     
13260     // private
13261     initEvents: function()
13262     {   
13263         if (this._initEventsCalled) { // as we call render... prevent looping...
13264             return;
13265         }
13266         this._initEventsCalled = true;
13267         
13268         if (!this.store) {
13269             throw "can not find store for combo";
13270         }
13271         
13272         this.indicator = this.indicatorEl();
13273         
13274         this.store = Roo.factory(this.store, Roo.data);
13275         this.store.parent = this;
13276         
13277         // if we are building from html. then this element is so complex, that we can not really
13278         // use the rendered HTML.
13279         // so we have to trash and replace the previous code.
13280         if (Roo.XComponent.build_from_html) {
13281             // remove this element....
13282             var e = this.el.dom, k=0;
13283             while (e ) { e = e.previousSibling;  ++k;}
13284
13285             this.el.remove();
13286             
13287             this.el=false;
13288             this.rendered = false;
13289             
13290             this.render(this.parent().getChildContainer(true), k);
13291         }
13292         
13293         if(Roo.isIOS && this.useNativeIOS){
13294             this.initIOSView();
13295             return;
13296         }
13297         
13298         /*
13299          * Touch Devices
13300          */
13301         
13302         if(Roo.isTouch && this.mobileTouchView){
13303             this.initTouchView();
13304             return;
13305         }
13306         
13307         if(this.tickable){
13308             this.initTickableEvents();
13309             return;
13310         }
13311         
13312         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13313         
13314         if(this.hiddenName){
13315             
13316             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13317             
13318             this.hiddenField.dom.value =
13319                 this.hiddenValue !== undefined ? this.hiddenValue :
13320                 this.value !== undefined ? this.value : '';
13321
13322             // prevent input submission
13323             this.el.dom.removeAttribute('name');
13324             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13325              
13326              
13327         }
13328         //if(Roo.isGecko){
13329         //    this.el.dom.setAttribute('autocomplete', 'off');
13330         //}
13331         
13332         var cls = 'x-combo-list';
13333         
13334         //this.list = new Roo.Layer({
13335         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13336         //});
13337         
13338         var _this = this;
13339         
13340         (function(){
13341             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13342             _this.list.setWidth(lw);
13343         }).defer(100);
13344         
13345         this.list.on('mouseover', this.onViewOver, this);
13346         this.list.on('mousemove', this.onViewMove, this);
13347         this.list.on('scroll', this.onViewScroll, this);
13348         
13349         /*
13350         this.list.swallowEvent('mousewheel');
13351         this.assetHeight = 0;
13352
13353         if(this.title){
13354             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13355             this.assetHeight += this.header.getHeight();
13356         }
13357
13358         this.innerList = this.list.createChild({cls:cls+'-inner'});
13359         this.innerList.on('mouseover', this.onViewOver, this);
13360         this.innerList.on('mousemove', this.onViewMove, this);
13361         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13362         
13363         if(this.allowBlank && !this.pageSize && !this.disableClear){
13364             this.footer = this.list.createChild({cls:cls+'-ft'});
13365             this.pageTb = new Roo.Toolbar(this.footer);
13366            
13367         }
13368         if(this.pageSize){
13369             this.footer = this.list.createChild({cls:cls+'-ft'});
13370             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13371                     {pageSize: this.pageSize});
13372             
13373         }
13374         
13375         if (this.pageTb && this.allowBlank && !this.disableClear) {
13376             var _this = this;
13377             this.pageTb.add(new Roo.Toolbar.Fill(), {
13378                 cls: 'x-btn-icon x-btn-clear',
13379                 text: '&#160;',
13380                 handler: function()
13381                 {
13382                     _this.collapse();
13383                     _this.clearValue();
13384                     _this.onSelect(false, -1);
13385                 }
13386             });
13387         }
13388         if (this.footer) {
13389             this.assetHeight += this.footer.getHeight();
13390         }
13391         */
13392             
13393         if(!this.tpl){
13394             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13395         }
13396
13397         this.view = new Roo.View(this.list, this.tpl, {
13398             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13399         });
13400         //this.view.wrapEl.setDisplayed(false);
13401         this.view.on('click', this.onViewClick, this);
13402         
13403         
13404         this.store.on('beforeload', this.onBeforeLoad, this);
13405         this.store.on('load', this.onLoad, this);
13406         this.store.on('loadexception', this.onLoadException, this);
13407         /*
13408         if(this.resizable){
13409             this.resizer = new Roo.Resizable(this.list,  {
13410                pinned:true, handles:'se'
13411             });
13412             this.resizer.on('resize', function(r, w, h){
13413                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13414                 this.listWidth = w;
13415                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13416                 this.restrictHeight();
13417             }, this);
13418             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13419         }
13420         */
13421         if(!this.editable){
13422             this.editable = true;
13423             this.setEditable(false);
13424         }
13425         
13426         /*
13427         
13428         if (typeof(this.events.add.listeners) != 'undefined') {
13429             
13430             this.addicon = this.wrap.createChild(
13431                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13432        
13433             this.addicon.on('click', function(e) {
13434                 this.fireEvent('add', this);
13435             }, this);
13436         }
13437         if (typeof(this.events.edit.listeners) != 'undefined') {
13438             
13439             this.editicon = this.wrap.createChild(
13440                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13441             if (this.addicon) {
13442                 this.editicon.setStyle('margin-left', '40px');
13443             }
13444             this.editicon.on('click', function(e) {
13445                 
13446                 // we fire even  if inothing is selected..
13447                 this.fireEvent('edit', this, this.lastData );
13448                 
13449             }, this);
13450         }
13451         */
13452         
13453         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13454             "up" : function(e){
13455                 this.inKeyMode = true;
13456                 this.selectPrev();
13457             },
13458
13459             "down" : function(e){
13460                 if(!this.isExpanded()){
13461                     this.onTriggerClick();
13462                 }else{
13463                     this.inKeyMode = true;
13464                     this.selectNext();
13465                 }
13466             },
13467
13468             "enter" : function(e){
13469 //                this.onViewClick();
13470                 //return true;
13471                 this.collapse();
13472                 
13473                 if(this.fireEvent("specialkey", this, e)){
13474                     this.onViewClick(false);
13475                 }
13476                 
13477                 return true;
13478             },
13479
13480             "esc" : function(e){
13481                 this.collapse();
13482             },
13483
13484             "tab" : function(e){
13485                 this.collapse();
13486                 
13487                 if(this.fireEvent("specialkey", this, e)){
13488                     this.onViewClick(false);
13489                 }
13490                 
13491                 return true;
13492             },
13493
13494             scope : this,
13495
13496             doRelay : function(foo, bar, hname){
13497                 if(hname == 'down' || this.scope.isExpanded()){
13498                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13499                 }
13500                 return true;
13501             },
13502
13503             forceKeyDown: true
13504         });
13505         
13506         
13507         this.queryDelay = Math.max(this.queryDelay || 10,
13508                 this.mode == 'local' ? 10 : 250);
13509         
13510         
13511         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13512         
13513         if(this.typeAhead){
13514             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13515         }
13516         if(this.editable !== false){
13517             this.inputEl().on("keyup", this.onKeyUp, this);
13518         }
13519         if(this.forceSelection){
13520             this.inputEl().on('blur', this.doForce, this);
13521         }
13522         
13523         if(this.multiple){
13524             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13525             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13526         }
13527     },
13528     
13529     initTickableEvents: function()
13530     {   
13531         this.createList();
13532         
13533         if(this.hiddenName){
13534             
13535             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13536             
13537             this.hiddenField.dom.value =
13538                 this.hiddenValue !== undefined ? this.hiddenValue :
13539                 this.value !== undefined ? this.value : '';
13540
13541             // prevent input submission
13542             this.el.dom.removeAttribute('name');
13543             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13544              
13545              
13546         }
13547         
13548 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13549         
13550         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13551         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13552         if(this.triggerList){
13553             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13554         }
13555          
13556         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13557         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13558         
13559         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13560         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13561         
13562         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13563         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13564         
13565         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13566         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13567         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13568         
13569         this.okBtn.hide();
13570         this.cancelBtn.hide();
13571         
13572         var _this = this;
13573         
13574         (function(){
13575             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13576             _this.list.setWidth(lw);
13577         }).defer(100);
13578         
13579         this.list.on('mouseover', this.onViewOver, this);
13580         this.list.on('mousemove', this.onViewMove, this);
13581         
13582         this.list.on('scroll', this.onViewScroll, this);
13583         
13584         if(!this.tpl){
13585             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>';
13586         }
13587
13588         this.view = new Roo.View(this.list, this.tpl, {
13589             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13590         });
13591         
13592         //this.view.wrapEl.setDisplayed(false);
13593         this.view.on('click', this.onViewClick, this);
13594         
13595         
13596         
13597         this.store.on('beforeload', this.onBeforeLoad, this);
13598         this.store.on('load', this.onLoad, this);
13599         this.store.on('loadexception', this.onLoadException, this);
13600         
13601         if(this.editable){
13602             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13603                 "up" : function(e){
13604                     this.inKeyMode = true;
13605                     this.selectPrev();
13606                 },
13607
13608                 "down" : function(e){
13609                     this.inKeyMode = true;
13610                     this.selectNext();
13611                 },
13612
13613                 "enter" : function(e){
13614                     if(this.fireEvent("specialkey", this, e)){
13615                         this.onViewClick(false);
13616                     }
13617                     
13618                     return true;
13619                 },
13620
13621                 "esc" : function(e){
13622                     this.onTickableFooterButtonClick(e, false, false);
13623                 },
13624
13625                 "tab" : function(e){
13626                     this.fireEvent("specialkey", this, e);
13627                     
13628                     this.onTickableFooterButtonClick(e, false, false);
13629                     
13630                     return true;
13631                 },
13632
13633                 scope : this,
13634
13635                 doRelay : function(e, fn, key){
13636                     if(this.scope.isExpanded()){
13637                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13638                     }
13639                     return true;
13640                 },
13641
13642                 forceKeyDown: true
13643             });
13644         }
13645         
13646         this.queryDelay = Math.max(this.queryDelay || 10,
13647                 this.mode == 'local' ? 10 : 250);
13648         
13649         
13650         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13651         
13652         if(this.typeAhead){
13653             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13654         }
13655         
13656         if(this.editable !== false){
13657             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13658         }
13659         
13660         this.indicator = this.indicatorEl();
13661         
13662         if(this.indicator){
13663             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13664             this.indicator.hide();
13665         }
13666         
13667     },
13668
13669     onDestroy : function(){
13670         if(this.view){
13671             this.view.setStore(null);
13672             this.view.el.removeAllListeners();
13673             this.view.el.remove();
13674             this.view.purgeListeners();
13675         }
13676         if(this.list){
13677             this.list.dom.innerHTML  = '';
13678         }
13679         
13680         if(this.store){
13681             this.store.un('beforeload', this.onBeforeLoad, this);
13682             this.store.un('load', this.onLoad, this);
13683             this.store.un('loadexception', this.onLoadException, this);
13684         }
13685         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13686     },
13687
13688     // private
13689     fireKey : function(e){
13690         if(e.isNavKeyPress() && !this.list.isVisible()){
13691             this.fireEvent("specialkey", this, e);
13692         }
13693     },
13694
13695     // private
13696     onResize: function(w, h){
13697 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13698 //        
13699 //        if(typeof w != 'number'){
13700 //            // we do not handle it!?!?
13701 //            return;
13702 //        }
13703 //        var tw = this.trigger.getWidth();
13704 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13705 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13706 //        var x = w - tw;
13707 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13708 //            
13709 //        //this.trigger.setStyle('left', x+'px');
13710 //        
13711 //        if(this.list && this.listWidth === undefined){
13712 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13713 //            this.list.setWidth(lw);
13714 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13715 //        }
13716         
13717     
13718         
13719     },
13720
13721     /**
13722      * Allow or prevent the user from directly editing the field text.  If false is passed,
13723      * the user will only be able to select from the items defined in the dropdown list.  This method
13724      * is the runtime equivalent of setting the 'editable' config option at config time.
13725      * @param {Boolean} value True to allow the user to directly edit the field text
13726      */
13727     setEditable : function(value){
13728         if(value == this.editable){
13729             return;
13730         }
13731         this.editable = value;
13732         if(!value){
13733             this.inputEl().dom.setAttribute('readOnly', true);
13734             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13735             this.inputEl().addClass('x-combo-noedit');
13736         }else{
13737             this.inputEl().dom.setAttribute('readOnly', false);
13738             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13739             this.inputEl().removeClass('x-combo-noedit');
13740         }
13741     },
13742
13743     // private
13744     
13745     onBeforeLoad : function(combo,opts){
13746         if(!this.hasFocus){
13747             return;
13748         }
13749          if (!opts.add) {
13750             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13751          }
13752         this.restrictHeight();
13753         this.selectedIndex = -1;
13754     },
13755
13756     // private
13757     onLoad : function(){
13758         
13759         this.hasQuery = false;
13760         
13761         if(!this.hasFocus){
13762             return;
13763         }
13764         
13765         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13766             this.loading.hide();
13767         }
13768         
13769         if(this.store.getCount() > 0){
13770             
13771             this.expand();
13772             this.restrictHeight();
13773             if(this.lastQuery == this.allQuery){
13774                 if(this.editable && !this.tickable){
13775                     this.inputEl().dom.select();
13776                 }
13777                 
13778                 if(
13779                     !this.selectByValue(this.value, true) &&
13780                     this.autoFocus && 
13781                     (
13782                         !this.store.lastOptions ||
13783                         typeof(this.store.lastOptions.add) == 'undefined' || 
13784                         this.store.lastOptions.add != true
13785                     )
13786                 ){
13787                     this.select(0, true);
13788                 }
13789             }else{
13790                 if(this.autoFocus){
13791                     this.selectNext();
13792                 }
13793                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13794                     this.taTask.delay(this.typeAheadDelay);
13795                 }
13796             }
13797         }else{
13798             this.onEmptyResults();
13799         }
13800         
13801         //this.el.focus();
13802     },
13803     // private
13804     onLoadException : function()
13805     {
13806         this.hasQuery = false;
13807         
13808         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13809             this.loading.hide();
13810         }
13811         
13812         if(this.tickable && this.editable){
13813             return;
13814         }
13815         
13816         this.collapse();
13817         // only causes errors at present
13818         //Roo.log(this.store.reader.jsonData);
13819         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13820             // fixme
13821             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13822         //}
13823         
13824         
13825     },
13826     // private
13827     onTypeAhead : function(){
13828         if(this.store.getCount() > 0){
13829             var r = this.store.getAt(0);
13830             var newValue = r.data[this.displayField];
13831             var len = newValue.length;
13832             var selStart = this.getRawValue().length;
13833             
13834             if(selStart != len){
13835                 this.setRawValue(newValue);
13836                 this.selectText(selStart, newValue.length);
13837             }
13838         }
13839     },
13840
13841     // private
13842     onSelect : function(record, index){
13843         
13844         if(this.fireEvent('beforeselect', this, record, index) !== false){
13845         
13846             this.setFromData(index > -1 ? record.data : false);
13847             
13848             this.collapse();
13849             this.fireEvent('select', this, record, index);
13850         }
13851     },
13852
13853     /**
13854      * Returns the currently selected field value or empty string if no value is set.
13855      * @return {String} value The selected value
13856      */
13857     getValue : function()
13858     {
13859         if(Roo.isIOS && this.useNativeIOS){
13860             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13861         }
13862         
13863         if(this.multiple){
13864             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13865         }
13866         
13867         if(this.valueField){
13868             return typeof this.value != 'undefined' ? this.value : '';
13869         }else{
13870             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13871         }
13872     },
13873     
13874     getRawValue : function()
13875     {
13876         if(Roo.isIOS && this.useNativeIOS){
13877             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13878         }
13879         
13880         var v = this.inputEl().getValue();
13881         
13882         return v;
13883     },
13884
13885     /**
13886      * Clears any text/value currently set in the field
13887      */
13888     clearValue : function(){
13889         
13890         if(this.hiddenField){
13891             this.hiddenField.dom.value = '';
13892         }
13893         this.value = '';
13894         this.setRawValue('');
13895         this.lastSelectionText = '';
13896         this.lastData = false;
13897         
13898         var close = this.closeTriggerEl();
13899         
13900         if(close){
13901             close.hide();
13902         }
13903         
13904         this.validate();
13905         
13906     },
13907
13908     /**
13909      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13910      * will be displayed in the field.  If the value does not match the data value of an existing item,
13911      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13912      * Otherwise the field will be blank (although the value will still be set).
13913      * @param {String} value The value to match
13914      */
13915     setValue : function(v)
13916     {
13917         if(Roo.isIOS && this.useNativeIOS){
13918             this.setIOSValue(v);
13919             return;
13920         }
13921         
13922         if(this.multiple){
13923             this.syncValue();
13924             return;
13925         }
13926         
13927         var text = v;
13928         if(this.valueField){
13929             var r = this.findRecord(this.valueField, v);
13930             if(r){
13931                 text = r.data[this.displayField];
13932             }else if(this.valueNotFoundText !== undefined){
13933                 text = this.valueNotFoundText;
13934             }
13935         }
13936         this.lastSelectionText = text;
13937         if(this.hiddenField){
13938             this.hiddenField.dom.value = v;
13939         }
13940         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13941         this.value = v;
13942         
13943         var close = this.closeTriggerEl();
13944         
13945         if(close){
13946             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13947         }
13948         
13949         this.validate();
13950     },
13951     /**
13952      * @property {Object} the last set data for the element
13953      */
13954     
13955     lastData : false,
13956     /**
13957      * Sets the value of the field based on a object which is related to the record format for the store.
13958      * @param {Object} value the value to set as. or false on reset?
13959      */
13960     setFromData : function(o){
13961         
13962         if(this.multiple){
13963             this.addItem(o);
13964             return;
13965         }
13966             
13967         var dv = ''; // display value
13968         var vv = ''; // value value..
13969         this.lastData = o;
13970         if (this.displayField) {
13971             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13972         } else {
13973             // this is an error condition!!!
13974             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13975         }
13976         
13977         if(this.valueField){
13978             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13979         }
13980         
13981         var close = this.closeTriggerEl();
13982         
13983         if(close){
13984             if(dv.length || vv * 1 > 0){
13985                 close.show() ;
13986                 this.blockFocus=true;
13987             } else {
13988                 close.hide();
13989             }             
13990         }
13991         
13992         if(this.hiddenField){
13993             this.hiddenField.dom.value = vv;
13994             
13995             this.lastSelectionText = dv;
13996             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13997             this.value = vv;
13998             return;
13999         }
14000         // no hidden field.. - we store the value in 'value', but still display
14001         // display field!!!!
14002         this.lastSelectionText = dv;
14003         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14004         this.value = vv;
14005         
14006         
14007         
14008     },
14009     // private
14010     reset : function(){
14011         // overridden so that last data is reset..
14012         
14013         if(this.multiple){
14014             this.clearItem();
14015             return;
14016         }
14017         
14018         this.setValue(this.originalValue);
14019         //this.clearInvalid();
14020         this.lastData = false;
14021         if (this.view) {
14022             this.view.clearSelections();
14023         }
14024         
14025         this.validate();
14026     },
14027     // private
14028     findRecord : function(prop, value){
14029         var record;
14030         if(this.store.getCount() > 0){
14031             this.store.each(function(r){
14032                 if(r.data[prop] == value){
14033                     record = r;
14034                     return false;
14035                 }
14036                 return true;
14037             });
14038         }
14039         return record;
14040     },
14041     
14042     getName: function()
14043     {
14044         // returns hidden if it's set..
14045         if (!this.rendered) {return ''};
14046         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14047         
14048     },
14049     // private
14050     onViewMove : function(e, t){
14051         this.inKeyMode = false;
14052     },
14053
14054     // private
14055     onViewOver : function(e, t){
14056         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14057             return;
14058         }
14059         var item = this.view.findItemFromChild(t);
14060         
14061         if(item){
14062             var index = this.view.indexOf(item);
14063             this.select(index, false);
14064         }
14065     },
14066
14067     // private
14068     onViewClick : function(view, doFocus, el, e)
14069     {
14070         var index = this.view.getSelectedIndexes()[0];
14071         
14072         var r = this.store.getAt(index);
14073         
14074         if(this.tickable){
14075             
14076             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14077                 return;
14078             }
14079             
14080             var rm = false;
14081             var _this = this;
14082             
14083             Roo.each(this.tickItems, function(v,k){
14084                 
14085                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14086                     Roo.log(v);
14087                     _this.tickItems.splice(k, 1);
14088                     
14089                     if(typeof(e) == 'undefined' && view == false){
14090                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14091                     }
14092                     
14093                     rm = true;
14094                     return;
14095                 }
14096             });
14097             
14098             if(rm){
14099                 return;
14100             }
14101             
14102             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14103                 this.tickItems.push(r.data);
14104             }
14105             
14106             if(typeof(e) == 'undefined' && view == false){
14107                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14108             }
14109                     
14110             return;
14111         }
14112         
14113         if(r){
14114             this.onSelect(r, index);
14115         }
14116         if(doFocus !== false && !this.blockFocus){
14117             this.inputEl().focus();
14118         }
14119     },
14120
14121     // private
14122     restrictHeight : function(){
14123         //this.innerList.dom.style.height = '';
14124         //var inner = this.innerList.dom;
14125         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14126         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14127         //this.list.beginUpdate();
14128         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14129         this.list.alignTo(this.inputEl(), this.listAlign);
14130         this.list.alignTo(this.inputEl(), this.listAlign);
14131         //this.list.endUpdate();
14132     },
14133
14134     // private
14135     onEmptyResults : function(){
14136         
14137         if(this.tickable && this.editable){
14138             this.hasFocus = false;
14139             this.restrictHeight();
14140             return;
14141         }
14142         
14143         this.collapse();
14144     },
14145
14146     /**
14147      * Returns true if the dropdown list is expanded, else false.
14148      */
14149     isExpanded : function(){
14150         return this.list.isVisible();
14151     },
14152
14153     /**
14154      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14155      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14156      * @param {String} value The data value of the item to select
14157      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14158      * selected item if it is not currently in view (defaults to true)
14159      * @return {Boolean} True if the value matched an item in the list, else false
14160      */
14161     selectByValue : function(v, scrollIntoView){
14162         if(v !== undefined && v !== null){
14163             var r = this.findRecord(this.valueField || this.displayField, v);
14164             if(r){
14165                 this.select(this.store.indexOf(r), scrollIntoView);
14166                 return true;
14167             }
14168         }
14169         return false;
14170     },
14171
14172     /**
14173      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14174      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14175      * @param {Number} index The zero-based index of the list item to select
14176      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14177      * selected item if it is not currently in view (defaults to true)
14178      */
14179     select : function(index, scrollIntoView){
14180         this.selectedIndex = index;
14181         this.view.select(index);
14182         if(scrollIntoView !== false){
14183             var el = this.view.getNode(index);
14184             /*
14185              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14186              */
14187             if(el){
14188                 this.list.scrollChildIntoView(el, false);
14189             }
14190         }
14191     },
14192
14193     // private
14194     selectNext : function(){
14195         var ct = this.store.getCount();
14196         if(ct > 0){
14197             if(this.selectedIndex == -1){
14198                 this.select(0);
14199             }else if(this.selectedIndex < ct-1){
14200                 this.select(this.selectedIndex+1);
14201             }
14202         }
14203     },
14204
14205     // private
14206     selectPrev : function(){
14207         var ct = this.store.getCount();
14208         if(ct > 0){
14209             if(this.selectedIndex == -1){
14210                 this.select(0);
14211             }else if(this.selectedIndex != 0){
14212                 this.select(this.selectedIndex-1);
14213             }
14214         }
14215     },
14216
14217     // private
14218     onKeyUp : function(e){
14219         if(this.editable !== false && !e.isSpecialKey()){
14220             this.lastKey = e.getKey();
14221             this.dqTask.delay(this.queryDelay);
14222         }
14223     },
14224
14225     // private
14226     validateBlur : function(){
14227         return !this.list || !this.list.isVisible();   
14228     },
14229
14230     // private
14231     initQuery : function(){
14232         
14233         var v = this.getRawValue();
14234         
14235         if(this.tickable && this.editable){
14236             v = this.tickableInputEl().getValue();
14237         }
14238         
14239         this.doQuery(v);
14240     },
14241
14242     // private
14243     doForce : function(){
14244         if(this.inputEl().dom.value.length > 0){
14245             this.inputEl().dom.value =
14246                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14247              
14248         }
14249     },
14250
14251     /**
14252      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14253      * query allowing the query action to be canceled if needed.
14254      * @param {String} query The SQL query to execute
14255      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14256      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14257      * saved in the current store (defaults to false)
14258      */
14259     doQuery : function(q, forceAll){
14260         
14261         if(q === undefined || q === null){
14262             q = '';
14263         }
14264         var qe = {
14265             query: q,
14266             forceAll: forceAll,
14267             combo: this,
14268             cancel:false
14269         };
14270         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14271             return false;
14272         }
14273         q = qe.query;
14274         
14275         forceAll = qe.forceAll;
14276         if(forceAll === true || (q.length >= this.minChars)){
14277             
14278             this.hasQuery = true;
14279             
14280             if(this.lastQuery != q || this.alwaysQuery){
14281                 this.lastQuery = q;
14282                 if(this.mode == 'local'){
14283                     this.selectedIndex = -1;
14284                     if(forceAll){
14285                         this.store.clearFilter();
14286                     }else{
14287                         
14288                         if(this.specialFilter){
14289                             this.fireEvent('specialfilter', this);
14290                             this.onLoad();
14291                             return;
14292                         }
14293                         
14294                         this.store.filter(this.displayField, q);
14295                     }
14296                     
14297                     this.store.fireEvent("datachanged", this.store);
14298                     
14299                     this.onLoad();
14300                     
14301                     
14302                 }else{
14303                     
14304                     this.store.baseParams[this.queryParam] = q;
14305                     
14306                     var options = {params : this.getParams(q)};
14307                     
14308                     if(this.loadNext){
14309                         options.add = true;
14310                         options.params.start = this.page * this.pageSize;
14311                     }
14312                     
14313                     this.store.load(options);
14314                     
14315                     /*
14316                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14317                      *  we should expand the list on onLoad
14318                      *  so command out it
14319                      */
14320 //                    this.expand();
14321                 }
14322             }else{
14323                 this.selectedIndex = -1;
14324                 this.onLoad();   
14325             }
14326         }
14327         
14328         this.loadNext = false;
14329     },
14330     
14331     // private
14332     getParams : function(q){
14333         var p = {};
14334         //p[this.queryParam] = q;
14335         
14336         if(this.pageSize){
14337             p.start = 0;
14338             p.limit = this.pageSize;
14339         }
14340         return p;
14341     },
14342
14343     /**
14344      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14345      */
14346     collapse : function(){
14347         if(!this.isExpanded()){
14348             return;
14349         }
14350         
14351         this.list.hide();
14352         
14353         this.hasFocus = false;
14354         
14355         if(this.tickable){
14356             this.okBtn.hide();
14357             this.cancelBtn.hide();
14358             this.trigger.show();
14359             
14360             if(this.editable){
14361                 this.tickableInputEl().dom.value = '';
14362                 this.tickableInputEl().blur();
14363             }
14364             
14365         }
14366         
14367         Roo.get(document).un('mousedown', this.collapseIf, this);
14368         Roo.get(document).un('mousewheel', this.collapseIf, this);
14369         if (!this.editable) {
14370             Roo.get(document).un('keydown', this.listKeyPress, this);
14371         }
14372         this.fireEvent('collapse', this);
14373         
14374         this.validate();
14375     },
14376
14377     // private
14378     collapseIf : function(e){
14379         var in_combo  = e.within(this.el);
14380         var in_list =  e.within(this.list);
14381         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14382         
14383         if (in_combo || in_list || is_list) {
14384             //e.stopPropagation();
14385             return;
14386         }
14387         
14388         if(this.tickable){
14389             this.onTickableFooterButtonClick(e, false, false);
14390         }
14391
14392         this.collapse();
14393         
14394     },
14395
14396     /**
14397      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14398      */
14399     expand : function(){
14400        
14401         if(this.isExpanded() || !this.hasFocus){
14402             return;
14403         }
14404         
14405         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14406         this.list.setWidth(lw);
14407         
14408         Roo.log('expand');
14409         
14410         this.list.show();
14411         
14412         this.restrictHeight();
14413         
14414         if(this.tickable){
14415             
14416             this.tickItems = Roo.apply([], this.item);
14417             
14418             this.okBtn.show();
14419             this.cancelBtn.show();
14420             this.trigger.hide();
14421             
14422             if(this.editable){
14423                 this.tickableInputEl().focus();
14424             }
14425             
14426         }
14427         
14428         Roo.get(document).on('mousedown', this.collapseIf, this);
14429         Roo.get(document).on('mousewheel', this.collapseIf, this);
14430         if (!this.editable) {
14431             Roo.get(document).on('keydown', this.listKeyPress, this);
14432         }
14433         
14434         this.fireEvent('expand', this);
14435     },
14436
14437     // private
14438     // Implements the default empty TriggerField.onTriggerClick function
14439     onTriggerClick : function(e)
14440     {
14441         Roo.log('trigger click');
14442         
14443         if(this.disabled || !this.triggerList){
14444             return;
14445         }
14446         
14447         this.page = 0;
14448         this.loadNext = false;
14449         
14450         if(this.isExpanded()){
14451             this.collapse();
14452             if (!this.blockFocus) {
14453                 this.inputEl().focus();
14454             }
14455             
14456         }else {
14457             this.hasFocus = true;
14458             if(this.triggerAction == 'all') {
14459                 this.doQuery(this.allQuery, true);
14460             } else {
14461                 this.doQuery(this.getRawValue());
14462             }
14463             if (!this.blockFocus) {
14464                 this.inputEl().focus();
14465             }
14466         }
14467     },
14468     
14469     onTickableTriggerClick : function(e)
14470     {
14471         if(this.disabled){
14472             return;
14473         }
14474         
14475         this.page = 0;
14476         this.loadNext = false;
14477         this.hasFocus = true;
14478         
14479         if(this.triggerAction == 'all') {
14480             this.doQuery(this.allQuery, true);
14481         } else {
14482             this.doQuery(this.getRawValue());
14483         }
14484     },
14485     
14486     onSearchFieldClick : function(e)
14487     {
14488         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14489             this.onTickableFooterButtonClick(e, false, false);
14490             return;
14491         }
14492         
14493         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14494             return;
14495         }
14496         
14497         this.page = 0;
14498         this.loadNext = false;
14499         this.hasFocus = true;
14500         
14501         if(this.triggerAction == 'all') {
14502             this.doQuery(this.allQuery, true);
14503         } else {
14504             this.doQuery(this.getRawValue());
14505         }
14506     },
14507     
14508     listKeyPress : function(e)
14509     {
14510         //Roo.log('listkeypress');
14511         // scroll to first matching element based on key pres..
14512         if (e.isSpecialKey()) {
14513             return false;
14514         }
14515         var k = String.fromCharCode(e.getKey()).toUpperCase();
14516         //Roo.log(k);
14517         var match  = false;
14518         var csel = this.view.getSelectedNodes();
14519         var cselitem = false;
14520         if (csel.length) {
14521             var ix = this.view.indexOf(csel[0]);
14522             cselitem  = this.store.getAt(ix);
14523             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14524                 cselitem = false;
14525             }
14526             
14527         }
14528         
14529         this.store.each(function(v) { 
14530             if (cselitem) {
14531                 // start at existing selection.
14532                 if (cselitem.id == v.id) {
14533                     cselitem = false;
14534                 }
14535                 return true;
14536             }
14537                 
14538             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14539                 match = this.store.indexOf(v);
14540                 return false;
14541             }
14542             return true;
14543         }, this);
14544         
14545         if (match === false) {
14546             return true; // no more action?
14547         }
14548         // scroll to?
14549         this.view.select(match);
14550         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14551         sn.scrollIntoView(sn.dom.parentNode, false);
14552     },
14553     
14554     onViewScroll : function(e, t){
14555         
14556         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){
14557             return;
14558         }
14559         
14560         this.hasQuery = true;
14561         
14562         this.loading = this.list.select('.loading', true).first();
14563         
14564         if(this.loading === null){
14565             this.list.createChild({
14566                 tag: 'div',
14567                 cls: 'loading roo-select2-more-results roo-select2-active',
14568                 html: 'Loading more results...'
14569             });
14570             
14571             this.loading = this.list.select('.loading', true).first();
14572             
14573             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14574             
14575             this.loading.hide();
14576         }
14577         
14578         this.loading.show();
14579         
14580         var _combo = this;
14581         
14582         this.page++;
14583         this.loadNext = true;
14584         
14585         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14586         
14587         return;
14588     },
14589     
14590     addItem : function(o)
14591     {   
14592         var dv = ''; // display value
14593         
14594         if (this.displayField) {
14595             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14596         } else {
14597             // this is an error condition!!!
14598             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14599         }
14600         
14601         if(!dv.length){
14602             return;
14603         }
14604         
14605         var choice = this.choices.createChild({
14606             tag: 'li',
14607             cls: 'roo-select2-search-choice',
14608             cn: [
14609                 {
14610                     tag: 'div',
14611                     html: dv
14612                 },
14613                 {
14614                     tag: 'a',
14615                     href: '#',
14616                     cls: 'roo-select2-search-choice-close fa fa-times',
14617                     tabindex: '-1'
14618                 }
14619             ]
14620             
14621         }, this.searchField);
14622         
14623         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14624         
14625         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14626         
14627         this.item.push(o);
14628         
14629         this.lastData = o;
14630         
14631         this.syncValue();
14632         
14633         this.inputEl().dom.value = '';
14634         
14635         this.validate();
14636     },
14637     
14638     onRemoveItem : function(e, _self, o)
14639     {
14640         e.preventDefault();
14641         
14642         this.lastItem = Roo.apply([], this.item);
14643         
14644         var index = this.item.indexOf(o.data) * 1;
14645         
14646         if( index < 0){
14647             Roo.log('not this item?!');
14648             return;
14649         }
14650         
14651         this.item.splice(index, 1);
14652         o.item.remove();
14653         
14654         this.syncValue();
14655         
14656         this.fireEvent('remove', this, e);
14657         
14658         this.validate();
14659         
14660     },
14661     
14662     syncValue : function()
14663     {
14664         if(!this.item.length){
14665             this.clearValue();
14666             return;
14667         }
14668             
14669         var value = [];
14670         var _this = this;
14671         Roo.each(this.item, function(i){
14672             if(_this.valueField){
14673                 value.push(i[_this.valueField]);
14674                 return;
14675             }
14676
14677             value.push(i);
14678         });
14679
14680         this.value = value.join(',');
14681
14682         if(this.hiddenField){
14683             this.hiddenField.dom.value = this.value;
14684         }
14685         
14686         this.store.fireEvent("datachanged", this.store);
14687         
14688         this.validate();
14689     },
14690     
14691     clearItem : function()
14692     {
14693         if(!this.multiple){
14694             return;
14695         }
14696         
14697         this.item = [];
14698         
14699         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14700            c.remove();
14701         });
14702         
14703         this.syncValue();
14704         
14705         this.validate();
14706         
14707         if(this.tickable && !Roo.isTouch){
14708             this.view.refresh();
14709         }
14710     },
14711     
14712     inputEl: function ()
14713     {
14714         if(Roo.isIOS && this.useNativeIOS){
14715             return this.el.select('select.roo-ios-select', true).first();
14716         }
14717         
14718         if(Roo.isTouch && this.mobileTouchView){
14719             return this.el.select('input.form-control',true).first();
14720         }
14721         
14722         if(this.tickable){
14723             return this.searchField;
14724         }
14725         
14726         return this.el.select('input.form-control',true).first();
14727     },
14728     
14729     onTickableFooterButtonClick : function(e, btn, el)
14730     {
14731         e.preventDefault();
14732         
14733         this.lastItem = Roo.apply([], this.item);
14734         
14735         if(btn && btn.name == 'cancel'){
14736             this.tickItems = Roo.apply([], this.item);
14737             this.collapse();
14738             return;
14739         }
14740         
14741         this.clearItem();
14742         
14743         var _this = this;
14744         
14745         Roo.each(this.tickItems, function(o){
14746             _this.addItem(o);
14747         });
14748         
14749         this.collapse();
14750         
14751     },
14752     
14753     validate : function()
14754     {
14755         if(this.getVisibilityEl().hasClass('hidden')){
14756             return true;
14757         }
14758         
14759         var v = this.getRawValue();
14760         
14761         if(this.multiple){
14762             v = this.getValue();
14763         }
14764         
14765         if(this.disabled || this.allowBlank || v.length){
14766             this.markValid();
14767             return true;
14768         }
14769         
14770         this.markInvalid();
14771         return false;
14772     },
14773     
14774     tickableInputEl : function()
14775     {
14776         if(!this.tickable || !this.editable){
14777             return this.inputEl();
14778         }
14779         
14780         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14781     },
14782     
14783     
14784     getAutoCreateTouchView : function()
14785     {
14786         var id = Roo.id();
14787         
14788         var cfg = {
14789             cls: 'form-group' //input-group
14790         };
14791         
14792         var input =  {
14793             tag: 'input',
14794             id : id,
14795             type : this.inputType,
14796             cls : 'form-control x-combo-noedit',
14797             autocomplete: 'new-password',
14798             placeholder : this.placeholder || '',
14799             readonly : true
14800         };
14801         
14802         if (this.name) {
14803             input.name = this.name;
14804         }
14805         
14806         if (this.size) {
14807             input.cls += ' input-' + this.size;
14808         }
14809         
14810         if (this.disabled) {
14811             input.disabled = true;
14812         }
14813         
14814         var inputblock = {
14815             cls : '',
14816             cn : [
14817                 input
14818             ]
14819         };
14820         
14821         if(this.before){
14822             inputblock.cls += ' input-group';
14823             
14824             inputblock.cn.unshift({
14825                 tag :'span',
14826                 cls : 'input-group-addon',
14827                 html : this.before
14828             });
14829         }
14830         
14831         if(this.removable && !this.multiple){
14832             inputblock.cls += ' roo-removable';
14833             
14834             inputblock.cn.push({
14835                 tag: 'button',
14836                 html : 'x',
14837                 cls : 'roo-combo-removable-btn close'
14838             });
14839         }
14840
14841         if(this.hasFeedback && !this.allowBlank){
14842             
14843             inputblock.cls += ' has-feedback';
14844             
14845             inputblock.cn.push({
14846                 tag: 'span',
14847                 cls: 'glyphicon form-control-feedback'
14848             });
14849             
14850         }
14851         
14852         if (this.after) {
14853             
14854             inputblock.cls += (this.before) ? '' : ' input-group';
14855             
14856             inputblock.cn.push({
14857                 tag :'span',
14858                 cls : 'input-group-addon',
14859                 html : this.after
14860             });
14861         }
14862
14863         var box = {
14864             tag: 'div',
14865             cn: [
14866                 {
14867                     tag: 'input',
14868                     type : 'hidden',
14869                     cls: 'form-hidden-field'
14870                 },
14871                 inputblock
14872             ]
14873             
14874         };
14875         
14876         if(this.multiple){
14877             box = {
14878                 tag: 'div',
14879                 cn: [
14880                     {
14881                         tag: 'input',
14882                         type : 'hidden',
14883                         cls: 'form-hidden-field'
14884                     },
14885                     {
14886                         tag: 'ul',
14887                         cls: 'roo-select2-choices',
14888                         cn:[
14889                             {
14890                                 tag: 'li',
14891                                 cls: 'roo-select2-search-field',
14892                                 cn: [
14893
14894                                     inputblock
14895                                 ]
14896                             }
14897                         ]
14898                     }
14899                 ]
14900             }
14901         };
14902         
14903         var combobox = {
14904             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14905             cn: [
14906                 box
14907             ]
14908         };
14909         
14910         if(!this.multiple && this.showToggleBtn){
14911             
14912             var caret = {
14913                         tag: 'span',
14914                         cls: 'caret'
14915             };
14916             
14917             if (this.caret != false) {
14918                 caret = {
14919                      tag: 'i',
14920                      cls: 'fa fa-' + this.caret
14921                 };
14922                 
14923             }
14924             
14925             combobox.cn.push({
14926                 tag :'span',
14927                 cls : 'input-group-addon btn dropdown-toggle',
14928                 cn : [
14929                     caret,
14930                     {
14931                         tag: 'span',
14932                         cls: 'combobox-clear',
14933                         cn  : [
14934                             {
14935                                 tag : 'i',
14936                                 cls: 'icon-remove'
14937                             }
14938                         ]
14939                     }
14940                 ]
14941
14942             })
14943         }
14944         
14945         if(this.multiple){
14946             combobox.cls += ' roo-select2-container-multi';
14947         }
14948         
14949         var align = this.labelAlign || this.parentLabelAlign();
14950         
14951         if (align ==='left' && this.fieldLabel.length) {
14952
14953             cfg.cn = [
14954                 {
14955                    tag : 'i',
14956                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14957                    tooltip : 'This field is required'
14958                 },
14959                 {
14960                     tag: 'label',
14961                     cls : 'control-label',
14962                     html : this.fieldLabel
14963
14964                 },
14965                 {
14966                     cls : '', 
14967                     cn: [
14968                         combobox
14969                     ]
14970                 }
14971             ];
14972             
14973             var labelCfg = cfg.cn[1];
14974             var contentCfg = cfg.cn[2];
14975             
14976
14977             if(this.indicatorpos == 'right'){
14978                 cfg.cn = [
14979                     {
14980                         tag: 'label',
14981                         'for' :  id,
14982                         cls : 'control-label',
14983                         cn : [
14984                             {
14985                                 tag : 'span',
14986                                 html : this.fieldLabel
14987                             },
14988                             {
14989                                 tag : 'i',
14990                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14991                                 tooltip : 'This field is required'
14992                             }
14993                         ]
14994                     },
14995                     {
14996                         cls : "",
14997                         cn: [
14998                             combobox
14999                         ]
15000                     }
15001
15002                 ];
15003                 
15004                 labelCfg = cfg.cn[0];
15005                 contentCfg = cfg.cn[1];
15006             }
15007             
15008            
15009             
15010             if(this.labelWidth > 12){
15011                 labelCfg.style = "width: " + this.labelWidth + 'px';
15012             }
15013             
15014             if(this.labelWidth < 13 && this.labelmd == 0){
15015                 this.labelmd = this.labelWidth;
15016             }
15017             
15018             if(this.labellg > 0){
15019                 labelCfg.cls += ' col-lg-' + this.labellg;
15020                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15021             }
15022             
15023             if(this.labelmd > 0){
15024                 labelCfg.cls += ' col-md-' + this.labelmd;
15025                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15026             }
15027             
15028             if(this.labelsm > 0){
15029                 labelCfg.cls += ' col-sm-' + this.labelsm;
15030                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15031             }
15032             
15033             if(this.labelxs > 0){
15034                 labelCfg.cls += ' col-xs-' + this.labelxs;
15035                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15036             }
15037                 
15038                 
15039         } else if ( this.fieldLabel.length) {
15040             cfg.cn = [
15041                 {
15042                    tag : 'i',
15043                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15044                    tooltip : 'This field is required'
15045                 },
15046                 {
15047                     tag: 'label',
15048                     cls : 'control-label',
15049                     html : this.fieldLabel
15050
15051                 },
15052                 {
15053                     cls : '', 
15054                     cn: [
15055                         combobox
15056                     ]
15057                 }
15058             ];
15059             
15060             if(this.indicatorpos == 'right'){
15061                 cfg.cn = [
15062                     {
15063                         tag: 'label',
15064                         cls : 'control-label',
15065                         html : this.fieldLabel,
15066                         cn : [
15067                             {
15068                                tag : 'i',
15069                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15070                                tooltip : 'This field is required'
15071                             }
15072                         ]
15073                     },
15074                     {
15075                         cls : '', 
15076                         cn: [
15077                             combobox
15078                         ]
15079                     }
15080                 ];
15081             }
15082         } else {
15083             cfg.cn = combobox;    
15084         }
15085         
15086         
15087         var settings = this;
15088         
15089         ['xs','sm','md','lg'].map(function(size){
15090             if (settings[size]) {
15091                 cfg.cls += ' col-' + size + '-' + settings[size];
15092             }
15093         });
15094         
15095         return cfg;
15096     },
15097     
15098     initTouchView : function()
15099     {
15100         this.renderTouchView();
15101         
15102         this.touchViewEl.on('scroll', function(){
15103             this.el.dom.scrollTop = 0;
15104         }, this);
15105         
15106         this.originalValue = this.getValue();
15107         
15108         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15109         
15110         this.inputEl().on("click", this.showTouchView, this);
15111         if (this.triggerEl) {
15112             this.triggerEl.on("click", this.showTouchView, this);
15113         }
15114         
15115         
15116         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15117         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15118         
15119         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15120         
15121         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15122         this.store.on('load', this.onTouchViewLoad, this);
15123         this.store.on('loadexception', this.onTouchViewLoadException, this);
15124         
15125         if(this.hiddenName){
15126             
15127             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15128             
15129             this.hiddenField.dom.value =
15130                 this.hiddenValue !== undefined ? this.hiddenValue :
15131                 this.value !== undefined ? this.value : '';
15132         
15133             this.el.dom.removeAttribute('name');
15134             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15135         }
15136         
15137         if(this.multiple){
15138             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15139             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15140         }
15141         
15142         if(this.removable && !this.multiple){
15143             var close = this.closeTriggerEl();
15144             if(close){
15145                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15146                 close.on('click', this.removeBtnClick, this, close);
15147             }
15148         }
15149         /*
15150          * fix the bug in Safari iOS8
15151          */
15152         this.inputEl().on("focus", function(e){
15153             document.activeElement.blur();
15154         }, this);
15155         
15156         return;
15157         
15158         
15159     },
15160     
15161     renderTouchView : function()
15162     {
15163         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15164         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15165         
15166         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15167         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15168         
15169         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15170         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15171         this.touchViewBodyEl.setStyle('overflow', 'auto');
15172         
15173         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15174         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15175         
15176         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15177         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15178         
15179     },
15180     
15181     showTouchView : function()
15182     {
15183         if(this.disabled){
15184             return;
15185         }
15186         
15187         this.touchViewHeaderEl.hide();
15188
15189         if(this.modalTitle.length){
15190             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15191             this.touchViewHeaderEl.show();
15192         }
15193
15194         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15195         this.touchViewEl.show();
15196
15197         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15198         
15199         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15200         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15201
15202         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15203
15204         if(this.modalTitle.length){
15205             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15206         }
15207         
15208         this.touchViewBodyEl.setHeight(bodyHeight);
15209
15210         if(this.animate){
15211             var _this = this;
15212             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15213         }else{
15214             this.touchViewEl.addClass('in');
15215         }
15216
15217         this.doTouchViewQuery();
15218         
15219     },
15220     
15221     hideTouchView : function()
15222     {
15223         this.touchViewEl.removeClass('in');
15224
15225         if(this.animate){
15226             var _this = this;
15227             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15228         }else{
15229             this.touchViewEl.setStyle('display', 'none');
15230         }
15231         
15232     },
15233     
15234     setTouchViewValue : function()
15235     {
15236         if(this.multiple){
15237             this.clearItem();
15238         
15239             var _this = this;
15240
15241             Roo.each(this.tickItems, function(o){
15242                 this.addItem(o);
15243             }, this);
15244         }
15245         
15246         this.hideTouchView();
15247     },
15248     
15249     doTouchViewQuery : function()
15250     {
15251         var qe = {
15252             query: '',
15253             forceAll: true,
15254             combo: this,
15255             cancel:false
15256         };
15257         
15258         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15259             return false;
15260         }
15261         
15262         if(!this.alwaysQuery || this.mode == 'local'){
15263             this.onTouchViewLoad();
15264             return;
15265         }
15266         
15267         this.store.load();
15268     },
15269     
15270     onTouchViewBeforeLoad : function(combo,opts)
15271     {
15272         return;
15273     },
15274
15275     // private
15276     onTouchViewLoad : function()
15277     {
15278         if(this.store.getCount() < 1){
15279             this.onTouchViewEmptyResults();
15280             return;
15281         }
15282         
15283         this.clearTouchView();
15284         
15285         var rawValue = this.getRawValue();
15286         
15287         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15288         
15289         this.tickItems = [];
15290         
15291         this.store.data.each(function(d, rowIndex){
15292             var row = this.touchViewListGroup.createChild(template);
15293             
15294             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15295                 row.addClass(d.data.cls);
15296             }
15297             
15298             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15299                 var cfg = {
15300                     data : d.data,
15301                     html : d.data[this.displayField]
15302                 };
15303                 
15304                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15305                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15306                 }
15307             }
15308             row.removeClass('selected');
15309             if(!this.multiple && this.valueField &&
15310                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15311             {
15312                 // radio buttons..
15313                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15314                 row.addClass('selected');
15315             }
15316             
15317             if(this.multiple && this.valueField &&
15318                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15319             {
15320                 
15321                 // checkboxes...
15322                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15323                 this.tickItems.push(d.data);
15324             }
15325             
15326             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15327             
15328         }, this);
15329         
15330         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15331         
15332         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15333
15334         if(this.modalTitle.length){
15335             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15336         }
15337
15338         var listHeight = this.touchViewListGroup.getHeight();
15339         
15340         var _this = this;
15341         
15342         if(firstChecked && listHeight > bodyHeight){
15343             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15344         }
15345         
15346     },
15347     
15348     onTouchViewLoadException : function()
15349     {
15350         this.hideTouchView();
15351     },
15352     
15353     onTouchViewEmptyResults : function()
15354     {
15355         this.clearTouchView();
15356         
15357         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15358         
15359         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15360         
15361     },
15362     
15363     clearTouchView : function()
15364     {
15365         this.touchViewListGroup.dom.innerHTML = '';
15366     },
15367     
15368     onTouchViewClick : function(e, el, o)
15369     {
15370         e.preventDefault();
15371         
15372         var row = o.row;
15373         var rowIndex = o.rowIndex;
15374         
15375         var r = this.store.getAt(rowIndex);
15376         
15377         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15378             
15379             if(!this.multiple){
15380                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15381                     c.dom.removeAttribute('checked');
15382                 }, this);
15383
15384                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15385
15386                 this.setFromData(r.data);
15387
15388                 var close = this.closeTriggerEl();
15389
15390                 if(close){
15391                     close.show();
15392                 }
15393
15394                 this.hideTouchView();
15395
15396                 this.fireEvent('select', this, r, rowIndex);
15397
15398                 return;
15399             }
15400
15401             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15402                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15403                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15404                 return;
15405             }
15406
15407             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15408             this.addItem(r.data);
15409             this.tickItems.push(r.data);
15410         }
15411     },
15412     
15413     getAutoCreateNativeIOS : function()
15414     {
15415         var cfg = {
15416             cls: 'form-group' //input-group,
15417         };
15418         
15419         var combobox =  {
15420             tag: 'select',
15421             cls : 'roo-ios-select'
15422         };
15423         
15424         if (this.name) {
15425             combobox.name = this.name;
15426         }
15427         
15428         if (this.disabled) {
15429             combobox.disabled = true;
15430         }
15431         
15432         var settings = this;
15433         
15434         ['xs','sm','md','lg'].map(function(size){
15435             if (settings[size]) {
15436                 cfg.cls += ' col-' + size + '-' + settings[size];
15437             }
15438         });
15439         
15440         cfg.cn = combobox;
15441         
15442         return cfg;
15443         
15444     },
15445     
15446     initIOSView : function()
15447     {
15448         this.store.on('load', this.onIOSViewLoad, this);
15449         
15450         return;
15451     },
15452     
15453     onIOSViewLoad : function()
15454     {
15455         if(this.store.getCount() < 1){
15456             return;
15457         }
15458         
15459         this.clearIOSView();
15460         
15461         if(this.allowBlank) {
15462             
15463             var default_text = '-- SELECT --';
15464             
15465             if(this.placeholder.length){
15466                 default_text = this.placeholder;
15467             }
15468             
15469             if(this.emptyTitle.length){
15470                 default_text += ' - ' + this.emptyTitle + ' -';
15471             }
15472             
15473             var opt = this.inputEl().createChild({
15474                 tag: 'option',
15475                 value : 0,
15476                 html : default_text
15477             });
15478             
15479             var o = {};
15480             o[this.valueField] = 0;
15481             o[this.displayField] = default_text;
15482             
15483             this.ios_options.push({
15484                 data : o,
15485                 el : opt
15486             });
15487             
15488         }
15489         
15490         this.store.data.each(function(d, rowIndex){
15491             
15492             var html = '';
15493             
15494             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15495                 html = d.data[this.displayField];
15496             }
15497             
15498             var value = '';
15499             
15500             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15501                 value = d.data[this.valueField];
15502             }
15503             
15504             var option = {
15505                 tag: 'option',
15506                 value : value,
15507                 html : html
15508             };
15509             
15510             if(this.value == d.data[this.valueField]){
15511                 option['selected'] = true;
15512             }
15513             
15514             var opt = this.inputEl().createChild(option);
15515             
15516             this.ios_options.push({
15517                 data : d.data,
15518                 el : opt
15519             });
15520             
15521         }, this);
15522         
15523         this.inputEl().on('change', function(){
15524            this.fireEvent('select', this);
15525         }, this);
15526         
15527     },
15528     
15529     clearIOSView: function()
15530     {
15531         this.inputEl().dom.innerHTML = '';
15532         
15533         this.ios_options = [];
15534     },
15535     
15536     setIOSValue: function(v)
15537     {
15538         this.value = v;
15539         
15540         if(!this.ios_options){
15541             return;
15542         }
15543         
15544         Roo.each(this.ios_options, function(opts){
15545            
15546            opts.el.dom.removeAttribute('selected');
15547            
15548            if(opts.data[this.valueField] != v){
15549                return;
15550            }
15551            
15552            opts.el.dom.setAttribute('selected', true);
15553            
15554         }, this);
15555     }
15556
15557     /** 
15558     * @cfg {Boolean} grow 
15559     * @hide 
15560     */
15561     /** 
15562     * @cfg {Number} growMin 
15563     * @hide 
15564     */
15565     /** 
15566     * @cfg {Number} growMax 
15567     * @hide 
15568     */
15569     /**
15570      * @hide
15571      * @method autoSize
15572      */
15573 });
15574
15575 Roo.apply(Roo.bootstrap.ComboBox,  {
15576     
15577     header : {
15578         tag: 'div',
15579         cls: 'modal-header',
15580         cn: [
15581             {
15582                 tag: 'h4',
15583                 cls: 'modal-title'
15584             }
15585         ]
15586     },
15587     
15588     body : {
15589         tag: 'div',
15590         cls: 'modal-body',
15591         cn: [
15592             {
15593                 tag: 'ul',
15594                 cls: 'list-group'
15595             }
15596         ]
15597     },
15598     
15599     listItemRadio : {
15600         tag: 'li',
15601         cls: 'list-group-item',
15602         cn: [
15603             {
15604                 tag: 'span',
15605                 cls: 'roo-combobox-list-group-item-value'
15606             },
15607             {
15608                 tag: 'div',
15609                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15610                 cn: [
15611                     {
15612                         tag: 'input',
15613                         type: 'radio'
15614                     },
15615                     {
15616                         tag: 'label'
15617                     }
15618                 ]
15619             }
15620         ]
15621     },
15622     
15623     listItemCheckbox : {
15624         tag: 'li',
15625         cls: 'list-group-item',
15626         cn: [
15627             {
15628                 tag: 'span',
15629                 cls: 'roo-combobox-list-group-item-value'
15630             },
15631             {
15632                 tag: 'div',
15633                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15634                 cn: [
15635                     {
15636                         tag: 'input',
15637                         type: 'checkbox'
15638                     },
15639                     {
15640                         tag: 'label'
15641                     }
15642                 ]
15643             }
15644         ]
15645     },
15646     
15647     emptyResult : {
15648         tag: 'div',
15649         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15650     },
15651     
15652     footer : {
15653         tag: 'div',
15654         cls: 'modal-footer',
15655         cn: [
15656             {
15657                 tag: 'div',
15658                 cls: 'row',
15659                 cn: [
15660                     {
15661                         tag: 'div',
15662                         cls: 'col-xs-6 text-left',
15663                         cn: {
15664                             tag: 'button',
15665                             cls: 'btn btn-danger roo-touch-view-cancel',
15666                             html: 'Cancel'
15667                         }
15668                     },
15669                     {
15670                         tag: 'div',
15671                         cls: 'col-xs-6 text-right',
15672                         cn: {
15673                             tag: 'button',
15674                             cls: 'btn btn-success roo-touch-view-ok',
15675                             html: 'OK'
15676                         }
15677                     }
15678                 ]
15679             }
15680         ]
15681         
15682     }
15683 });
15684
15685 Roo.apply(Roo.bootstrap.ComboBox,  {
15686     
15687     touchViewTemplate : {
15688         tag: 'div',
15689         cls: 'modal fade roo-combobox-touch-view',
15690         cn: [
15691             {
15692                 tag: 'div',
15693                 cls: 'modal-dialog',
15694                 style : 'position:fixed', // we have to fix position....
15695                 cn: [
15696                     {
15697                         tag: 'div',
15698                         cls: 'modal-content',
15699                         cn: [
15700                             Roo.bootstrap.ComboBox.header,
15701                             Roo.bootstrap.ComboBox.body,
15702                             Roo.bootstrap.ComboBox.footer
15703                         ]
15704                     }
15705                 ]
15706             }
15707         ]
15708     }
15709 });/*
15710  * Based on:
15711  * Ext JS Library 1.1.1
15712  * Copyright(c) 2006-2007, Ext JS, LLC.
15713  *
15714  * Originally Released Under LGPL - original licence link has changed is not relivant.
15715  *
15716  * Fork - LGPL
15717  * <script type="text/javascript">
15718  */
15719
15720 /**
15721  * @class Roo.View
15722  * @extends Roo.util.Observable
15723  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15724  * This class also supports single and multi selection modes. <br>
15725  * Create a data model bound view:
15726  <pre><code>
15727  var store = new Roo.data.Store(...);
15728
15729  var view = new Roo.View({
15730     el : "my-element",
15731     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15732  
15733     singleSelect: true,
15734     selectedClass: "ydataview-selected",
15735     store: store
15736  });
15737
15738  // listen for node click?
15739  view.on("click", function(vw, index, node, e){
15740  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15741  });
15742
15743  // load XML data
15744  dataModel.load("foobar.xml");
15745  </code></pre>
15746  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15747  * <br><br>
15748  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15749  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15750  * 
15751  * Note: old style constructor is still suported (container, template, config)
15752  * 
15753  * @constructor
15754  * Create a new View
15755  * @param {Object} config The config object
15756  * 
15757  */
15758 Roo.View = function(config, depreciated_tpl, depreciated_config){
15759     
15760     this.parent = false;
15761     
15762     if (typeof(depreciated_tpl) == 'undefined') {
15763         // new way.. - universal constructor.
15764         Roo.apply(this, config);
15765         this.el  = Roo.get(this.el);
15766     } else {
15767         // old format..
15768         this.el  = Roo.get(config);
15769         this.tpl = depreciated_tpl;
15770         Roo.apply(this, depreciated_config);
15771     }
15772     this.wrapEl  = this.el.wrap().wrap();
15773     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15774     
15775     
15776     if(typeof(this.tpl) == "string"){
15777         this.tpl = new Roo.Template(this.tpl);
15778     } else {
15779         // support xtype ctors..
15780         this.tpl = new Roo.factory(this.tpl, Roo);
15781     }
15782     
15783     
15784     this.tpl.compile();
15785     
15786     /** @private */
15787     this.addEvents({
15788         /**
15789          * @event beforeclick
15790          * Fires before a click is processed. Returns false to cancel the default action.
15791          * @param {Roo.View} this
15792          * @param {Number} index The index of the target node
15793          * @param {HTMLElement} node The target node
15794          * @param {Roo.EventObject} e The raw event object
15795          */
15796             "beforeclick" : true,
15797         /**
15798          * @event click
15799          * Fires when a template node is clicked.
15800          * @param {Roo.View} this
15801          * @param {Number} index The index of the target node
15802          * @param {HTMLElement} node The target node
15803          * @param {Roo.EventObject} e The raw event object
15804          */
15805             "click" : true,
15806         /**
15807          * @event dblclick
15808          * Fires when a template node is double clicked.
15809          * @param {Roo.View} this
15810          * @param {Number} index The index of the target node
15811          * @param {HTMLElement} node The target node
15812          * @param {Roo.EventObject} e The raw event object
15813          */
15814             "dblclick" : true,
15815         /**
15816          * @event contextmenu
15817          * Fires when a template node is right clicked.
15818          * @param {Roo.View} this
15819          * @param {Number} index The index of the target node
15820          * @param {HTMLElement} node The target node
15821          * @param {Roo.EventObject} e The raw event object
15822          */
15823             "contextmenu" : true,
15824         /**
15825          * @event selectionchange
15826          * Fires when the selected nodes change.
15827          * @param {Roo.View} this
15828          * @param {Array} selections Array of the selected nodes
15829          */
15830             "selectionchange" : true,
15831     
15832         /**
15833          * @event beforeselect
15834          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15835          * @param {Roo.View} this
15836          * @param {HTMLElement} node The node to be selected
15837          * @param {Array} selections Array of currently selected nodes
15838          */
15839             "beforeselect" : true,
15840         /**
15841          * @event preparedata
15842          * Fires on every row to render, to allow you to change the data.
15843          * @param {Roo.View} this
15844          * @param {Object} data to be rendered (change this)
15845          */
15846           "preparedata" : true
15847           
15848           
15849         });
15850
15851
15852
15853     this.el.on({
15854         "click": this.onClick,
15855         "dblclick": this.onDblClick,
15856         "contextmenu": this.onContextMenu,
15857         scope:this
15858     });
15859
15860     this.selections = [];
15861     this.nodes = [];
15862     this.cmp = new Roo.CompositeElementLite([]);
15863     if(this.store){
15864         this.store = Roo.factory(this.store, Roo.data);
15865         this.setStore(this.store, true);
15866     }
15867     
15868     if ( this.footer && this.footer.xtype) {
15869            
15870          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15871         
15872         this.footer.dataSource = this.store;
15873         this.footer.container = fctr;
15874         this.footer = Roo.factory(this.footer, Roo);
15875         fctr.insertFirst(this.el);
15876         
15877         // this is a bit insane - as the paging toolbar seems to detach the el..
15878 //        dom.parentNode.parentNode.parentNode
15879          // they get detached?
15880     }
15881     
15882     
15883     Roo.View.superclass.constructor.call(this);
15884     
15885     
15886 };
15887
15888 Roo.extend(Roo.View, Roo.util.Observable, {
15889     
15890      /**
15891      * @cfg {Roo.data.Store} store Data store to load data from.
15892      */
15893     store : false,
15894     
15895     /**
15896      * @cfg {String|Roo.Element} el The container element.
15897      */
15898     el : '',
15899     
15900     /**
15901      * @cfg {String|Roo.Template} tpl The template used by this View 
15902      */
15903     tpl : false,
15904     /**
15905      * @cfg {String} dataName the named area of the template to use as the data area
15906      *                          Works with domtemplates roo-name="name"
15907      */
15908     dataName: false,
15909     /**
15910      * @cfg {String} selectedClass The css class to add to selected nodes
15911      */
15912     selectedClass : "x-view-selected",
15913      /**
15914      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15915      */
15916     emptyText : "",
15917     
15918     /**
15919      * @cfg {String} text to display on mask (default Loading)
15920      */
15921     mask : false,
15922     /**
15923      * @cfg {Boolean} multiSelect Allow multiple selection
15924      */
15925     multiSelect : false,
15926     /**
15927      * @cfg {Boolean} singleSelect Allow single selection
15928      */
15929     singleSelect:  false,
15930     
15931     /**
15932      * @cfg {Boolean} toggleSelect - selecting 
15933      */
15934     toggleSelect : false,
15935     
15936     /**
15937      * @cfg {Boolean} tickable - selecting 
15938      */
15939     tickable : false,
15940     
15941     /**
15942      * Returns the element this view is bound to.
15943      * @return {Roo.Element}
15944      */
15945     getEl : function(){
15946         return this.wrapEl;
15947     },
15948     
15949     
15950
15951     /**
15952      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15953      */
15954     refresh : function(){
15955         //Roo.log('refresh');
15956         var t = this.tpl;
15957         
15958         // if we are using something like 'domtemplate', then
15959         // the what gets used is:
15960         // t.applySubtemplate(NAME, data, wrapping data..)
15961         // the outer template then get' applied with
15962         //     the store 'extra data'
15963         // and the body get's added to the
15964         //      roo-name="data" node?
15965         //      <span class='roo-tpl-{name}'></span> ?????
15966         
15967         
15968         
15969         this.clearSelections();
15970         this.el.update("");
15971         var html = [];
15972         var records = this.store.getRange();
15973         if(records.length < 1) {
15974             
15975             // is this valid??  = should it render a template??
15976             
15977             this.el.update(this.emptyText);
15978             return;
15979         }
15980         var el = this.el;
15981         if (this.dataName) {
15982             this.el.update(t.apply(this.store.meta)); //????
15983             el = this.el.child('.roo-tpl-' + this.dataName);
15984         }
15985         
15986         for(var i = 0, len = records.length; i < len; i++){
15987             var data = this.prepareData(records[i].data, i, records[i]);
15988             this.fireEvent("preparedata", this, data, i, records[i]);
15989             
15990             var d = Roo.apply({}, data);
15991             
15992             if(this.tickable){
15993                 Roo.apply(d, {'roo-id' : Roo.id()});
15994                 
15995                 var _this = this;
15996             
15997                 Roo.each(this.parent.item, function(item){
15998                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15999                         return;
16000                     }
16001                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16002                 });
16003             }
16004             
16005             html[html.length] = Roo.util.Format.trim(
16006                 this.dataName ?
16007                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16008                     t.apply(d)
16009             );
16010         }
16011         
16012         
16013         
16014         el.update(html.join(""));
16015         this.nodes = el.dom.childNodes;
16016         this.updateIndexes(0);
16017     },
16018     
16019
16020     /**
16021      * Function to override to reformat the data that is sent to
16022      * the template for each node.
16023      * DEPRICATED - use the preparedata event handler.
16024      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16025      * a JSON object for an UpdateManager bound view).
16026      */
16027     prepareData : function(data, index, record)
16028     {
16029         this.fireEvent("preparedata", this, data, index, record);
16030         return data;
16031     },
16032
16033     onUpdate : function(ds, record){
16034         // Roo.log('on update');   
16035         this.clearSelections();
16036         var index = this.store.indexOf(record);
16037         var n = this.nodes[index];
16038         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16039         n.parentNode.removeChild(n);
16040         this.updateIndexes(index, index);
16041     },
16042
16043     
16044     
16045 // --------- FIXME     
16046     onAdd : function(ds, records, index)
16047     {
16048         //Roo.log(['on Add', ds, records, index] );        
16049         this.clearSelections();
16050         if(this.nodes.length == 0){
16051             this.refresh();
16052             return;
16053         }
16054         var n = this.nodes[index];
16055         for(var i = 0, len = records.length; i < len; i++){
16056             var d = this.prepareData(records[i].data, i, records[i]);
16057             if(n){
16058                 this.tpl.insertBefore(n, d);
16059             }else{
16060                 
16061                 this.tpl.append(this.el, d);
16062             }
16063         }
16064         this.updateIndexes(index);
16065     },
16066
16067     onRemove : function(ds, record, index){
16068        // Roo.log('onRemove');
16069         this.clearSelections();
16070         var el = this.dataName  ?
16071             this.el.child('.roo-tpl-' + this.dataName) :
16072             this.el; 
16073         
16074         el.dom.removeChild(this.nodes[index]);
16075         this.updateIndexes(index);
16076     },
16077
16078     /**
16079      * Refresh an individual node.
16080      * @param {Number} index
16081      */
16082     refreshNode : function(index){
16083         this.onUpdate(this.store, this.store.getAt(index));
16084     },
16085
16086     updateIndexes : function(startIndex, endIndex){
16087         var ns = this.nodes;
16088         startIndex = startIndex || 0;
16089         endIndex = endIndex || ns.length - 1;
16090         for(var i = startIndex; i <= endIndex; i++){
16091             ns[i].nodeIndex = i;
16092         }
16093     },
16094
16095     /**
16096      * Changes the data store this view uses and refresh the view.
16097      * @param {Store} store
16098      */
16099     setStore : function(store, initial){
16100         if(!initial && this.store){
16101             this.store.un("datachanged", this.refresh);
16102             this.store.un("add", this.onAdd);
16103             this.store.un("remove", this.onRemove);
16104             this.store.un("update", this.onUpdate);
16105             this.store.un("clear", this.refresh);
16106             this.store.un("beforeload", this.onBeforeLoad);
16107             this.store.un("load", this.onLoad);
16108             this.store.un("loadexception", this.onLoad);
16109         }
16110         if(store){
16111           
16112             store.on("datachanged", this.refresh, this);
16113             store.on("add", this.onAdd, this);
16114             store.on("remove", this.onRemove, this);
16115             store.on("update", this.onUpdate, this);
16116             store.on("clear", this.refresh, this);
16117             store.on("beforeload", this.onBeforeLoad, this);
16118             store.on("load", this.onLoad, this);
16119             store.on("loadexception", this.onLoad, this);
16120         }
16121         
16122         if(store){
16123             this.refresh();
16124         }
16125     },
16126     /**
16127      * onbeforeLoad - masks the loading area.
16128      *
16129      */
16130     onBeforeLoad : function(store,opts)
16131     {
16132          //Roo.log('onBeforeLoad');   
16133         if (!opts.add) {
16134             this.el.update("");
16135         }
16136         this.el.mask(this.mask ? this.mask : "Loading" ); 
16137     },
16138     onLoad : function ()
16139     {
16140         this.el.unmask();
16141     },
16142     
16143
16144     /**
16145      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16146      * @param {HTMLElement} node
16147      * @return {HTMLElement} The template node
16148      */
16149     findItemFromChild : function(node){
16150         var el = this.dataName  ?
16151             this.el.child('.roo-tpl-' + this.dataName,true) :
16152             this.el.dom; 
16153         
16154         if(!node || node.parentNode == el){
16155                     return node;
16156             }
16157             var p = node.parentNode;
16158             while(p && p != el){
16159             if(p.parentNode == el){
16160                 return p;
16161             }
16162             p = p.parentNode;
16163         }
16164             return null;
16165     },
16166
16167     /** @ignore */
16168     onClick : function(e){
16169         var item = this.findItemFromChild(e.getTarget());
16170         if(item){
16171             var index = this.indexOf(item);
16172             if(this.onItemClick(item, index, e) !== false){
16173                 this.fireEvent("click", this, index, item, e);
16174             }
16175         }else{
16176             this.clearSelections();
16177         }
16178     },
16179
16180     /** @ignore */
16181     onContextMenu : function(e){
16182         var item = this.findItemFromChild(e.getTarget());
16183         if(item){
16184             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16185         }
16186     },
16187
16188     /** @ignore */
16189     onDblClick : function(e){
16190         var item = this.findItemFromChild(e.getTarget());
16191         if(item){
16192             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16193         }
16194     },
16195
16196     onItemClick : function(item, index, e)
16197     {
16198         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16199             return false;
16200         }
16201         if (this.toggleSelect) {
16202             var m = this.isSelected(item) ? 'unselect' : 'select';
16203             //Roo.log(m);
16204             var _t = this;
16205             _t[m](item, true, false);
16206             return true;
16207         }
16208         if(this.multiSelect || this.singleSelect){
16209             if(this.multiSelect && e.shiftKey && this.lastSelection){
16210                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16211             }else{
16212                 this.select(item, this.multiSelect && e.ctrlKey);
16213                 this.lastSelection = item;
16214             }
16215             
16216             if(!this.tickable){
16217                 e.preventDefault();
16218             }
16219             
16220         }
16221         return true;
16222     },
16223
16224     /**
16225      * Get the number of selected nodes.
16226      * @return {Number}
16227      */
16228     getSelectionCount : function(){
16229         return this.selections.length;
16230     },
16231
16232     /**
16233      * Get the currently selected nodes.
16234      * @return {Array} An array of HTMLElements
16235      */
16236     getSelectedNodes : function(){
16237         return this.selections;
16238     },
16239
16240     /**
16241      * Get the indexes of the selected nodes.
16242      * @return {Array}
16243      */
16244     getSelectedIndexes : function(){
16245         var indexes = [], s = this.selections;
16246         for(var i = 0, len = s.length; i < len; i++){
16247             indexes.push(s[i].nodeIndex);
16248         }
16249         return indexes;
16250     },
16251
16252     /**
16253      * Clear all selections
16254      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16255      */
16256     clearSelections : function(suppressEvent){
16257         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16258             this.cmp.elements = this.selections;
16259             this.cmp.removeClass(this.selectedClass);
16260             this.selections = [];
16261             if(!suppressEvent){
16262                 this.fireEvent("selectionchange", this, this.selections);
16263             }
16264         }
16265     },
16266
16267     /**
16268      * Returns true if the passed node is selected
16269      * @param {HTMLElement/Number} node The node or node index
16270      * @return {Boolean}
16271      */
16272     isSelected : function(node){
16273         var s = this.selections;
16274         if(s.length < 1){
16275             return false;
16276         }
16277         node = this.getNode(node);
16278         return s.indexOf(node) !== -1;
16279     },
16280
16281     /**
16282      * Selects nodes.
16283      * @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
16284      * @param {Boolean} keepExisting (optional) true to keep existing selections
16285      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16286      */
16287     select : function(nodeInfo, keepExisting, suppressEvent){
16288         if(nodeInfo instanceof Array){
16289             if(!keepExisting){
16290                 this.clearSelections(true);
16291             }
16292             for(var i = 0, len = nodeInfo.length; i < len; i++){
16293                 this.select(nodeInfo[i], true, true);
16294             }
16295             return;
16296         } 
16297         var node = this.getNode(nodeInfo);
16298         if(!node || this.isSelected(node)){
16299             return; // already selected.
16300         }
16301         if(!keepExisting){
16302             this.clearSelections(true);
16303         }
16304         
16305         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16306             Roo.fly(node).addClass(this.selectedClass);
16307             this.selections.push(node);
16308             if(!suppressEvent){
16309                 this.fireEvent("selectionchange", this, this.selections);
16310             }
16311         }
16312         
16313         
16314     },
16315       /**
16316      * Unselects nodes.
16317      * @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
16318      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16319      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16320      */
16321     unselect : function(nodeInfo, keepExisting, suppressEvent)
16322     {
16323         if(nodeInfo instanceof Array){
16324             Roo.each(this.selections, function(s) {
16325                 this.unselect(s, nodeInfo);
16326             }, this);
16327             return;
16328         }
16329         var node = this.getNode(nodeInfo);
16330         if(!node || !this.isSelected(node)){
16331             //Roo.log("not selected");
16332             return; // not selected.
16333         }
16334         // fireevent???
16335         var ns = [];
16336         Roo.each(this.selections, function(s) {
16337             if (s == node ) {
16338                 Roo.fly(node).removeClass(this.selectedClass);
16339
16340                 return;
16341             }
16342             ns.push(s);
16343         },this);
16344         
16345         this.selections= ns;
16346         this.fireEvent("selectionchange", this, this.selections);
16347     },
16348
16349     /**
16350      * Gets a template node.
16351      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16352      * @return {HTMLElement} The node or null if it wasn't found
16353      */
16354     getNode : function(nodeInfo){
16355         if(typeof nodeInfo == "string"){
16356             return document.getElementById(nodeInfo);
16357         }else if(typeof nodeInfo == "number"){
16358             return this.nodes[nodeInfo];
16359         }
16360         return nodeInfo;
16361     },
16362
16363     /**
16364      * Gets a range template nodes.
16365      * @param {Number} startIndex
16366      * @param {Number} endIndex
16367      * @return {Array} An array of nodes
16368      */
16369     getNodes : function(start, end){
16370         var ns = this.nodes;
16371         start = start || 0;
16372         end = typeof end == "undefined" ? ns.length - 1 : end;
16373         var nodes = [];
16374         if(start <= end){
16375             for(var i = start; i <= end; i++){
16376                 nodes.push(ns[i]);
16377             }
16378         } else{
16379             for(var i = start; i >= end; i--){
16380                 nodes.push(ns[i]);
16381             }
16382         }
16383         return nodes;
16384     },
16385
16386     /**
16387      * Finds the index of the passed node
16388      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16389      * @return {Number} The index of the node or -1
16390      */
16391     indexOf : function(node){
16392         node = this.getNode(node);
16393         if(typeof node.nodeIndex == "number"){
16394             return node.nodeIndex;
16395         }
16396         var ns = this.nodes;
16397         for(var i = 0, len = ns.length; i < len; i++){
16398             if(ns[i] == node){
16399                 return i;
16400             }
16401         }
16402         return -1;
16403     }
16404 });
16405 /*
16406  * - LGPL
16407  *
16408  * based on jquery fullcalendar
16409  * 
16410  */
16411
16412 Roo.bootstrap = Roo.bootstrap || {};
16413 /**
16414  * @class Roo.bootstrap.Calendar
16415  * @extends Roo.bootstrap.Component
16416  * Bootstrap Calendar class
16417  * @cfg {Boolean} loadMask (true|false) default false
16418  * @cfg {Object} header generate the user specific header of the calendar, default false
16419
16420  * @constructor
16421  * Create a new Container
16422  * @param {Object} config The config object
16423  */
16424
16425
16426
16427 Roo.bootstrap.Calendar = function(config){
16428     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16429      this.addEvents({
16430         /**
16431              * @event select
16432              * Fires when a date is selected
16433              * @param {DatePicker} this
16434              * @param {Date} date The selected date
16435              */
16436         'select': true,
16437         /**
16438              * @event monthchange
16439              * Fires when the displayed month changes 
16440              * @param {DatePicker} this
16441              * @param {Date} date The selected month
16442              */
16443         'monthchange': true,
16444         /**
16445              * @event evententer
16446              * Fires when mouse over an event
16447              * @param {Calendar} this
16448              * @param {event} Event
16449              */
16450         'evententer': true,
16451         /**
16452              * @event eventleave
16453              * Fires when the mouse leaves an
16454              * @param {Calendar} this
16455              * @param {event}
16456              */
16457         'eventleave': true,
16458         /**
16459              * @event eventclick
16460              * Fires when the mouse click an
16461              * @param {Calendar} this
16462              * @param {event}
16463              */
16464         'eventclick': true
16465         
16466     });
16467
16468 };
16469
16470 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16471     
16472      /**
16473      * @cfg {Number} startDay
16474      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16475      */
16476     startDay : 0,
16477     
16478     loadMask : false,
16479     
16480     header : false,
16481       
16482     getAutoCreate : function(){
16483         
16484         
16485         var fc_button = function(name, corner, style, content ) {
16486             return Roo.apply({},{
16487                 tag : 'span',
16488                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16489                          (corner.length ?
16490                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16491                             ''
16492                         ),
16493                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16494                 unselectable: 'on'
16495             });
16496         };
16497         
16498         var header = {};
16499         
16500         if(!this.header){
16501             header = {
16502                 tag : 'table',
16503                 cls : 'fc-header',
16504                 style : 'width:100%',
16505                 cn : [
16506                     {
16507                         tag: 'tr',
16508                         cn : [
16509                             {
16510                                 tag : 'td',
16511                                 cls : 'fc-header-left',
16512                                 cn : [
16513                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16514                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16515                                     { tag: 'span', cls: 'fc-header-space' },
16516                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16517
16518
16519                                 ]
16520                             },
16521
16522                             {
16523                                 tag : 'td',
16524                                 cls : 'fc-header-center',
16525                                 cn : [
16526                                     {
16527                                         tag: 'span',
16528                                         cls: 'fc-header-title',
16529                                         cn : {
16530                                             tag: 'H2',
16531                                             html : 'month / year'
16532                                         }
16533                                     }
16534
16535                                 ]
16536                             },
16537                             {
16538                                 tag : 'td',
16539                                 cls : 'fc-header-right',
16540                                 cn : [
16541                               /*      fc_button('month', 'left', '', 'month' ),
16542                                     fc_button('week', '', '', 'week' ),
16543                                     fc_button('day', 'right', '', 'day' )
16544                                 */    
16545
16546                                 ]
16547                             }
16548
16549                         ]
16550                     }
16551                 ]
16552             };
16553         }
16554         
16555         header = this.header;
16556         
16557        
16558         var cal_heads = function() {
16559             var ret = [];
16560             // fixme - handle this.
16561             
16562             for (var i =0; i < Date.dayNames.length; i++) {
16563                 var d = Date.dayNames[i];
16564                 ret.push({
16565                     tag: 'th',
16566                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16567                     html : d.substring(0,3)
16568                 });
16569                 
16570             }
16571             ret[0].cls += ' fc-first';
16572             ret[6].cls += ' fc-last';
16573             return ret;
16574         };
16575         var cal_cell = function(n) {
16576             return  {
16577                 tag: 'td',
16578                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16579                 cn : [
16580                     {
16581                         cn : [
16582                             {
16583                                 cls: 'fc-day-number',
16584                                 html: 'D'
16585                             },
16586                             {
16587                                 cls: 'fc-day-content',
16588                              
16589                                 cn : [
16590                                      {
16591                                         style: 'position: relative;' // height: 17px;
16592                                     }
16593                                 ]
16594                             }
16595                             
16596                             
16597                         ]
16598                     }
16599                 ]
16600                 
16601             }
16602         };
16603         var cal_rows = function() {
16604             
16605             var ret = [];
16606             for (var r = 0; r < 6; r++) {
16607                 var row= {
16608                     tag : 'tr',
16609                     cls : 'fc-week',
16610                     cn : []
16611                 };
16612                 
16613                 for (var i =0; i < Date.dayNames.length; i++) {
16614                     var d = Date.dayNames[i];
16615                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16616
16617                 }
16618                 row.cn[0].cls+=' fc-first';
16619                 row.cn[0].cn[0].style = 'min-height:90px';
16620                 row.cn[6].cls+=' fc-last';
16621                 ret.push(row);
16622                 
16623             }
16624             ret[0].cls += ' fc-first';
16625             ret[4].cls += ' fc-prev-last';
16626             ret[5].cls += ' fc-last';
16627             return ret;
16628             
16629         };
16630         
16631         var cal_table = {
16632             tag: 'table',
16633             cls: 'fc-border-separate',
16634             style : 'width:100%',
16635             cellspacing  : 0,
16636             cn : [
16637                 { 
16638                     tag: 'thead',
16639                     cn : [
16640                         { 
16641                             tag: 'tr',
16642                             cls : 'fc-first fc-last',
16643                             cn : cal_heads()
16644                         }
16645                     ]
16646                 },
16647                 { 
16648                     tag: 'tbody',
16649                     cn : cal_rows()
16650                 }
16651                   
16652             ]
16653         };
16654          
16655          var cfg = {
16656             cls : 'fc fc-ltr',
16657             cn : [
16658                 header,
16659                 {
16660                     cls : 'fc-content',
16661                     style : "position: relative;",
16662                     cn : [
16663                         {
16664                             cls : 'fc-view fc-view-month fc-grid',
16665                             style : 'position: relative',
16666                             unselectable : 'on',
16667                             cn : [
16668                                 {
16669                                     cls : 'fc-event-container',
16670                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16671                                 },
16672                                 cal_table
16673                             ]
16674                         }
16675                     ]
16676     
16677                 }
16678            ] 
16679             
16680         };
16681         
16682          
16683         
16684         return cfg;
16685     },
16686     
16687     
16688     initEvents : function()
16689     {
16690         if(!this.store){
16691             throw "can not find store for calendar";
16692         }
16693         
16694         var mark = {
16695             tag: "div",
16696             cls:"x-dlg-mask",
16697             style: "text-align:center",
16698             cn: [
16699                 {
16700                     tag: "div",
16701                     style: "background-color:white;width:50%;margin:250 auto",
16702                     cn: [
16703                         {
16704                             tag: "img",
16705                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16706                         },
16707                         {
16708                             tag: "span",
16709                             html: "Loading"
16710                         }
16711                         
16712                     ]
16713                 }
16714             ]
16715         };
16716         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16717         
16718         var size = this.el.select('.fc-content', true).first().getSize();
16719         this.maskEl.setSize(size.width, size.height);
16720         this.maskEl.enableDisplayMode("block");
16721         if(!this.loadMask){
16722             this.maskEl.hide();
16723         }
16724         
16725         this.store = Roo.factory(this.store, Roo.data);
16726         this.store.on('load', this.onLoad, this);
16727         this.store.on('beforeload', this.onBeforeLoad, this);
16728         
16729         this.resize();
16730         
16731         this.cells = this.el.select('.fc-day',true);
16732         //Roo.log(this.cells);
16733         this.textNodes = this.el.query('.fc-day-number');
16734         this.cells.addClassOnOver('fc-state-hover');
16735         
16736         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16737         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16738         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16739         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16740         
16741         this.on('monthchange', this.onMonthChange, this);
16742         
16743         this.update(new Date().clearTime());
16744     },
16745     
16746     resize : function() {
16747         var sz  = this.el.getSize();
16748         
16749         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16750         this.el.select('.fc-day-content div',true).setHeight(34);
16751     },
16752     
16753     
16754     // private
16755     showPrevMonth : function(e){
16756         this.update(this.activeDate.add("mo", -1));
16757     },
16758     showToday : function(e){
16759         this.update(new Date().clearTime());
16760     },
16761     // private
16762     showNextMonth : function(e){
16763         this.update(this.activeDate.add("mo", 1));
16764     },
16765
16766     // private
16767     showPrevYear : function(){
16768         this.update(this.activeDate.add("y", -1));
16769     },
16770
16771     // private
16772     showNextYear : function(){
16773         this.update(this.activeDate.add("y", 1));
16774     },
16775
16776     
16777    // private
16778     update : function(date)
16779     {
16780         var vd = this.activeDate;
16781         this.activeDate = date;
16782 //        if(vd && this.el){
16783 //            var t = date.getTime();
16784 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16785 //                Roo.log('using add remove');
16786 //                
16787 //                this.fireEvent('monthchange', this, date);
16788 //                
16789 //                this.cells.removeClass("fc-state-highlight");
16790 //                this.cells.each(function(c){
16791 //                   if(c.dateValue == t){
16792 //                       c.addClass("fc-state-highlight");
16793 //                       setTimeout(function(){
16794 //                            try{c.dom.firstChild.focus();}catch(e){}
16795 //                       }, 50);
16796 //                       return false;
16797 //                   }
16798 //                   return true;
16799 //                });
16800 //                return;
16801 //            }
16802 //        }
16803         
16804         var days = date.getDaysInMonth();
16805         
16806         var firstOfMonth = date.getFirstDateOfMonth();
16807         var startingPos = firstOfMonth.getDay()-this.startDay;
16808         
16809         if(startingPos < this.startDay){
16810             startingPos += 7;
16811         }
16812         
16813         var pm = date.add(Date.MONTH, -1);
16814         var prevStart = pm.getDaysInMonth()-startingPos;
16815 //        
16816         this.cells = this.el.select('.fc-day',true);
16817         this.textNodes = this.el.query('.fc-day-number');
16818         this.cells.addClassOnOver('fc-state-hover');
16819         
16820         var cells = this.cells.elements;
16821         var textEls = this.textNodes;
16822         
16823         Roo.each(cells, function(cell){
16824             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16825         });
16826         
16827         days += startingPos;
16828
16829         // convert everything to numbers so it's fast
16830         var day = 86400000;
16831         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16832         //Roo.log(d);
16833         //Roo.log(pm);
16834         //Roo.log(prevStart);
16835         
16836         var today = new Date().clearTime().getTime();
16837         var sel = date.clearTime().getTime();
16838         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16839         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16840         var ddMatch = this.disabledDatesRE;
16841         var ddText = this.disabledDatesText;
16842         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16843         var ddaysText = this.disabledDaysText;
16844         var format = this.format;
16845         
16846         var setCellClass = function(cal, cell){
16847             cell.row = 0;
16848             cell.events = [];
16849             cell.more = [];
16850             //Roo.log('set Cell Class');
16851             cell.title = "";
16852             var t = d.getTime();
16853             
16854             //Roo.log(d);
16855             
16856             cell.dateValue = t;
16857             if(t == today){
16858                 cell.className += " fc-today";
16859                 cell.className += " fc-state-highlight";
16860                 cell.title = cal.todayText;
16861             }
16862             if(t == sel){
16863                 // disable highlight in other month..
16864                 //cell.className += " fc-state-highlight";
16865                 
16866             }
16867             // disabling
16868             if(t < min) {
16869                 cell.className = " fc-state-disabled";
16870                 cell.title = cal.minText;
16871                 return;
16872             }
16873             if(t > max) {
16874                 cell.className = " fc-state-disabled";
16875                 cell.title = cal.maxText;
16876                 return;
16877             }
16878             if(ddays){
16879                 if(ddays.indexOf(d.getDay()) != -1){
16880                     cell.title = ddaysText;
16881                     cell.className = " fc-state-disabled";
16882                 }
16883             }
16884             if(ddMatch && format){
16885                 var fvalue = d.dateFormat(format);
16886                 if(ddMatch.test(fvalue)){
16887                     cell.title = ddText.replace("%0", fvalue);
16888                     cell.className = " fc-state-disabled";
16889                 }
16890             }
16891             
16892             if (!cell.initialClassName) {
16893                 cell.initialClassName = cell.dom.className;
16894             }
16895             
16896             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16897         };
16898
16899         var i = 0;
16900         
16901         for(; i < startingPos; i++) {
16902             textEls[i].innerHTML = (++prevStart);
16903             d.setDate(d.getDate()+1);
16904             
16905             cells[i].className = "fc-past fc-other-month";
16906             setCellClass(this, cells[i]);
16907         }
16908         
16909         var intDay = 0;
16910         
16911         for(; i < days; i++){
16912             intDay = i - startingPos + 1;
16913             textEls[i].innerHTML = (intDay);
16914             d.setDate(d.getDate()+1);
16915             
16916             cells[i].className = ''; // "x-date-active";
16917             setCellClass(this, cells[i]);
16918         }
16919         var extraDays = 0;
16920         
16921         for(; i < 42; i++) {
16922             textEls[i].innerHTML = (++extraDays);
16923             d.setDate(d.getDate()+1);
16924             
16925             cells[i].className = "fc-future fc-other-month";
16926             setCellClass(this, cells[i]);
16927         }
16928         
16929         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16930         
16931         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16932         
16933         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16934         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16935         
16936         if(totalRows != 6){
16937             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16938             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16939         }
16940         
16941         this.fireEvent('monthchange', this, date);
16942         
16943         
16944         /*
16945         if(!this.internalRender){
16946             var main = this.el.dom.firstChild;
16947             var w = main.offsetWidth;
16948             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16949             Roo.fly(main).setWidth(w);
16950             this.internalRender = true;
16951             // opera does not respect the auto grow header center column
16952             // then, after it gets a width opera refuses to recalculate
16953             // without a second pass
16954             if(Roo.isOpera && !this.secondPass){
16955                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16956                 this.secondPass = true;
16957                 this.update.defer(10, this, [date]);
16958             }
16959         }
16960         */
16961         
16962     },
16963     
16964     findCell : function(dt) {
16965         dt = dt.clearTime().getTime();
16966         var ret = false;
16967         this.cells.each(function(c){
16968             //Roo.log("check " +c.dateValue + '?=' + dt);
16969             if(c.dateValue == dt){
16970                 ret = c;
16971                 return false;
16972             }
16973             return true;
16974         });
16975         
16976         return ret;
16977     },
16978     
16979     findCells : function(ev) {
16980         var s = ev.start.clone().clearTime().getTime();
16981        // Roo.log(s);
16982         var e= ev.end.clone().clearTime().getTime();
16983        // Roo.log(e);
16984         var ret = [];
16985         this.cells.each(function(c){
16986              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16987             
16988             if(c.dateValue > e){
16989                 return ;
16990             }
16991             if(c.dateValue < s){
16992                 return ;
16993             }
16994             ret.push(c);
16995         });
16996         
16997         return ret;    
16998     },
16999     
17000 //    findBestRow: function(cells)
17001 //    {
17002 //        var ret = 0;
17003 //        
17004 //        for (var i =0 ; i < cells.length;i++) {
17005 //            ret  = Math.max(cells[i].rows || 0,ret);
17006 //        }
17007 //        return ret;
17008 //        
17009 //    },
17010     
17011     
17012     addItem : function(ev)
17013     {
17014         // look for vertical location slot in
17015         var cells = this.findCells(ev);
17016         
17017 //        ev.row = this.findBestRow(cells);
17018         
17019         // work out the location.
17020         
17021         var crow = false;
17022         var rows = [];
17023         for(var i =0; i < cells.length; i++) {
17024             
17025             cells[i].row = cells[0].row;
17026             
17027             if(i == 0){
17028                 cells[i].row = cells[i].row + 1;
17029             }
17030             
17031             if (!crow) {
17032                 crow = {
17033                     start : cells[i],
17034                     end :  cells[i]
17035                 };
17036                 continue;
17037             }
17038             if (crow.start.getY() == cells[i].getY()) {
17039                 // on same row.
17040                 crow.end = cells[i];
17041                 continue;
17042             }
17043             // different row.
17044             rows.push(crow);
17045             crow = {
17046                 start: cells[i],
17047                 end : cells[i]
17048             };
17049             
17050         }
17051         
17052         rows.push(crow);
17053         ev.els = [];
17054         ev.rows = rows;
17055         ev.cells = cells;
17056         
17057         cells[0].events.push(ev);
17058         
17059         this.calevents.push(ev);
17060     },
17061     
17062     clearEvents: function() {
17063         
17064         if(!this.calevents){
17065             return;
17066         }
17067         
17068         Roo.each(this.cells.elements, function(c){
17069             c.row = 0;
17070             c.events = [];
17071             c.more = [];
17072         });
17073         
17074         Roo.each(this.calevents, function(e) {
17075             Roo.each(e.els, function(el) {
17076                 el.un('mouseenter' ,this.onEventEnter, this);
17077                 el.un('mouseleave' ,this.onEventLeave, this);
17078                 el.remove();
17079             },this);
17080         },this);
17081         
17082         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17083             e.remove();
17084         });
17085         
17086     },
17087     
17088     renderEvents: function()
17089     {   
17090         var _this = this;
17091         
17092         this.cells.each(function(c) {
17093             
17094             if(c.row < 5){
17095                 return;
17096             }
17097             
17098             var ev = c.events;
17099             
17100             var r = 4;
17101             if(c.row != c.events.length){
17102                 r = 4 - (4 - (c.row - c.events.length));
17103             }
17104             
17105             c.events = ev.slice(0, r);
17106             c.more = ev.slice(r);
17107             
17108             if(c.more.length && c.more.length == 1){
17109                 c.events.push(c.more.pop());
17110             }
17111             
17112             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17113             
17114         });
17115             
17116         this.cells.each(function(c) {
17117             
17118             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17119             
17120             
17121             for (var e = 0; e < c.events.length; e++){
17122                 var ev = c.events[e];
17123                 var rows = ev.rows;
17124                 
17125                 for(var i = 0; i < rows.length; i++) {
17126                 
17127                     // how many rows should it span..
17128
17129                     var  cfg = {
17130                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17131                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17132
17133                         unselectable : "on",
17134                         cn : [
17135                             {
17136                                 cls: 'fc-event-inner',
17137                                 cn : [
17138     //                                {
17139     //                                  tag:'span',
17140     //                                  cls: 'fc-event-time',
17141     //                                  html : cells.length > 1 ? '' : ev.time
17142     //                                },
17143                                     {
17144                                       tag:'span',
17145                                       cls: 'fc-event-title',
17146                                       html : String.format('{0}', ev.title)
17147                                     }
17148
17149
17150                                 ]
17151                             },
17152                             {
17153                                 cls: 'ui-resizable-handle ui-resizable-e',
17154                                 html : '&nbsp;&nbsp;&nbsp'
17155                             }
17156
17157                         ]
17158                     };
17159
17160                     if (i == 0) {
17161                         cfg.cls += ' fc-event-start';
17162                     }
17163                     if ((i+1) == rows.length) {
17164                         cfg.cls += ' fc-event-end';
17165                     }
17166
17167                     var ctr = _this.el.select('.fc-event-container',true).first();
17168                     var cg = ctr.createChild(cfg);
17169
17170                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17171                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17172
17173                     var r = (c.more.length) ? 1 : 0;
17174                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17175                     cg.setWidth(ebox.right - sbox.x -2);
17176
17177                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17178                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17179                     cg.on('click', _this.onEventClick, _this, ev);
17180
17181                     ev.els.push(cg);
17182                     
17183                 }
17184                 
17185             }
17186             
17187             
17188             if(c.more.length){
17189                 var  cfg = {
17190                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17191                     style : 'position: absolute',
17192                     unselectable : "on",
17193                     cn : [
17194                         {
17195                             cls: 'fc-event-inner',
17196                             cn : [
17197                                 {
17198                                   tag:'span',
17199                                   cls: 'fc-event-title',
17200                                   html : 'More'
17201                                 }
17202
17203
17204                             ]
17205                         },
17206                         {
17207                             cls: 'ui-resizable-handle ui-resizable-e',
17208                             html : '&nbsp;&nbsp;&nbsp'
17209                         }
17210
17211                     ]
17212                 };
17213
17214                 var ctr = _this.el.select('.fc-event-container',true).first();
17215                 var cg = ctr.createChild(cfg);
17216
17217                 var sbox = c.select('.fc-day-content',true).first().getBox();
17218                 var ebox = c.select('.fc-day-content',true).first().getBox();
17219                 //Roo.log(cg);
17220                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17221                 cg.setWidth(ebox.right - sbox.x -2);
17222
17223                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17224                 
17225             }
17226             
17227         });
17228         
17229         
17230         
17231     },
17232     
17233     onEventEnter: function (e, el,event,d) {
17234         this.fireEvent('evententer', this, el, event);
17235     },
17236     
17237     onEventLeave: function (e, el,event,d) {
17238         this.fireEvent('eventleave', this, el, event);
17239     },
17240     
17241     onEventClick: function (e, el,event,d) {
17242         this.fireEvent('eventclick', this, el, event);
17243     },
17244     
17245     onMonthChange: function () {
17246         this.store.load();
17247     },
17248     
17249     onMoreEventClick: function(e, el, more)
17250     {
17251         var _this = this;
17252         
17253         this.calpopover.placement = 'right';
17254         this.calpopover.setTitle('More');
17255         
17256         this.calpopover.setContent('');
17257         
17258         var ctr = this.calpopover.el.select('.popover-content', true).first();
17259         
17260         Roo.each(more, function(m){
17261             var cfg = {
17262                 cls : 'fc-event-hori fc-event-draggable',
17263                 html : m.title
17264             };
17265             var cg = ctr.createChild(cfg);
17266             
17267             cg.on('click', _this.onEventClick, _this, m);
17268         });
17269         
17270         this.calpopover.show(el);
17271         
17272         
17273     },
17274     
17275     onLoad: function () 
17276     {   
17277         this.calevents = [];
17278         var cal = this;
17279         
17280         if(this.store.getCount() > 0){
17281             this.store.data.each(function(d){
17282                cal.addItem({
17283                     id : d.data.id,
17284                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17285                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17286                     time : d.data.start_time,
17287                     title : d.data.title,
17288                     description : d.data.description,
17289                     venue : d.data.venue
17290                 });
17291             });
17292         }
17293         
17294         this.renderEvents();
17295         
17296         if(this.calevents.length && this.loadMask){
17297             this.maskEl.hide();
17298         }
17299     },
17300     
17301     onBeforeLoad: function()
17302     {
17303         this.clearEvents();
17304         if(this.loadMask){
17305             this.maskEl.show();
17306         }
17307     }
17308 });
17309
17310  
17311  /*
17312  * - LGPL
17313  *
17314  * element
17315  * 
17316  */
17317
17318 /**
17319  * @class Roo.bootstrap.Popover
17320  * @extends Roo.bootstrap.Component
17321  * Bootstrap Popover class
17322  * @cfg {String} html contents of the popover   (or false to use children..)
17323  * @cfg {String} title of popover (or false to hide)
17324  * @cfg {String} placement how it is placed
17325  * @cfg {String} trigger click || hover (or false to trigger manually)
17326  * @cfg {String} over what (parent or false to trigger manually.)
17327  * @cfg {Number} delay - delay before showing
17328  
17329  * @constructor
17330  * Create a new Popover
17331  * @param {Object} config The config object
17332  */
17333
17334 Roo.bootstrap.Popover = function(config){
17335     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17336     
17337     this.addEvents({
17338         // raw events
17339          /**
17340          * @event show
17341          * After the popover show
17342          * 
17343          * @param {Roo.bootstrap.Popover} this
17344          */
17345         "show" : true,
17346         /**
17347          * @event hide
17348          * After the popover hide
17349          * 
17350          * @param {Roo.bootstrap.Popover} this
17351          */
17352         "hide" : true
17353     });
17354 };
17355
17356 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17357     
17358     title: 'Fill in a title',
17359     html: false,
17360     
17361     placement : 'right',
17362     trigger : 'hover', // hover
17363     
17364     delay : 0,
17365     
17366     over: 'parent',
17367     
17368     can_build_overlaid : false,
17369     
17370     getChildContainer : function()
17371     {
17372         return this.el.select('.popover-content',true).first();
17373     },
17374     
17375     getAutoCreate : function(){
17376          
17377         var cfg = {
17378            cls : 'popover roo-dynamic',
17379            style: 'display:block',
17380            cn : [
17381                 {
17382                     cls : 'arrow'
17383                 },
17384                 {
17385                     cls : 'popover-inner',
17386                     cn : [
17387                         {
17388                             tag: 'h3',
17389                             cls: 'popover-title',
17390                             html : this.title
17391                         },
17392                         {
17393                             cls : 'popover-content',
17394                             html : this.html
17395                         }
17396                     ]
17397                     
17398                 }
17399            ]
17400         };
17401         
17402         return cfg;
17403     },
17404     setTitle: function(str)
17405     {
17406         this.title = str;
17407         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17408     },
17409     setContent: function(str)
17410     {
17411         this.html = str;
17412         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17413     },
17414     // as it get's added to the bottom of the page.
17415     onRender : function(ct, position)
17416     {
17417         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17418         if(!this.el){
17419             var cfg = Roo.apply({},  this.getAutoCreate());
17420             cfg.id = Roo.id();
17421             
17422             if (this.cls) {
17423                 cfg.cls += ' ' + this.cls;
17424             }
17425             if (this.style) {
17426                 cfg.style = this.style;
17427             }
17428             //Roo.log("adding to ");
17429             this.el = Roo.get(document.body).createChild(cfg, position);
17430 //            Roo.log(this.el);
17431         }
17432         this.initEvents();
17433     },
17434     
17435     initEvents : function()
17436     {
17437         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17438         this.el.enableDisplayMode('block');
17439         this.el.hide();
17440         if (this.over === false) {
17441             return; 
17442         }
17443         if (this.triggers === false) {
17444             return;
17445         }
17446         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17447         var triggers = this.trigger ? this.trigger.split(' ') : [];
17448         Roo.each(triggers, function(trigger) {
17449         
17450             if (trigger == 'click') {
17451                 on_el.on('click', this.toggle, this);
17452             } else if (trigger != 'manual') {
17453                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17454                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17455       
17456                 on_el.on(eventIn  ,this.enter, this);
17457                 on_el.on(eventOut, this.leave, this);
17458             }
17459         }, this);
17460         
17461     },
17462     
17463     
17464     // private
17465     timeout : null,
17466     hoverState : null,
17467     
17468     toggle : function () {
17469         this.hoverState == 'in' ? this.leave() : this.enter();
17470     },
17471     
17472     enter : function () {
17473         
17474         clearTimeout(this.timeout);
17475     
17476         this.hoverState = 'in';
17477     
17478         if (!this.delay || !this.delay.show) {
17479             this.show();
17480             return;
17481         }
17482         var _t = this;
17483         this.timeout = setTimeout(function () {
17484             if (_t.hoverState == 'in') {
17485                 _t.show();
17486             }
17487         }, this.delay.show)
17488     },
17489     
17490     leave : function() {
17491         clearTimeout(this.timeout);
17492     
17493         this.hoverState = 'out';
17494     
17495         if (!this.delay || !this.delay.hide) {
17496             this.hide();
17497             return;
17498         }
17499         var _t = this;
17500         this.timeout = setTimeout(function () {
17501             if (_t.hoverState == 'out') {
17502                 _t.hide();
17503             }
17504         }, this.delay.hide)
17505     },
17506     
17507     show : function (on_el)
17508     {
17509         if (!on_el) {
17510             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17511         }
17512         
17513         // set content.
17514         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17515         if (this.html !== false) {
17516             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17517         }
17518         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17519         if (!this.title.length) {
17520             this.el.select('.popover-title',true).hide();
17521         }
17522         
17523         var placement = typeof this.placement == 'function' ?
17524             this.placement.call(this, this.el, on_el) :
17525             this.placement;
17526             
17527         var autoToken = /\s?auto?\s?/i;
17528         var autoPlace = autoToken.test(placement);
17529         if (autoPlace) {
17530             placement = placement.replace(autoToken, '') || 'top';
17531         }
17532         
17533         //this.el.detach()
17534         //this.el.setXY([0,0]);
17535         this.el.show();
17536         this.el.dom.style.display='block';
17537         this.el.addClass(placement);
17538         
17539         //this.el.appendTo(on_el);
17540         
17541         var p = this.getPosition();
17542         var box = this.el.getBox();
17543         
17544         if (autoPlace) {
17545             // fixme..
17546         }
17547         var align = Roo.bootstrap.Popover.alignment[placement];
17548         
17549 //        Roo.log(align);
17550         this.el.alignTo(on_el, align[0],align[1]);
17551         //var arrow = this.el.select('.arrow',true).first();
17552         //arrow.set(align[2], 
17553         
17554         this.el.addClass('in');
17555         
17556         
17557         if (this.el.hasClass('fade')) {
17558             // fade it?
17559         }
17560         
17561         this.hoverState = 'in';
17562         
17563         this.fireEvent('show', this);
17564         
17565     },
17566     hide : function()
17567     {
17568         this.el.setXY([0,0]);
17569         this.el.removeClass('in');
17570         this.el.hide();
17571         this.hoverState = null;
17572         
17573         this.fireEvent('hide', this);
17574     }
17575     
17576 });
17577
17578 Roo.bootstrap.Popover.alignment = {
17579     'left' : ['r-l', [-10,0], 'right'],
17580     'right' : ['l-r', [10,0], 'left'],
17581     'bottom' : ['t-b', [0,10], 'top'],
17582     'top' : [ 'b-t', [0,-10], 'bottom']
17583 };
17584
17585  /*
17586  * - LGPL
17587  *
17588  * Progress
17589  * 
17590  */
17591
17592 /**
17593  * @class Roo.bootstrap.Progress
17594  * @extends Roo.bootstrap.Component
17595  * Bootstrap Progress class
17596  * @cfg {Boolean} striped striped of the progress bar
17597  * @cfg {Boolean} active animated of the progress bar
17598  * 
17599  * 
17600  * @constructor
17601  * Create a new Progress
17602  * @param {Object} config The config object
17603  */
17604
17605 Roo.bootstrap.Progress = function(config){
17606     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17607 };
17608
17609 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17610     
17611     striped : false,
17612     active: false,
17613     
17614     getAutoCreate : function(){
17615         var cfg = {
17616             tag: 'div',
17617             cls: 'progress'
17618         };
17619         
17620         
17621         if(this.striped){
17622             cfg.cls += ' progress-striped';
17623         }
17624       
17625         if(this.active){
17626             cfg.cls += ' active';
17627         }
17628         
17629         
17630         return cfg;
17631     }
17632    
17633 });
17634
17635  
17636
17637  /*
17638  * - LGPL
17639  *
17640  * ProgressBar
17641  * 
17642  */
17643
17644 /**
17645  * @class Roo.bootstrap.ProgressBar
17646  * @extends Roo.bootstrap.Component
17647  * Bootstrap ProgressBar class
17648  * @cfg {Number} aria_valuenow aria-value now
17649  * @cfg {Number} aria_valuemin aria-value min
17650  * @cfg {Number} aria_valuemax aria-value max
17651  * @cfg {String} label label for the progress bar
17652  * @cfg {String} panel (success | info | warning | danger )
17653  * @cfg {String} role role of the progress bar
17654  * @cfg {String} sr_only text
17655  * 
17656  * 
17657  * @constructor
17658  * Create a new ProgressBar
17659  * @param {Object} config The config object
17660  */
17661
17662 Roo.bootstrap.ProgressBar = function(config){
17663     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17664 };
17665
17666 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17667     
17668     aria_valuenow : 0,
17669     aria_valuemin : 0,
17670     aria_valuemax : 100,
17671     label : false,
17672     panel : false,
17673     role : false,
17674     sr_only: false,
17675     
17676     getAutoCreate : function()
17677     {
17678         
17679         var cfg = {
17680             tag: 'div',
17681             cls: 'progress-bar',
17682             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17683         };
17684         
17685         if(this.sr_only){
17686             cfg.cn = {
17687                 tag: 'span',
17688                 cls: 'sr-only',
17689                 html: this.sr_only
17690             }
17691         }
17692         
17693         if(this.role){
17694             cfg.role = this.role;
17695         }
17696         
17697         if(this.aria_valuenow){
17698             cfg['aria-valuenow'] = this.aria_valuenow;
17699         }
17700         
17701         if(this.aria_valuemin){
17702             cfg['aria-valuemin'] = this.aria_valuemin;
17703         }
17704         
17705         if(this.aria_valuemax){
17706             cfg['aria-valuemax'] = this.aria_valuemax;
17707         }
17708         
17709         if(this.label && !this.sr_only){
17710             cfg.html = this.label;
17711         }
17712         
17713         if(this.panel){
17714             cfg.cls += ' progress-bar-' + this.panel;
17715         }
17716         
17717         return cfg;
17718     },
17719     
17720     update : function(aria_valuenow)
17721     {
17722         this.aria_valuenow = aria_valuenow;
17723         
17724         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17725     }
17726    
17727 });
17728
17729  
17730
17731  /*
17732  * - LGPL
17733  *
17734  * column
17735  * 
17736  */
17737
17738 /**
17739  * @class Roo.bootstrap.TabGroup
17740  * @extends Roo.bootstrap.Column
17741  * Bootstrap Column class
17742  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17743  * @cfg {Boolean} carousel true to make the group behave like a carousel
17744  * @cfg {Boolean} bullets show bullets for the panels
17745  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17746  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17747  * @cfg {Boolean} showarrow (true|false) show arrow default true
17748  * 
17749  * @constructor
17750  * Create a new TabGroup
17751  * @param {Object} config The config object
17752  */
17753
17754 Roo.bootstrap.TabGroup = function(config){
17755     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17756     if (!this.navId) {
17757         this.navId = Roo.id();
17758     }
17759     this.tabs = [];
17760     Roo.bootstrap.TabGroup.register(this);
17761     
17762 };
17763
17764 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17765     
17766     carousel : false,
17767     transition : false,
17768     bullets : 0,
17769     timer : 0,
17770     autoslide : false,
17771     slideFn : false,
17772     slideOnTouch : false,
17773     showarrow : true,
17774     
17775     getAutoCreate : function()
17776     {
17777         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17778         
17779         cfg.cls += ' tab-content';
17780         
17781         if (this.carousel) {
17782             cfg.cls += ' carousel slide';
17783             
17784             cfg.cn = [{
17785                cls : 'carousel-inner',
17786                cn : []
17787             }];
17788         
17789             if(this.bullets  && !Roo.isTouch){
17790                 
17791                 var bullets = {
17792                     cls : 'carousel-bullets',
17793                     cn : []
17794                 };
17795                
17796                 if(this.bullets_cls){
17797                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17798                 }
17799                 
17800                 bullets.cn.push({
17801                     cls : 'clear'
17802                 });
17803                 
17804                 cfg.cn[0].cn.push(bullets);
17805             }
17806             
17807             if(this.showarrow){
17808                 cfg.cn[0].cn.push({
17809                     tag : 'div',
17810                     class : 'carousel-arrow',
17811                     cn : [
17812                         {
17813                             tag : 'div',
17814                             class : 'carousel-prev',
17815                             cn : [
17816                                 {
17817                                     tag : 'i',
17818                                     class : 'fa fa-chevron-left'
17819                                 }
17820                             ]
17821                         },
17822                         {
17823                             tag : 'div',
17824                             class : 'carousel-next',
17825                             cn : [
17826                                 {
17827                                     tag : 'i',
17828                                     class : 'fa fa-chevron-right'
17829                                 }
17830                             ]
17831                         }
17832                     ]
17833                 });
17834             }
17835             
17836         }
17837         
17838         return cfg;
17839     },
17840     
17841     initEvents:  function()
17842     {
17843 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17844 //            this.el.on("touchstart", this.onTouchStart, this);
17845 //        }
17846         
17847         if(this.autoslide){
17848             var _this = this;
17849             
17850             this.slideFn = window.setInterval(function() {
17851                 _this.showPanelNext();
17852             }, this.timer);
17853         }
17854         
17855         if(this.showarrow){
17856             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17857             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17858         }
17859         
17860         
17861     },
17862     
17863 //    onTouchStart : function(e, el, o)
17864 //    {
17865 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17866 //            return;
17867 //        }
17868 //        
17869 //        this.showPanelNext();
17870 //    },
17871     
17872     
17873     getChildContainer : function()
17874     {
17875         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17876     },
17877     
17878     /**
17879     * register a Navigation item
17880     * @param {Roo.bootstrap.NavItem} the navitem to add
17881     */
17882     register : function(item)
17883     {
17884         this.tabs.push( item);
17885         item.navId = this.navId; // not really needed..
17886         this.addBullet();
17887     
17888     },
17889     
17890     getActivePanel : function()
17891     {
17892         var r = false;
17893         Roo.each(this.tabs, function(t) {
17894             if (t.active) {
17895                 r = t;
17896                 return false;
17897             }
17898             return null;
17899         });
17900         return r;
17901         
17902     },
17903     getPanelByName : function(n)
17904     {
17905         var r = false;
17906         Roo.each(this.tabs, function(t) {
17907             if (t.tabId == n) {
17908                 r = t;
17909                 return false;
17910             }
17911             return null;
17912         });
17913         return r;
17914     },
17915     indexOfPanel : function(p)
17916     {
17917         var r = false;
17918         Roo.each(this.tabs, function(t,i) {
17919             if (t.tabId == p.tabId) {
17920                 r = i;
17921                 return false;
17922             }
17923             return null;
17924         });
17925         return r;
17926     },
17927     /**
17928      * show a specific panel
17929      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17930      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17931      */
17932     showPanel : function (pan)
17933     {
17934         if(this.transition || typeof(pan) == 'undefined'){
17935             Roo.log("waiting for the transitionend");
17936             return;
17937         }
17938         
17939         if (typeof(pan) == 'number') {
17940             pan = this.tabs[pan];
17941         }
17942         
17943         if (typeof(pan) == 'string') {
17944             pan = this.getPanelByName(pan);
17945         }
17946         
17947         var cur = this.getActivePanel();
17948         
17949         if(!pan || !cur){
17950             Roo.log('pan or acitve pan is undefined');
17951             return false;
17952         }
17953         
17954         if (pan.tabId == this.getActivePanel().tabId) {
17955             return true;
17956         }
17957         
17958         if (false === cur.fireEvent('beforedeactivate')) {
17959             return false;
17960         }
17961         
17962         if(this.bullets > 0 && !Roo.isTouch){
17963             this.setActiveBullet(this.indexOfPanel(pan));
17964         }
17965         
17966         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17967             
17968             this.transition = true;
17969             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17970             var lr = dir == 'next' ? 'left' : 'right';
17971             pan.el.addClass(dir); // or prev
17972             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17973             cur.el.addClass(lr); // or right
17974             pan.el.addClass(lr);
17975             
17976             var _this = this;
17977             cur.el.on('transitionend', function() {
17978                 Roo.log("trans end?");
17979                 
17980                 pan.el.removeClass([lr,dir]);
17981                 pan.setActive(true);
17982                 
17983                 cur.el.removeClass([lr]);
17984                 cur.setActive(false);
17985                 
17986                 _this.transition = false;
17987                 
17988             }, this, { single:  true } );
17989             
17990             return true;
17991         }
17992         
17993         cur.setActive(false);
17994         pan.setActive(true);
17995         
17996         return true;
17997         
17998     },
17999     showPanelNext : function()
18000     {
18001         var i = this.indexOfPanel(this.getActivePanel());
18002         
18003         if (i >= this.tabs.length - 1 && !this.autoslide) {
18004             return;
18005         }
18006         
18007         if (i >= this.tabs.length - 1 && this.autoslide) {
18008             i = -1;
18009         }
18010         
18011         this.showPanel(this.tabs[i+1]);
18012     },
18013     
18014     showPanelPrev : function()
18015     {
18016         var i = this.indexOfPanel(this.getActivePanel());
18017         
18018         if (i  < 1 && !this.autoslide) {
18019             return;
18020         }
18021         
18022         if (i < 1 && this.autoslide) {
18023             i = this.tabs.length;
18024         }
18025         
18026         this.showPanel(this.tabs[i-1]);
18027     },
18028     
18029     
18030     addBullet: function()
18031     {
18032         if(!this.bullets || Roo.isTouch){
18033             return;
18034         }
18035         var ctr = this.el.select('.carousel-bullets',true).first();
18036         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18037         var bullet = ctr.createChild({
18038             cls : 'bullet bullet-' + i
18039         },ctr.dom.lastChild);
18040         
18041         
18042         var _this = this;
18043         
18044         bullet.on('click', (function(e, el, o, ii, t){
18045
18046             e.preventDefault();
18047
18048             this.showPanel(ii);
18049
18050             if(this.autoslide && this.slideFn){
18051                 clearInterval(this.slideFn);
18052                 this.slideFn = window.setInterval(function() {
18053                     _this.showPanelNext();
18054                 }, this.timer);
18055             }
18056
18057         }).createDelegate(this, [i, bullet], true));
18058                 
18059         
18060     },
18061      
18062     setActiveBullet : function(i)
18063     {
18064         if(Roo.isTouch){
18065             return;
18066         }
18067         
18068         Roo.each(this.el.select('.bullet', true).elements, function(el){
18069             el.removeClass('selected');
18070         });
18071
18072         var bullet = this.el.select('.bullet-' + i, true).first();
18073         
18074         if(!bullet){
18075             return;
18076         }
18077         
18078         bullet.addClass('selected');
18079     }
18080     
18081     
18082   
18083 });
18084
18085  
18086
18087  
18088  
18089 Roo.apply(Roo.bootstrap.TabGroup, {
18090     
18091     groups: {},
18092      /**
18093     * register a Navigation Group
18094     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18095     */
18096     register : function(navgrp)
18097     {
18098         this.groups[navgrp.navId] = navgrp;
18099         
18100     },
18101     /**
18102     * fetch a Navigation Group based on the navigation ID
18103     * if one does not exist , it will get created.
18104     * @param {string} the navgroup to add
18105     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18106     */
18107     get: function(navId) {
18108         if (typeof(this.groups[navId]) == 'undefined') {
18109             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18110         }
18111         return this.groups[navId] ;
18112     }
18113     
18114     
18115     
18116 });
18117
18118  /*
18119  * - LGPL
18120  *
18121  * TabPanel
18122  * 
18123  */
18124
18125 /**
18126  * @class Roo.bootstrap.TabPanel
18127  * @extends Roo.bootstrap.Component
18128  * Bootstrap TabPanel class
18129  * @cfg {Boolean} active panel active
18130  * @cfg {String} html panel content
18131  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18132  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18133  * @cfg {String} href click to link..
18134  * 
18135  * 
18136  * @constructor
18137  * Create a new TabPanel
18138  * @param {Object} config The config object
18139  */
18140
18141 Roo.bootstrap.TabPanel = function(config){
18142     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18143     this.addEvents({
18144         /**
18145              * @event changed
18146              * Fires when the active status changes
18147              * @param {Roo.bootstrap.TabPanel} this
18148              * @param {Boolean} state the new state
18149             
18150          */
18151         'changed': true,
18152         /**
18153              * @event beforedeactivate
18154              * Fires before a tab is de-activated - can be used to do validation on a form.
18155              * @param {Roo.bootstrap.TabPanel} this
18156              * @return {Boolean} false if there is an error
18157             
18158          */
18159         'beforedeactivate': true
18160      });
18161     
18162     this.tabId = this.tabId || Roo.id();
18163   
18164 };
18165
18166 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18167     
18168     active: false,
18169     html: false,
18170     tabId: false,
18171     navId : false,
18172     href : '',
18173     
18174     getAutoCreate : function(){
18175         var cfg = {
18176             tag: 'div',
18177             // item is needed for carousel - not sure if it has any effect otherwise
18178             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18179             html: this.html || ''
18180         };
18181         
18182         if(this.active){
18183             cfg.cls += ' active';
18184         }
18185         
18186         if(this.tabId){
18187             cfg.tabId = this.tabId;
18188         }
18189         
18190         
18191         return cfg;
18192     },
18193     
18194     initEvents:  function()
18195     {
18196         var p = this.parent();
18197         
18198         this.navId = this.navId || p.navId;
18199         
18200         if (typeof(this.navId) != 'undefined') {
18201             // not really needed.. but just in case.. parent should be a NavGroup.
18202             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18203             
18204             tg.register(this);
18205             
18206             var i = tg.tabs.length - 1;
18207             
18208             if(this.active && tg.bullets > 0 && i < tg.bullets){
18209                 tg.setActiveBullet(i);
18210             }
18211         }
18212         
18213         this.el.on('click', this.onClick, this);
18214         
18215         if(Roo.isTouch){
18216             this.el.on("touchstart", this.onTouchStart, this);
18217             this.el.on("touchmove", this.onTouchMove, this);
18218             this.el.on("touchend", this.onTouchEnd, this);
18219         }
18220         
18221     },
18222     
18223     onRender : function(ct, position)
18224     {
18225         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18226     },
18227     
18228     setActive : function(state)
18229     {
18230         Roo.log("panel - set active " + this.tabId + "=" + state);
18231         
18232         this.active = state;
18233         if (!state) {
18234             this.el.removeClass('active');
18235             
18236         } else  if (!this.el.hasClass('active')) {
18237             this.el.addClass('active');
18238         }
18239         
18240         this.fireEvent('changed', this, state);
18241     },
18242     
18243     onClick : function(e)
18244     {
18245         e.preventDefault();
18246         
18247         if(!this.href.length){
18248             return;
18249         }
18250         
18251         window.location.href = this.href;
18252     },
18253     
18254     startX : 0,
18255     startY : 0,
18256     endX : 0,
18257     endY : 0,
18258     swiping : false,
18259     
18260     onTouchStart : function(e)
18261     {
18262         this.swiping = false;
18263         
18264         this.startX = e.browserEvent.touches[0].clientX;
18265         this.startY = e.browserEvent.touches[0].clientY;
18266     },
18267     
18268     onTouchMove : function(e)
18269     {
18270         this.swiping = true;
18271         
18272         this.endX = e.browserEvent.touches[0].clientX;
18273         this.endY = e.browserEvent.touches[0].clientY;
18274     },
18275     
18276     onTouchEnd : function(e)
18277     {
18278         if(!this.swiping){
18279             this.onClick(e);
18280             return;
18281         }
18282         
18283         var tabGroup = this.parent();
18284         
18285         if(this.endX > this.startX){ // swiping right
18286             tabGroup.showPanelPrev();
18287             return;
18288         }
18289         
18290         if(this.startX > this.endX){ // swiping left
18291             tabGroup.showPanelNext();
18292             return;
18293         }
18294     }
18295     
18296     
18297 });
18298  
18299
18300  
18301
18302  /*
18303  * - LGPL
18304  *
18305  * DateField
18306  * 
18307  */
18308
18309 /**
18310  * @class Roo.bootstrap.DateField
18311  * @extends Roo.bootstrap.Input
18312  * Bootstrap DateField class
18313  * @cfg {Number} weekStart default 0
18314  * @cfg {String} viewMode default empty, (months|years)
18315  * @cfg {String} minViewMode default empty, (months|years)
18316  * @cfg {Number} startDate default -Infinity
18317  * @cfg {Number} endDate default Infinity
18318  * @cfg {Boolean} todayHighlight default false
18319  * @cfg {Boolean} todayBtn default false
18320  * @cfg {Boolean} calendarWeeks default false
18321  * @cfg {Object} daysOfWeekDisabled default empty
18322  * @cfg {Boolean} singleMode default false (true | false)
18323  * 
18324  * @cfg {Boolean} keyboardNavigation default true
18325  * @cfg {String} language default en
18326  * 
18327  * @constructor
18328  * Create a new DateField
18329  * @param {Object} config The config object
18330  */
18331
18332 Roo.bootstrap.DateField = function(config){
18333     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18334      this.addEvents({
18335             /**
18336              * @event show
18337              * Fires when this field show.
18338              * @param {Roo.bootstrap.DateField} this
18339              * @param {Mixed} date The date value
18340              */
18341             show : true,
18342             /**
18343              * @event show
18344              * Fires when this field hide.
18345              * @param {Roo.bootstrap.DateField} this
18346              * @param {Mixed} date The date value
18347              */
18348             hide : true,
18349             /**
18350              * @event select
18351              * Fires when select a date.
18352              * @param {Roo.bootstrap.DateField} this
18353              * @param {Mixed} date The date value
18354              */
18355             select : true,
18356             /**
18357              * @event beforeselect
18358              * Fires when before select a date.
18359              * @param {Roo.bootstrap.DateField} this
18360              * @param {Mixed} date The date value
18361              */
18362             beforeselect : true
18363         });
18364 };
18365
18366 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18367     
18368     /**
18369      * @cfg {String} format
18370      * The default date format string which can be overriden for localization support.  The format must be
18371      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18372      */
18373     format : "m/d/y",
18374     /**
18375      * @cfg {String} altFormats
18376      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18377      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18378      */
18379     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18380     
18381     weekStart : 0,
18382     
18383     viewMode : '',
18384     
18385     minViewMode : '',
18386     
18387     todayHighlight : false,
18388     
18389     todayBtn: false,
18390     
18391     language: 'en',
18392     
18393     keyboardNavigation: true,
18394     
18395     calendarWeeks: false,
18396     
18397     startDate: -Infinity,
18398     
18399     endDate: Infinity,
18400     
18401     daysOfWeekDisabled: [],
18402     
18403     _events: [],
18404     
18405     singleMode : false,
18406     
18407     UTCDate: function()
18408     {
18409         return new Date(Date.UTC.apply(Date, arguments));
18410     },
18411     
18412     UTCToday: function()
18413     {
18414         var today = new Date();
18415         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18416     },
18417     
18418     getDate: function() {
18419             var d = this.getUTCDate();
18420             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18421     },
18422     
18423     getUTCDate: function() {
18424             return this.date;
18425     },
18426     
18427     setDate: function(d) {
18428             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18429     },
18430     
18431     setUTCDate: function(d) {
18432             this.date = d;
18433             this.setValue(this.formatDate(this.date));
18434     },
18435         
18436     onRender: function(ct, position)
18437     {
18438         
18439         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18440         
18441         this.language = this.language || 'en';
18442         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18443         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18444         
18445         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18446         this.format = this.format || 'm/d/y';
18447         this.isInline = false;
18448         this.isInput = true;
18449         this.component = this.el.select('.add-on', true).first() || false;
18450         this.component = (this.component && this.component.length === 0) ? false : this.component;
18451         this.hasInput = this.component && this.inputEl().length;
18452         
18453         if (typeof(this.minViewMode === 'string')) {
18454             switch (this.minViewMode) {
18455                 case 'months':
18456                     this.minViewMode = 1;
18457                     break;
18458                 case 'years':
18459                     this.minViewMode = 2;
18460                     break;
18461                 default:
18462                     this.minViewMode = 0;
18463                     break;
18464             }
18465         }
18466         
18467         if (typeof(this.viewMode === 'string')) {
18468             switch (this.viewMode) {
18469                 case 'months':
18470                     this.viewMode = 1;
18471                     break;
18472                 case 'years':
18473                     this.viewMode = 2;
18474                     break;
18475                 default:
18476                     this.viewMode = 0;
18477                     break;
18478             }
18479         }
18480                 
18481         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18482         
18483 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18484         
18485         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18486         
18487         this.picker().on('mousedown', this.onMousedown, this);
18488         this.picker().on('click', this.onClick, this);
18489         
18490         this.picker().addClass('datepicker-dropdown');
18491         
18492         this.startViewMode = this.viewMode;
18493         
18494         if(this.singleMode){
18495             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18496                 v.setVisibilityMode(Roo.Element.DISPLAY);
18497                 v.hide();
18498             });
18499             
18500             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18501                 v.setStyle('width', '189px');
18502             });
18503         }
18504         
18505         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18506             if(!this.calendarWeeks){
18507                 v.remove();
18508                 return;
18509             }
18510             
18511             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18512             v.attr('colspan', function(i, val){
18513                 return parseInt(val) + 1;
18514             });
18515         });
18516                         
18517         
18518         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18519         
18520         this.setStartDate(this.startDate);
18521         this.setEndDate(this.endDate);
18522         
18523         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18524         
18525         this.fillDow();
18526         this.fillMonths();
18527         this.update();
18528         this.showMode();
18529         
18530         if(this.isInline) {
18531             this.show();
18532         }
18533     },
18534     
18535     picker : function()
18536     {
18537         return this.pickerEl;
18538 //        return this.el.select('.datepicker', true).first();
18539     },
18540     
18541     fillDow: function()
18542     {
18543         var dowCnt = this.weekStart;
18544         
18545         var dow = {
18546             tag: 'tr',
18547             cn: [
18548                 
18549             ]
18550         };
18551         
18552         if(this.calendarWeeks){
18553             dow.cn.push({
18554                 tag: 'th',
18555                 cls: 'cw',
18556                 html: '&nbsp;'
18557             })
18558         }
18559         
18560         while (dowCnt < this.weekStart + 7) {
18561             dow.cn.push({
18562                 tag: 'th',
18563                 cls: 'dow',
18564                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18565             });
18566         }
18567         
18568         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18569     },
18570     
18571     fillMonths: function()
18572     {    
18573         var i = 0;
18574         var months = this.picker().select('>.datepicker-months td', true).first();
18575         
18576         months.dom.innerHTML = '';
18577         
18578         while (i < 12) {
18579             var month = {
18580                 tag: 'span',
18581                 cls: 'month',
18582                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18583             };
18584             
18585             months.createChild(month);
18586         }
18587         
18588     },
18589     
18590     update: function()
18591     {
18592         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;
18593         
18594         if (this.date < this.startDate) {
18595             this.viewDate = new Date(this.startDate);
18596         } else if (this.date > this.endDate) {
18597             this.viewDate = new Date(this.endDate);
18598         } else {
18599             this.viewDate = new Date(this.date);
18600         }
18601         
18602         this.fill();
18603     },
18604     
18605     fill: function() 
18606     {
18607         var d = new Date(this.viewDate),
18608                 year = d.getUTCFullYear(),
18609                 month = d.getUTCMonth(),
18610                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18611                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18612                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18613                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18614                 currentDate = this.date && this.date.valueOf(),
18615                 today = this.UTCToday();
18616         
18617         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18618         
18619 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18620         
18621 //        this.picker.select('>tfoot th.today').
18622 //                                              .text(dates[this.language].today)
18623 //                                              .toggle(this.todayBtn !== false);
18624     
18625         this.updateNavArrows();
18626         this.fillMonths();
18627                                                 
18628         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18629         
18630         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18631          
18632         prevMonth.setUTCDate(day);
18633         
18634         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18635         
18636         var nextMonth = new Date(prevMonth);
18637         
18638         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18639         
18640         nextMonth = nextMonth.valueOf();
18641         
18642         var fillMonths = false;
18643         
18644         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18645         
18646         while(prevMonth.valueOf() < nextMonth) {
18647             var clsName = '';
18648             
18649             if (prevMonth.getUTCDay() === this.weekStart) {
18650                 if(fillMonths){
18651                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18652                 }
18653                     
18654                 fillMonths = {
18655                     tag: 'tr',
18656                     cn: []
18657                 };
18658                 
18659                 if(this.calendarWeeks){
18660                     // ISO 8601: First week contains first thursday.
18661                     // ISO also states week starts on Monday, but we can be more abstract here.
18662                     var
18663                     // Start of current week: based on weekstart/current date
18664                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18665                     // Thursday of this week
18666                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18667                     // First Thursday of year, year from thursday
18668                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18669                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18670                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18671                     
18672                     fillMonths.cn.push({
18673                         tag: 'td',
18674                         cls: 'cw',
18675                         html: calWeek
18676                     });
18677                 }
18678             }
18679             
18680             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18681                 clsName += ' old';
18682             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18683                 clsName += ' new';
18684             }
18685             if (this.todayHighlight &&
18686                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18687                 prevMonth.getUTCMonth() == today.getMonth() &&
18688                 prevMonth.getUTCDate() == today.getDate()) {
18689                 clsName += ' today';
18690             }
18691             
18692             if (currentDate && prevMonth.valueOf() === currentDate) {
18693                 clsName += ' active';
18694             }
18695             
18696             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18697                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18698                     clsName += ' disabled';
18699             }
18700             
18701             fillMonths.cn.push({
18702                 tag: 'td',
18703                 cls: 'day ' + clsName,
18704                 html: prevMonth.getDate()
18705             });
18706             
18707             prevMonth.setDate(prevMonth.getDate()+1);
18708         }
18709           
18710         var currentYear = this.date && this.date.getUTCFullYear();
18711         var currentMonth = this.date && this.date.getUTCMonth();
18712         
18713         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18714         
18715         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18716             v.removeClass('active');
18717             
18718             if(currentYear === year && k === currentMonth){
18719                 v.addClass('active');
18720             }
18721             
18722             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18723                 v.addClass('disabled');
18724             }
18725             
18726         });
18727         
18728         
18729         year = parseInt(year/10, 10) * 10;
18730         
18731         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18732         
18733         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18734         
18735         year -= 1;
18736         for (var i = -1; i < 11; i++) {
18737             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18738                 tag: 'span',
18739                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18740                 html: year
18741             });
18742             
18743             year += 1;
18744         }
18745     },
18746     
18747     showMode: function(dir) 
18748     {
18749         if (dir) {
18750             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18751         }
18752         
18753         Roo.each(this.picker().select('>div',true).elements, function(v){
18754             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18755             v.hide();
18756         });
18757         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18758     },
18759     
18760     place: function()
18761     {
18762         if(this.isInline) {
18763             return;
18764         }
18765         
18766         this.picker().removeClass(['bottom', 'top']);
18767         
18768         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18769             /*
18770              * place to the top of element!
18771              *
18772              */
18773             
18774             this.picker().addClass('top');
18775             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18776             
18777             return;
18778         }
18779         
18780         this.picker().addClass('bottom');
18781         
18782         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18783     },
18784     
18785     parseDate : function(value)
18786     {
18787         if(!value || value instanceof Date){
18788             return value;
18789         }
18790         var v = Date.parseDate(value, this.format);
18791         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18792             v = Date.parseDate(value, 'Y-m-d');
18793         }
18794         if(!v && this.altFormats){
18795             if(!this.altFormatsArray){
18796                 this.altFormatsArray = this.altFormats.split("|");
18797             }
18798             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18799                 v = Date.parseDate(value, this.altFormatsArray[i]);
18800             }
18801         }
18802         return v;
18803     },
18804     
18805     formatDate : function(date, fmt)
18806     {   
18807         return (!date || !(date instanceof Date)) ?
18808         date : date.dateFormat(fmt || this.format);
18809     },
18810     
18811     onFocus : function()
18812     {
18813         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18814         this.show();
18815     },
18816     
18817     onBlur : function()
18818     {
18819         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18820         
18821         var d = this.inputEl().getValue();
18822         
18823         this.setValue(d);
18824                 
18825         this.hide();
18826     },
18827     
18828     show : function()
18829     {
18830         this.picker().show();
18831         this.update();
18832         this.place();
18833         
18834         this.fireEvent('show', this, this.date);
18835     },
18836     
18837     hide : function()
18838     {
18839         if(this.isInline) {
18840             return;
18841         }
18842         this.picker().hide();
18843         this.viewMode = this.startViewMode;
18844         this.showMode();
18845         
18846         this.fireEvent('hide', this, this.date);
18847         
18848     },
18849     
18850     onMousedown: function(e)
18851     {
18852         e.stopPropagation();
18853         e.preventDefault();
18854     },
18855     
18856     keyup: function(e)
18857     {
18858         Roo.bootstrap.DateField.superclass.keyup.call(this);
18859         this.update();
18860     },
18861
18862     setValue: function(v)
18863     {
18864         if(this.fireEvent('beforeselect', this, v) !== false){
18865             var d = new Date(this.parseDate(v) ).clearTime();
18866         
18867             if(isNaN(d.getTime())){
18868                 this.date = this.viewDate = '';
18869                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18870                 return;
18871             }
18872
18873             v = this.formatDate(d);
18874
18875             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18876
18877             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18878
18879             this.update();
18880
18881             this.fireEvent('select', this, this.date);
18882         }
18883     },
18884     
18885     getValue: function()
18886     {
18887         return this.formatDate(this.date);
18888     },
18889     
18890     fireKey: function(e)
18891     {
18892         if (!this.picker().isVisible()){
18893             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18894                 this.show();
18895             }
18896             return;
18897         }
18898         
18899         var dateChanged = false,
18900         dir, day, month,
18901         newDate, newViewDate;
18902         
18903         switch(e.keyCode){
18904             case 27: // escape
18905                 this.hide();
18906                 e.preventDefault();
18907                 break;
18908             case 37: // left
18909             case 39: // right
18910                 if (!this.keyboardNavigation) {
18911                     break;
18912                 }
18913                 dir = e.keyCode == 37 ? -1 : 1;
18914                 
18915                 if (e.ctrlKey){
18916                     newDate = this.moveYear(this.date, dir);
18917                     newViewDate = this.moveYear(this.viewDate, dir);
18918                 } else if (e.shiftKey){
18919                     newDate = this.moveMonth(this.date, dir);
18920                     newViewDate = this.moveMonth(this.viewDate, dir);
18921                 } else {
18922                     newDate = new Date(this.date);
18923                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18924                     newViewDate = new Date(this.viewDate);
18925                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18926                 }
18927                 if (this.dateWithinRange(newDate)){
18928                     this.date = newDate;
18929                     this.viewDate = newViewDate;
18930                     this.setValue(this.formatDate(this.date));
18931 //                    this.update();
18932                     e.preventDefault();
18933                     dateChanged = true;
18934                 }
18935                 break;
18936             case 38: // up
18937             case 40: // down
18938                 if (!this.keyboardNavigation) {
18939                     break;
18940                 }
18941                 dir = e.keyCode == 38 ? -1 : 1;
18942                 if (e.ctrlKey){
18943                     newDate = this.moveYear(this.date, dir);
18944                     newViewDate = this.moveYear(this.viewDate, dir);
18945                 } else if (e.shiftKey){
18946                     newDate = this.moveMonth(this.date, dir);
18947                     newViewDate = this.moveMonth(this.viewDate, dir);
18948                 } else {
18949                     newDate = new Date(this.date);
18950                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18951                     newViewDate = new Date(this.viewDate);
18952                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18953                 }
18954                 if (this.dateWithinRange(newDate)){
18955                     this.date = newDate;
18956                     this.viewDate = newViewDate;
18957                     this.setValue(this.formatDate(this.date));
18958 //                    this.update();
18959                     e.preventDefault();
18960                     dateChanged = true;
18961                 }
18962                 break;
18963             case 13: // enter
18964                 this.setValue(this.formatDate(this.date));
18965                 this.hide();
18966                 e.preventDefault();
18967                 break;
18968             case 9: // tab
18969                 this.setValue(this.formatDate(this.date));
18970                 this.hide();
18971                 break;
18972             case 16: // shift
18973             case 17: // ctrl
18974             case 18: // alt
18975                 break;
18976             default :
18977                 this.hide();
18978                 
18979         }
18980     },
18981     
18982     
18983     onClick: function(e) 
18984     {
18985         e.stopPropagation();
18986         e.preventDefault();
18987         
18988         var target = e.getTarget();
18989         
18990         if(target.nodeName.toLowerCase() === 'i'){
18991             target = Roo.get(target).dom.parentNode;
18992         }
18993         
18994         var nodeName = target.nodeName;
18995         var className = target.className;
18996         var html = target.innerHTML;
18997         //Roo.log(nodeName);
18998         
18999         switch(nodeName.toLowerCase()) {
19000             case 'th':
19001                 switch(className) {
19002                     case 'switch':
19003                         this.showMode(1);
19004                         break;
19005                     case 'prev':
19006                     case 'next':
19007                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19008                         switch(this.viewMode){
19009                                 case 0:
19010                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19011                                         break;
19012                                 case 1:
19013                                 case 2:
19014                                         this.viewDate = this.moveYear(this.viewDate, dir);
19015                                         break;
19016                         }
19017                         this.fill();
19018                         break;
19019                     case 'today':
19020                         var date = new Date();
19021                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19022 //                        this.fill()
19023                         this.setValue(this.formatDate(this.date));
19024                         
19025                         this.hide();
19026                         break;
19027                 }
19028                 break;
19029             case 'span':
19030                 if (className.indexOf('disabled') < 0) {
19031                     this.viewDate.setUTCDate(1);
19032                     if (className.indexOf('month') > -1) {
19033                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19034                     } else {
19035                         var year = parseInt(html, 10) || 0;
19036                         this.viewDate.setUTCFullYear(year);
19037                         
19038                     }
19039                     
19040                     if(this.singleMode){
19041                         this.setValue(this.formatDate(this.viewDate));
19042                         this.hide();
19043                         return;
19044                     }
19045                     
19046                     this.showMode(-1);
19047                     this.fill();
19048                 }
19049                 break;
19050                 
19051             case 'td':
19052                 //Roo.log(className);
19053                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19054                     var day = parseInt(html, 10) || 1;
19055                     var year = this.viewDate.getUTCFullYear(),
19056                         month = this.viewDate.getUTCMonth();
19057
19058                     if (className.indexOf('old') > -1) {
19059                         if(month === 0 ){
19060                             month = 11;
19061                             year -= 1;
19062                         }else{
19063                             month -= 1;
19064                         }
19065                     } else if (className.indexOf('new') > -1) {
19066                         if (month == 11) {
19067                             month = 0;
19068                             year += 1;
19069                         } else {
19070                             month += 1;
19071                         }
19072                     }
19073                     //Roo.log([year,month,day]);
19074                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19075                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19076 //                    this.fill();
19077                     //Roo.log(this.formatDate(this.date));
19078                     this.setValue(this.formatDate(this.date));
19079                     this.hide();
19080                 }
19081                 break;
19082         }
19083     },
19084     
19085     setStartDate: function(startDate)
19086     {
19087         this.startDate = startDate || -Infinity;
19088         if (this.startDate !== -Infinity) {
19089             this.startDate = this.parseDate(this.startDate);
19090         }
19091         this.update();
19092         this.updateNavArrows();
19093     },
19094
19095     setEndDate: function(endDate)
19096     {
19097         this.endDate = endDate || Infinity;
19098         if (this.endDate !== Infinity) {
19099             this.endDate = this.parseDate(this.endDate);
19100         }
19101         this.update();
19102         this.updateNavArrows();
19103     },
19104     
19105     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19106     {
19107         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19108         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19109             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19110         }
19111         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19112             return parseInt(d, 10);
19113         });
19114         this.update();
19115         this.updateNavArrows();
19116     },
19117     
19118     updateNavArrows: function() 
19119     {
19120         if(this.singleMode){
19121             return;
19122         }
19123         
19124         var d = new Date(this.viewDate),
19125         year = d.getUTCFullYear(),
19126         month = d.getUTCMonth();
19127         
19128         Roo.each(this.picker().select('.prev', true).elements, function(v){
19129             v.show();
19130             switch (this.viewMode) {
19131                 case 0:
19132
19133                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19134                         v.hide();
19135                     }
19136                     break;
19137                 case 1:
19138                 case 2:
19139                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19140                         v.hide();
19141                     }
19142                     break;
19143             }
19144         });
19145         
19146         Roo.each(this.picker().select('.next', true).elements, function(v){
19147             v.show();
19148             switch (this.viewMode) {
19149                 case 0:
19150
19151                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19152                         v.hide();
19153                     }
19154                     break;
19155                 case 1:
19156                 case 2:
19157                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19158                         v.hide();
19159                     }
19160                     break;
19161             }
19162         })
19163     },
19164     
19165     moveMonth: function(date, dir)
19166     {
19167         if (!dir) {
19168             return date;
19169         }
19170         var new_date = new Date(date.valueOf()),
19171         day = new_date.getUTCDate(),
19172         month = new_date.getUTCMonth(),
19173         mag = Math.abs(dir),
19174         new_month, test;
19175         dir = dir > 0 ? 1 : -1;
19176         if (mag == 1){
19177             test = dir == -1
19178             // If going back one month, make sure month is not current month
19179             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19180             ? function(){
19181                 return new_date.getUTCMonth() == month;
19182             }
19183             // If going forward one month, make sure month is as expected
19184             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19185             : function(){
19186                 return new_date.getUTCMonth() != new_month;
19187             };
19188             new_month = month + dir;
19189             new_date.setUTCMonth(new_month);
19190             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19191             if (new_month < 0 || new_month > 11) {
19192                 new_month = (new_month + 12) % 12;
19193             }
19194         } else {
19195             // For magnitudes >1, move one month at a time...
19196             for (var i=0; i<mag; i++) {
19197                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19198                 new_date = this.moveMonth(new_date, dir);
19199             }
19200             // ...then reset the day, keeping it in the new month
19201             new_month = new_date.getUTCMonth();
19202             new_date.setUTCDate(day);
19203             test = function(){
19204                 return new_month != new_date.getUTCMonth();
19205             };
19206         }
19207         // Common date-resetting loop -- if date is beyond end of month, make it
19208         // end of month
19209         while (test()){
19210             new_date.setUTCDate(--day);
19211             new_date.setUTCMonth(new_month);
19212         }
19213         return new_date;
19214     },
19215
19216     moveYear: function(date, dir)
19217     {
19218         return this.moveMonth(date, dir*12);
19219     },
19220
19221     dateWithinRange: function(date)
19222     {
19223         return date >= this.startDate && date <= this.endDate;
19224     },
19225
19226     
19227     remove: function() 
19228     {
19229         this.picker().remove();
19230     },
19231     
19232     validateValue : function(value)
19233     {
19234         if(this.getVisibilityEl().hasClass('hidden')){
19235             return true;
19236         }
19237         
19238         if(value.length < 1)  {
19239             if(this.allowBlank){
19240                 return true;
19241             }
19242             return false;
19243         }
19244         
19245         if(value.length < this.minLength){
19246             return false;
19247         }
19248         if(value.length > this.maxLength){
19249             return false;
19250         }
19251         if(this.vtype){
19252             var vt = Roo.form.VTypes;
19253             if(!vt[this.vtype](value, this)){
19254                 return false;
19255             }
19256         }
19257         if(typeof this.validator == "function"){
19258             var msg = this.validator(value);
19259             if(msg !== true){
19260                 return false;
19261             }
19262         }
19263         
19264         if(this.regex && !this.regex.test(value)){
19265             return false;
19266         }
19267         
19268         if(typeof(this.parseDate(value)) == 'undefined'){
19269             return false;
19270         }
19271         
19272         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19273             return false;
19274         }      
19275         
19276         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19277             return false;
19278         } 
19279         
19280         
19281         return true;
19282     },
19283     
19284     setVisible : function(visible)
19285     {
19286         if(!this.getEl()){
19287             return;
19288         }
19289         
19290         this.getEl().removeClass('hidden');
19291         
19292         if(visible){
19293             return;
19294         }
19295         
19296         this.getEl().addClass('hidden');
19297     }
19298    
19299 });
19300
19301 Roo.apply(Roo.bootstrap.DateField,  {
19302     
19303     head : {
19304         tag: 'thead',
19305         cn: [
19306         {
19307             tag: 'tr',
19308             cn: [
19309             {
19310                 tag: 'th',
19311                 cls: 'prev',
19312                 html: '<i class="fa fa-arrow-left"/>'
19313             },
19314             {
19315                 tag: 'th',
19316                 cls: 'switch',
19317                 colspan: '5'
19318             },
19319             {
19320                 tag: 'th',
19321                 cls: 'next',
19322                 html: '<i class="fa fa-arrow-right"/>'
19323             }
19324
19325             ]
19326         }
19327         ]
19328     },
19329     
19330     content : {
19331         tag: 'tbody',
19332         cn: [
19333         {
19334             tag: 'tr',
19335             cn: [
19336             {
19337                 tag: 'td',
19338                 colspan: '7'
19339             }
19340             ]
19341         }
19342         ]
19343     },
19344     
19345     footer : {
19346         tag: 'tfoot',
19347         cn: [
19348         {
19349             tag: 'tr',
19350             cn: [
19351             {
19352                 tag: 'th',
19353                 colspan: '7',
19354                 cls: 'today'
19355             }
19356                     
19357             ]
19358         }
19359         ]
19360     },
19361     
19362     dates:{
19363         en: {
19364             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19365             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19366             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19367             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19368             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19369             today: "Today"
19370         }
19371     },
19372     
19373     modes: [
19374     {
19375         clsName: 'days',
19376         navFnc: 'Month',
19377         navStep: 1
19378     },
19379     {
19380         clsName: 'months',
19381         navFnc: 'FullYear',
19382         navStep: 1
19383     },
19384     {
19385         clsName: 'years',
19386         navFnc: 'FullYear',
19387         navStep: 10
19388     }]
19389 });
19390
19391 Roo.apply(Roo.bootstrap.DateField,  {
19392   
19393     template : {
19394         tag: 'div',
19395         cls: 'datepicker dropdown-menu roo-dynamic',
19396         cn: [
19397         {
19398             tag: 'div',
19399             cls: 'datepicker-days',
19400             cn: [
19401             {
19402                 tag: 'table',
19403                 cls: 'table-condensed',
19404                 cn:[
19405                 Roo.bootstrap.DateField.head,
19406                 {
19407                     tag: 'tbody'
19408                 },
19409                 Roo.bootstrap.DateField.footer
19410                 ]
19411             }
19412             ]
19413         },
19414         {
19415             tag: 'div',
19416             cls: 'datepicker-months',
19417             cn: [
19418             {
19419                 tag: 'table',
19420                 cls: 'table-condensed',
19421                 cn:[
19422                 Roo.bootstrap.DateField.head,
19423                 Roo.bootstrap.DateField.content,
19424                 Roo.bootstrap.DateField.footer
19425                 ]
19426             }
19427             ]
19428         },
19429         {
19430             tag: 'div',
19431             cls: 'datepicker-years',
19432             cn: [
19433             {
19434                 tag: 'table',
19435                 cls: 'table-condensed',
19436                 cn:[
19437                 Roo.bootstrap.DateField.head,
19438                 Roo.bootstrap.DateField.content,
19439                 Roo.bootstrap.DateField.footer
19440                 ]
19441             }
19442             ]
19443         }
19444         ]
19445     }
19446 });
19447
19448  
19449
19450  /*
19451  * - LGPL
19452  *
19453  * TimeField
19454  * 
19455  */
19456
19457 /**
19458  * @class Roo.bootstrap.TimeField
19459  * @extends Roo.bootstrap.Input
19460  * Bootstrap DateField class
19461  * 
19462  * 
19463  * @constructor
19464  * Create a new TimeField
19465  * @param {Object} config The config object
19466  */
19467
19468 Roo.bootstrap.TimeField = function(config){
19469     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19470     this.addEvents({
19471             /**
19472              * @event show
19473              * Fires when this field show.
19474              * @param {Roo.bootstrap.DateField} thisthis
19475              * @param {Mixed} date The date value
19476              */
19477             show : true,
19478             /**
19479              * @event show
19480              * Fires when this field hide.
19481              * @param {Roo.bootstrap.DateField} this
19482              * @param {Mixed} date The date value
19483              */
19484             hide : true,
19485             /**
19486              * @event select
19487              * Fires when select a date.
19488              * @param {Roo.bootstrap.DateField} this
19489              * @param {Mixed} date The date value
19490              */
19491             select : true
19492         });
19493 };
19494
19495 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19496     
19497     /**
19498      * @cfg {String} format
19499      * The default time format string which can be overriden for localization support.  The format must be
19500      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19501      */
19502     format : "H:i",
19503        
19504     onRender: function(ct, position)
19505     {
19506         
19507         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19508                 
19509         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19510         
19511         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19512         
19513         this.pop = this.picker().select('>.datepicker-time',true).first();
19514         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19515         
19516         this.picker().on('mousedown', this.onMousedown, this);
19517         this.picker().on('click', this.onClick, this);
19518         
19519         this.picker().addClass('datepicker-dropdown');
19520     
19521         this.fillTime();
19522         this.update();
19523             
19524         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19525         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19526         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19527         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19528         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19529         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19530
19531     },
19532     
19533     fireKey: function(e){
19534         if (!this.picker().isVisible()){
19535             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19536                 this.show();
19537             }
19538             return;
19539         }
19540
19541         e.preventDefault();
19542         
19543         switch(e.keyCode){
19544             case 27: // escape
19545                 this.hide();
19546                 break;
19547             case 37: // left
19548             case 39: // right
19549                 this.onTogglePeriod();
19550                 break;
19551             case 38: // up
19552                 this.onIncrementMinutes();
19553                 break;
19554             case 40: // down
19555                 this.onDecrementMinutes();
19556                 break;
19557             case 13: // enter
19558             case 9: // tab
19559                 this.setTime();
19560                 break;
19561         }
19562     },
19563     
19564     onClick: function(e) {
19565         e.stopPropagation();
19566         e.preventDefault();
19567     },
19568     
19569     picker : function()
19570     {
19571         return this.el.select('.datepicker', true).first();
19572     },
19573     
19574     fillTime: function()
19575     {    
19576         var time = this.pop.select('tbody', true).first();
19577         
19578         time.dom.innerHTML = '';
19579         
19580         time.createChild({
19581             tag: 'tr',
19582             cn: [
19583                 {
19584                     tag: 'td',
19585                     cn: [
19586                         {
19587                             tag: 'a',
19588                             href: '#',
19589                             cls: 'btn',
19590                             cn: [
19591                                 {
19592                                     tag: 'span',
19593                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19594                                 }
19595                             ]
19596                         } 
19597                     ]
19598                 },
19599                 {
19600                     tag: 'td',
19601                     cls: 'separator'
19602                 },
19603                 {
19604                     tag: 'td',
19605                     cn: [
19606                         {
19607                             tag: 'a',
19608                             href: '#',
19609                             cls: 'btn',
19610                             cn: [
19611                                 {
19612                                     tag: 'span',
19613                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19614                                 }
19615                             ]
19616                         }
19617                     ]
19618                 },
19619                 {
19620                     tag: 'td',
19621                     cls: 'separator'
19622                 }
19623             ]
19624         });
19625         
19626         time.createChild({
19627             tag: 'tr',
19628             cn: [
19629                 {
19630                     tag: 'td',
19631                     cn: [
19632                         {
19633                             tag: 'span',
19634                             cls: 'timepicker-hour',
19635                             html: '00'
19636                         }  
19637                     ]
19638                 },
19639                 {
19640                     tag: 'td',
19641                     cls: 'separator',
19642                     html: ':'
19643                 },
19644                 {
19645                     tag: 'td',
19646                     cn: [
19647                         {
19648                             tag: 'span',
19649                             cls: 'timepicker-minute',
19650                             html: '00'
19651                         }  
19652                     ]
19653                 },
19654                 {
19655                     tag: 'td',
19656                     cls: 'separator'
19657                 },
19658                 {
19659                     tag: 'td',
19660                     cn: [
19661                         {
19662                             tag: 'button',
19663                             type: 'button',
19664                             cls: 'btn btn-primary period',
19665                             html: 'AM'
19666                             
19667                         }
19668                     ]
19669                 }
19670             ]
19671         });
19672         
19673         time.createChild({
19674             tag: 'tr',
19675             cn: [
19676                 {
19677                     tag: 'td',
19678                     cn: [
19679                         {
19680                             tag: 'a',
19681                             href: '#',
19682                             cls: 'btn',
19683                             cn: [
19684                                 {
19685                                     tag: 'span',
19686                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19687                                 }
19688                             ]
19689                         }
19690                     ]
19691                 },
19692                 {
19693                     tag: 'td',
19694                     cls: 'separator'
19695                 },
19696                 {
19697                     tag: 'td',
19698                     cn: [
19699                         {
19700                             tag: 'a',
19701                             href: '#',
19702                             cls: 'btn',
19703                             cn: [
19704                                 {
19705                                     tag: 'span',
19706                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19707                                 }
19708                             ]
19709                         }
19710                     ]
19711                 },
19712                 {
19713                     tag: 'td',
19714                     cls: 'separator'
19715                 }
19716             ]
19717         });
19718         
19719     },
19720     
19721     update: function()
19722     {
19723         
19724         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19725         
19726         this.fill();
19727     },
19728     
19729     fill: function() 
19730     {
19731         var hours = this.time.getHours();
19732         var minutes = this.time.getMinutes();
19733         var period = 'AM';
19734         
19735         if(hours > 11){
19736             period = 'PM';
19737         }
19738         
19739         if(hours == 0){
19740             hours = 12;
19741         }
19742         
19743         
19744         if(hours > 12){
19745             hours = hours - 12;
19746         }
19747         
19748         if(hours < 10){
19749             hours = '0' + hours;
19750         }
19751         
19752         if(minutes < 10){
19753             minutes = '0' + minutes;
19754         }
19755         
19756         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19757         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19758         this.pop.select('button', true).first().dom.innerHTML = period;
19759         
19760     },
19761     
19762     place: function()
19763     {   
19764         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19765         
19766         var cls = ['bottom'];
19767         
19768         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19769             cls.pop();
19770             cls.push('top');
19771         }
19772         
19773         cls.push('right');
19774         
19775         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19776             cls.pop();
19777             cls.push('left');
19778         }
19779         
19780         this.picker().addClass(cls.join('-'));
19781         
19782         var _this = this;
19783         
19784         Roo.each(cls, function(c){
19785             if(c == 'bottom'){
19786                 _this.picker().setTop(_this.inputEl().getHeight());
19787                 return;
19788             }
19789             if(c == 'top'){
19790                 _this.picker().setTop(0 - _this.picker().getHeight());
19791                 return;
19792             }
19793             
19794             if(c == 'left'){
19795                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19796                 return;
19797             }
19798             if(c == 'right'){
19799                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19800                 return;
19801             }
19802         });
19803         
19804     },
19805   
19806     onFocus : function()
19807     {
19808         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19809         this.show();
19810     },
19811     
19812     onBlur : function()
19813     {
19814         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19815         this.hide();
19816     },
19817     
19818     show : function()
19819     {
19820         this.picker().show();
19821         this.pop.show();
19822         this.update();
19823         this.place();
19824         
19825         this.fireEvent('show', this, this.date);
19826     },
19827     
19828     hide : function()
19829     {
19830         this.picker().hide();
19831         this.pop.hide();
19832         
19833         this.fireEvent('hide', this, this.date);
19834     },
19835     
19836     setTime : function()
19837     {
19838         this.hide();
19839         this.setValue(this.time.format(this.format));
19840         
19841         this.fireEvent('select', this, this.date);
19842         
19843         
19844     },
19845     
19846     onMousedown: function(e){
19847         e.stopPropagation();
19848         e.preventDefault();
19849     },
19850     
19851     onIncrementHours: function()
19852     {
19853         Roo.log('onIncrementHours');
19854         this.time = this.time.add(Date.HOUR, 1);
19855         this.update();
19856         
19857     },
19858     
19859     onDecrementHours: function()
19860     {
19861         Roo.log('onDecrementHours');
19862         this.time = this.time.add(Date.HOUR, -1);
19863         this.update();
19864     },
19865     
19866     onIncrementMinutes: function()
19867     {
19868         Roo.log('onIncrementMinutes');
19869         this.time = this.time.add(Date.MINUTE, 1);
19870         this.update();
19871     },
19872     
19873     onDecrementMinutes: function()
19874     {
19875         Roo.log('onDecrementMinutes');
19876         this.time = this.time.add(Date.MINUTE, -1);
19877         this.update();
19878     },
19879     
19880     onTogglePeriod: function()
19881     {
19882         Roo.log('onTogglePeriod');
19883         this.time = this.time.add(Date.HOUR, 12);
19884         this.update();
19885     }
19886     
19887    
19888 });
19889
19890 Roo.apply(Roo.bootstrap.TimeField,  {
19891     
19892     content : {
19893         tag: 'tbody',
19894         cn: [
19895             {
19896                 tag: 'tr',
19897                 cn: [
19898                 {
19899                     tag: 'td',
19900                     colspan: '7'
19901                 }
19902                 ]
19903             }
19904         ]
19905     },
19906     
19907     footer : {
19908         tag: 'tfoot',
19909         cn: [
19910             {
19911                 tag: 'tr',
19912                 cn: [
19913                 {
19914                     tag: 'th',
19915                     colspan: '7',
19916                     cls: '',
19917                     cn: [
19918                         {
19919                             tag: 'button',
19920                             cls: 'btn btn-info ok',
19921                             html: 'OK'
19922                         }
19923                     ]
19924                 }
19925
19926                 ]
19927             }
19928         ]
19929     }
19930 });
19931
19932 Roo.apply(Roo.bootstrap.TimeField,  {
19933   
19934     template : {
19935         tag: 'div',
19936         cls: 'datepicker dropdown-menu',
19937         cn: [
19938             {
19939                 tag: 'div',
19940                 cls: 'datepicker-time',
19941                 cn: [
19942                 {
19943                     tag: 'table',
19944                     cls: 'table-condensed',
19945                     cn:[
19946                     Roo.bootstrap.TimeField.content,
19947                     Roo.bootstrap.TimeField.footer
19948                     ]
19949                 }
19950                 ]
19951             }
19952         ]
19953     }
19954 });
19955
19956  
19957
19958  /*
19959  * - LGPL
19960  *
19961  * MonthField
19962  * 
19963  */
19964
19965 /**
19966  * @class Roo.bootstrap.MonthField
19967  * @extends Roo.bootstrap.Input
19968  * Bootstrap MonthField class
19969  * 
19970  * @cfg {String} language default en
19971  * 
19972  * @constructor
19973  * Create a new MonthField
19974  * @param {Object} config The config object
19975  */
19976
19977 Roo.bootstrap.MonthField = function(config){
19978     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19979     
19980     this.addEvents({
19981         /**
19982          * @event show
19983          * Fires when this field show.
19984          * @param {Roo.bootstrap.MonthField} this
19985          * @param {Mixed} date The date value
19986          */
19987         show : true,
19988         /**
19989          * @event show
19990          * Fires when this field hide.
19991          * @param {Roo.bootstrap.MonthField} this
19992          * @param {Mixed} date The date value
19993          */
19994         hide : true,
19995         /**
19996          * @event select
19997          * Fires when select a date.
19998          * @param {Roo.bootstrap.MonthField} this
19999          * @param {String} oldvalue The old value
20000          * @param {String} newvalue The new value
20001          */
20002         select : true
20003     });
20004 };
20005
20006 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20007     
20008     onRender: function(ct, position)
20009     {
20010         
20011         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20012         
20013         this.language = this.language || 'en';
20014         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20015         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20016         
20017         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20018         this.isInline = false;
20019         this.isInput = true;
20020         this.component = this.el.select('.add-on', true).first() || false;
20021         this.component = (this.component && this.component.length === 0) ? false : this.component;
20022         this.hasInput = this.component && this.inputEL().length;
20023         
20024         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20025         
20026         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20027         
20028         this.picker().on('mousedown', this.onMousedown, this);
20029         this.picker().on('click', this.onClick, this);
20030         
20031         this.picker().addClass('datepicker-dropdown');
20032         
20033         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20034             v.setStyle('width', '189px');
20035         });
20036         
20037         this.fillMonths();
20038         
20039         this.update();
20040         
20041         if(this.isInline) {
20042             this.show();
20043         }
20044         
20045     },
20046     
20047     setValue: function(v, suppressEvent)
20048     {   
20049         var o = this.getValue();
20050         
20051         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20052         
20053         this.update();
20054
20055         if(suppressEvent !== true){
20056             this.fireEvent('select', this, o, v);
20057         }
20058         
20059     },
20060     
20061     getValue: function()
20062     {
20063         return this.value;
20064     },
20065     
20066     onClick: function(e) 
20067     {
20068         e.stopPropagation();
20069         e.preventDefault();
20070         
20071         var target = e.getTarget();
20072         
20073         if(target.nodeName.toLowerCase() === 'i'){
20074             target = Roo.get(target).dom.parentNode;
20075         }
20076         
20077         var nodeName = target.nodeName;
20078         var className = target.className;
20079         var html = target.innerHTML;
20080         
20081         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20082             return;
20083         }
20084         
20085         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20086         
20087         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20088         
20089         this.hide();
20090                         
20091     },
20092     
20093     picker : function()
20094     {
20095         return this.pickerEl;
20096     },
20097     
20098     fillMonths: function()
20099     {    
20100         var i = 0;
20101         var months = this.picker().select('>.datepicker-months td', true).first();
20102         
20103         months.dom.innerHTML = '';
20104         
20105         while (i < 12) {
20106             var month = {
20107                 tag: 'span',
20108                 cls: 'month',
20109                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20110             };
20111             
20112             months.createChild(month);
20113         }
20114         
20115     },
20116     
20117     update: function()
20118     {
20119         var _this = this;
20120         
20121         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20122             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20123         }
20124         
20125         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20126             e.removeClass('active');
20127             
20128             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20129                 e.addClass('active');
20130             }
20131         })
20132     },
20133     
20134     place: function()
20135     {
20136         if(this.isInline) {
20137             return;
20138         }
20139         
20140         this.picker().removeClass(['bottom', 'top']);
20141         
20142         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20143             /*
20144              * place to the top of element!
20145              *
20146              */
20147             
20148             this.picker().addClass('top');
20149             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20150             
20151             return;
20152         }
20153         
20154         this.picker().addClass('bottom');
20155         
20156         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20157     },
20158     
20159     onFocus : function()
20160     {
20161         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20162         this.show();
20163     },
20164     
20165     onBlur : function()
20166     {
20167         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20168         
20169         var d = this.inputEl().getValue();
20170         
20171         this.setValue(d);
20172                 
20173         this.hide();
20174     },
20175     
20176     show : function()
20177     {
20178         this.picker().show();
20179         this.picker().select('>.datepicker-months', true).first().show();
20180         this.update();
20181         this.place();
20182         
20183         this.fireEvent('show', this, this.date);
20184     },
20185     
20186     hide : function()
20187     {
20188         if(this.isInline) {
20189             return;
20190         }
20191         this.picker().hide();
20192         this.fireEvent('hide', this, this.date);
20193         
20194     },
20195     
20196     onMousedown: function(e)
20197     {
20198         e.stopPropagation();
20199         e.preventDefault();
20200     },
20201     
20202     keyup: function(e)
20203     {
20204         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20205         this.update();
20206     },
20207
20208     fireKey: function(e)
20209     {
20210         if (!this.picker().isVisible()){
20211             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20212                 this.show();
20213             }
20214             return;
20215         }
20216         
20217         var dir;
20218         
20219         switch(e.keyCode){
20220             case 27: // escape
20221                 this.hide();
20222                 e.preventDefault();
20223                 break;
20224             case 37: // left
20225             case 39: // right
20226                 dir = e.keyCode == 37 ? -1 : 1;
20227                 
20228                 this.vIndex = this.vIndex + dir;
20229                 
20230                 if(this.vIndex < 0){
20231                     this.vIndex = 0;
20232                 }
20233                 
20234                 if(this.vIndex > 11){
20235                     this.vIndex = 11;
20236                 }
20237                 
20238                 if(isNaN(this.vIndex)){
20239                     this.vIndex = 0;
20240                 }
20241                 
20242                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20243                 
20244                 break;
20245             case 38: // up
20246             case 40: // down
20247                 
20248                 dir = e.keyCode == 38 ? -1 : 1;
20249                 
20250                 this.vIndex = this.vIndex + dir * 4;
20251                 
20252                 if(this.vIndex < 0){
20253                     this.vIndex = 0;
20254                 }
20255                 
20256                 if(this.vIndex > 11){
20257                     this.vIndex = 11;
20258                 }
20259                 
20260                 if(isNaN(this.vIndex)){
20261                     this.vIndex = 0;
20262                 }
20263                 
20264                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20265                 break;
20266                 
20267             case 13: // enter
20268                 
20269                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20270                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20271                 }
20272                 
20273                 this.hide();
20274                 e.preventDefault();
20275                 break;
20276             case 9: // tab
20277                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20278                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20279                 }
20280                 this.hide();
20281                 break;
20282             case 16: // shift
20283             case 17: // ctrl
20284             case 18: // alt
20285                 break;
20286             default :
20287                 this.hide();
20288                 
20289         }
20290     },
20291     
20292     remove: function() 
20293     {
20294         this.picker().remove();
20295     }
20296    
20297 });
20298
20299 Roo.apply(Roo.bootstrap.MonthField,  {
20300     
20301     content : {
20302         tag: 'tbody',
20303         cn: [
20304         {
20305             tag: 'tr',
20306             cn: [
20307             {
20308                 tag: 'td',
20309                 colspan: '7'
20310             }
20311             ]
20312         }
20313         ]
20314     },
20315     
20316     dates:{
20317         en: {
20318             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20319             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20320         }
20321     }
20322 });
20323
20324 Roo.apply(Roo.bootstrap.MonthField,  {
20325   
20326     template : {
20327         tag: 'div',
20328         cls: 'datepicker dropdown-menu roo-dynamic',
20329         cn: [
20330             {
20331                 tag: 'div',
20332                 cls: 'datepicker-months',
20333                 cn: [
20334                 {
20335                     tag: 'table',
20336                     cls: 'table-condensed',
20337                     cn:[
20338                         Roo.bootstrap.DateField.content
20339                     ]
20340                 }
20341                 ]
20342             }
20343         ]
20344     }
20345 });
20346
20347  
20348
20349  
20350  /*
20351  * - LGPL
20352  *
20353  * CheckBox
20354  * 
20355  */
20356
20357 /**
20358  * @class Roo.bootstrap.CheckBox
20359  * @extends Roo.bootstrap.Input
20360  * Bootstrap CheckBox class
20361  * 
20362  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20363  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20364  * @cfg {String} boxLabel The text that appears beside the checkbox
20365  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20366  * @cfg {Boolean} checked initnal the element
20367  * @cfg {Boolean} inline inline the element (default false)
20368  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20369  * @cfg {String} tooltip label tooltip
20370  * 
20371  * @constructor
20372  * Create a new CheckBox
20373  * @param {Object} config The config object
20374  */
20375
20376 Roo.bootstrap.CheckBox = function(config){
20377     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20378    
20379     this.addEvents({
20380         /**
20381         * @event check
20382         * Fires when the element is checked or unchecked.
20383         * @param {Roo.bootstrap.CheckBox} this This input
20384         * @param {Boolean} checked The new checked value
20385         */
20386        check : true,
20387        /**
20388         * @event click
20389         * Fires when the element is click.
20390         * @param {Roo.bootstrap.CheckBox} this This input
20391         */
20392        click : true
20393     });
20394     
20395 };
20396
20397 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20398   
20399     inputType: 'checkbox',
20400     inputValue: 1,
20401     valueOff: 0,
20402     boxLabel: false,
20403     checked: false,
20404     weight : false,
20405     inline: false,
20406     tooltip : '',
20407     
20408     getAutoCreate : function()
20409     {
20410         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20411         
20412         var id = Roo.id();
20413         
20414         var cfg = {};
20415         
20416         cfg.cls = 'form-group ' + this.inputType; //input-group
20417         
20418         if(this.inline){
20419             cfg.cls += ' ' + this.inputType + '-inline';
20420         }
20421         
20422         var input =  {
20423             tag: 'input',
20424             id : id,
20425             type : this.inputType,
20426             value : this.inputValue,
20427             cls : 'roo-' + this.inputType, //'form-box',
20428             placeholder : this.placeholder || ''
20429             
20430         };
20431         
20432         if(this.inputType != 'radio'){
20433             var hidden =  {
20434                 tag: 'input',
20435                 type : 'hidden',
20436                 cls : 'roo-hidden-value',
20437                 value : this.checked ? this.inputValue : this.valueOff
20438             };
20439         }
20440         
20441             
20442         if (this.weight) { // Validity check?
20443             cfg.cls += " " + this.inputType + "-" + this.weight;
20444         }
20445         
20446         if (this.disabled) {
20447             input.disabled=true;
20448         }
20449         
20450         if(this.checked){
20451             input.checked = this.checked;
20452         }
20453         
20454         if (this.name) {
20455             
20456             input.name = this.name;
20457             
20458             if(this.inputType != 'radio'){
20459                 hidden.name = this.name;
20460                 input.name = '_hidden_' + this.name;
20461             }
20462         }
20463         
20464         if (this.size) {
20465             input.cls += ' input-' + this.size;
20466         }
20467         
20468         var settings=this;
20469         
20470         ['xs','sm','md','lg'].map(function(size){
20471             if (settings[size]) {
20472                 cfg.cls += ' col-' + size + '-' + settings[size];
20473             }
20474         });
20475         
20476         var inputblock = input;
20477          
20478         if (this.before || this.after) {
20479             
20480             inputblock = {
20481                 cls : 'input-group',
20482                 cn :  [] 
20483             };
20484             
20485             if (this.before) {
20486                 inputblock.cn.push({
20487                     tag :'span',
20488                     cls : 'input-group-addon',
20489                     html : this.before
20490                 });
20491             }
20492             
20493             inputblock.cn.push(input);
20494             
20495             if(this.inputType != 'radio'){
20496                 inputblock.cn.push(hidden);
20497             }
20498             
20499             if (this.after) {
20500                 inputblock.cn.push({
20501                     tag :'span',
20502                     cls : 'input-group-addon',
20503                     html : this.after
20504                 });
20505             }
20506             
20507         }
20508         
20509         if (align ==='left' && this.fieldLabel.length) {
20510 //                Roo.log("left and has label");
20511             cfg.cn = [
20512                 {
20513                     tag: 'label',
20514                     'for' :  id,
20515                     cls : 'control-label',
20516                     html : this.fieldLabel
20517                 },
20518                 {
20519                     cls : "", 
20520                     cn: [
20521                         inputblock
20522                     ]
20523                 }
20524             ];
20525             
20526             if(this.labelWidth > 12){
20527                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20528             }
20529             
20530             if(this.labelWidth < 13 && this.labelmd == 0){
20531                 this.labelmd = this.labelWidth;
20532             }
20533             
20534             if(this.labellg > 0){
20535                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20536                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20537             }
20538             
20539             if(this.labelmd > 0){
20540                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20541                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20542             }
20543             
20544             if(this.labelsm > 0){
20545                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20546                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20547             }
20548             
20549             if(this.labelxs > 0){
20550                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20551                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20552             }
20553             
20554         } else if ( this.fieldLabel.length) {
20555 //                Roo.log(" label");
20556                 cfg.cn = [
20557                    
20558                     {
20559                         tag: this.boxLabel ? 'span' : 'label',
20560                         'for': id,
20561                         cls: 'control-label box-input-label',
20562                         //cls : 'input-group-addon',
20563                         html : this.fieldLabel
20564                     },
20565                     
20566                     inputblock
20567                     
20568                 ];
20569
20570         } else {
20571             
20572 //                Roo.log(" no label && no align");
20573                 cfg.cn = [  inputblock ] ;
20574                 
20575                 
20576         }
20577         
20578         if(this.boxLabel){
20579              var boxLabelCfg = {
20580                 tag: 'label',
20581                 //'for': id, // box label is handled by onclick - so no for...
20582                 cls: 'box-label',
20583                 html: this.boxLabel
20584             };
20585             
20586             if(this.tooltip){
20587                 boxLabelCfg.tooltip = this.tooltip;
20588             }
20589              
20590             cfg.cn.push(boxLabelCfg);
20591         }
20592         
20593         if(this.inputType != 'radio'){
20594             cfg.cn.push(hidden);
20595         }
20596         
20597         return cfg;
20598         
20599     },
20600     
20601     /**
20602      * return the real input element.
20603      */
20604     inputEl: function ()
20605     {
20606         return this.el.select('input.roo-' + this.inputType,true).first();
20607     },
20608     hiddenEl: function ()
20609     {
20610         return this.el.select('input.roo-hidden-value',true).first();
20611     },
20612     
20613     labelEl: function()
20614     {
20615         return this.el.select('label.control-label',true).first();
20616     },
20617     /* depricated... */
20618     
20619     label: function()
20620     {
20621         return this.labelEl();
20622     },
20623     
20624     boxLabelEl: function()
20625     {
20626         return this.el.select('label.box-label',true).first();
20627     },
20628     
20629     initEvents : function()
20630     {
20631 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20632         
20633         this.inputEl().on('click', this.onClick,  this);
20634         
20635         if (this.boxLabel) { 
20636             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20637         }
20638         
20639         this.startValue = this.getValue();
20640         
20641         if(this.groupId){
20642             Roo.bootstrap.CheckBox.register(this);
20643         }
20644     },
20645     
20646     onClick : function(e)
20647     {   
20648         if(this.fireEvent('click', this, e) !== false){
20649             this.setChecked(!this.checked);
20650         }
20651         
20652     },
20653     
20654     setChecked : function(state,suppressEvent)
20655     {
20656         this.startValue = this.getValue();
20657
20658         if(this.inputType == 'radio'){
20659             
20660             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20661                 e.dom.checked = false;
20662             });
20663             
20664             this.inputEl().dom.checked = true;
20665             
20666             this.inputEl().dom.value = this.inputValue;
20667             
20668             if(suppressEvent !== true){
20669                 this.fireEvent('check', this, true);
20670             }
20671             
20672             this.validate();
20673             
20674             return;
20675         }
20676         
20677         this.checked = state;
20678         
20679         this.inputEl().dom.checked = state;
20680         
20681         
20682         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20683         
20684         if(suppressEvent !== true){
20685             this.fireEvent('check', this, state);
20686         }
20687         
20688         this.validate();
20689     },
20690     
20691     getValue : function()
20692     {
20693         if(this.inputType == 'radio'){
20694             return this.getGroupValue();
20695         }
20696         
20697         return this.hiddenEl().dom.value;
20698         
20699     },
20700     
20701     getGroupValue : function()
20702     {
20703         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20704             return '';
20705         }
20706         
20707         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20708     },
20709     
20710     setValue : function(v,suppressEvent)
20711     {
20712         if(this.inputType == 'radio'){
20713             this.setGroupValue(v, suppressEvent);
20714             return;
20715         }
20716         
20717         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20718         
20719         this.validate();
20720     },
20721     
20722     setGroupValue : function(v, suppressEvent)
20723     {
20724         this.startValue = this.getValue();
20725         
20726         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20727             e.dom.checked = false;
20728             
20729             if(e.dom.value == v){
20730                 e.dom.checked = true;
20731             }
20732         });
20733         
20734         if(suppressEvent !== true){
20735             this.fireEvent('check', this, true);
20736         }
20737
20738         this.validate();
20739         
20740         return;
20741     },
20742     
20743     validate : function()
20744     {
20745         if(this.getVisibilityEl().hasClass('hidden')){
20746             return true;
20747         }
20748         
20749         if(
20750                 this.disabled || 
20751                 (this.inputType == 'radio' && this.validateRadio()) ||
20752                 (this.inputType == 'checkbox' && this.validateCheckbox())
20753         ){
20754             this.markValid();
20755             return true;
20756         }
20757         
20758         this.markInvalid();
20759         return false;
20760     },
20761     
20762     validateRadio : function()
20763     {
20764         if(this.getVisibilityEl().hasClass('hidden')){
20765             return true;
20766         }
20767         
20768         if(this.allowBlank){
20769             return true;
20770         }
20771         
20772         var valid = false;
20773         
20774         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20775             if(!e.dom.checked){
20776                 return;
20777             }
20778             
20779             valid = true;
20780             
20781             return false;
20782         });
20783         
20784         return valid;
20785     },
20786     
20787     validateCheckbox : function()
20788     {
20789         if(!this.groupId){
20790             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20791             //return (this.getValue() == this.inputValue) ? true : false;
20792         }
20793         
20794         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20795         
20796         if(!group){
20797             return false;
20798         }
20799         
20800         var r = false;
20801         
20802         for(var i in group){
20803             if(group[i].el.isVisible(true)){
20804                 r = false;
20805                 break;
20806             }
20807             
20808             r = true;
20809         }
20810         
20811         for(var i in group){
20812             if(r){
20813                 break;
20814             }
20815             
20816             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20817         }
20818         
20819         return r;
20820     },
20821     
20822     /**
20823      * Mark this field as valid
20824      */
20825     markValid : function()
20826     {
20827         var _this = this;
20828         
20829         this.fireEvent('valid', this);
20830         
20831         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20832         
20833         if(this.groupId){
20834             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20835         }
20836         
20837         if(label){
20838             label.markValid();
20839         }
20840
20841         if(this.inputType == 'radio'){
20842             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20843                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20844                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20845             });
20846             
20847             return;
20848         }
20849
20850         if(!this.groupId){
20851             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20852             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20853             return;
20854         }
20855         
20856         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20857         
20858         if(!group){
20859             return;
20860         }
20861         
20862         for(var i in group){
20863             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20864             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20865         }
20866     },
20867     
20868      /**
20869      * Mark this field as invalid
20870      * @param {String} msg The validation message
20871      */
20872     markInvalid : function(msg)
20873     {
20874         if(this.allowBlank){
20875             return;
20876         }
20877         
20878         var _this = this;
20879         
20880         this.fireEvent('invalid', this, msg);
20881         
20882         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20883         
20884         if(this.groupId){
20885             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20886         }
20887         
20888         if(label){
20889             label.markInvalid();
20890         }
20891             
20892         if(this.inputType == 'radio'){
20893             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20894                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20895                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20896             });
20897             
20898             return;
20899         }
20900         
20901         if(!this.groupId){
20902             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20903             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20904             return;
20905         }
20906         
20907         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20908         
20909         if(!group){
20910             return;
20911         }
20912         
20913         for(var i in group){
20914             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20915             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20916         }
20917         
20918     },
20919     
20920     clearInvalid : function()
20921     {
20922         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20923         
20924         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20925         
20926         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20927         
20928         if (label && label.iconEl) {
20929             label.iconEl.removeClass(label.validClass);
20930             label.iconEl.removeClass(label.invalidClass);
20931         }
20932     },
20933     
20934     disable : function()
20935     {
20936         if(this.inputType != 'radio'){
20937             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20938             return;
20939         }
20940         
20941         var _this = this;
20942         
20943         if(this.rendered){
20944             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20945                 _this.getActionEl().addClass(this.disabledClass);
20946                 e.dom.disabled = true;
20947             });
20948         }
20949         
20950         this.disabled = true;
20951         this.fireEvent("disable", this);
20952         return this;
20953     },
20954
20955     enable : function()
20956     {
20957         if(this.inputType != 'radio'){
20958             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20959             return;
20960         }
20961         
20962         var _this = this;
20963         
20964         if(this.rendered){
20965             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20966                 _this.getActionEl().removeClass(this.disabledClass);
20967                 e.dom.disabled = false;
20968             });
20969         }
20970         
20971         this.disabled = false;
20972         this.fireEvent("enable", this);
20973         return this;
20974     },
20975     
20976     setBoxLabel : function(v)
20977     {
20978         this.boxLabel = v;
20979         
20980         if(this.rendered){
20981             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20982         }
20983     }
20984
20985 });
20986
20987 Roo.apply(Roo.bootstrap.CheckBox, {
20988     
20989     groups: {},
20990     
20991      /**
20992     * register a CheckBox Group
20993     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20994     */
20995     register : function(checkbox)
20996     {
20997         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20998             this.groups[checkbox.groupId] = {};
20999         }
21000         
21001         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21002             return;
21003         }
21004         
21005         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21006         
21007     },
21008     /**
21009     * fetch a CheckBox Group based on the group ID
21010     * @param {string} the group ID
21011     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21012     */
21013     get: function(groupId) {
21014         if (typeof(this.groups[groupId]) == 'undefined') {
21015             return false;
21016         }
21017         
21018         return this.groups[groupId] ;
21019     }
21020     
21021     
21022 });
21023 /*
21024  * - LGPL
21025  *
21026  * RadioItem
21027  * 
21028  */
21029
21030 /**
21031  * @class Roo.bootstrap.Radio
21032  * @extends Roo.bootstrap.Component
21033  * Bootstrap Radio class
21034  * @cfg {String} boxLabel - the label associated
21035  * @cfg {String} value - the value of radio
21036  * 
21037  * @constructor
21038  * Create a new Radio
21039  * @param {Object} config The config object
21040  */
21041 Roo.bootstrap.Radio = function(config){
21042     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21043     
21044 };
21045
21046 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21047     
21048     boxLabel : '',
21049     
21050     value : '',
21051     
21052     getAutoCreate : function()
21053     {
21054         var cfg = {
21055             tag : 'div',
21056             cls : 'form-group radio',
21057             cn : [
21058                 {
21059                     tag : 'label',
21060                     cls : 'box-label',
21061                     html : this.boxLabel
21062                 }
21063             ]
21064         };
21065         
21066         return cfg;
21067     },
21068     
21069     initEvents : function() 
21070     {
21071         this.parent().register(this);
21072         
21073         this.el.on('click', this.onClick, this);
21074         
21075     },
21076     
21077     onClick : function(e)
21078     {
21079         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21080             this.setChecked(true);
21081         }
21082     },
21083     
21084     setChecked : function(state, suppressEvent)
21085     {
21086         this.parent().setValue(this.value, suppressEvent);
21087         
21088     },
21089     
21090     setBoxLabel : function(v)
21091     {
21092         this.boxLabel = v;
21093         
21094         if(this.rendered){
21095             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21096         }
21097     }
21098     
21099 });
21100  
21101
21102  /*
21103  * - LGPL
21104  *
21105  * Input
21106  * 
21107  */
21108
21109 /**
21110  * @class Roo.bootstrap.SecurePass
21111  * @extends Roo.bootstrap.Input
21112  * Bootstrap SecurePass class
21113  *
21114  * 
21115  * @constructor
21116  * Create a new SecurePass
21117  * @param {Object} config The config object
21118  */
21119  
21120 Roo.bootstrap.SecurePass = function (config) {
21121     // these go here, so the translation tool can replace them..
21122     this.errors = {
21123         PwdEmpty: "Please type a password, and then retype it to confirm.",
21124         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21125         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21126         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21127         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21128         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21129         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21130         TooWeak: "Your password is Too Weak."
21131     },
21132     this.meterLabel = "Password strength:";
21133     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21134     this.meterClass = [
21135         "roo-password-meter-tooweak", 
21136         "roo-password-meter-weak", 
21137         "roo-password-meter-medium", 
21138         "roo-password-meter-strong", 
21139         "roo-password-meter-grey"
21140     ];
21141     
21142     this.errors = {};
21143     
21144     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21145 }
21146
21147 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21148     /**
21149      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21150      * {
21151      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21152      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21153      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21154      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21155      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21156      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21157      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21158      * })
21159      */
21160     // private
21161     
21162     meterWidth: 300,
21163     errorMsg :'',    
21164     errors: false,
21165     imageRoot: '/',
21166     /**
21167      * @cfg {String/Object} Label for the strength meter (defaults to
21168      * 'Password strength:')
21169      */
21170     // private
21171     meterLabel: '',
21172     /**
21173      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21174      * ['Weak', 'Medium', 'Strong'])
21175      */
21176     // private    
21177     pwdStrengths: false,    
21178     // private
21179     strength: 0,
21180     // private
21181     _lastPwd: null,
21182     // private
21183     kCapitalLetter: 0,
21184     kSmallLetter: 1,
21185     kDigit: 2,
21186     kPunctuation: 3,
21187     
21188     insecure: false,
21189     // private
21190     initEvents: function ()
21191     {
21192         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21193
21194         if (this.el.is('input[type=password]') && Roo.isSafari) {
21195             this.el.on('keydown', this.SafariOnKeyDown, this);
21196         }
21197
21198         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21199     },
21200     // private
21201     onRender: function (ct, position)
21202     {
21203         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21204         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21205         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21206
21207         this.trigger.createChild({
21208                    cn: [
21209                     {
21210                     //id: 'PwdMeter',
21211                     tag: 'div',
21212                     cls: 'roo-password-meter-grey col-xs-12',
21213                     style: {
21214                         //width: 0,
21215                         //width: this.meterWidth + 'px'                                                
21216                         }
21217                     },
21218                     {                            
21219                          cls: 'roo-password-meter-text'                          
21220                     }
21221                 ]            
21222         });
21223
21224          
21225         if (this.hideTrigger) {
21226             this.trigger.setDisplayed(false);
21227         }
21228         this.setSize(this.width || '', this.height || '');
21229     },
21230     // private
21231     onDestroy: function ()
21232     {
21233         if (this.trigger) {
21234             this.trigger.removeAllListeners();
21235             this.trigger.remove();
21236         }
21237         if (this.wrap) {
21238             this.wrap.remove();
21239         }
21240         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21241     },
21242     // private
21243     checkStrength: function ()
21244     {
21245         var pwd = this.inputEl().getValue();
21246         if (pwd == this._lastPwd) {
21247             return;
21248         }
21249
21250         var strength;
21251         if (this.ClientSideStrongPassword(pwd)) {
21252             strength = 3;
21253         } else if (this.ClientSideMediumPassword(pwd)) {
21254             strength = 2;
21255         } else if (this.ClientSideWeakPassword(pwd)) {
21256             strength = 1;
21257         } else {
21258             strength = 0;
21259         }
21260         
21261         Roo.log('strength1: ' + strength);
21262         
21263         //var pm = this.trigger.child('div/div/div').dom;
21264         var pm = this.trigger.child('div/div');
21265         pm.removeClass(this.meterClass);
21266         pm.addClass(this.meterClass[strength]);
21267                 
21268         
21269         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21270                 
21271         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21272         
21273         this._lastPwd = pwd;
21274     },
21275     reset: function ()
21276     {
21277         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21278         
21279         this._lastPwd = '';
21280         
21281         var pm = this.trigger.child('div/div');
21282         pm.removeClass(this.meterClass);
21283         pm.addClass('roo-password-meter-grey');        
21284         
21285         
21286         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21287         
21288         pt.innerHTML = '';
21289         this.inputEl().dom.type='password';
21290     },
21291     // private
21292     validateValue: function (value)
21293     {
21294         
21295         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21296             return false;
21297         }
21298         if (value.length == 0) {
21299             if (this.allowBlank) {
21300                 this.clearInvalid();
21301                 return true;
21302             }
21303
21304             this.markInvalid(this.errors.PwdEmpty);
21305             this.errorMsg = this.errors.PwdEmpty;
21306             return false;
21307         }
21308         
21309         if(this.insecure){
21310             return true;
21311         }
21312         
21313         if ('[\x21-\x7e]*'.match(value)) {
21314             this.markInvalid(this.errors.PwdBadChar);
21315             this.errorMsg = this.errors.PwdBadChar;
21316             return false;
21317         }
21318         if (value.length < 6) {
21319             this.markInvalid(this.errors.PwdShort);
21320             this.errorMsg = this.errors.PwdShort;
21321             return false;
21322         }
21323         if (value.length > 16) {
21324             this.markInvalid(this.errors.PwdLong);
21325             this.errorMsg = this.errors.PwdLong;
21326             return false;
21327         }
21328         var strength;
21329         if (this.ClientSideStrongPassword(value)) {
21330             strength = 3;
21331         } else if (this.ClientSideMediumPassword(value)) {
21332             strength = 2;
21333         } else if (this.ClientSideWeakPassword(value)) {
21334             strength = 1;
21335         } else {
21336             strength = 0;
21337         }
21338
21339         
21340         if (strength < 2) {
21341             //this.markInvalid(this.errors.TooWeak);
21342             this.errorMsg = this.errors.TooWeak;
21343             //return false;
21344         }
21345         
21346         
21347         console.log('strength2: ' + strength);
21348         
21349         //var pm = this.trigger.child('div/div/div').dom;
21350         
21351         var pm = this.trigger.child('div/div');
21352         pm.removeClass(this.meterClass);
21353         pm.addClass(this.meterClass[strength]);
21354                 
21355         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21356                 
21357         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21358         
21359         this.errorMsg = ''; 
21360         return true;
21361     },
21362     // private
21363     CharacterSetChecks: function (type)
21364     {
21365         this.type = type;
21366         this.fResult = false;
21367     },
21368     // private
21369     isctype: function (character, type)
21370     {
21371         switch (type) {  
21372             case this.kCapitalLetter:
21373                 if (character >= 'A' && character <= 'Z') {
21374                     return true;
21375                 }
21376                 break;
21377             
21378             case this.kSmallLetter:
21379                 if (character >= 'a' && character <= 'z') {
21380                     return true;
21381                 }
21382                 break;
21383             
21384             case this.kDigit:
21385                 if (character >= '0' && character <= '9') {
21386                     return true;
21387                 }
21388                 break;
21389             
21390             case this.kPunctuation:
21391                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21392                     return true;
21393                 }
21394                 break;
21395             
21396             default:
21397                 return false;
21398         }
21399
21400     },
21401     // private
21402     IsLongEnough: function (pwd, size)
21403     {
21404         return !(pwd == null || isNaN(size) || pwd.length < size);
21405     },
21406     // private
21407     SpansEnoughCharacterSets: function (word, nb)
21408     {
21409         if (!this.IsLongEnough(word, nb))
21410         {
21411             return false;
21412         }
21413
21414         var characterSetChecks = new Array(
21415             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21416             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21417         );
21418         
21419         for (var index = 0; index < word.length; ++index) {
21420             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21421                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21422                     characterSetChecks[nCharSet].fResult = true;
21423                     break;
21424                 }
21425             }
21426         }
21427
21428         var nCharSets = 0;
21429         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21430             if (characterSetChecks[nCharSet].fResult) {
21431                 ++nCharSets;
21432             }
21433         }
21434
21435         if (nCharSets < nb) {
21436             return false;
21437         }
21438         return true;
21439     },
21440     // private
21441     ClientSideStrongPassword: function (pwd)
21442     {
21443         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21444     },
21445     // private
21446     ClientSideMediumPassword: function (pwd)
21447     {
21448         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21449     },
21450     // private
21451     ClientSideWeakPassword: function (pwd)
21452     {
21453         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21454     }
21455           
21456 })//<script type="text/javascript">
21457
21458 /*
21459  * Based  Ext JS Library 1.1.1
21460  * Copyright(c) 2006-2007, Ext JS, LLC.
21461  * LGPL
21462  *
21463  */
21464  
21465 /**
21466  * @class Roo.HtmlEditorCore
21467  * @extends Roo.Component
21468  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21469  *
21470  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21471  */
21472
21473 Roo.HtmlEditorCore = function(config){
21474     
21475     
21476     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21477     
21478     
21479     this.addEvents({
21480         /**
21481          * @event initialize
21482          * Fires when the editor is fully initialized (including the iframe)
21483          * @param {Roo.HtmlEditorCore} this
21484          */
21485         initialize: true,
21486         /**
21487          * @event activate
21488          * Fires when the editor is first receives the focus. Any insertion must wait
21489          * until after this event.
21490          * @param {Roo.HtmlEditorCore} this
21491          */
21492         activate: true,
21493          /**
21494          * @event beforesync
21495          * Fires before the textarea is updated with content from the editor iframe. Return false
21496          * to cancel the sync.
21497          * @param {Roo.HtmlEditorCore} this
21498          * @param {String} html
21499          */
21500         beforesync: true,
21501          /**
21502          * @event beforepush
21503          * Fires before the iframe editor is updated with content from the textarea. Return false
21504          * to cancel the push.
21505          * @param {Roo.HtmlEditorCore} this
21506          * @param {String} html
21507          */
21508         beforepush: true,
21509          /**
21510          * @event sync
21511          * Fires when the textarea is updated with content from the editor iframe.
21512          * @param {Roo.HtmlEditorCore} this
21513          * @param {String} html
21514          */
21515         sync: true,
21516          /**
21517          * @event push
21518          * Fires when the iframe editor is updated with content from the textarea.
21519          * @param {Roo.HtmlEditorCore} this
21520          * @param {String} html
21521          */
21522         push: true,
21523         
21524         /**
21525          * @event editorevent
21526          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21527          * @param {Roo.HtmlEditorCore} this
21528          */
21529         editorevent: true
21530         
21531     });
21532     
21533     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21534     
21535     // defaults : white / black...
21536     this.applyBlacklists();
21537     
21538     
21539     
21540 };
21541
21542
21543 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21544
21545
21546      /**
21547      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21548      */
21549     
21550     owner : false,
21551     
21552      /**
21553      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21554      *                        Roo.resizable.
21555      */
21556     resizable : false,
21557      /**
21558      * @cfg {Number} height (in pixels)
21559      */   
21560     height: 300,
21561    /**
21562      * @cfg {Number} width (in pixels)
21563      */   
21564     width: 500,
21565     
21566     /**
21567      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21568      * 
21569      */
21570     stylesheets: false,
21571     
21572     // id of frame..
21573     frameId: false,
21574     
21575     // private properties
21576     validationEvent : false,
21577     deferHeight: true,
21578     initialized : false,
21579     activated : false,
21580     sourceEditMode : false,
21581     onFocus : Roo.emptyFn,
21582     iframePad:3,
21583     hideMode:'offsets',
21584     
21585     clearUp: true,
21586     
21587     // blacklist + whitelisted elements..
21588     black: false,
21589     white: false,
21590      
21591     bodyCls : '',
21592
21593     /**
21594      * Protected method that will not generally be called directly. It
21595      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21596      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21597      */
21598     getDocMarkup : function(){
21599         // body styles..
21600         var st = '';
21601         
21602         // inherit styels from page...?? 
21603         if (this.stylesheets === false) {
21604             
21605             Roo.get(document.head).select('style').each(function(node) {
21606                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21607             });
21608             
21609             Roo.get(document.head).select('link').each(function(node) { 
21610                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21611             });
21612             
21613         } else if (!this.stylesheets.length) {
21614                 // simple..
21615                 st = '<style type="text/css">' +
21616                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21617                    '</style>';
21618         } else { 
21619             st = '<style type="text/css">' +
21620                     this.stylesheets +
21621                 '</style>';
21622         }
21623         
21624         st +=  '<style type="text/css">' +
21625             'IMG { cursor: pointer } ' +
21626         '</style>';
21627
21628         var cls = 'roo-htmleditor-body';
21629         
21630         if(this.bodyCls.length){
21631             cls += ' ' + this.bodyCls;
21632         }
21633         
21634         return '<html><head>' + st  +
21635             //<style type="text/css">' +
21636             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21637             //'</style>' +
21638             ' </head><body class="' +  cls + '"></body></html>';
21639     },
21640
21641     // private
21642     onRender : function(ct, position)
21643     {
21644         var _t = this;
21645         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21646         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21647         
21648         
21649         this.el.dom.style.border = '0 none';
21650         this.el.dom.setAttribute('tabIndex', -1);
21651         this.el.addClass('x-hidden hide');
21652         
21653         
21654         
21655         if(Roo.isIE){ // fix IE 1px bogus margin
21656             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21657         }
21658        
21659         
21660         this.frameId = Roo.id();
21661         
21662          
21663         
21664         var iframe = this.owner.wrap.createChild({
21665             tag: 'iframe',
21666             cls: 'form-control', // bootstrap..
21667             id: this.frameId,
21668             name: this.frameId,
21669             frameBorder : 'no',
21670             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21671         }, this.el
21672         );
21673         
21674         
21675         this.iframe = iframe.dom;
21676
21677          this.assignDocWin();
21678         
21679         this.doc.designMode = 'on';
21680        
21681         this.doc.open();
21682         this.doc.write(this.getDocMarkup());
21683         this.doc.close();
21684
21685         
21686         var task = { // must defer to wait for browser to be ready
21687             run : function(){
21688                 //console.log("run task?" + this.doc.readyState);
21689                 this.assignDocWin();
21690                 if(this.doc.body || this.doc.readyState == 'complete'){
21691                     try {
21692                         this.doc.designMode="on";
21693                     } catch (e) {
21694                         return;
21695                     }
21696                     Roo.TaskMgr.stop(task);
21697                     this.initEditor.defer(10, this);
21698                 }
21699             },
21700             interval : 10,
21701             duration: 10000,
21702             scope: this
21703         };
21704         Roo.TaskMgr.start(task);
21705
21706     },
21707
21708     // private
21709     onResize : function(w, h)
21710     {
21711          Roo.log('resize: ' +w + ',' + h );
21712         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21713         if(!this.iframe){
21714             return;
21715         }
21716         if(typeof w == 'number'){
21717             
21718             this.iframe.style.width = w + 'px';
21719         }
21720         if(typeof h == 'number'){
21721             
21722             this.iframe.style.height = h + 'px';
21723             if(this.doc){
21724                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21725             }
21726         }
21727         
21728     },
21729
21730     /**
21731      * Toggles the editor between standard and source edit mode.
21732      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21733      */
21734     toggleSourceEdit : function(sourceEditMode){
21735         
21736         this.sourceEditMode = sourceEditMode === true;
21737         
21738         if(this.sourceEditMode){
21739  
21740             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21741             
21742         }else{
21743             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21744             //this.iframe.className = '';
21745             this.deferFocus();
21746         }
21747         //this.setSize(this.owner.wrap.getSize());
21748         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21749     },
21750
21751     
21752   
21753
21754     /**
21755      * Protected method that will not generally be called directly. If you need/want
21756      * custom HTML cleanup, this is the method you should override.
21757      * @param {String} html The HTML to be cleaned
21758      * return {String} The cleaned HTML
21759      */
21760     cleanHtml : function(html){
21761         html = String(html);
21762         if(html.length > 5){
21763             if(Roo.isSafari){ // strip safari nonsense
21764                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21765             }
21766         }
21767         if(html == '&nbsp;'){
21768             html = '';
21769         }
21770         return html;
21771     },
21772
21773     /**
21774      * HTML Editor -> Textarea
21775      * Protected method that will not generally be called directly. Syncs the contents
21776      * of the editor iframe with the textarea.
21777      */
21778     syncValue : function(){
21779         if(this.initialized){
21780             var bd = (this.doc.body || this.doc.documentElement);
21781             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21782             var html = bd.innerHTML;
21783             if(Roo.isSafari){
21784                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21785                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21786                 if(m && m[1]){
21787                     html = '<div style="'+m[0]+'">' + html + '</div>';
21788                 }
21789             }
21790             html = this.cleanHtml(html);
21791             // fix up the special chars.. normaly like back quotes in word...
21792             // however we do not want to do this with chinese..
21793             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21794                 var cc = b.charCodeAt();
21795                 if (
21796                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21797                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21798                     (cc >= 0xf900 && cc < 0xfb00 )
21799                 ) {
21800                         return b;
21801                 }
21802                 return "&#"+cc+";" 
21803             });
21804             if(this.owner.fireEvent('beforesync', this, html) !== false){
21805                 this.el.dom.value = html;
21806                 this.owner.fireEvent('sync', this, html);
21807             }
21808         }
21809     },
21810
21811     /**
21812      * Protected method that will not generally be called directly. Pushes the value of the textarea
21813      * into the iframe editor.
21814      */
21815     pushValue : function(){
21816         if(this.initialized){
21817             var v = this.el.dom.value.trim();
21818             
21819 //            if(v.length < 1){
21820 //                v = '&#160;';
21821 //            }
21822             
21823             if(this.owner.fireEvent('beforepush', this, v) !== false){
21824                 var d = (this.doc.body || this.doc.documentElement);
21825                 d.innerHTML = v;
21826                 this.cleanUpPaste();
21827                 this.el.dom.value = d.innerHTML;
21828                 this.owner.fireEvent('push', this, v);
21829             }
21830         }
21831     },
21832
21833     // private
21834     deferFocus : function(){
21835         this.focus.defer(10, this);
21836     },
21837
21838     // doc'ed in Field
21839     focus : function(){
21840         if(this.win && !this.sourceEditMode){
21841             this.win.focus();
21842         }else{
21843             this.el.focus();
21844         }
21845     },
21846     
21847     assignDocWin: function()
21848     {
21849         var iframe = this.iframe;
21850         
21851          if(Roo.isIE){
21852             this.doc = iframe.contentWindow.document;
21853             this.win = iframe.contentWindow;
21854         } else {
21855 //            if (!Roo.get(this.frameId)) {
21856 //                return;
21857 //            }
21858 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21859 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21860             
21861             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21862                 return;
21863             }
21864             
21865             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21866             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21867         }
21868     },
21869     
21870     // private
21871     initEditor : function(){
21872         //console.log("INIT EDITOR");
21873         this.assignDocWin();
21874         
21875         
21876         
21877         this.doc.designMode="on";
21878         this.doc.open();
21879         this.doc.write(this.getDocMarkup());
21880         this.doc.close();
21881         
21882         var dbody = (this.doc.body || this.doc.documentElement);
21883         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21884         // this copies styles from the containing element into thsi one..
21885         // not sure why we need all of this..
21886         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21887         
21888         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21889         //ss['background-attachment'] = 'fixed'; // w3c
21890         dbody.bgProperties = 'fixed'; // ie
21891         //Roo.DomHelper.applyStyles(dbody, ss);
21892         Roo.EventManager.on(this.doc, {
21893             //'mousedown': this.onEditorEvent,
21894             'mouseup': this.onEditorEvent,
21895             'dblclick': this.onEditorEvent,
21896             'click': this.onEditorEvent,
21897             'keyup': this.onEditorEvent,
21898             buffer:100,
21899             scope: this
21900         });
21901         if(Roo.isGecko){
21902             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21903         }
21904         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21905             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21906         }
21907         this.initialized = true;
21908
21909         this.owner.fireEvent('initialize', this);
21910         this.pushValue();
21911     },
21912
21913     // private
21914     onDestroy : function(){
21915         
21916         
21917         
21918         if(this.rendered){
21919             
21920             //for (var i =0; i < this.toolbars.length;i++) {
21921             //    // fixme - ask toolbars for heights?
21922             //    this.toolbars[i].onDestroy();
21923            // }
21924             
21925             //this.wrap.dom.innerHTML = '';
21926             //this.wrap.remove();
21927         }
21928     },
21929
21930     // private
21931     onFirstFocus : function(){
21932         
21933         this.assignDocWin();
21934         
21935         
21936         this.activated = true;
21937          
21938     
21939         if(Roo.isGecko){ // prevent silly gecko errors
21940             this.win.focus();
21941             var s = this.win.getSelection();
21942             if(!s.focusNode || s.focusNode.nodeType != 3){
21943                 var r = s.getRangeAt(0);
21944                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21945                 r.collapse(true);
21946                 this.deferFocus();
21947             }
21948             try{
21949                 this.execCmd('useCSS', true);
21950                 this.execCmd('styleWithCSS', false);
21951             }catch(e){}
21952         }
21953         this.owner.fireEvent('activate', this);
21954     },
21955
21956     // private
21957     adjustFont: function(btn){
21958         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21959         //if(Roo.isSafari){ // safari
21960         //    adjust *= 2;
21961        // }
21962         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21963         if(Roo.isSafari){ // safari
21964             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21965             v =  (v < 10) ? 10 : v;
21966             v =  (v > 48) ? 48 : v;
21967             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21968             
21969         }
21970         
21971         
21972         v = Math.max(1, v+adjust);
21973         
21974         this.execCmd('FontSize', v  );
21975     },
21976
21977     onEditorEvent : function(e)
21978     {
21979         this.owner.fireEvent('editorevent', this, e);
21980       //  this.updateToolbar();
21981         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21982     },
21983
21984     insertTag : function(tg)
21985     {
21986         // could be a bit smarter... -> wrap the current selected tRoo..
21987         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21988             
21989             range = this.createRange(this.getSelection());
21990             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21991             wrappingNode.appendChild(range.extractContents());
21992             range.insertNode(wrappingNode);
21993
21994             return;
21995             
21996             
21997             
21998         }
21999         this.execCmd("formatblock",   tg);
22000         
22001     },
22002     
22003     insertText : function(txt)
22004     {
22005         
22006         
22007         var range = this.createRange();
22008         range.deleteContents();
22009                //alert(Sender.getAttribute('label'));
22010                
22011         range.insertNode(this.doc.createTextNode(txt));
22012     } ,
22013     
22014      
22015
22016     /**
22017      * Executes a Midas editor command on the editor document and performs necessary focus and
22018      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22019      * @param {String} cmd The Midas command
22020      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22021      */
22022     relayCmd : function(cmd, value){
22023         this.win.focus();
22024         this.execCmd(cmd, value);
22025         this.owner.fireEvent('editorevent', this);
22026         //this.updateToolbar();
22027         this.owner.deferFocus();
22028     },
22029
22030     /**
22031      * Executes a Midas editor command directly on the editor document.
22032      * For visual commands, you should use {@link #relayCmd} instead.
22033      * <b>This should only be called after the editor is initialized.</b>
22034      * @param {String} cmd The Midas command
22035      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22036      */
22037     execCmd : function(cmd, value){
22038         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22039         this.syncValue();
22040     },
22041  
22042  
22043    
22044     /**
22045      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22046      * to insert tRoo.
22047      * @param {String} text | dom node.. 
22048      */
22049     insertAtCursor : function(text)
22050     {
22051         
22052         if(!this.activated){
22053             return;
22054         }
22055         /*
22056         if(Roo.isIE){
22057             this.win.focus();
22058             var r = this.doc.selection.createRange();
22059             if(r){
22060                 r.collapse(true);
22061                 r.pasteHTML(text);
22062                 this.syncValue();
22063                 this.deferFocus();
22064             
22065             }
22066             return;
22067         }
22068         */
22069         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22070             this.win.focus();
22071             
22072             
22073             // from jquery ui (MIT licenced)
22074             var range, node;
22075             var win = this.win;
22076             
22077             if (win.getSelection && win.getSelection().getRangeAt) {
22078                 range = win.getSelection().getRangeAt(0);
22079                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22080                 range.insertNode(node);
22081             } else if (win.document.selection && win.document.selection.createRange) {
22082                 // no firefox support
22083                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22084                 win.document.selection.createRange().pasteHTML(txt);
22085             } else {
22086                 // no firefox support
22087                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22088                 this.execCmd('InsertHTML', txt);
22089             } 
22090             
22091             this.syncValue();
22092             
22093             this.deferFocus();
22094         }
22095     },
22096  // private
22097     mozKeyPress : function(e){
22098         if(e.ctrlKey){
22099             var c = e.getCharCode(), cmd;
22100           
22101             if(c > 0){
22102                 c = String.fromCharCode(c).toLowerCase();
22103                 switch(c){
22104                     case 'b':
22105                         cmd = 'bold';
22106                         break;
22107                     case 'i':
22108                         cmd = 'italic';
22109                         break;
22110                     
22111                     case 'u':
22112                         cmd = 'underline';
22113                         break;
22114                     
22115                     case 'v':
22116                         this.cleanUpPaste.defer(100, this);
22117                         return;
22118                         
22119                 }
22120                 if(cmd){
22121                     this.win.focus();
22122                     this.execCmd(cmd);
22123                     this.deferFocus();
22124                     e.preventDefault();
22125                 }
22126                 
22127             }
22128         }
22129     },
22130
22131     // private
22132     fixKeys : function(){ // load time branching for fastest keydown performance
22133         if(Roo.isIE){
22134             return function(e){
22135                 var k = e.getKey(), r;
22136                 if(k == e.TAB){
22137                     e.stopEvent();
22138                     r = this.doc.selection.createRange();
22139                     if(r){
22140                         r.collapse(true);
22141                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22142                         this.deferFocus();
22143                     }
22144                     return;
22145                 }
22146                 
22147                 if(k == e.ENTER){
22148                     r = this.doc.selection.createRange();
22149                     if(r){
22150                         var target = r.parentElement();
22151                         if(!target || target.tagName.toLowerCase() != 'li'){
22152                             e.stopEvent();
22153                             r.pasteHTML('<br />');
22154                             r.collapse(false);
22155                             r.select();
22156                         }
22157                     }
22158                 }
22159                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22160                     this.cleanUpPaste.defer(100, this);
22161                     return;
22162                 }
22163                 
22164                 
22165             };
22166         }else if(Roo.isOpera){
22167             return function(e){
22168                 var k = e.getKey();
22169                 if(k == e.TAB){
22170                     e.stopEvent();
22171                     this.win.focus();
22172                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22173                     this.deferFocus();
22174                 }
22175                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22176                     this.cleanUpPaste.defer(100, this);
22177                     return;
22178                 }
22179                 
22180             };
22181         }else if(Roo.isSafari){
22182             return function(e){
22183                 var k = e.getKey();
22184                 
22185                 if(k == e.TAB){
22186                     e.stopEvent();
22187                     this.execCmd('InsertText','\t');
22188                     this.deferFocus();
22189                     return;
22190                 }
22191                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22192                     this.cleanUpPaste.defer(100, this);
22193                     return;
22194                 }
22195                 
22196              };
22197         }
22198     }(),
22199     
22200     getAllAncestors: function()
22201     {
22202         var p = this.getSelectedNode();
22203         var a = [];
22204         if (!p) {
22205             a.push(p); // push blank onto stack..
22206             p = this.getParentElement();
22207         }
22208         
22209         
22210         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22211             a.push(p);
22212             p = p.parentNode;
22213         }
22214         a.push(this.doc.body);
22215         return a;
22216     },
22217     lastSel : false,
22218     lastSelNode : false,
22219     
22220     
22221     getSelection : function() 
22222     {
22223         this.assignDocWin();
22224         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22225     },
22226     
22227     getSelectedNode: function() 
22228     {
22229         // this may only work on Gecko!!!
22230         
22231         // should we cache this!!!!
22232         
22233         
22234         
22235          
22236         var range = this.createRange(this.getSelection()).cloneRange();
22237         
22238         if (Roo.isIE) {
22239             var parent = range.parentElement();
22240             while (true) {
22241                 var testRange = range.duplicate();
22242                 testRange.moveToElementText(parent);
22243                 if (testRange.inRange(range)) {
22244                     break;
22245                 }
22246                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22247                     break;
22248                 }
22249                 parent = parent.parentElement;
22250             }
22251             return parent;
22252         }
22253         
22254         // is ancestor a text element.
22255         var ac =  range.commonAncestorContainer;
22256         if (ac.nodeType == 3) {
22257             ac = ac.parentNode;
22258         }
22259         
22260         var ar = ac.childNodes;
22261          
22262         var nodes = [];
22263         var other_nodes = [];
22264         var has_other_nodes = false;
22265         for (var i=0;i<ar.length;i++) {
22266             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22267                 continue;
22268             }
22269             // fullly contained node.
22270             
22271             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22272                 nodes.push(ar[i]);
22273                 continue;
22274             }
22275             
22276             // probably selected..
22277             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22278                 other_nodes.push(ar[i]);
22279                 continue;
22280             }
22281             // outer..
22282             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22283                 continue;
22284             }
22285             
22286             
22287             has_other_nodes = true;
22288         }
22289         if (!nodes.length && other_nodes.length) {
22290             nodes= other_nodes;
22291         }
22292         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22293             return false;
22294         }
22295         
22296         return nodes[0];
22297     },
22298     createRange: function(sel)
22299     {
22300         // this has strange effects when using with 
22301         // top toolbar - not sure if it's a great idea.
22302         //this.editor.contentWindow.focus();
22303         if (typeof sel != "undefined") {
22304             try {
22305                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22306             } catch(e) {
22307                 return this.doc.createRange();
22308             }
22309         } else {
22310             return this.doc.createRange();
22311         }
22312     },
22313     getParentElement: function()
22314     {
22315         
22316         this.assignDocWin();
22317         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22318         
22319         var range = this.createRange(sel);
22320          
22321         try {
22322             var p = range.commonAncestorContainer;
22323             while (p.nodeType == 3) { // text node
22324                 p = p.parentNode;
22325             }
22326             return p;
22327         } catch (e) {
22328             return null;
22329         }
22330     
22331     },
22332     /***
22333      *
22334      * Range intersection.. the hard stuff...
22335      *  '-1' = before
22336      *  '0' = hits..
22337      *  '1' = after.
22338      *         [ -- selected range --- ]
22339      *   [fail]                        [fail]
22340      *
22341      *    basically..
22342      *      if end is before start or  hits it. fail.
22343      *      if start is after end or hits it fail.
22344      *
22345      *   if either hits (but other is outside. - then it's not 
22346      *   
22347      *    
22348      **/
22349     
22350     
22351     // @see http://www.thismuchiknow.co.uk/?p=64.
22352     rangeIntersectsNode : function(range, node)
22353     {
22354         var nodeRange = node.ownerDocument.createRange();
22355         try {
22356             nodeRange.selectNode(node);
22357         } catch (e) {
22358             nodeRange.selectNodeContents(node);
22359         }
22360     
22361         var rangeStartRange = range.cloneRange();
22362         rangeStartRange.collapse(true);
22363     
22364         var rangeEndRange = range.cloneRange();
22365         rangeEndRange.collapse(false);
22366     
22367         var nodeStartRange = nodeRange.cloneRange();
22368         nodeStartRange.collapse(true);
22369     
22370         var nodeEndRange = nodeRange.cloneRange();
22371         nodeEndRange.collapse(false);
22372     
22373         return rangeStartRange.compareBoundaryPoints(
22374                  Range.START_TO_START, nodeEndRange) == -1 &&
22375                rangeEndRange.compareBoundaryPoints(
22376                  Range.START_TO_START, nodeStartRange) == 1;
22377         
22378          
22379     },
22380     rangeCompareNode : function(range, node)
22381     {
22382         var nodeRange = node.ownerDocument.createRange();
22383         try {
22384             nodeRange.selectNode(node);
22385         } catch (e) {
22386             nodeRange.selectNodeContents(node);
22387         }
22388         
22389         
22390         range.collapse(true);
22391     
22392         nodeRange.collapse(true);
22393      
22394         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22395         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22396          
22397         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22398         
22399         var nodeIsBefore   =  ss == 1;
22400         var nodeIsAfter    = ee == -1;
22401         
22402         if (nodeIsBefore && nodeIsAfter) {
22403             return 0; // outer
22404         }
22405         if (!nodeIsBefore && nodeIsAfter) {
22406             return 1; //right trailed.
22407         }
22408         
22409         if (nodeIsBefore && !nodeIsAfter) {
22410             return 2;  // left trailed.
22411         }
22412         // fully contined.
22413         return 3;
22414     },
22415
22416     // private? - in a new class?
22417     cleanUpPaste :  function()
22418     {
22419         // cleans up the whole document..
22420         Roo.log('cleanuppaste');
22421         
22422         this.cleanUpChildren(this.doc.body);
22423         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22424         if (clean != this.doc.body.innerHTML) {
22425             this.doc.body.innerHTML = clean;
22426         }
22427         
22428     },
22429     
22430     cleanWordChars : function(input) {// change the chars to hex code
22431         var he = Roo.HtmlEditorCore;
22432         
22433         var output = input;
22434         Roo.each(he.swapCodes, function(sw) { 
22435             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22436             
22437             output = output.replace(swapper, sw[1]);
22438         });
22439         
22440         return output;
22441     },
22442     
22443     
22444     cleanUpChildren : function (n)
22445     {
22446         if (!n.childNodes.length) {
22447             return;
22448         }
22449         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22450            this.cleanUpChild(n.childNodes[i]);
22451         }
22452     },
22453     
22454     
22455         
22456     
22457     cleanUpChild : function (node)
22458     {
22459         var ed = this;
22460         //console.log(node);
22461         if (node.nodeName == "#text") {
22462             // clean up silly Windows -- stuff?
22463             return; 
22464         }
22465         if (node.nodeName == "#comment") {
22466             node.parentNode.removeChild(node);
22467             // clean up silly Windows -- stuff?
22468             return; 
22469         }
22470         var lcname = node.tagName.toLowerCase();
22471         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22472         // whitelist of tags..
22473         
22474         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22475             // remove node.
22476             node.parentNode.removeChild(node);
22477             return;
22478             
22479         }
22480         
22481         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22482         
22483         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22484         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22485         
22486         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22487         //    remove_keep_children = true;
22488         //}
22489         
22490         if (remove_keep_children) {
22491             this.cleanUpChildren(node);
22492             // inserts everything just before this node...
22493             while (node.childNodes.length) {
22494                 var cn = node.childNodes[0];
22495                 node.removeChild(cn);
22496                 node.parentNode.insertBefore(cn, node);
22497             }
22498             node.parentNode.removeChild(node);
22499             return;
22500         }
22501         
22502         if (!node.attributes || !node.attributes.length) {
22503             this.cleanUpChildren(node);
22504             return;
22505         }
22506         
22507         function cleanAttr(n,v)
22508         {
22509             
22510             if (v.match(/^\./) || v.match(/^\//)) {
22511                 return;
22512             }
22513             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22514                 return;
22515             }
22516             if (v.match(/^#/)) {
22517                 return;
22518             }
22519 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22520             node.removeAttribute(n);
22521             
22522         }
22523         
22524         var cwhite = this.cwhite;
22525         var cblack = this.cblack;
22526             
22527         function cleanStyle(n,v)
22528         {
22529             if (v.match(/expression/)) { //XSS?? should we even bother..
22530                 node.removeAttribute(n);
22531                 return;
22532             }
22533             
22534             var parts = v.split(/;/);
22535             var clean = [];
22536             
22537             Roo.each(parts, function(p) {
22538                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22539                 if (!p.length) {
22540                     return true;
22541                 }
22542                 var l = p.split(':').shift().replace(/\s+/g,'');
22543                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22544                 
22545                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22546 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22547                     //node.removeAttribute(n);
22548                     return true;
22549                 }
22550                 //Roo.log()
22551                 // only allow 'c whitelisted system attributes'
22552                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22553 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22554                     //node.removeAttribute(n);
22555                     return true;
22556                 }
22557                 
22558                 
22559                  
22560                 
22561                 clean.push(p);
22562                 return true;
22563             });
22564             if (clean.length) { 
22565                 node.setAttribute(n, clean.join(';'));
22566             } else {
22567                 node.removeAttribute(n);
22568             }
22569             
22570         }
22571         
22572         
22573         for (var i = node.attributes.length-1; i > -1 ; i--) {
22574             var a = node.attributes[i];
22575             //console.log(a);
22576             
22577             if (a.name.toLowerCase().substr(0,2)=='on')  {
22578                 node.removeAttribute(a.name);
22579                 continue;
22580             }
22581             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22582                 node.removeAttribute(a.name);
22583                 continue;
22584             }
22585             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22586                 cleanAttr(a.name,a.value); // fixme..
22587                 continue;
22588             }
22589             if (a.name == 'style') {
22590                 cleanStyle(a.name,a.value);
22591                 continue;
22592             }
22593             /// clean up MS crap..
22594             // tecnically this should be a list of valid class'es..
22595             
22596             
22597             if (a.name == 'class') {
22598                 if (a.value.match(/^Mso/)) {
22599                     node.className = '';
22600                 }
22601                 
22602                 if (a.value.match(/^body$/)) {
22603                     node.className = '';
22604                 }
22605                 continue;
22606             }
22607             
22608             // style cleanup!?
22609             // class cleanup?
22610             
22611         }
22612         
22613         
22614         this.cleanUpChildren(node);
22615         
22616         
22617     },
22618     
22619     /**
22620      * Clean up MS wordisms...
22621      */
22622     cleanWord : function(node)
22623     {
22624         
22625         
22626         if (!node) {
22627             this.cleanWord(this.doc.body);
22628             return;
22629         }
22630         if (node.nodeName == "#text") {
22631             // clean up silly Windows -- stuff?
22632             return; 
22633         }
22634         if (node.nodeName == "#comment") {
22635             node.parentNode.removeChild(node);
22636             // clean up silly Windows -- stuff?
22637             return; 
22638         }
22639         
22640         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22641             node.parentNode.removeChild(node);
22642             return;
22643         }
22644         
22645         // remove - but keep children..
22646         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22647             while (node.childNodes.length) {
22648                 var cn = node.childNodes[0];
22649                 node.removeChild(cn);
22650                 node.parentNode.insertBefore(cn, node);
22651             }
22652             node.parentNode.removeChild(node);
22653             this.iterateChildren(node, this.cleanWord);
22654             return;
22655         }
22656         // clean styles
22657         if (node.className.length) {
22658             
22659             var cn = node.className.split(/\W+/);
22660             var cna = [];
22661             Roo.each(cn, function(cls) {
22662                 if (cls.match(/Mso[a-zA-Z]+/)) {
22663                     return;
22664                 }
22665                 cna.push(cls);
22666             });
22667             node.className = cna.length ? cna.join(' ') : '';
22668             if (!cna.length) {
22669                 node.removeAttribute("class");
22670             }
22671         }
22672         
22673         if (node.hasAttribute("lang")) {
22674             node.removeAttribute("lang");
22675         }
22676         
22677         if (node.hasAttribute("style")) {
22678             
22679             var styles = node.getAttribute("style").split(";");
22680             var nstyle = [];
22681             Roo.each(styles, function(s) {
22682                 if (!s.match(/:/)) {
22683                     return;
22684                 }
22685                 var kv = s.split(":");
22686                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22687                     return;
22688                 }
22689                 // what ever is left... we allow.
22690                 nstyle.push(s);
22691             });
22692             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22693             if (!nstyle.length) {
22694                 node.removeAttribute('style');
22695             }
22696         }
22697         this.iterateChildren(node, this.cleanWord);
22698         
22699         
22700         
22701     },
22702     /**
22703      * iterateChildren of a Node, calling fn each time, using this as the scole..
22704      * @param {DomNode} node node to iterate children of.
22705      * @param {Function} fn method of this class to call on each item.
22706      */
22707     iterateChildren : function(node, fn)
22708     {
22709         if (!node.childNodes.length) {
22710                 return;
22711         }
22712         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22713            fn.call(this, node.childNodes[i])
22714         }
22715     },
22716     
22717     
22718     /**
22719      * cleanTableWidths.
22720      *
22721      * Quite often pasting from word etc.. results in tables with column and widths.
22722      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22723      *
22724      */
22725     cleanTableWidths : function(node)
22726     {
22727          
22728          
22729         if (!node) {
22730             this.cleanTableWidths(this.doc.body);
22731             return;
22732         }
22733         
22734         // ignore list...
22735         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22736             return; 
22737         }
22738         Roo.log(node.tagName);
22739         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22740             this.iterateChildren(node, this.cleanTableWidths);
22741             return;
22742         }
22743         if (node.hasAttribute('width')) {
22744             node.removeAttribute('width');
22745         }
22746         
22747          
22748         if (node.hasAttribute("style")) {
22749             // pretty basic...
22750             
22751             var styles = node.getAttribute("style").split(";");
22752             var nstyle = [];
22753             Roo.each(styles, function(s) {
22754                 if (!s.match(/:/)) {
22755                     return;
22756                 }
22757                 var kv = s.split(":");
22758                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22759                     return;
22760                 }
22761                 // what ever is left... we allow.
22762                 nstyle.push(s);
22763             });
22764             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22765             if (!nstyle.length) {
22766                 node.removeAttribute('style');
22767             }
22768         }
22769         
22770         this.iterateChildren(node, this.cleanTableWidths);
22771         
22772         
22773     },
22774     
22775     
22776     
22777     
22778     domToHTML : function(currentElement, depth, nopadtext) {
22779         
22780         depth = depth || 0;
22781         nopadtext = nopadtext || false;
22782     
22783         if (!currentElement) {
22784             return this.domToHTML(this.doc.body);
22785         }
22786         
22787         //Roo.log(currentElement);
22788         var j;
22789         var allText = false;
22790         var nodeName = currentElement.nodeName;
22791         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22792         
22793         if  (nodeName == '#text') {
22794             
22795             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22796         }
22797         
22798         
22799         var ret = '';
22800         if (nodeName != 'BODY') {
22801              
22802             var i = 0;
22803             // Prints the node tagName, such as <A>, <IMG>, etc
22804             if (tagName) {
22805                 var attr = [];
22806                 for(i = 0; i < currentElement.attributes.length;i++) {
22807                     // quoting?
22808                     var aname = currentElement.attributes.item(i).name;
22809                     if (!currentElement.attributes.item(i).value.length) {
22810                         continue;
22811                     }
22812                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22813                 }
22814                 
22815                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22816             } 
22817             else {
22818                 
22819                 // eack
22820             }
22821         } else {
22822             tagName = false;
22823         }
22824         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22825             return ret;
22826         }
22827         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22828             nopadtext = true;
22829         }
22830         
22831         
22832         // Traverse the tree
22833         i = 0;
22834         var currentElementChild = currentElement.childNodes.item(i);
22835         var allText = true;
22836         var innerHTML  = '';
22837         lastnode = '';
22838         while (currentElementChild) {
22839             // Formatting code (indent the tree so it looks nice on the screen)
22840             var nopad = nopadtext;
22841             if (lastnode == 'SPAN') {
22842                 nopad  = true;
22843             }
22844             // text
22845             if  (currentElementChild.nodeName == '#text') {
22846                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22847                 toadd = nopadtext ? toadd : toadd.trim();
22848                 if (!nopad && toadd.length > 80) {
22849                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22850                 }
22851                 innerHTML  += toadd;
22852                 
22853                 i++;
22854                 currentElementChild = currentElement.childNodes.item(i);
22855                 lastNode = '';
22856                 continue;
22857             }
22858             allText = false;
22859             
22860             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22861                 
22862             // Recursively traverse the tree structure of the child node
22863             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22864             lastnode = currentElementChild.nodeName;
22865             i++;
22866             currentElementChild=currentElement.childNodes.item(i);
22867         }
22868         
22869         ret += innerHTML;
22870         
22871         if (!allText) {
22872                 // The remaining code is mostly for formatting the tree
22873             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22874         }
22875         
22876         
22877         if (tagName) {
22878             ret+= "</"+tagName+">";
22879         }
22880         return ret;
22881         
22882     },
22883         
22884     applyBlacklists : function()
22885     {
22886         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22887         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22888         
22889         this.white = [];
22890         this.black = [];
22891         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22892             if (b.indexOf(tag) > -1) {
22893                 return;
22894             }
22895             this.white.push(tag);
22896             
22897         }, this);
22898         
22899         Roo.each(w, function(tag) {
22900             if (b.indexOf(tag) > -1) {
22901                 return;
22902             }
22903             if (this.white.indexOf(tag) > -1) {
22904                 return;
22905             }
22906             this.white.push(tag);
22907             
22908         }, this);
22909         
22910         
22911         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22912             if (w.indexOf(tag) > -1) {
22913                 return;
22914             }
22915             this.black.push(tag);
22916             
22917         }, this);
22918         
22919         Roo.each(b, function(tag) {
22920             if (w.indexOf(tag) > -1) {
22921                 return;
22922             }
22923             if (this.black.indexOf(tag) > -1) {
22924                 return;
22925             }
22926             this.black.push(tag);
22927             
22928         }, this);
22929         
22930         
22931         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22932         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22933         
22934         this.cwhite = [];
22935         this.cblack = [];
22936         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22937             if (b.indexOf(tag) > -1) {
22938                 return;
22939             }
22940             this.cwhite.push(tag);
22941             
22942         }, this);
22943         
22944         Roo.each(w, function(tag) {
22945             if (b.indexOf(tag) > -1) {
22946                 return;
22947             }
22948             if (this.cwhite.indexOf(tag) > -1) {
22949                 return;
22950             }
22951             this.cwhite.push(tag);
22952             
22953         }, this);
22954         
22955         
22956         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22957             if (w.indexOf(tag) > -1) {
22958                 return;
22959             }
22960             this.cblack.push(tag);
22961             
22962         }, this);
22963         
22964         Roo.each(b, function(tag) {
22965             if (w.indexOf(tag) > -1) {
22966                 return;
22967             }
22968             if (this.cblack.indexOf(tag) > -1) {
22969                 return;
22970             }
22971             this.cblack.push(tag);
22972             
22973         }, this);
22974     },
22975     
22976     setStylesheets : function(stylesheets)
22977     {
22978         if(typeof(stylesheets) == 'string'){
22979             Roo.get(this.iframe.contentDocument.head).createChild({
22980                 tag : 'link',
22981                 rel : 'stylesheet',
22982                 type : 'text/css',
22983                 href : stylesheets
22984             });
22985             
22986             return;
22987         }
22988         var _this = this;
22989      
22990         Roo.each(stylesheets, function(s) {
22991             if(!s.length){
22992                 return;
22993             }
22994             
22995             Roo.get(_this.iframe.contentDocument.head).createChild({
22996                 tag : 'link',
22997                 rel : 'stylesheet',
22998                 type : 'text/css',
22999                 href : s
23000             });
23001         });
23002
23003         
23004     },
23005     
23006     removeStylesheets : function()
23007     {
23008         var _this = this;
23009         
23010         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23011             s.remove();
23012         });
23013     },
23014     
23015     setStyle : function(style)
23016     {
23017         Roo.get(this.iframe.contentDocument.head).createChild({
23018             tag : 'style',
23019             type : 'text/css',
23020             html : style
23021         });
23022
23023         return;
23024     }
23025     
23026     // hide stuff that is not compatible
23027     /**
23028      * @event blur
23029      * @hide
23030      */
23031     /**
23032      * @event change
23033      * @hide
23034      */
23035     /**
23036      * @event focus
23037      * @hide
23038      */
23039     /**
23040      * @event specialkey
23041      * @hide
23042      */
23043     /**
23044      * @cfg {String} fieldClass @hide
23045      */
23046     /**
23047      * @cfg {String} focusClass @hide
23048      */
23049     /**
23050      * @cfg {String} autoCreate @hide
23051      */
23052     /**
23053      * @cfg {String} inputType @hide
23054      */
23055     /**
23056      * @cfg {String} invalidClass @hide
23057      */
23058     /**
23059      * @cfg {String} invalidText @hide
23060      */
23061     /**
23062      * @cfg {String} msgFx @hide
23063      */
23064     /**
23065      * @cfg {String} validateOnBlur @hide
23066      */
23067 });
23068
23069 Roo.HtmlEditorCore.white = [
23070         'area', 'br', 'img', 'input', 'hr', 'wbr',
23071         
23072        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23073        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23074        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23075        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23076        'table',   'ul',         'xmp', 
23077        
23078        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23079       'thead',   'tr', 
23080      
23081       'dir', 'menu', 'ol', 'ul', 'dl',
23082        
23083       'embed',  'object'
23084 ];
23085
23086
23087 Roo.HtmlEditorCore.black = [
23088     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23089         'applet', // 
23090         'base',   'basefont', 'bgsound', 'blink',  'body', 
23091         'frame',  'frameset', 'head',    'html',   'ilayer', 
23092         'iframe', 'layer',  'link',     'meta',    'object',   
23093         'script', 'style' ,'title',  'xml' // clean later..
23094 ];
23095 Roo.HtmlEditorCore.clean = [
23096     'script', 'style', 'title', 'xml'
23097 ];
23098 Roo.HtmlEditorCore.remove = [
23099     'font'
23100 ];
23101 // attributes..
23102
23103 Roo.HtmlEditorCore.ablack = [
23104     'on'
23105 ];
23106     
23107 Roo.HtmlEditorCore.aclean = [ 
23108     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23109 ];
23110
23111 // protocols..
23112 Roo.HtmlEditorCore.pwhite= [
23113         'http',  'https',  'mailto'
23114 ];
23115
23116 // white listed style attributes.
23117 Roo.HtmlEditorCore.cwhite= [
23118       //  'text-align', /// default is to allow most things..
23119       
23120          
23121 //        'font-size'//??
23122 ];
23123
23124 // black listed style attributes.
23125 Roo.HtmlEditorCore.cblack= [
23126       //  'font-size' -- this can be set by the project 
23127 ];
23128
23129
23130 Roo.HtmlEditorCore.swapCodes   =[ 
23131     [    8211, "--" ], 
23132     [    8212, "--" ], 
23133     [    8216,  "'" ],  
23134     [    8217, "'" ],  
23135     [    8220, '"' ],  
23136     [    8221, '"' ],  
23137     [    8226, "*" ],  
23138     [    8230, "..." ]
23139 ]; 
23140
23141     /*
23142  * - LGPL
23143  *
23144  * HtmlEditor
23145  * 
23146  */
23147
23148 /**
23149  * @class Roo.bootstrap.HtmlEditor
23150  * @extends Roo.bootstrap.TextArea
23151  * Bootstrap HtmlEditor class
23152
23153  * @constructor
23154  * Create a new HtmlEditor
23155  * @param {Object} config The config object
23156  */
23157
23158 Roo.bootstrap.HtmlEditor = function(config){
23159     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23160     if (!this.toolbars) {
23161         this.toolbars = [];
23162     }
23163     
23164     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23165     this.addEvents({
23166             /**
23167              * @event initialize
23168              * Fires when the editor is fully initialized (including the iframe)
23169              * @param {HtmlEditor} this
23170              */
23171             initialize: true,
23172             /**
23173              * @event activate
23174              * Fires when the editor is first receives the focus. Any insertion must wait
23175              * until after this event.
23176              * @param {HtmlEditor} this
23177              */
23178             activate: true,
23179              /**
23180              * @event beforesync
23181              * Fires before the textarea is updated with content from the editor iframe. Return false
23182              * to cancel the sync.
23183              * @param {HtmlEditor} this
23184              * @param {String} html
23185              */
23186             beforesync: true,
23187              /**
23188              * @event beforepush
23189              * Fires before the iframe editor is updated with content from the textarea. Return false
23190              * to cancel the push.
23191              * @param {HtmlEditor} this
23192              * @param {String} html
23193              */
23194             beforepush: true,
23195              /**
23196              * @event sync
23197              * Fires when the textarea is updated with content from the editor iframe.
23198              * @param {HtmlEditor} this
23199              * @param {String} html
23200              */
23201             sync: true,
23202              /**
23203              * @event push
23204              * Fires when the iframe editor is updated with content from the textarea.
23205              * @param {HtmlEditor} this
23206              * @param {String} html
23207              */
23208             push: true,
23209              /**
23210              * @event editmodechange
23211              * Fires when the editor switches edit modes
23212              * @param {HtmlEditor} this
23213              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23214              */
23215             editmodechange: true,
23216             /**
23217              * @event editorevent
23218              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23219              * @param {HtmlEditor} this
23220              */
23221             editorevent: true,
23222             /**
23223              * @event firstfocus
23224              * Fires when on first focus - needed by toolbars..
23225              * @param {HtmlEditor} this
23226              */
23227             firstfocus: true,
23228             /**
23229              * @event autosave
23230              * Auto save the htmlEditor value as a file into Events
23231              * @param {HtmlEditor} this
23232              */
23233             autosave: true,
23234             /**
23235              * @event savedpreview
23236              * preview the saved version of htmlEditor
23237              * @param {HtmlEditor} this
23238              */
23239             savedpreview: true
23240         });
23241 };
23242
23243
23244 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23245     
23246     
23247       /**
23248      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23249      */
23250     toolbars : false,
23251     
23252      /**
23253     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23254     */
23255     btns : [],
23256    
23257      /**
23258      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23259      *                        Roo.resizable.
23260      */
23261     resizable : false,
23262      /**
23263      * @cfg {Number} height (in pixels)
23264      */   
23265     height: 300,
23266    /**
23267      * @cfg {Number} width (in pixels)
23268      */   
23269     width: false,
23270     
23271     /**
23272      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23273      * 
23274      */
23275     stylesheets: false,
23276     
23277     // id of frame..
23278     frameId: false,
23279     
23280     // private properties
23281     validationEvent : false,
23282     deferHeight: true,
23283     initialized : false,
23284     activated : false,
23285     
23286     onFocus : Roo.emptyFn,
23287     iframePad:3,
23288     hideMode:'offsets',
23289     
23290     tbContainer : false,
23291     
23292     bodyCls : '',
23293     
23294     toolbarContainer :function() {
23295         return this.wrap.select('.x-html-editor-tb',true).first();
23296     },
23297
23298     /**
23299      * Protected method that will not generally be called directly. It
23300      * is called when the editor creates its toolbar. Override this method if you need to
23301      * add custom toolbar buttons.
23302      * @param {HtmlEditor} editor
23303      */
23304     createToolbar : function(){
23305         Roo.log('renewing');
23306         Roo.log("create toolbars");
23307         
23308         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23309         this.toolbars[0].render(this.toolbarContainer());
23310         
23311         return;
23312         
23313 //        if (!editor.toolbars || !editor.toolbars.length) {
23314 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23315 //        }
23316 //        
23317 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23318 //            editor.toolbars[i] = Roo.factory(
23319 //                    typeof(editor.toolbars[i]) == 'string' ?
23320 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23321 //                Roo.bootstrap.HtmlEditor);
23322 //            editor.toolbars[i].init(editor);
23323 //        }
23324     },
23325
23326      
23327     // private
23328     onRender : function(ct, position)
23329     {
23330        // Roo.log("Call onRender: " + this.xtype);
23331         var _t = this;
23332         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23333       
23334         this.wrap = this.inputEl().wrap({
23335             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23336         });
23337         
23338         this.editorcore.onRender(ct, position);
23339          
23340         if (this.resizable) {
23341             this.resizeEl = new Roo.Resizable(this.wrap, {
23342                 pinned : true,
23343                 wrap: true,
23344                 dynamic : true,
23345                 minHeight : this.height,
23346                 height: this.height,
23347                 handles : this.resizable,
23348                 width: this.width,
23349                 listeners : {
23350                     resize : function(r, w, h) {
23351                         _t.onResize(w,h); // -something
23352                     }
23353                 }
23354             });
23355             
23356         }
23357         this.createToolbar(this);
23358        
23359         
23360         if(!this.width && this.resizable){
23361             this.setSize(this.wrap.getSize());
23362         }
23363         if (this.resizeEl) {
23364             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23365             // should trigger onReize..
23366         }
23367         
23368     },
23369
23370     // private
23371     onResize : function(w, h)
23372     {
23373         Roo.log('resize: ' +w + ',' + h );
23374         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23375         var ew = false;
23376         var eh = false;
23377         
23378         if(this.inputEl() ){
23379             if(typeof w == 'number'){
23380                 var aw = w - this.wrap.getFrameWidth('lr');
23381                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23382                 ew = aw;
23383             }
23384             if(typeof h == 'number'){
23385                  var tbh = -11;  // fixme it needs to tool bar size!
23386                 for (var i =0; i < this.toolbars.length;i++) {
23387                     // fixme - ask toolbars for heights?
23388                     tbh += this.toolbars[i].el.getHeight();
23389                     //if (this.toolbars[i].footer) {
23390                     //    tbh += this.toolbars[i].footer.el.getHeight();
23391                     //}
23392                 }
23393               
23394                 
23395                 
23396                 
23397                 
23398                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23399                 ah -= 5; // knock a few pixes off for look..
23400                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23401                 var eh = ah;
23402             }
23403         }
23404         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23405         this.editorcore.onResize(ew,eh);
23406         
23407     },
23408
23409     /**
23410      * Toggles the editor between standard and source edit mode.
23411      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23412      */
23413     toggleSourceEdit : function(sourceEditMode)
23414     {
23415         this.editorcore.toggleSourceEdit(sourceEditMode);
23416         
23417         if(this.editorcore.sourceEditMode){
23418             Roo.log('editor - showing textarea');
23419             
23420 //            Roo.log('in');
23421 //            Roo.log(this.syncValue());
23422             this.syncValue();
23423             this.inputEl().removeClass(['hide', 'x-hidden']);
23424             this.inputEl().dom.removeAttribute('tabIndex');
23425             this.inputEl().focus();
23426         }else{
23427             Roo.log('editor - hiding textarea');
23428 //            Roo.log('out')
23429 //            Roo.log(this.pushValue()); 
23430             this.pushValue();
23431             
23432             this.inputEl().addClass(['hide', 'x-hidden']);
23433             this.inputEl().dom.setAttribute('tabIndex', -1);
23434             //this.deferFocus();
23435         }
23436          
23437         if(this.resizable){
23438             this.setSize(this.wrap.getSize());
23439         }
23440         
23441         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23442     },
23443  
23444     // private (for BoxComponent)
23445     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23446
23447     // private (for BoxComponent)
23448     getResizeEl : function(){
23449         return this.wrap;
23450     },
23451
23452     // private (for BoxComponent)
23453     getPositionEl : function(){
23454         return this.wrap;
23455     },
23456
23457     // private
23458     initEvents : function(){
23459         this.originalValue = this.getValue();
23460     },
23461
23462 //    /**
23463 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23464 //     * @method
23465 //     */
23466 //    markInvalid : Roo.emptyFn,
23467 //    /**
23468 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23469 //     * @method
23470 //     */
23471 //    clearInvalid : Roo.emptyFn,
23472
23473     setValue : function(v){
23474         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23475         this.editorcore.pushValue();
23476     },
23477
23478      
23479     // private
23480     deferFocus : function(){
23481         this.focus.defer(10, this);
23482     },
23483
23484     // doc'ed in Field
23485     focus : function(){
23486         this.editorcore.focus();
23487         
23488     },
23489       
23490
23491     // private
23492     onDestroy : function(){
23493         
23494         
23495         
23496         if(this.rendered){
23497             
23498             for (var i =0; i < this.toolbars.length;i++) {
23499                 // fixme - ask toolbars for heights?
23500                 this.toolbars[i].onDestroy();
23501             }
23502             
23503             this.wrap.dom.innerHTML = '';
23504             this.wrap.remove();
23505         }
23506     },
23507
23508     // private
23509     onFirstFocus : function(){
23510         //Roo.log("onFirstFocus");
23511         this.editorcore.onFirstFocus();
23512          for (var i =0; i < this.toolbars.length;i++) {
23513             this.toolbars[i].onFirstFocus();
23514         }
23515         
23516     },
23517     
23518     // private
23519     syncValue : function()
23520     {   
23521         this.editorcore.syncValue();
23522     },
23523     
23524     pushValue : function()
23525     {   
23526         this.editorcore.pushValue();
23527     }
23528      
23529     
23530     // hide stuff that is not compatible
23531     /**
23532      * @event blur
23533      * @hide
23534      */
23535     /**
23536      * @event change
23537      * @hide
23538      */
23539     /**
23540      * @event focus
23541      * @hide
23542      */
23543     /**
23544      * @event specialkey
23545      * @hide
23546      */
23547     /**
23548      * @cfg {String} fieldClass @hide
23549      */
23550     /**
23551      * @cfg {String} focusClass @hide
23552      */
23553     /**
23554      * @cfg {String} autoCreate @hide
23555      */
23556     /**
23557      * @cfg {String} inputType @hide
23558      */
23559     /**
23560      * @cfg {String} invalidClass @hide
23561      */
23562     /**
23563      * @cfg {String} invalidText @hide
23564      */
23565     /**
23566      * @cfg {String} msgFx @hide
23567      */
23568     /**
23569      * @cfg {String} validateOnBlur @hide
23570      */
23571 });
23572  
23573     
23574    
23575    
23576    
23577       
23578 Roo.namespace('Roo.bootstrap.htmleditor');
23579 /**
23580  * @class Roo.bootstrap.HtmlEditorToolbar1
23581  * Basic Toolbar
23582  * 
23583  * Usage:
23584  *
23585  new Roo.bootstrap.HtmlEditor({
23586     ....
23587     toolbars : [
23588         new Roo.bootstrap.HtmlEditorToolbar1({
23589             disable : { fonts: 1 , format: 1, ..., ... , ...],
23590             btns : [ .... ]
23591         })
23592     }
23593      
23594  * 
23595  * @cfg {Object} disable List of elements to disable..
23596  * @cfg {Array} btns List of additional buttons.
23597  * 
23598  * 
23599  * NEEDS Extra CSS? 
23600  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23601  */
23602  
23603 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23604 {
23605     
23606     Roo.apply(this, config);
23607     
23608     // default disabled, based on 'good practice'..
23609     this.disable = this.disable || {};
23610     Roo.applyIf(this.disable, {
23611         fontSize : true,
23612         colors : true,
23613         specialElements : true
23614     });
23615     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23616     
23617     this.editor = config.editor;
23618     this.editorcore = config.editor.editorcore;
23619     
23620     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23621     
23622     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23623     // dont call parent... till later.
23624 }
23625 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23626      
23627     bar : true,
23628     
23629     editor : false,
23630     editorcore : false,
23631     
23632     
23633     formats : [
23634         "p" ,  
23635         "h1","h2","h3","h4","h5","h6", 
23636         "pre", "code", 
23637         "abbr", "acronym", "address", "cite", "samp", "var",
23638         'div','span'
23639     ],
23640     
23641     onRender : function(ct, position)
23642     {
23643        // Roo.log("Call onRender: " + this.xtype);
23644         
23645        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23646        Roo.log(this.el);
23647        this.el.dom.style.marginBottom = '0';
23648        var _this = this;
23649        var editorcore = this.editorcore;
23650        var editor= this.editor;
23651        
23652        var children = [];
23653        var btn = function(id,cmd , toggle, handler, html){
23654        
23655             var  event = toggle ? 'toggle' : 'click';
23656        
23657             var a = {
23658                 size : 'sm',
23659                 xtype: 'Button',
23660                 xns: Roo.bootstrap,
23661                 glyphicon : id,
23662                 cmd : id || cmd,
23663                 enableToggle:toggle !== false,
23664                 html : html || '',
23665                 pressed : toggle ? false : null,
23666                 listeners : {}
23667             };
23668             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23669                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23670             };
23671             children.push(a);
23672             return a;
23673        }
23674        
23675     //    var cb_box = function...
23676         
23677         var style = {
23678                 xtype: 'Button',
23679                 size : 'sm',
23680                 xns: Roo.bootstrap,
23681                 glyphicon : 'font',
23682                 //html : 'submit'
23683                 menu : {
23684                     xtype: 'Menu',
23685                     xns: Roo.bootstrap,
23686                     items:  []
23687                 }
23688         };
23689         Roo.each(this.formats, function(f) {
23690             style.menu.items.push({
23691                 xtype :'MenuItem',
23692                 xns: Roo.bootstrap,
23693                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23694                 tagname : f,
23695                 listeners : {
23696                     click : function()
23697                     {
23698                         editorcore.insertTag(this.tagname);
23699                         editor.focus();
23700                     }
23701                 }
23702                 
23703             });
23704         });
23705         children.push(style);   
23706         
23707         btn('bold',false,true);
23708         btn('italic',false,true);
23709         btn('align-left', 'justifyleft',true);
23710         btn('align-center', 'justifycenter',true);
23711         btn('align-right' , 'justifyright',true);
23712         btn('link', false, false, function(btn) {
23713             //Roo.log("create link?");
23714             var url = prompt(this.createLinkText, this.defaultLinkValue);
23715             if(url && url != 'http:/'+'/'){
23716                 this.editorcore.relayCmd('createlink', url);
23717             }
23718         }),
23719         btn('list','insertunorderedlist',true);
23720         btn('pencil', false,true, function(btn){
23721                 Roo.log(this);
23722                 this.toggleSourceEdit(btn.pressed);
23723         });
23724         
23725         if (this.editor.btns.length > 0) {
23726             for (var i = 0; i<this.editor.btns.length; i++) {
23727                 children.push(this.editor.btns[i]);
23728             }
23729         }
23730         
23731         /*
23732         var cog = {
23733                 xtype: 'Button',
23734                 size : 'sm',
23735                 xns: Roo.bootstrap,
23736                 glyphicon : 'cog',
23737                 //html : 'submit'
23738                 menu : {
23739                     xtype: 'Menu',
23740                     xns: Roo.bootstrap,
23741                     items:  []
23742                 }
23743         };
23744         
23745         cog.menu.items.push({
23746             xtype :'MenuItem',
23747             xns: Roo.bootstrap,
23748             html : Clean styles,
23749             tagname : f,
23750             listeners : {
23751                 click : function()
23752                 {
23753                     editorcore.insertTag(this.tagname);
23754                     editor.focus();
23755                 }
23756             }
23757             
23758         });
23759        */
23760         
23761          
23762        this.xtype = 'NavSimplebar';
23763         
23764         for(var i=0;i< children.length;i++) {
23765             
23766             this.buttons.add(this.addxtypeChild(children[i]));
23767             
23768         }
23769         
23770         editor.on('editorevent', this.updateToolbar, this);
23771     },
23772     onBtnClick : function(id)
23773     {
23774        this.editorcore.relayCmd(id);
23775        this.editorcore.focus();
23776     },
23777     
23778     /**
23779      * Protected method that will not generally be called directly. It triggers
23780      * a toolbar update by reading the markup state of the current selection in the editor.
23781      */
23782     updateToolbar: function(){
23783
23784         if(!this.editorcore.activated){
23785             this.editor.onFirstFocus(); // is this neeed?
23786             return;
23787         }
23788
23789         var btns = this.buttons; 
23790         var doc = this.editorcore.doc;
23791         btns.get('bold').setActive(doc.queryCommandState('bold'));
23792         btns.get('italic').setActive(doc.queryCommandState('italic'));
23793         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23794         
23795         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23796         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23797         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23798         
23799         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23800         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23801          /*
23802         
23803         var ans = this.editorcore.getAllAncestors();
23804         if (this.formatCombo) {
23805             
23806             
23807             var store = this.formatCombo.store;
23808             this.formatCombo.setValue("");
23809             for (var i =0; i < ans.length;i++) {
23810                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23811                     // select it..
23812                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23813                     break;
23814                 }
23815             }
23816         }
23817         
23818         
23819         
23820         // hides menus... - so this cant be on a menu...
23821         Roo.bootstrap.MenuMgr.hideAll();
23822         */
23823         Roo.bootstrap.MenuMgr.hideAll();
23824         //this.editorsyncValue();
23825     },
23826     onFirstFocus: function() {
23827         this.buttons.each(function(item){
23828            item.enable();
23829         });
23830     },
23831     toggleSourceEdit : function(sourceEditMode){
23832         
23833           
23834         if(sourceEditMode){
23835             Roo.log("disabling buttons");
23836            this.buttons.each( function(item){
23837                 if(item.cmd != 'pencil'){
23838                     item.disable();
23839                 }
23840             });
23841           
23842         }else{
23843             Roo.log("enabling buttons");
23844             if(this.editorcore.initialized){
23845                 this.buttons.each( function(item){
23846                     item.enable();
23847                 });
23848             }
23849             
23850         }
23851         Roo.log("calling toggole on editor");
23852         // tell the editor that it's been pressed..
23853         this.editor.toggleSourceEdit(sourceEditMode);
23854        
23855     }
23856 });
23857
23858
23859
23860
23861
23862 /**
23863  * @class Roo.bootstrap.Table.AbstractSelectionModel
23864  * @extends Roo.util.Observable
23865  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23866  * implemented by descendant classes.  This class should not be directly instantiated.
23867  * @constructor
23868  */
23869 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23870     this.locked = false;
23871     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23872 };
23873
23874
23875 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23876     /** @ignore Called by the grid automatically. Do not call directly. */
23877     init : function(grid){
23878         this.grid = grid;
23879         this.initEvents();
23880     },
23881
23882     /**
23883      * Locks the selections.
23884      */
23885     lock : function(){
23886         this.locked = true;
23887     },
23888
23889     /**
23890      * Unlocks the selections.
23891      */
23892     unlock : function(){
23893         this.locked = false;
23894     },
23895
23896     /**
23897      * Returns true if the selections are locked.
23898      * @return {Boolean}
23899      */
23900     isLocked : function(){
23901         return this.locked;
23902     }
23903 });
23904 /**
23905  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23906  * @class Roo.bootstrap.Table.RowSelectionModel
23907  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23908  * It supports multiple selections and keyboard selection/navigation. 
23909  * @constructor
23910  * @param {Object} config
23911  */
23912
23913 Roo.bootstrap.Table.RowSelectionModel = function(config){
23914     Roo.apply(this, config);
23915     this.selections = new Roo.util.MixedCollection(false, function(o){
23916         return o.id;
23917     });
23918
23919     this.last = false;
23920     this.lastActive = false;
23921
23922     this.addEvents({
23923         /**
23924              * @event selectionchange
23925              * Fires when the selection changes
23926              * @param {SelectionModel} this
23927              */
23928             "selectionchange" : true,
23929         /**
23930              * @event afterselectionchange
23931              * Fires after the selection changes (eg. by key press or clicking)
23932              * @param {SelectionModel} this
23933              */
23934             "afterselectionchange" : true,
23935         /**
23936              * @event beforerowselect
23937              * Fires when a row is selected being selected, return false to cancel.
23938              * @param {SelectionModel} this
23939              * @param {Number} rowIndex The selected index
23940              * @param {Boolean} keepExisting False if other selections will be cleared
23941              */
23942             "beforerowselect" : true,
23943         /**
23944              * @event rowselect
23945              * Fires when a row is selected.
23946              * @param {SelectionModel} this
23947              * @param {Number} rowIndex The selected index
23948              * @param {Roo.data.Record} r The record
23949              */
23950             "rowselect" : true,
23951         /**
23952              * @event rowdeselect
23953              * Fires when a row is deselected.
23954              * @param {SelectionModel} this
23955              * @param {Number} rowIndex The selected index
23956              */
23957         "rowdeselect" : true
23958     });
23959     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23960     this.locked = false;
23961  };
23962
23963 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23964     /**
23965      * @cfg {Boolean} singleSelect
23966      * True to allow selection of only one row at a time (defaults to false)
23967      */
23968     singleSelect : false,
23969
23970     // private
23971     initEvents : function()
23972     {
23973
23974         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23975         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23976         //}else{ // allow click to work like normal
23977          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23978         //}
23979         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23980         this.grid.on("rowclick", this.handleMouseDown, this);
23981         
23982         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23983             "up" : function(e){
23984                 if(!e.shiftKey){
23985                     this.selectPrevious(e.shiftKey);
23986                 }else if(this.last !== false && this.lastActive !== false){
23987                     var last = this.last;
23988                     this.selectRange(this.last,  this.lastActive-1);
23989                     this.grid.getView().focusRow(this.lastActive);
23990                     if(last !== false){
23991                         this.last = last;
23992                     }
23993                 }else{
23994                     this.selectFirstRow();
23995                 }
23996                 this.fireEvent("afterselectionchange", this);
23997             },
23998             "down" : function(e){
23999                 if(!e.shiftKey){
24000                     this.selectNext(e.shiftKey);
24001                 }else if(this.last !== false && this.lastActive !== false){
24002                     var last = this.last;
24003                     this.selectRange(this.last,  this.lastActive+1);
24004                     this.grid.getView().focusRow(this.lastActive);
24005                     if(last !== false){
24006                         this.last = last;
24007                     }
24008                 }else{
24009                     this.selectFirstRow();
24010                 }
24011                 this.fireEvent("afterselectionchange", this);
24012             },
24013             scope: this
24014         });
24015         this.grid.store.on('load', function(){
24016             this.selections.clear();
24017         },this);
24018         /*
24019         var view = this.grid.view;
24020         view.on("refresh", this.onRefresh, this);
24021         view.on("rowupdated", this.onRowUpdated, this);
24022         view.on("rowremoved", this.onRemove, this);
24023         */
24024     },
24025
24026     // private
24027     onRefresh : function()
24028     {
24029         var ds = this.grid.store, i, v = this.grid.view;
24030         var s = this.selections;
24031         s.each(function(r){
24032             if((i = ds.indexOfId(r.id)) != -1){
24033                 v.onRowSelect(i);
24034             }else{
24035                 s.remove(r);
24036             }
24037         });
24038     },
24039
24040     // private
24041     onRemove : function(v, index, r){
24042         this.selections.remove(r);
24043     },
24044
24045     // private
24046     onRowUpdated : function(v, index, r){
24047         if(this.isSelected(r)){
24048             v.onRowSelect(index);
24049         }
24050     },
24051
24052     /**
24053      * Select records.
24054      * @param {Array} records The records to select
24055      * @param {Boolean} keepExisting (optional) True to keep existing selections
24056      */
24057     selectRecords : function(records, keepExisting)
24058     {
24059         if(!keepExisting){
24060             this.clearSelections();
24061         }
24062             var ds = this.grid.store;
24063         for(var i = 0, len = records.length; i < len; i++){
24064             this.selectRow(ds.indexOf(records[i]), true);
24065         }
24066     },
24067
24068     /**
24069      * Gets the number of selected rows.
24070      * @return {Number}
24071      */
24072     getCount : function(){
24073         return this.selections.length;
24074     },
24075
24076     /**
24077      * Selects the first row in the grid.
24078      */
24079     selectFirstRow : function(){
24080         this.selectRow(0);
24081     },
24082
24083     /**
24084      * Select the last row.
24085      * @param {Boolean} keepExisting (optional) True to keep existing selections
24086      */
24087     selectLastRow : function(keepExisting){
24088         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24089         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24090     },
24091
24092     /**
24093      * Selects the row immediately following the last selected row.
24094      * @param {Boolean} keepExisting (optional) True to keep existing selections
24095      */
24096     selectNext : function(keepExisting)
24097     {
24098             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24099             this.selectRow(this.last+1, keepExisting);
24100             this.grid.getView().focusRow(this.last);
24101         }
24102     },
24103
24104     /**
24105      * Selects the row that precedes the last selected row.
24106      * @param {Boolean} keepExisting (optional) True to keep existing selections
24107      */
24108     selectPrevious : function(keepExisting){
24109         if(this.last){
24110             this.selectRow(this.last-1, keepExisting);
24111             this.grid.getView().focusRow(this.last);
24112         }
24113     },
24114
24115     /**
24116      * Returns the selected records
24117      * @return {Array} Array of selected records
24118      */
24119     getSelections : function(){
24120         return [].concat(this.selections.items);
24121     },
24122
24123     /**
24124      * Returns the first selected record.
24125      * @return {Record}
24126      */
24127     getSelected : function(){
24128         return this.selections.itemAt(0);
24129     },
24130
24131
24132     /**
24133      * Clears all selections.
24134      */
24135     clearSelections : function(fast)
24136     {
24137         if(this.locked) {
24138             return;
24139         }
24140         if(fast !== true){
24141                 var ds = this.grid.store;
24142             var s = this.selections;
24143             s.each(function(r){
24144                 this.deselectRow(ds.indexOfId(r.id));
24145             }, this);
24146             s.clear();
24147         }else{
24148             this.selections.clear();
24149         }
24150         this.last = false;
24151     },
24152
24153
24154     /**
24155      * Selects all rows.
24156      */
24157     selectAll : function(){
24158         if(this.locked) {
24159             return;
24160         }
24161         this.selections.clear();
24162         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24163             this.selectRow(i, true);
24164         }
24165     },
24166
24167     /**
24168      * Returns True if there is a selection.
24169      * @return {Boolean}
24170      */
24171     hasSelection : function(){
24172         return this.selections.length > 0;
24173     },
24174
24175     /**
24176      * Returns True if the specified row is selected.
24177      * @param {Number/Record} record The record or index of the record to check
24178      * @return {Boolean}
24179      */
24180     isSelected : function(index){
24181             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24182         return (r && this.selections.key(r.id) ? true : false);
24183     },
24184
24185     /**
24186      * Returns True if the specified record id is selected.
24187      * @param {String} id The id of record to check
24188      * @return {Boolean}
24189      */
24190     isIdSelected : function(id){
24191         return (this.selections.key(id) ? true : false);
24192     },
24193
24194
24195     // private
24196     handleMouseDBClick : function(e, t){
24197         
24198     },
24199     // private
24200     handleMouseDown : function(e, t)
24201     {
24202             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24203         if(this.isLocked() || rowIndex < 0 ){
24204             return;
24205         };
24206         if(e.shiftKey && this.last !== false){
24207             var last = this.last;
24208             this.selectRange(last, rowIndex, e.ctrlKey);
24209             this.last = last; // reset the last
24210             t.focus();
24211     
24212         }else{
24213             var isSelected = this.isSelected(rowIndex);
24214             //Roo.log("select row:" + rowIndex);
24215             if(isSelected){
24216                 this.deselectRow(rowIndex);
24217             } else {
24218                         this.selectRow(rowIndex, true);
24219             }
24220     
24221             /*
24222                 if(e.button !== 0 && isSelected){
24223                 alert('rowIndex 2: ' + rowIndex);
24224                     view.focusRow(rowIndex);
24225                 }else if(e.ctrlKey && isSelected){
24226                     this.deselectRow(rowIndex);
24227                 }else if(!isSelected){
24228                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24229                     view.focusRow(rowIndex);
24230                 }
24231             */
24232         }
24233         this.fireEvent("afterselectionchange", this);
24234     },
24235     // private
24236     handleDragableRowClick :  function(grid, rowIndex, e) 
24237     {
24238         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24239             this.selectRow(rowIndex, false);
24240             grid.view.focusRow(rowIndex);
24241              this.fireEvent("afterselectionchange", this);
24242         }
24243     },
24244     
24245     /**
24246      * Selects multiple rows.
24247      * @param {Array} rows Array of the indexes of the row to select
24248      * @param {Boolean} keepExisting (optional) True to keep existing selections
24249      */
24250     selectRows : function(rows, keepExisting){
24251         if(!keepExisting){
24252             this.clearSelections();
24253         }
24254         for(var i = 0, len = rows.length; i < len; i++){
24255             this.selectRow(rows[i], true);
24256         }
24257     },
24258
24259     /**
24260      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24261      * @param {Number} startRow The index of the first row in the range
24262      * @param {Number} endRow The index of the last row in the range
24263      * @param {Boolean} keepExisting (optional) True to retain existing selections
24264      */
24265     selectRange : function(startRow, endRow, keepExisting){
24266         if(this.locked) {
24267             return;
24268         }
24269         if(!keepExisting){
24270             this.clearSelections();
24271         }
24272         if(startRow <= endRow){
24273             for(var i = startRow; i <= endRow; i++){
24274                 this.selectRow(i, true);
24275             }
24276         }else{
24277             for(var i = startRow; i >= endRow; i--){
24278                 this.selectRow(i, true);
24279             }
24280         }
24281     },
24282
24283     /**
24284      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24285      * @param {Number} startRow The index of the first row in the range
24286      * @param {Number} endRow The index of the last row in the range
24287      */
24288     deselectRange : function(startRow, endRow, preventViewNotify){
24289         if(this.locked) {
24290             return;
24291         }
24292         for(var i = startRow; i <= endRow; i++){
24293             this.deselectRow(i, preventViewNotify);
24294         }
24295     },
24296
24297     /**
24298      * Selects a row.
24299      * @param {Number} row The index of the row to select
24300      * @param {Boolean} keepExisting (optional) True to keep existing selections
24301      */
24302     selectRow : function(index, keepExisting, preventViewNotify)
24303     {
24304             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24305             return;
24306         }
24307         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24308             if(!keepExisting || this.singleSelect){
24309                 this.clearSelections();
24310             }
24311             
24312             var r = this.grid.store.getAt(index);
24313             //console.log('selectRow - record id :' + r.id);
24314             
24315             this.selections.add(r);
24316             this.last = this.lastActive = index;
24317             if(!preventViewNotify){
24318                 var proxy = new Roo.Element(
24319                                 this.grid.getRowDom(index)
24320                 );
24321                 proxy.addClass('bg-info info');
24322             }
24323             this.fireEvent("rowselect", this, index, r);
24324             this.fireEvent("selectionchange", this);
24325         }
24326     },
24327
24328     /**
24329      * Deselects a row.
24330      * @param {Number} row The index of the row to deselect
24331      */
24332     deselectRow : function(index, preventViewNotify)
24333     {
24334         if(this.locked) {
24335             return;
24336         }
24337         if(this.last == index){
24338             this.last = false;
24339         }
24340         if(this.lastActive == index){
24341             this.lastActive = false;
24342         }
24343         
24344         var r = this.grid.store.getAt(index);
24345         if (!r) {
24346             return;
24347         }
24348         
24349         this.selections.remove(r);
24350         //.console.log('deselectRow - record id :' + r.id);
24351         if(!preventViewNotify){
24352         
24353             var proxy = new Roo.Element(
24354                 this.grid.getRowDom(index)
24355             );
24356             proxy.removeClass('bg-info info');
24357         }
24358         this.fireEvent("rowdeselect", this, index);
24359         this.fireEvent("selectionchange", this);
24360     },
24361
24362     // private
24363     restoreLast : function(){
24364         if(this._last){
24365             this.last = this._last;
24366         }
24367     },
24368
24369     // private
24370     acceptsNav : function(row, col, cm){
24371         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24372     },
24373
24374     // private
24375     onEditorKey : function(field, e){
24376         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24377         if(k == e.TAB){
24378             e.stopEvent();
24379             ed.completeEdit();
24380             if(e.shiftKey){
24381                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24382             }else{
24383                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24384             }
24385         }else if(k == e.ENTER && !e.ctrlKey){
24386             e.stopEvent();
24387             ed.completeEdit();
24388             if(e.shiftKey){
24389                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24390             }else{
24391                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24392             }
24393         }else if(k == e.ESC){
24394             ed.cancelEdit();
24395         }
24396         if(newCell){
24397             g.startEditing(newCell[0], newCell[1]);
24398         }
24399     }
24400 });
24401 /*
24402  * Based on:
24403  * Ext JS Library 1.1.1
24404  * Copyright(c) 2006-2007, Ext JS, LLC.
24405  *
24406  * Originally Released Under LGPL - original licence link has changed is not relivant.
24407  *
24408  * Fork - LGPL
24409  * <script type="text/javascript">
24410  */
24411  
24412 /**
24413  * @class Roo.bootstrap.PagingToolbar
24414  * @extends Roo.bootstrap.NavSimplebar
24415  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24416  * @constructor
24417  * Create a new PagingToolbar
24418  * @param {Object} config The config object
24419  * @param {Roo.data.Store} store
24420  */
24421 Roo.bootstrap.PagingToolbar = function(config)
24422 {
24423     // old args format still supported... - xtype is prefered..
24424         // created from xtype...
24425     
24426     this.ds = config.dataSource;
24427     
24428     if (config.store && !this.ds) {
24429         this.store= Roo.factory(config.store, Roo.data);
24430         this.ds = this.store;
24431         this.ds.xmodule = this.xmodule || false;
24432     }
24433     
24434     this.toolbarItems = [];
24435     if (config.items) {
24436         this.toolbarItems = config.items;
24437     }
24438     
24439     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24440     
24441     this.cursor = 0;
24442     
24443     if (this.ds) { 
24444         this.bind(this.ds);
24445     }
24446     
24447     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24448     
24449 };
24450
24451 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24452     /**
24453      * @cfg {Roo.data.Store} dataSource
24454      * The underlying data store providing the paged data
24455      */
24456     /**
24457      * @cfg {String/HTMLElement/Element} container
24458      * container The id or element that will contain the toolbar
24459      */
24460     /**
24461      * @cfg {Boolean} displayInfo
24462      * True to display the displayMsg (defaults to false)
24463      */
24464     /**
24465      * @cfg {Number} pageSize
24466      * The number of records to display per page (defaults to 20)
24467      */
24468     pageSize: 20,
24469     /**
24470      * @cfg {String} displayMsg
24471      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24472      */
24473     displayMsg : 'Displaying {0} - {1} of {2}',
24474     /**
24475      * @cfg {String} emptyMsg
24476      * The message to display when no records are found (defaults to "No data to display")
24477      */
24478     emptyMsg : 'No data to display',
24479     /**
24480      * Customizable piece of the default paging text (defaults to "Page")
24481      * @type String
24482      */
24483     beforePageText : "Page",
24484     /**
24485      * Customizable piece of the default paging text (defaults to "of %0")
24486      * @type String
24487      */
24488     afterPageText : "of {0}",
24489     /**
24490      * Customizable piece of the default paging text (defaults to "First Page")
24491      * @type String
24492      */
24493     firstText : "First Page",
24494     /**
24495      * Customizable piece of the default paging text (defaults to "Previous Page")
24496      * @type String
24497      */
24498     prevText : "Previous Page",
24499     /**
24500      * Customizable piece of the default paging text (defaults to "Next Page")
24501      * @type String
24502      */
24503     nextText : "Next Page",
24504     /**
24505      * Customizable piece of the default paging text (defaults to "Last Page")
24506      * @type String
24507      */
24508     lastText : "Last Page",
24509     /**
24510      * Customizable piece of the default paging text (defaults to "Refresh")
24511      * @type String
24512      */
24513     refreshText : "Refresh",
24514
24515     buttons : false,
24516     // private
24517     onRender : function(ct, position) 
24518     {
24519         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24520         this.navgroup.parentId = this.id;
24521         this.navgroup.onRender(this.el, null);
24522         // add the buttons to the navgroup
24523         
24524         if(this.displayInfo){
24525             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24526             this.displayEl = this.el.select('.x-paging-info', true).first();
24527 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24528 //            this.displayEl = navel.el.select('span',true).first();
24529         }
24530         
24531         var _this = this;
24532         
24533         if(this.buttons){
24534             Roo.each(_this.buttons, function(e){ // this might need to use render????
24535                Roo.factory(e).render(_this.el);
24536             });
24537         }
24538             
24539         Roo.each(_this.toolbarItems, function(e) {
24540             _this.navgroup.addItem(e);
24541         });
24542         
24543         
24544         this.first = this.navgroup.addItem({
24545             tooltip: this.firstText,
24546             cls: "prev",
24547             icon : 'fa fa-backward',
24548             disabled: true,
24549             preventDefault: true,
24550             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24551         });
24552         
24553         this.prev =  this.navgroup.addItem({
24554             tooltip: this.prevText,
24555             cls: "prev",
24556             icon : 'fa fa-step-backward',
24557             disabled: true,
24558             preventDefault: true,
24559             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24560         });
24561     //this.addSeparator();
24562         
24563         
24564         var field = this.navgroup.addItem( {
24565             tagtype : 'span',
24566             cls : 'x-paging-position',
24567             
24568             html : this.beforePageText  +
24569                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24570                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24571          } ); //?? escaped?
24572         
24573         this.field = field.el.select('input', true).first();
24574         this.field.on("keydown", this.onPagingKeydown, this);
24575         this.field.on("focus", function(){this.dom.select();});
24576     
24577     
24578         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24579         //this.field.setHeight(18);
24580         //this.addSeparator();
24581         this.next = this.navgroup.addItem({
24582             tooltip: this.nextText,
24583             cls: "next",
24584             html : ' <i class="fa fa-step-forward">',
24585             disabled: true,
24586             preventDefault: true,
24587             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24588         });
24589         this.last = this.navgroup.addItem({
24590             tooltip: this.lastText,
24591             icon : 'fa fa-forward',
24592             cls: "next",
24593             disabled: true,
24594             preventDefault: true,
24595             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24596         });
24597     //this.addSeparator();
24598         this.loading = this.navgroup.addItem({
24599             tooltip: this.refreshText,
24600             icon: 'fa fa-refresh',
24601             preventDefault: true,
24602             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24603         });
24604         
24605     },
24606
24607     // private
24608     updateInfo : function(){
24609         if(this.displayEl){
24610             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24611             var msg = count == 0 ?
24612                 this.emptyMsg :
24613                 String.format(
24614                     this.displayMsg,
24615                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24616                 );
24617             this.displayEl.update(msg);
24618         }
24619     },
24620
24621     // private
24622     onLoad : function(ds, r, o)
24623     {
24624         this.cursor = o.params.start ? o.params.start : 0;
24625         
24626         var d = this.getPageData(),
24627             ap = d.activePage,
24628             ps = d.pages;
24629         
24630         
24631         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24632         this.field.dom.value = ap;
24633         this.first.setDisabled(ap == 1);
24634         this.prev.setDisabled(ap == 1);
24635         this.next.setDisabled(ap == ps);
24636         this.last.setDisabled(ap == ps);
24637         this.loading.enable();
24638         this.updateInfo();
24639     },
24640
24641     // private
24642     getPageData : function(){
24643         var total = this.ds.getTotalCount();
24644         return {
24645             total : total,
24646             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24647             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24648         };
24649     },
24650
24651     // private
24652     onLoadError : function(){
24653         this.loading.enable();
24654     },
24655
24656     // private
24657     onPagingKeydown : function(e){
24658         var k = e.getKey();
24659         var d = this.getPageData();
24660         if(k == e.RETURN){
24661             var v = this.field.dom.value, pageNum;
24662             if(!v || isNaN(pageNum = parseInt(v, 10))){
24663                 this.field.dom.value = d.activePage;
24664                 return;
24665             }
24666             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24667             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24668             e.stopEvent();
24669         }
24670         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))
24671         {
24672           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24673           this.field.dom.value = pageNum;
24674           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24675           e.stopEvent();
24676         }
24677         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24678         {
24679           var v = this.field.dom.value, pageNum; 
24680           var increment = (e.shiftKey) ? 10 : 1;
24681           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24682                 increment *= -1;
24683           }
24684           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24685             this.field.dom.value = d.activePage;
24686             return;
24687           }
24688           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24689           {
24690             this.field.dom.value = parseInt(v, 10) + increment;
24691             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24692             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24693           }
24694           e.stopEvent();
24695         }
24696     },
24697
24698     // private
24699     beforeLoad : function(){
24700         if(this.loading){
24701             this.loading.disable();
24702         }
24703     },
24704
24705     // private
24706     onClick : function(which){
24707         
24708         var ds = this.ds;
24709         if (!ds) {
24710             return;
24711         }
24712         
24713         switch(which){
24714             case "first":
24715                 ds.load({params:{start: 0, limit: this.pageSize}});
24716             break;
24717             case "prev":
24718                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24719             break;
24720             case "next":
24721                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24722             break;
24723             case "last":
24724                 var total = ds.getTotalCount();
24725                 var extra = total % this.pageSize;
24726                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24727                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24728             break;
24729             case "refresh":
24730                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24731             break;
24732         }
24733     },
24734
24735     /**
24736      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24737      * @param {Roo.data.Store} store The data store to unbind
24738      */
24739     unbind : function(ds){
24740         ds.un("beforeload", this.beforeLoad, this);
24741         ds.un("load", this.onLoad, this);
24742         ds.un("loadexception", this.onLoadError, this);
24743         ds.un("remove", this.updateInfo, this);
24744         ds.un("add", this.updateInfo, this);
24745         this.ds = undefined;
24746     },
24747
24748     /**
24749      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24750      * @param {Roo.data.Store} store The data store to bind
24751      */
24752     bind : function(ds){
24753         ds.on("beforeload", this.beforeLoad, this);
24754         ds.on("load", this.onLoad, this);
24755         ds.on("loadexception", this.onLoadError, this);
24756         ds.on("remove", this.updateInfo, this);
24757         ds.on("add", this.updateInfo, this);
24758         this.ds = ds;
24759     }
24760 });/*
24761  * - LGPL
24762  *
24763  * element
24764  * 
24765  */
24766
24767 /**
24768  * @class Roo.bootstrap.MessageBar
24769  * @extends Roo.bootstrap.Component
24770  * Bootstrap MessageBar class
24771  * @cfg {String} html contents of the MessageBar
24772  * @cfg {String} weight (info | success | warning | danger) default info
24773  * @cfg {String} beforeClass insert the bar before the given class
24774  * @cfg {Boolean} closable (true | false) default false
24775  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24776  * 
24777  * @constructor
24778  * Create a new Element
24779  * @param {Object} config The config object
24780  */
24781
24782 Roo.bootstrap.MessageBar = function(config){
24783     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24784 };
24785
24786 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24787     
24788     html: '',
24789     weight: 'info',
24790     closable: false,
24791     fixed: false,
24792     beforeClass: 'bootstrap-sticky-wrap',
24793     
24794     getAutoCreate : function(){
24795         
24796         var cfg = {
24797             tag: 'div',
24798             cls: 'alert alert-dismissable alert-' + this.weight,
24799             cn: [
24800                 {
24801                     tag: 'span',
24802                     cls: 'message',
24803                     html: this.html || ''
24804                 }
24805             ]
24806         };
24807         
24808         if(this.fixed){
24809             cfg.cls += ' alert-messages-fixed';
24810         }
24811         
24812         if(this.closable){
24813             cfg.cn.push({
24814                 tag: 'button',
24815                 cls: 'close',
24816                 html: 'x'
24817             });
24818         }
24819         
24820         return cfg;
24821     },
24822     
24823     onRender : function(ct, position)
24824     {
24825         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24826         
24827         if(!this.el){
24828             var cfg = Roo.apply({},  this.getAutoCreate());
24829             cfg.id = Roo.id();
24830             
24831             if (this.cls) {
24832                 cfg.cls += ' ' + this.cls;
24833             }
24834             if (this.style) {
24835                 cfg.style = this.style;
24836             }
24837             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24838             
24839             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24840         }
24841         
24842         this.el.select('>button.close').on('click', this.hide, this);
24843         
24844     },
24845     
24846     show : function()
24847     {
24848         if (!this.rendered) {
24849             this.render();
24850         }
24851         
24852         this.el.show();
24853         
24854         this.fireEvent('show', this);
24855         
24856     },
24857     
24858     hide : function()
24859     {
24860         if (!this.rendered) {
24861             this.render();
24862         }
24863         
24864         this.el.hide();
24865         
24866         this.fireEvent('hide', this);
24867     },
24868     
24869     update : function()
24870     {
24871 //        var e = this.el.dom.firstChild;
24872 //        
24873 //        if(this.closable){
24874 //            e = e.nextSibling;
24875 //        }
24876 //        
24877 //        e.data = this.html || '';
24878
24879         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24880     }
24881    
24882 });
24883
24884  
24885
24886      /*
24887  * - LGPL
24888  *
24889  * Graph
24890  * 
24891  */
24892
24893
24894 /**
24895  * @class Roo.bootstrap.Graph
24896  * @extends Roo.bootstrap.Component
24897  * Bootstrap Graph class
24898 > Prameters
24899  -sm {number} sm 4
24900  -md {number} md 5
24901  @cfg {String} graphtype  bar | vbar | pie
24902  @cfg {number} g_x coodinator | centre x (pie)
24903  @cfg {number} g_y coodinator | centre y (pie)
24904  @cfg {number} g_r radius (pie)
24905  @cfg {number} g_height height of the chart (respected by all elements in the set)
24906  @cfg {number} g_width width of the chart (respected by all elements in the set)
24907  @cfg {Object} title The title of the chart
24908     
24909  -{Array}  values
24910  -opts (object) options for the chart 
24911      o {
24912      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24913      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24914      o vgutter (number)
24915      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.
24916      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24917      o to
24918      o stretch (boolean)
24919      o }
24920  -opts (object) options for the pie
24921      o{
24922      o cut
24923      o startAngle (number)
24924      o endAngle (number)
24925      } 
24926  *
24927  * @constructor
24928  * Create a new Input
24929  * @param {Object} config The config object
24930  */
24931
24932 Roo.bootstrap.Graph = function(config){
24933     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24934     
24935     this.addEvents({
24936         // img events
24937         /**
24938          * @event click
24939          * The img click event for the img.
24940          * @param {Roo.EventObject} e
24941          */
24942         "click" : true
24943     });
24944 };
24945
24946 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24947     
24948     sm: 4,
24949     md: 5,
24950     graphtype: 'bar',
24951     g_height: 250,
24952     g_width: 400,
24953     g_x: 50,
24954     g_y: 50,
24955     g_r: 30,
24956     opts:{
24957         //g_colors: this.colors,
24958         g_type: 'soft',
24959         g_gutter: '20%'
24960
24961     },
24962     title : false,
24963
24964     getAutoCreate : function(){
24965         
24966         var cfg = {
24967             tag: 'div',
24968             html : null
24969         };
24970         
24971         
24972         return  cfg;
24973     },
24974
24975     onRender : function(ct,position){
24976         
24977         
24978         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24979         
24980         if (typeof(Raphael) == 'undefined') {
24981             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24982             return;
24983         }
24984         
24985         this.raphael = Raphael(this.el.dom);
24986         
24987                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24988                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24989                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24990                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24991                 /*
24992                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24993                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24994                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24995                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24996                 
24997                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24998                 r.barchart(330, 10, 300, 220, data1);
24999                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25000                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25001                 */
25002                 
25003                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25004                 // r.barchart(30, 30, 560, 250,  xdata, {
25005                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25006                 //     axis : "0 0 1 1",
25007                 //     axisxlabels :  xdata
25008                 //     //yvalues : cols,
25009                    
25010                 // });
25011 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25012 //        
25013 //        this.load(null,xdata,{
25014 //                axis : "0 0 1 1",
25015 //                axisxlabels :  xdata
25016 //                });
25017
25018     },
25019
25020     load : function(graphtype,xdata,opts)
25021     {
25022         this.raphael.clear();
25023         if(!graphtype) {
25024             graphtype = this.graphtype;
25025         }
25026         if(!opts){
25027             opts = this.opts;
25028         }
25029         var r = this.raphael,
25030             fin = function () {
25031                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25032             },
25033             fout = function () {
25034                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25035             },
25036             pfin = function() {
25037                 this.sector.stop();
25038                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25039
25040                 if (this.label) {
25041                     this.label[0].stop();
25042                     this.label[0].attr({ r: 7.5 });
25043                     this.label[1].attr({ "font-weight": 800 });
25044                 }
25045             },
25046             pfout = function() {
25047                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25048
25049                 if (this.label) {
25050                     this.label[0].animate({ r: 5 }, 500, "bounce");
25051                     this.label[1].attr({ "font-weight": 400 });
25052                 }
25053             };
25054
25055         switch(graphtype){
25056             case 'bar':
25057                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25058                 break;
25059             case 'hbar':
25060                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25061                 break;
25062             case 'pie':
25063 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25064 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25065 //            
25066                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25067                 
25068                 break;
25069
25070         }
25071         
25072         if(this.title){
25073             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25074         }
25075         
25076     },
25077     
25078     setTitle: function(o)
25079     {
25080         this.title = o;
25081     },
25082     
25083     initEvents: function() {
25084         
25085         if(!this.href){
25086             this.el.on('click', this.onClick, this);
25087         }
25088     },
25089     
25090     onClick : function(e)
25091     {
25092         Roo.log('img onclick');
25093         this.fireEvent('click', this, e);
25094     }
25095    
25096 });
25097
25098  
25099 /*
25100  * - LGPL
25101  *
25102  * numberBox
25103  * 
25104  */
25105 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25106
25107 /**
25108  * @class Roo.bootstrap.dash.NumberBox
25109  * @extends Roo.bootstrap.Component
25110  * Bootstrap NumberBox class
25111  * @cfg {String} headline Box headline
25112  * @cfg {String} content Box content
25113  * @cfg {String} icon Box icon
25114  * @cfg {String} footer Footer text
25115  * @cfg {String} fhref Footer href
25116  * 
25117  * @constructor
25118  * Create a new NumberBox
25119  * @param {Object} config The config object
25120  */
25121
25122
25123 Roo.bootstrap.dash.NumberBox = function(config){
25124     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25125     
25126 };
25127
25128 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25129     
25130     headline : '',
25131     content : '',
25132     icon : '',
25133     footer : '',
25134     fhref : '',
25135     ficon : '',
25136     
25137     getAutoCreate : function(){
25138         
25139         var cfg = {
25140             tag : 'div',
25141             cls : 'small-box ',
25142             cn : [
25143                 {
25144                     tag : 'div',
25145                     cls : 'inner',
25146                     cn :[
25147                         {
25148                             tag : 'h3',
25149                             cls : 'roo-headline',
25150                             html : this.headline
25151                         },
25152                         {
25153                             tag : 'p',
25154                             cls : 'roo-content',
25155                             html : this.content
25156                         }
25157                     ]
25158                 }
25159             ]
25160         };
25161         
25162         if(this.icon){
25163             cfg.cn.push({
25164                 tag : 'div',
25165                 cls : 'icon',
25166                 cn :[
25167                     {
25168                         tag : 'i',
25169                         cls : 'ion ' + this.icon
25170                     }
25171                 ]
25172             });
25173         }
25174         
25175         if(this.footer){
25176             var footer = {
25177                 tag : 'a',
25178                 cls : 'small-box-footer',
25179                 href : this.fhref || '#',
25180                 html : this.footer
25181             };
25182             
25183             cfg.cn.push(footer);
25184             
25185         }
25186         
25187         return  cfg;
25188     },
25189
25190     onRender : function(ct,position){
25191         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25192
25193
25194        
25195                 
25196     },
25197
25198     setHeadline: function (value)
25199     {
25200         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25201     },
25202     
25203     setFooter: function (value, href)
25204     {
25205         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25206         
25207         if(href){
25208             this.el.select('a.small-box-footer',true).first().attr('href', href);
25209         }
25210         
25211     },
25212
25213     setContent: function (value)
25214     {
25215         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25216     },
25217
25218     initEvents: function() 
25219     {   
25220         
25221     }
25222     
25223 });
25224
25225  
25226 /*
25227  * - LGPL
25228  *
25229  * TabBox
25230  * 
25231  */
25232 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25233
25234 /**
25235  * @class Roo.bootstrap.dash.TabBox
25236  * @extends Roo.bootstrap.Component
25237  * Bootstrap TabBox class
25238  * @cfg {String} title Title of the TabBox
25239  * @cfg {String} icon Icon of the TabBox
25240  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25241  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25242  * 
25243  * @constructor
25244  * Create a new TabBox
25245  * @param {Object} config The config object
25246  */
25247
25248
25249 Roo.bootstrap.dash.TabBox = function(config){
25250     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25251     this.addEvents({
25252         // raw events
25253         /**
25254          * @event addpane
25255          * When a pane is added
25256          * @param {Roo.bootstrap.dash.TabPane} pane
25257          */
25258         "addpane" : true,
25259         /**
25260          * @event activatepane
25261          * When a pane is activated
25262          * @param {Roo.bootstrap.dash.TabPane} pane
25263          */
25264         "activatepane" : true
25265         
25266          
25267     });
25268     
25269     this.panes = [];
25270 };
25271
25272 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25273
25274     title : '',
25275     icon : false,
25276     showtabs : true,
25277     tabScrollable : false,
25278     
25279     getChildContainer : function()
25280     {
25281         return this.el.select('.tab-content', true).first();
25282     },
25283     
25284     getAutoCreate : function(){
25285         
25286         var header = {
25287             tag: 'li',
25288             cls: 'pull-left header',
25289             html: this.title,
25290             cn : []
25291         };
25292         
25293         if(this.icon){
25294             header.cn.push({
25295                 tag: 'i',
25296                 cls: 'fa ' + this.icon
25297             });
25298         }
25299         
25300         var h = {
25301             tag: 'ul',
25302             cls: 'nav nav-tabs pull-right',
25303             cn: [
25304                 header
25305             ]
25306         };
25307         
25308         if(this.tabScrollable){
25309             h = {
25310                 tag: 'div',
25311                 cls: 'tab-header',
25312                 cn: [
25313                     {
25314                         tag: 'ul',
25315                         cls: 'nav nav-tabs pull-right',
25316                         cn: [
25317                             header
25318                         ]
25319                     }
25320                 ]
25321             };
25322         }
25323         
25324         var cfg = {
25325             tag: 'div',
25326             cls: 'nav-tabs-custom',
25327             cn: [
25328                 h,
25329                 {
25330                     tag: 'div',
25331                     cls: 'tab-content no-padding',
25332                     cn: []
25333                 }
25334             ]
25335         };
25336
25337         return  cfg;
25338     },
25339     initEvents : function()
25340     {
25341         //Roo.log('add add pane handler');
25342         this.on('addpane', this.onAddPane, this);
25343     },
25344      /**
25345      * Updates the box title
25346      * @param {String} html to set the title to.
25347      */
25348     setTitle : function(value)
25349     {
25350         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25351     },
25352     onAddPane : function(pane)
25353     {
25354         this.panes.push(pane);
25355         //Roo.log('addpane');
25356         //Roo.log(pane);
25357         // tabs are rendere left to right..
25358         if(!this.showtabs){
25359             return;
25360         }
25361         
25362         var ctr = this.el.select('.nav-tabs', true).first();
25363          
25364          
25365         var existing = ctr.select('.nav-tab',true);
25366         var qty = existing.getCount();;
25367         
25368         
25369         var tab = ctr.createChild({
25370             tag : 'li',
25371             cls : 'nav-tab' + (qty ? '' : ' active'),
25372             cn : [
25373                 {
25374                     tag : 'a',
25375                     href:'#',
25376                     html : pane.title
25377                 }
25378             ]
25379         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25380         pane.tab = tab;
25381         
25382         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25383         if (!qty) {
25384             pane.el.addClass('active');
25385         }
25386         
25387                 
25388     },
25389     onTabClick : function(ev,un,ob,pane)
25390     {
25391         //Roo.log('tab - prev default');
25392         ev.preventDefault();
25393         
25394         
25395         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25396         pane.tab.addClass('active');
25397         //Roo.log(pane.title);
25398         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25399         // technically we should have a deactivate event.. but maybe add later.
25400         // and it should not de-activate the selected tab...
25401         this.fireEvent('activatepane', pane);
25402         pane.el.addClass('active');
25403         pane.fireEvent('activate');
25404         
25405         
25406     },
25407     
25408     getActivePane : function()
25409     {
25410         var r = false;
25411         Roo.each(this.panes, function(p) {
25412             if(p.el.hasClass('active')){
25413                 r = p;
25414                 return false;
25415             }
25416             
25417             return;
25418         });
25419         
25420         return r;
25421     }
25422     
25423     
25424 });
25425
25426  
25427 /*
25428  * - LGPL
25429  *
25430  * Tab pane
25431  * 
25432  */
25433 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25434 /**
25435  * @class Roo.bootstrap.TabPane
25436  * @extends Roo.bootstrap.Component
25437  * Bootstrap TabPane class
25438  * @cfg {Boolean} active (false | true) Default false
25439  * @cfg {String} title title of panel
25440
25441  * 
25442  * @constructor
25443  * Create a new TabPane
25444  * @param {Object} config The config object
25445  */
25446
25447 Roo.bootstrap.dash.TabPane = function(config){
25448     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25449     
25450     this.addEvents({
25451         // raw events
25452         /**
25453          * @event activate
25454          * When a pane is activated
25455          * @param {Roo.bootstrap.dash.TabPane} pane
25456          */
25457         "activate" : true
25458          
25459     });
25460 };
25461
25462 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25463     
25464     active : false,
25465     title : '',
25466     
25467     // the tabBox that this is attached to.
25468     tab : false,
25469      
25470     getAutoCreate : function() 
25471     {
25472         var cfg = {
25473             tag: 'div',
25474             cls: 'tab-pane'
25475         };
25476         
25477         if(this.active){
25478             cfg.cls += ' active';
25479         }
25480         
25481         return cfg;
25482     },
25483     initEvents  : function()
25484     {
25485         //Roo.log('trigger add pane handler');
25486         this.parent().fireEvent('addpane', this)
25487     },
25488     
25489      /**
25490      * Updates the tab title 
25491      * @param {String} html to set the title to.
25492      */
25493     setTitle: function(str)
25494     {
25495         if (!this.tab) {
25496             return;
25497         }
25498         this.title = str;
25499         this.tab.select('a', true).first().dom.innerHTML = str;
25500         
25501     }
25502     
25503     
25504     
25505 });
25506
25507  
25508
25509
25510  /*
25511  * - LGPL
25512  *
25513  * menu
25514  * 
25515  */
25516 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25517
25518 /**
25519  * @class Roo.bootstrap.menu.Menu
25520  * @extends Roo.bootstrap.Component
25521  * Bootstrap Menu class - container for Menu
25522  * @cfg {String} html Text of the menu
25523  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25524  * @cfg {String} icon Font awesome icon
25525  * @cfg {String} pos Menu align to (top | bottom) default bottom
25526  * 
25527  * 
25528  * @constructor
25529  * Create a new Menu
25530  * @param {Object} config The config object
25531  */
25532
25533
25534 Roo.bootstrap.menu.Menu = function(config){
25535     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25536     
25537     this.addEvents({
25538         /**
25539          * @event beforeshow
25540          * Fires before this menu is displayed
25541          * @param {Roo.bootstrap.menu.Menu} this
25542          */
25543         beforeshow : true,
25544         /**
25545          * @event beforehide
25546          * Fires before this menu is hidden
25547          * @param {Roo.bootstrap.menu.Menu} this
25548          */
25549         beforehide : true,
25550         /**
25551          * @event show
25552          * Fires after this menu is displayed
25553          * @param {Roo.bootstrap.menu.Menu} this
25554          */
25555         show : true,
25556         /**
25557          * @event hide
25558          * Fires after this menu is hidden
25559          * @param {Roo.bootstrap.menu.Menu} this
25560          */
25561         hide : true,
25562         /**
25563          * @event click
25564          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25565          * @param {Roo.bootstrap.menu.Menu} this
25566          * @param {Roo.EventObject} e
25567          */
25568         click : true
25569     });
25570     
25571 };
25572
25573 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25574     
25575     submenu : false,
25576     html : '',
25577     weight : 'default',
25578     icon : false,
25579     pos : 'bottom',
25580     
25581     
25582     getChildContainer : function() {
25583         if(this.isSubMenu){
25584             return this.el;
25585         }
25586         
25587         return this.el.select('ul.dropdown-menu', true).first();  
25588     },
25589     
25590     getAutoCreate : function()
25591     {
25592         var text = [
25593             {
25594                 tag : 'span',
25595                 cls : 'roo-menu-text',
25596                 html : this.html
25597             }
25598         ];
25599         
25600         if(this.icon){
25601             text.unshift({
25602                 tag : 'i',
25603                 cls : 'fa ' + this.icon
25604             })
25605         }
25606         
25607         
25608         var cfg = {
25609             tag : 'div',
25610             cls : 'btn-group',
25611             cn : [
25612                 {
25613                     tag : 'button',
25614                     cls : 'dropdown-button btn btn-' + this.weight,
25615                     cn : text
25616                 },
25617                 {
25618                     tag : 'button',
25619                     cls : 'dropdown-toggle btn btn-' + this.weight,
25620                     cn : [
25621                         {
25622                             tag : 'span',
25623                             cls : 'caret'
25624                         }
25625                     ]
25626                 },
25627                 {
25628                     tag : 'ul',
25629                     cls : 'dropdown-menu'
25630                 }
25631             ]
25632             
25633         };
25634         
25635         if(this.pos == 'top'){
25636             cfg.cls += ' dropup';
25637         }
25638         
25639         if(this.isSubMenu){
25640             cfg = {
25641                 tag : 'ul',
25642                 cls : 'dropdown-menu'
25643             }
25644         }
25645         
25646         return cfg;
25647     },
25648     
25649     onRender : function(ct, position)
25650     {
25651         this.isSubMenu = ct.hasClass('dropdown-submenu');
25652         
25653         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25654     },
25655     
25656     initEvents : function() 
25657     {
25658         if(this.isSubMenu){
25659             return;
25660         }
25661         
25662         this.hidden = true;
25663         
25664         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25665         this.triggerEl.on('click', this.onTriggerPress, this);
25666         
25667         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25668         this.buttonEl.on('click', this.onClick, this);
25669         
25670     },
25671     
25672     list : function()
25673     {
25674         if(this.isSubMenu){
25675             return this.el;
25676         }
25677         
25678         return this.el.select('ul.dropdown-menu', true).first();
25679     },
25680     
25681     onClick : function(e)
25682     {
25683         this.fireEvent("click", this, e);
25684     },
25685     
25686     onTriggerPress  : function(e)
25687     {   
25688         if (this.isVisible()) {
25689             this.hide();
25690         } else {
25691             this.show();
25692         }
25693     },
25694     
25695     isVisible : function(){
25696         return !this.hidden;
25697     },
25698     
25699     show : function()
25700     {
25701         this.fireEvent("beforeshow", this);
25702         
25703         this.hidden = false;
25704         this.el.addClass('open');
25705         
25706         Roo.get(document).on("mouseup", this.onMouseUp, this);
25707         
25708         this.fireEvent("show", this);
25709         
25710         
25711     },
25712     
25713     hide : function()
25714     {
25715         this.fireEvent("beforehide", this);
25716         
25717         this.hidden = true;
25718         this.el.removeClass('open');
25719         
25720         Roo.get(document).un("mouseup", this.onMouseUp);
25721         
25722         this.fireEvent("hide", this);
25723     },
25724     
25725     onMouseUp : function()
25726     {
25727         this.hide();
25728     }
25729     
25730 });
25731
25732  
25733  /*
25734  * - LGPL
25735  *
25736  * menu item
25737  * 
25738  */
25739 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25740
25741 /**
25742  * @class Roo.bootstrap.menu.Item
25743  * @extends Roo.bootstrap.Component
25744  * Bootstrap MenuItem class
25745  * @cfg {Boolean} submenu (true | false) default false
25746  * @cfg {String} html text of the item
25747  * @cfg {String} href the link
25748  * @cfg {Boolean} disable (true | false) default false
25749  * @cfg {Boolean} preventDefault (true | false) default true
25750  * @cfg {String} icon Font awesome icon
25751  * @cfg {String} pos Submenu align to (left | right) default right 
25752  * 
25753  * 
25754  * @constructor
25755  * Create a new Item
25756  * @param {Object} config The config object
25757  */
25758
25759
25760 Roo.bootstrap.menu.Item = function(config){
25761     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25762     this.addEvents({
25763         /**
25764          * @event mouseover
25765          * Fires when the mouse is hovering over this menu
25766          * @param {Roo.bootstrap.menu.Item} this
25767          * @param {Roo.EventObject} e
25768          */
25769         mouseover : true,
25770         /**
25771          * @event mouseout
25772          * Fires when the mouse exits this menu
25773          * @param {Roo.bootstrap.menu.Item} this
25774          * @param {Roo.EventObject} e
25775          */
25776         mouseout : true,
25777         // raw events
25778         /**
25779          * @event click
25780          * The raw click event for the entire grid.
25781          * @param {Roo.EventObject} e
25782          */
25783         click : true
25784     });
25785 };
25786
25787 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25788     
25789     submenu : false,
25790     href : '',
25791     html : '',
25792     preventDefault: true,
25793     disable : false,
25794     icon : false,
25795     pos : 'right',
25796     
25797     getAutoCreate : function()
25798     {
25799         var text = [
25800             {
25801                 tag : 'span',
25802                 cls : 'roo-menu-item-text',
25803                 html : this.html
25804             }
25805         ];
25806         
25807         if(this.icon){
25808             text.unshift({
25809                 tag : 'i',
25810                 cls : 'fa ' + this.icon
25811             })
25812         }
25813         
25814         var cfg = {
25815             tag : 'li',
25816             cn : [
25817                 {
25818                     tag : 'a',
25819                     href : this.href || '#',
25820                     cn : text
25821                 }
25822             ]
25823         };
25824         
25825         if(this.disable){
25826             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25827         }
25828         
25829         if(this.submenu){
25830             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25831             
25832             if(this.pos == 'left'){
25833                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25834             }
25835         }
25836         
25837         return cfg;
25838     },
25839     
25840     initEvents : function() 
25841     {
25842         this.el.on('mouseover', this.onMouseOver, this);
25843         this.el.on('mouseout', this.onMouseOut, this);
25844         
25845         this.el.select('a', true).first().on('click', this.onClick, this);
25846         
25847     },
25848     
25849     onClick : function(e)
25850     {
25851         if(this.preventDefault){
25852             e.preventDefault();
25853         }
25854         
25855         this.fireEvent("click", this, e);
25856     },
25857     
25858     onMouseOver : function(e)
25859     {
25860         if(this.submenu && this.pos == 'left'){
25861             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25862         }
25863         
25864         this.fireEvent("mouseover", this, e);
25865     },
25866     
25867     onMouseOut : function(e)
25868     {
25869         this.fireEvent("mouseout", this, e);
25870     }
25871 });
25872
25873  
25874
25875  /*
25876  * - LGPL
25877  *
25878  * menu separator
25879  * 
25880  */
25881 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25882
25883 /**
25884  * @class Roo.bootstrap.menu.Separator
25885  * @extends Roo.bootstrap.Component
25886  * Bootstrap Separator class
25887  * 
25888  * @constructor
25889  * Create a new Separator
25890  * @param {Object} config The config object
25891  */
25892
25893
25894 Roo.bootstrap.menu.Separator = function(config){
25895     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25896 };
25897
25898 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25899     
25900     getAutoCreate : function(){
25901         var cfg = {
25902             tag : 'li',
25903             cls: 'divider'
25904         };
25905         
25906         return cfg;
25907     }
25908    
25909 });
25910
25911  
25912
25913  /*
25914  * - LGPL
25915  *
25916  * Tooltip
25917  * 
25918  */
25919
25920 /**
25921  * @class Roo.bootstrap.Tooltip
25922  * Bootstrap Tooltip class
25923  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25924  * to determine which dom element triggers the tooltip.
25925  * 
25926  * It needs to add support for additional attributes like tooltip-position
25927  * 
25928  * @constructor
25929  * Create a new Toolti
25930  * @param {Object} config The config object
25931  */
25932
25933 Roo.bootstrap.Tooltip = function(config){
25934     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25935     
25936     this.alignment = Roo.bootstrap.Tooltip.alignment;
25937     
25938     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25939         this.alignment = config.alignment;
25940     }
25941     
25942 };
25943
25944 Roo.apply(Roo.bootstrap.Tooltip, {
25945     /**
25946      * @function init initialize tooltip monitoring.
25947      * @static
25948      */
25949     currentEl : false,
25950     currentTip : false,
25951     currentRegion : false,
25952     
25953     //  init : delay?
25954     
25955     init : function()
25956     {
25957         Roo.get(document).on('mouseover', this.enter ,this);
25958         Roo.get(document).on('mouseout', this.leave, this);
25959          
25960         
25961         this.currentTip = new Roo.bootstrap.Tooltip();
25962     },
25963     
25964     enter : function(ev)
25965     {
25966         var dom = ev.getTarget();
25967         
25968         //Roo.log(['enter',dom]);
25969         var el = Roo.fly(dom);
25970         if (this.currentEl) {
25971             //Roo.log(dom);
25972             //Roo.log(this.currentEl);
25973             //Roo.log(this.currentEl.contains(dom));
25974             if (this.currentEl == el) {
25975                 return;
25976             }
25977             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25978                 return;
25979             }
25980
25981         }
25982         
25983         if (this.currentTip.el) {
25984             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25985         }    
25986         //Roo.log(ev);
25987         
25988         if(!el || el.dom == document){
25989             return;
25990         }
25991         
25992         var bindEl = el;
25993         
25994         // you can not look for children, as if el is the body.. then everythign is the child..
25995         if (!el.attr('tooltip')) { //
25996             if (!el.select("[tooltip]").elements.length) {
25997                 return;
25998             }
25999             // is the mouse over this child...?
26000             bindEl = el.select("[tooltip]").first();
26001             var xy = ev.getXY();
26002             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26003                 //Roo.log("not in region.");
26004                 return;
26005             }
26006             //Roo.log("child element over..");
26007             
26008         }
26009         this.currentEl = bindEl;
26010         this.currentTip.bind(bindEl);
26011         this.currentRegion = Roo.lib.Region.getRegion(dom);
26012         this.currentTip.enter();
26013         
26014     },
26015     leave : function(ev)
26016     {
26017         var dom = ev.getTarget();
26018         //Roo.log(['leave',dom]);
26019         if (!this.currentEl) {
26020             return;
26021         }
26022         
26023         
26024         if (dom != this.currentEl.dom) {
26025             return;
26026         }
26027         var xy = ev.getXY();
26028         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26029             return;
26030         }
26031         // only activate leave if mouse cursor is outside... bounding box..
26032         
26033         
26034         
26035         
26036         if (this.currentTip) {
26037             this.currentTip.leave();
26038         }
26039         //Roo.log('clear currentEl');
26040         this.currentEl = false;
26041         
26042         
26043     },
26044     alignment : {
26045         'left' : ['r-l', [-2,0], 'right'],
26046         'right' : ['l-r', [2,0], 'left'],
26047         'bottom' : ['t-b', [0,2], 'top'],
26048         'top' : [ 'b-t', [0,-2], 'bottom']
26049     }
26050     
26051 });
26052
26053
26054 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26055     
26056     
26057     bindEl : false,
26058     
26059     delay : null, // can be { show : 300 , hide: 500}
26060     
26061     timeout : null,
26062     
26063     hoverState : null, //???
26064     
26065     placement : 'bottom', 
26066     
26067     alignment : false,
26068     
26069     getAutoCreate : function(){
26070     
26071         var cfg = {
26072            cls : 'tooltip',
26073            role : 'tooltip',
26074            cn : [
26075                 {
26076                     cls : 'tooltip-arrow'
26077                 },
26078                 {
26079                     cls : 'tooltip-inner'
26080                 }
26081            ]
26082         };
26083         
26084         return cfg;
26085     },
26086     bind : function(el)
26087     {
26088         this.bindEl = el;
26089     },
26090       
26091     
26092     enter : function () {
26093        
26094         if (this.timeout != null) {
26095             clearTimeout(this.timeout);
26096         }
26097         
26098         this.hoverState = 'in';
26099          //Roo.log("enter - show");
26100         if (!this.delay || !this.delay.show) {
26101             this.show();
26102             return;
26103         }
26104         var _t = this;
26105         this.timeout = setTimeout(function () {
26106             if (_t.hoverState == 'in') {
26107                 _t.show();
26108             }
26109         }, this.delay.show);
26110     },
26111     leave : function()
26112     {
26113         clearTimeout(this.timeout);
26114     
26115         this.hoverState = 'out';
26116          if (!this.delay || !this.delay.hide) {
26117             this.hide();
26118             return;
26119         }
26120        
26121         var _t = this;
26122         this.timeout = setTimeout(function () {
26123             //Roo.log("leave - timeout");
26124             
26125             if (_t.hoverState == 'out') {
26126                 _t.hide();
26127                 Roo.bootstrap.Tooltip.currentEl = false;
26128             }
26129         }, delay);
26130     },
26131     
26132     show : function (msg)
26133     {
26134         if (!this.el) {
26135             this.render(document.body);
26136         }
26137         // set content.
26138         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26139         
26140         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26141         
26142         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26143         
26144         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26145         
26146         var placement = typeof this.placement == 'function' ?
26147             this.placement.call(this, this.el, on_el) :
26148             this.placement;
26149             
26150         var autoToken = /\s?auto?\s?/i;
26151         var autoPlace = autoToken.test(placement);
26152         if (autoPlace) {
26153             placement = placement.replace(autoToken, '') || 'top';
26154         }
26155         
26156         //this.el.detach()
26157         //this.el.setXY([0,0]);
26158         this.el.show();
26159         //this.el.dom.style.display='block';
26160         
26161         //this.el.appendTo(on_el);
26162         
26163         var p = this.getPosition();
26164         var box = this.el.getBox();
26165         
26166         if (autoPlace) {
26167             // fixme..
26168         }
26169         
26170         var align = this.alignment[placement];
26171         
26172         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26173         
26174         if(placement == 'top' || placement == 'bottom'){
26175             if(xy[0] < 0){
26176                 placement = 'right';
26177             }
26178             
26179             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26180                 placement = 'left';
26181             }
26182             
26183             var scroll = Roo.select('body', true).first().getScroll();
26184             
26185             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26186                 placement = 'top';
26187             }
26188             
26189         }
26190         
26191         this.el.alignTo(this.bindEl, align[0],align[1]);
26192         //var arrow = this.el.select('.arrow',true).first();
26193         //arrow.set(align[2], 
26194         
26195         this.el.addClass(placement);
26196         
26197         this.el.addClass('in fade');
26198         
26199         this.hoverState = null;
26200         
26201         if (this.el.hasClass('fade')) {
26202             // fade it?
26203         }
26204         
26205     },
26206     hide : function()
26207     {
26208          
26209         if (!this.el) {
26210             return;
26211         }
26212         //this.el.setXY([0,0]);
26213         this.el.removeClass('in');
26214         //this.el.hide();
26215         
26216     }
26217     
26218 });
26219  
26220
26221  /*
26222  * - LGPL
26223  *
26224  * Location Picker
26225  * 
26226  */
26227
26228 /**
26229  * @class Roo.bootstrap.LocationPicker
26230  * @extends Roo.bootstrap.Component
26231  * Bootstrap LocationPicker class
26232  * @cfg {Number} latitude Position when init default 0
26233  * @cfg {Number} longitude Position when init default 0
26234  * @cfg {Number} zoom default 15
26235  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26236  * @cfg {Boolean} mapTypeControl default false
26237  * @cfg {Boolean} disableDoubleClickZoom default false
26238  * @cfg {Boolean} scrollwheel default true
26239  * @cfg {Boolean} streetViewControl default false
26240  * @cfg {Number} radius default 0
26241  * @cfg {String} locationName
26242  * @cfg {Boolean} draggable default true
26243  * @cfg {Boolean} enableAutocomplete default false
26244  * @cfg {Boolean} enableReverseGeocode default true
26245  * @cfg {String} markerTitle
26246  * 
26247  * @constructor
26248  * Create a new LocationPicker
26249  * @param {Object} config The config object
26250  */
26251
26252
26253 Roo.bootstrap.LocationPicker = function(config){
26254     
26255     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26256     
26257     this.addEvents({
26258         /**
26259          * @event initial
26260          * Fires when the picker initialized.
26261          * @param {Roo.bootstrap.LocationPicker} this
26262          * @param {Google Location} location
26263          */
26264         initial : true,
26265         /**
26266          * @event positionchanged
26267          * Fires when the picker position changed.
26268          * @param {Roo.bootstrap.LocationPicker} this
26269          * @param {Google Location} location
26270          */
26271         positionchanged : true,
26272         /**
26273          * @event resize
26274          * Fires when the map resize.
26275          * @param {Roo.bootstrap.LocationPicker} this
26276          */
26277         resize : true,
26278         /**
26279          * @event show
26280          * Fires when the map show.
26281          * @param {Roo.bootstrap.LocationPicker} this
26282          */
26283         show : true,
26284         /**
26285          * @event hide
26286          * Fires when the map hide.
26287          * @param {Roo.bootstrap.LocationPicker} this
26288          */
26289         hide : true,
26290         /**
26291          * @event mapClick
26292          * Fires when click the map.
26293          * @param {Roo.bootstrap.LocationPicker} this
26294          * @param {Map event} e
26295          */
26296         mapClick : true,
26297         /**
26298          * @event mapRightClick
26299          * Fires when right click the map.
26300          * @param {Roo.bootstrap.LocationPicker} this
26301          * @param {Map event} e
26302          */
26303         mapRightClick : true,
26304         /**
26305          * @event markerClick
26306          * Fires when click the marker.
26307          * @param {Roo.bootstrap.LocationPicker} this
26308          * @param {Map event} e
26309          */
26310         markerClick : true,
26311         /**
26312          * @event markerRightClick
26313          * Fires when right click the marker.
26314          * @param {Roo.bootstrap.LocationPicker} this
26315          * @param {Map event} e
26316          */
26317         markerRightClick : true,
26318         /**
26319          * @event OverlayViewDraw
26320          * Fires when OverlayView Draw
26321          * @param {Roo.bootstrap.LocationPicker} this
26322          */
26323         OverlayViewDraw : true,
26324         /**
26325          * @event OverlayViewOnAdd
26326          * Fires when OverlayView Draw
26327          * @param {Roo.bootstrap.LocationPicker} this
26328          */
26329         OverlayViewOnAdd : true,
26330         /**
26331          * @event OverlayViewOnRemove
26332          * Fires when OverlayView Draw
26333          * @param {Roo.bootstrap.LocationPicker} this
26334          */
26335         OverlayViewOnRemove : true,
26336         /**
26337          * @event OverlayViewShow
26338          * Fires when OverlayView Draw
26339          * @param {Roo.bootstrap.LocationPicker} this
26340          * @param {Pixel} cpx
26341          */
26342         OverlayViewShow : true,
26343         /**
26344          * @event OverlayViewHide
26345          * Fires when OverlayView Draw
26346          * @param {Roo.bootstrap.LocationPicker} this
26347          */
26348         OverlayViewHide : true,
26349         /**
26350          * @event loadexception
26351          * Fires when load google lib failed.
26352          * @param {Roo.bootstrap.LocationPicker} this
26353          */
26354         loadexception : true
26355     });
26356         
26357 };
26358
26359 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26360     
26361     gMapContext: false,
26362     
26363     latitude: 0,
26364     longitude: 0,
26365     zoom: 15,
26366     mapTypeId: false,
26367     mapTypeControl: false,
26368     disableDoubleClickZoom: false,
26369     scrollwheel: true,
26370     streetViewControl: false,
26371     radius: 0,
26372     locationName: '',
26373     draggable: true,
26374     enableAutocomplete: false,
26375     enableReverseGeocode: true,
26376     markerTitle: '',
26377     
26378     getAutoCreate: function()
26379     {
26380
26381         var cfg = {
26382             tag: 'div',
26383             cls: 'roo-location-picker'
26384         };
26385         
26386         return cfg
26387     },
26388     
26389     initEvents: function(ct, position)
26390     {       
26391         if(!this.el.getWidth() || this.isApplied()){
26392             return;
26393         }
26394         
26395         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26396         
26397         this.initial();
26398     },
26399     
26400     initial: function()
26401     {
26402         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26403             this.fireEvent('loadexception', this);
26404             return;
26405         }
26406         
26407         if(!this.mapTypeId){
26408             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26409         }
26410         
26411         this.gMapContext = this.GMapContext();
26412         
26413         this.initOverlayView();
26414         
26415         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26416         
26417         var _this = this;
26418                 
26419         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26420             _this.setPosition(_this.gMapContext.marker.position);
26421         });
26422         
26423         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26424             _this.fireEvent('mapClick', this, event);
26425             
26426         });
26427
26428         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26429             _this.fireEvent('mapRightClick', this, event);
26430             
26431         });
26432         
26433         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26434             _this.fireEvent('markerClick', this, event);
26435             
26436         });
26437
26438         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26439             _this.fireEvent('markerRightClick', this, event);
26440             
26441         });
26442         
26443         this.setPosition(this.gMapContext.location);
26444         
26445         this.fireEvent('initial', this, this.gMapContext.location);
26446     },
26447     
26448     initOverlayView: function()
26449     {
26450         var _this = this;
26451         
26452         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26453             
26454             draw: function()
26455             {
26456                 _this.fireEvent('OverlayViewDraw', _this);
26457             },
26458             
26459             onAdd: function()
26460             {
26461                 _this.fireEvent('OverlayViewOnAdd', _this);
26462             },
26463             
26464             onRemove: function()
26465             {
26466                 _this.fireEvent('OverlayViewOnRemove', _this);
26467             },
26468             
26469             show: function(cpx)
26470             {
26471                 _this.fireEvent('OverlayViewShow', _this, cpx);
26472             },
26473             
26474             hide: function()
26475             {
26476                 _this.fireEvent('OverlayViewHide', _this);
26477             }
26478             
26479         });
26480     },
26481     
26482     fromLatLngToContainerPixel: function(event)
26483     {
26484         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26485     },
26486     
26487     isApplied: function() 
26488     {
26489         return this.getGmapContext() == false ? false : true;
26490     },
26491     
26492     getGmapContext: function() 
26493     {
26494         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26495     },
26496     
26497     GMapContext: function() 
26498     {
26499         var position = new google.maps.LatLng(this.latitude, this.longitude);
26500         
26501         var _map = new google.maps.Map(this.el.dom, {
26502             center: position,
26503             zoom: this.zoom,
26504             mapTypeId: this.mapTypeId,
26505             mapTypeControl: this.mapTypeControl,
26506             disableDoubleClickZoom: this.disableDoubleClickZoom,
26507             scrollwheel: this.scrollwheel,
26508             streetViewControl: this.streetViewControl,
26509             locationName: this.locationName,
26510             draggable: this.draggable,
26511             enableAutocomplete: this.enableAutocomplete,
26512             enableReverseGeocode: this.enableReverseGeocode
26513         });
26514         
26515         var _marker = new google.maps.Marker({
26516             position: position,
26517             map: _map,
26518             title: this.markerTitle,
26519             draggable: this.draggable
26520         });
26521         
26522         return {
26523             map: _map,
26524             marker: _marker,
26525             circle: null,
26526             location: position,
26527             radius: this.radius,
26528             locationName: this.locationName,
26529             addressComponents: {
26530                 formatted_address: null,
26531                 addressLine1: null,
26532                 addressLine2: null,
26533                 streetName: null,
26534                 streetNumber: null,
26535                 city: null,
26536                 district: null,
26537                 state: null,
26538                 stateOrProvince: null
26539             },
26540             settings: this,
26541             domContainer: this.el.dom,
26542             geodecoder: new google.maps.Geocoder()
26543         };
26544     },
26545     
26546     drawCircle: function(center, radius, options) 
26547     {
26548         if (this.gMapContext.circle != null) {
26549             this.gMapContext.circle.setMap(null);
26550         }
26551         if (radius > 0) {
26552             radius *= 1;
26553             options = Roo.apply({}, options, {
26554                 strokeColor: "#0000FF",
26555                 strokeOpacity: .35,
26556                 strokeWeight: 2,
26557                 fillColor: "#0000FF",
26558                 fillOpacity: .2
26559             });
26560             
26561             options.map = this.gMapContext.map;
26562             options.radius = radius;
26563             options.center = center;
26564             this.gMapContext.circle = new google.maps.Circle(options);
26565             return this.gMapContext.circle;
26566         }
26567         
26568         return null;
26569     },
26570     
26571     setPosition: function(location) 
26572     {
26573         this.gMapContext.location = location;
26574         this.gMapContext.marker.setPosition(location);
26575         this.gMapContext.map.panTo(location);
26576         this.drawCircle(location, this.gMapContext.radius, {});
26577         
26578         var _this = this;
26579         
26580         if (this.gMapContext.settings.enableReverseGeocode) {
26581             this.gMapContext.geodecoder.geocode({
26582                 latLng: this.gMapContext.location
26583             }, function(results, status) {
26584                 
26585                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26586                     _this.gMapContext.locationName = results[0].formatted_address;
26587                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26588                     
26589                     _this.fireEvent('positionchanged', this, location);
26590                 }
26591             });
26592             
26593             return;
26594         }
26595         
26596         this.fireEvent('positionchanged', this, location);
26597     },
26598     
26599     resize: function()
26600     {
26601         google.maps.event.trigger(this.gMapContext.map, "resize");
26602         
26603         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26604         
26605         this.fireEvent('resize', this);
26606     },
26607     
26608     setPositionByLatLng: function(latitude, longitude)
26609     {
26610         this.setPosition(new google.maps.LatLng(latitude, longitude));
26611     },
26612     
26613     getCurrentPosition: function() 
26614     {
26615         return {
26616             latitude: this.gMapContext.location.lat(),
26617             longitude: this.gMapContext.location.lng()
26618         };
26619     },
26620     
26621     getAddressName: function() 
26622     {
26623         return this.gMapContext.locationName;
26624     },
26625     
26626     getAddressComponents: function() 
26627     {
26628         return this.gMapContext.addressComponents;
26629     },
26630     
26631     address_component_from_google_geocode: function(address_components) 
26632     {
26633         var result = {};
26634         
26635         for (var i = 0; i < address_components.length; i++) {
26636             var component = address_components[i];
26637             if (component.types.indexOf("postal_code") >= 0) {
26638                 result.postalCode = component.short_name;
26639             } else if (component.types.indexOf("street_number") >= 0) {
26640                 result.streetNumber = component.short_name;
26641             } else if (component.types.indexOf("route") >= 0) {
26642                 result.streetName = component.short_name;
26643             } else if (component.types.indexOf("neighborhood") >= 0) {
26644                 result.city = component.short_name;
26645             } else if (component.types.indexOf("locality") >= 0) {
26646                 result.city = component.short_name;
26647             } else if (component.types.indexOf("sublocality") >= 0) {
26648                 result.district = component.short_name;
26649             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26650                 result.stateOrProvince = component.short_name;
26651             } else if (component.types.indexOf("country") >= 0) {
26652                 result.country = component.short_name;
26653             }
26654         }
26655         
26656         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26657         result.addressLine2 = "";
26658         return result;
26659     },
26660     
26661     setZoomLevel: function(zoom)
26662     {
26663         this.gMapContext.map.setZoom(zoom);
26664     },
26665     
26666     show: function()
26667     {
26668         if(!this.el){
26669             return;
26670         }
26671         
26672         this.el.show();
26673         
26674         this.resize();
26675         
26676         this.fireEvent('show', this);
26677     },
26678     
26679     hide: function()
26680     {
26681         if(!this.el){
26682             return;
26683         }
26684         
26685         this.el.hide();
26686         
26687         this.fireEvent('hide', this);
26688     }
26689     
26690 });
26691
26692 Roo.apply(Roo.bootstrap.LocationPicker, {
26693     
26694     OverlayView : function(map, options)
26695     {
26696         options = options || {};
26697         
26698         this.setMap(map);
26699     }
26700     
26701     
26702 });/*
26703  * - LGPL
26704  *
26705  * Alert
26706  * 
26707  */
26708
26709 /**
26710  * @class Roo.bootstrap.Alert
26711  * @extends Roo.bootstrap.Component
26712  * Bootstrap Alert class
26713  * @cfg {String} title The title of alert
26714  * @cfg {String} html The content of alert
26715  * @cfg {String} weight (  success | info | warning | danger )
26716  * @cfg {String} faicon font-awesomeicon
26717  * 
26718  * @constructor
26719  * Create a new alert
26720  * @param {Object} config The config object
26721  */
26722
26723
26724 Roo.bootstrap.Alert = function(config){
26725     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26726     
26727 };
26728
26729 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26730     
26731     title: '',
26732     html: '',
26733     weight: false,
26734     faicon: false,
26735     
26736     getAutoCreate : function()
26737     {
26738         
26739         var cfg = {
26740             tag : 'div',
26741             cls : 'alert',
26742             cn : [
26743                 {
26744                     tag : 'i',
26745                     cls : 'roo-alert-icon'
26746                     
26747                 },
26748                 {
26749                     tag : 'b',
26750                     cls : 'roo-alert-title',
26751                     html : this.title
26752                 },
26753                 {
26754                     tag : 'span',
26755                     cls : 'roo-alert-text',
26756                     html : this.html
26757                 }
26758             ]
26759         };
26760         
26761         if(this.faicon){
26762             cfg.cn[0].cls += ' fa ' + this.faicon;
26763         }
26764         
26765         if(this.weight){
26766             cfg.cls += ' alert-' + this.weight;
26767         }
26768         
26769         return cfg;
26770     },
26771     
26772     initEvents: function() 
26773     {
26774         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26775     },
26776     
26777     setTitle : function(str)
26778     {
26779         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26780     },
26781     
26782     setText : function(str)
26783     {
26784         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26785     },
26786     
26787     setWeight : function(weight)
26788     {
26789         if(this.weight){
26790             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26791         }
26792         
26793         this.weight = weight;
26794         
26795         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26796     },
26797     
26798     setIcon : function(icon)
26799     {
26800         if(this.faicon){
26801             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26802         }
26803         
26804         this.faicon = icon;
26805         
26806         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26807     },
26808     
26809     hide: function() 
26810     {
26811         this.el.hide();   
26812     },
26813     
26814     show: function() 
26815     {  
26816         this.el.show();   
26817     }
26818     
26819 });
26820
26821  
26822 /*
26823 * Licence: LGPL
26824 */
26825
26826 /**
26827  * @class Roo.bootstrap.UploadCropbox
26828  * @extends Roo.bootstrap.Component
26829  * Bootstrap UploadCropbox class
26830  * @cfg {String} emptyText show when image has been loaded
26831  * @cfg {String} rotateNotify show when image too small to rotate
26832  * @cfg {Number} errorTimeout default 3000
26833  * @cfg {Number} minWidth default 300
26834  * @cfg {Number} minHeight default 300
26835  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26836  * @cfg {Boolean} isDocument (true|false) default false
26837  * @cfg {String} url action url
26838  * @cfg {String} paramName default 'imageUpload'
26839  * @cfg {String} method default POST
26840  * @cfg {Boolean} loadMask (true|false) default true
26841  * @cfg {Boolean} loadingText default 'Loading...'
26842  * 
26843  * @constructor
26844  * Create a new UploadCropbox
26845  * @param {Object} config The config object
26846  */
26847
26848 Roo.bootstrap.UploadCropbox = function(config){
26849     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26850     
26851     this.addEvents({
26852         /**
26853          * @event beforeselectfile
26854          * Fire before select file
26855          * @param {Roo.bootstrap.UploadCropbox} this
26856          */
26857         "beforeselectfile" : true,
26858         /**
26859          * @event initial
26860          * Fire after initEvent
26861          * @param {Roo.bootstrap.UploadCropbox} this
26862          */
26863         "initial" : true,
26864         /**
26865          * @event crop
26866          * Fire after initEvent
26867          * @param {Roo.bootstrap.UploadCropbox} this
26868          * @param {String} data
26869          */
26870         "crop" : true,
26871         /**
26872          * @event prepare
26873          * Fire when preparing the file data
26874          * @param {Roo.bootstrap.UploadCropbox} this
26875          * @param {Object} file
26876          */
26877         "prepare" : true,
26878         /**
26879          * @event exception
26880          * Fire when get exception
26881          * @param {Roo.bootstrap.UploadCropbox} this
26882          * @param {XMLHttpRequest} xhr
26883          */
26884         "exception" : true,
26885         /**
26886          * @event beforeloadcanvas
26887          * Fire before load the canvas
26888          * @param {Roo.bootstrap.UploadCropbox} this
26889          * @param {String} src
26890          */
26891         "beforeloadcanvas" : true,
26892         /**
26893          * @event trash
26894          * Fire when trash image
26895          * @param {Roo.bootstrap.UploadCropbox} this
26896          */
26897         "trash" : true,
26898         /**
26899          * @event download
26900          * Fire when download the image
26901          * @param {Roo.bootstrap.UploadCropbox} this
26902          */
26903         "download" : true,
26904         /**
26905          * @event footerbuttonclick
26906          * Fire when footerbuttonclick
26907          * @param {Roo.bootstrap.UploadCropbox} this
26908          * @param {String} type
26909          */
26910         "footerbuttonclick" : true,
26911         /**
26912          * @event resize
26913          * Fire when resize
26914          * @param {Roo.bootstrap.UploadCropbox} this
26915          */
26916         "resize" : true,
26917         /**
26918          * @event rotate
26919          * Fire when rotate the image
26920          * @param {Roo.bootstrap.UploadCropbox} this
26921          * @param {String} pos
26922          */
26923         "rotate" : true,
26924         /**
26925          * @event inspect
26926          * Fire when inspect the file
26927          * @param {Roo.bootstrap.UploadCropbox} this
26928          * @param {Object} file
26929          */
26930         "inspect" : true,
26931         /**
26932          * @event upload
26933          * Fire when xhr upload the file
26934          * @param {Roo.bootstrap.UploadCropbox} this
26935          * @param {Object} data
26936          */
26937         "upload" : true,
26938         /**
26939          * @event arrange
26940          * Fire when arrange the file data
26941          * @param {Roo.bootstrap.UploadCropbox} this
26942          * @param {Object} formData
26943          */
26944         "arrange" : true
26945     });
26946     
26947     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26948 };
26949
26950 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26951     
26952     emptyText : 'Click to upload image',
26953     rotateNotify : 'Image is too small to rotate',
26954     errorTimeout : 3000,
26955     scale : 0,
26956     baseScale : 1,
26957     rotate : 0,
26958     dragable : false,
26959     pinching : false,
26960     mouseX : 0,
26961     mouseY : 0,
26962     cropData : false,
26963     minWidth : 300,
26964     minHeight : 300,
26965     file : false,
26966     exif : {},
26967     baseRotate : 1,
26968     cropType : 'image/jpeg',
26969     buttons : false,
26970     canvasLoaded : false,
26971     isDocument : false,
26972     method : 'POST',
26973     paramName : 'imageUpload',
26974     loadMask : true,
26975     loadingText : 'Loading...',
26976     maskEl : false,
26977     
26978     getAutoCreate : function()
26979     {
26980         var cfg = {
26981             tag : 'div',
26982             cls : 'roo-upload-cropbox',
26983             cn : [
26984                 {
26985                     tag : 'input',
26986                     cls : 'roo-upload-cropbox-selector',
26987                     type : 'file'
26988                 },
26989                 {
26990                     tag : 'div',
26991                     cls : 'roo-upload-cropbox-body',
26992                     style : 'cursor:pointer',
26993                     cn : [
26994                         {
26995                             tag : 'div',
26996                             cls : 'roo-upload-cropbox-preview'
26997                         },
26998                         {
26999                             tag : 'div',
27000                             cls : 'roo-upload-cropbox-thumb'
27001                         },
27002                         {
27003                             tag : 'div',
27004                             cls : 'roo-upload-cropbox-empty-notify',
27005                             html : this.emptyText
27006                         },
27007                         {
27008                             tag : 'div',
27009                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27010                             html : this.rotateNotify
27011                         }
27012                     ]
27013                 },
27014                 {
27015                     tag : 'div',
27016                     cls : 'roo-upload-cropbox-footer',
27017                     cn : {
27018                         tag : 'div',
27019                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27020                         cn : []
27021                     }
27022                 }
27023             ]
27024         };
27025         
27026         return cfg;
27027     },
27028     
27029     onRender : function(ct, position)
27030     {
27031         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27032         
27033         if (this.buttons.length) {
27034             
27035             Roo.each(this.buttons, function(bb) {
27036                 
27037                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27038                 
27039                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27040                 
27041             }, this);
27042         }
27043         
27044         if(this.loadMask){
27045             this.maskEl = this.el;
27046         }
27047     },
27048     
27049     initEvents : function()
27050     {
27051         this.urlAPI = (window.createObjectURL && window) || 
27052                                 (window.URL && URL.revokeObjectURL && URL) || 
27053                                 (window.webkitURL && webkitURL);
27054                         
27055         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27056         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27057         
27058         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27059         this.selectorEl.hide();
27060         
27061         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27062         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27063         
27064         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27065         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27066         this.thumbEl.hide();
27067         
27068         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27069         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070         
27071         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27072         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073         this.errorEl.hide();
27074         
27075         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27076         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27077         this.footerEl.hide();
27078         
27079         this.setThumbBoxSize();
27080         
27081         this.bind();
27082         
27083         this.resize();
27084         
27085         this.fireEvent('initial', this);
27086     },
27087
27088     bind : function()
27089     {
27090         var _this = this;
27091         
27092         window.addEventListener("resize", function() { _this.resize(); } );
27093         
27094         this.bodyEl.on('click', this.beforeSelectFile, this);
27095         
27096         if(Roo.isTouch){
27097             this.bodyEl.on('touchstart', this.onTouchStart, this);
27098             this.bodyEl.on('touchmove', this.onTouchMove, this);
27099             this.bodyEl.on('touchend', this.onTouchEnd, this);
27100         }
27101         
27102         if(!Roo.isTouch){
27103             this.bodyEl.on('mousedown', this.onMouseDown, this);
27104             this.bodyEl.on('mousemove', this.onMouseMove, this);
27105             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27106             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27107             Roo.get(document).on('mouseup', this.onMouseUp, this);
27108         }
27109         
27110         this.selectorEl.on('change', this.onFileSelected, this);
27111     },
27112     
27113     reset : function()
27114     {    
27115         this.scale = 0;
27116         this.baseScale = 1;
27117         this.rotate = 0;
27118         this.baseRotate = 1;
27119         this.dragable = false;
27120         this.pinching = false;
27121         this.mouseX = 0;
27122         this.mouseY = 0;
27123         this.cropData = false;
27124         this.notifyEl.dom.innerHTML = this.emptyText;
27125         
27126         this.selectorEl.dom.value = '';
27127         
27128     },
27129     
27130     resize : function()
27131     {
27132         if(this.fireEvent('resize', this) != false){
27133             this.setThumbBoxPosition();
27134             this.setCanvasPosition();
27135         }
27136     },
27137     
27138     onFooterButtonClick : function(e, el, o, type)
27139     {
27140         switch (type) {
27141             case 'rotate-left' :
27142                 this.onRotateLeft(e);
27143                 break;
27144             case 'rotate-right' :
27145                 this.onRotateRight(e);
27146                 break;
27147             case 'picture' :
27148                 this.beforeSelectFile(e);
27149                 break;
27150             case 'trash' :
27151                 this.trash(e);
27152                 break;
27153             case 'crop' :
27154                 this.crop(e);
27155                 break;
27156             case 'download' :
27157                 this.download(e);
27158                 break;
27159             default :
27160                 break;
27161         }
27162         
27163         this.fireEvent('footerbuttonclick', this, type);
27164     },
27165     
27166     beforeSelectFile : function(e)
27167     {
27168         e.preventDefault();
27169         
27170         if(this.fireEvent('beforeselectfile', this) != false){
27171             this.selectorEl.dom.click();
27172         }
27173     },
27174     
27175     onFileSelected : function(e)
27176     {
27177         e.preventDefault();
27178         
27179         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27180             return;
27181         }
27182         
27183         var file = this.selectorEl.dom.files[0];
27184         
27185         if(this.fireEvent('inspect', this, file) != false){
27186             this.prepare(file);
27187         }
27188         
27189     },
27190     
27191     trash : function(e)
27192     {
27193         this.fireEvent('trash', this);
27194     },
27195     
27196     download : function(e)
27197     {
27198         this.fireEvent('download', this);
27199     },
27200     
27201     loadCanvas : function(src)
27202     {   
27203         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27204             
27205             this.reset();
27206             
27207             this.imageEl = document.createElement('img');
27208             
27209             var _this = this;
27210             
27211             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27212             
27213             this.imageEl.src = src;
27214         }
27215     },
27216     
27217     onLoadCanvas : function()
27218     {   
27219         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27220         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27221         
27222         this.bodyEl.un('click', this.beforeSelectFile, this);
27223         
27224         this.notifyEl.hide();
27225         this.thumbEl.show();
27226         this.footerEl.show();
27227         
27228         this.baseRotateLevel();
27229         
27230         if(this.isDocument){
27231             this.setThumbBoxSize();
27232         }
27233         
27234         this.setThumbBoxPosition();
27235         
27236         this.baseScaleLevel();
27237         
27238         this.draw();
27239         
27240         this.resize();
27241         
27242         this.canvasLoaded = true;
27243         
27244         if(this.loadMask){
27245             this.maskEl.unmask();
27246         }
27247         
27248     },
27249     
27250     setCanvasPosition : function()
27251     {   
27252         if(!this.canvasEl){
27253             return;
27254         }
27255         
27256         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27257         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27258         
27259         this.previewEl.setLeft(pw);
27260         this.previewEl.setTop(ph);
27261         
27262     },
27263     
27264     onMouseDown : function(e)
27265     {   
27266         e.stopEvent();
27267         
27268         this.dragable = true;
27269         this.pinching = false;
27270         
27271         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27272             this.dragable = false;
27273             return;
27274         }
27275         
27276         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27277         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27278         
27279     },
27280     
27281     onMouseMove : function(e)
27282     {   
27283         e.stopEvent();
27284         
27285         if(!this.canvasLoaded){
27286             return;
27287         }
27288         
27289         if (!this.dragable){
27290             return;
27291         }
27292         
27293         var minX = Math.ceil(this.thumbEl.getLeft(true));
27294         var minY = Math.ceil(this.thumbEl.getTop(true));
27295         
27296         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27297         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27298         
27299         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27300         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27301         
27302         x = x - this.mouseX;
27303         y = y - this.mouseY;
27304         
27305         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27306         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27307         
27308         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27309         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27310         
27311         this.previewEl.setLeft(bgX);
27312         this.previewEl.setTop(bgY);
27313         
27314         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27315         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27316     },
27317     
27318     onMouseUp : function(e)
27319     {   
27320         e.stopEvent();
27321         
27322         this.dragable = false;
27323     },
27324     
27325     onMouseWheel : function(e)
27326     {   
27327         e.stopEvent();
27328         
27329         this.startScale = this.scale;
27330         
27331         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27332         
27333         if(!this.zoomable()){
27334             this.scale = this.startScale;
27335             return;
27336         }
27337         
27338         this.draw();
27339         
27340         return;
27341     },
27342     
27343     zoomable : function()
27344     {
27345         var minScale = this.thumbEl.getWidth() / this.minWidth;
27346         
27347         if(this.minWidth < this.minHeight){
27348             minScale = this.thumbEl.getHeight() / this.minHeight;
27349         }
27350         
27351         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27352         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27353         
27354         if(
27355                 this.isDocument &&
27356                 (this.rotate == 0 || this.rotate == 180) && 
27357                 (
27358                     width > this.imageEl.OriginWidth || 
27359                     height > this.imageEl.OriginHeight ||
27360                     (width < this.minWidth && height < this.minHeight)
27361                 )
27362         ){
27363             return false;
27364         }
27365         
27366         if(
27367                 this.isDocument &&
27368                 (this.rotate == 90 || this.rotate == 270) && 
27369                 (
27370                     width > this.imageEl.OriginWidth || 
27371                     height > this.imageEl.OriginHeight ||
27372                     (width < this.minHeight && height < this.minWidth)
27373                 )
27374         ){
27375             return false;
27376         }
27377         
27378         if(
27379                 !this.isDocument &&
27380                 (this.rotate == 0 || this.rotate == 180) && 
27381                 (
27382                     width < this.minWidth || 
27383                     width > this.imageEl.OriginWidth || 
27384                     height < this.minHeight || 
27385                     height > this.imageEl.OriginHeight
27386                 )
27387         ){
27388             return false;
27389         }
27390         
27391         if(
27392                 !this.isDocument &&
27393                 (this.rotate == 90 || this.rotate == 270) && 
27394                 (
27395                     width < this.minHeight || 
27396                     width > this.imageEl.OriginWidth || 
27397                     height < this.minWidth || 
27398                     height > this.imageEl.OriginHeight
27399                 )
27400         ){
27401             return false;
27402         }
27403         
27404         return true;
27405         
27406     },
27407     
27408     onRotateLeft : function(e)
27409     {   
27410         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27411             
27412             var minScale = this.thumbEl.getWidth() / this.minWidth;
27413             
27414             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27415             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27416             
27417             this.startScale = this.scale;
27418             
27419             while (this.getScaleLevel() < minScale){
27420             
27421                 this.scale = this.scale + 1;
27422                 
27423                 if(!this.zoomable()){
27424                     break;
27425                 }
27426                 
27427                 if(
27428                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27429                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27430                 ){
27431                     continue;
27432                 }
27433                 
27434                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27435
27436                 this.draw();
27437                 
27438                 return;
27439             }
27440             
27441             this.scale = this.startScale;
27442             
27443             this.onRotateFail();
27444             
27445             return false;
27446         }
27447         
27448         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27449
27450         if(this.isDocument){
27451             this.setThumbBoxSize();
27452             this.setThumbBoxPosition();
27453             this.setCanvasPosition();
27454         }
27455         
27456         this.draw();
27457         
27458         this.fireEvent('rotate', this, 'left');
27459         
27460     },
27461     
27462     onRotateRight : function(e)
27463     {
27464         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27465             
27466             var minScale = this.thumbEl.getWidth() / this.minWidth;
27467         
27468             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27469             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27470             
27471             this.startScale = this.scale;
27472             
27473             while (this.getScaleLevel() < minScale){
27474             
27475                 this.scale = this.scale + 1;
27476                 
27477                 if(!this.zoomable()){
27478                     break;
27479                 }
27480                 
27481                 if(
27482                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27483                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27484                 ){
27485                     continue;
27486                 }
27487                 
27488                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27489
27490                 this.draw();
27491                 
27492                 return;
27493             }
27494             
27495             this.scale = this.startScale;
27496             
27497             this.onRotateFail();
27498             
27499             return false;
27500         }
27501         
27502         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27503
27504         if(this.isDocument){
27505             this.setThumbBoxSize();
27506             this.setThumbBoxPosition();
27507             this.setCanvasPosition();
27508         }
27509         
27510         this.draw();
27511         
27512         this.fireEvent('rotate', this, 'right');
27513     },
27514     
27515     onRotateFail : function()
27516     {
27517         this.errorEl.show(true);
27518         
27519         var _this = this;
27520         
27521         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27522     },
27523     
27524     draw : function()
27525     {
27526         this.previewEl.dom.innerHTML = '';
27527         
27528         var canvasEl = document.createElement("canvas");
27529         
27530         var contextEl = canvasEl.getContext("2d");
27531         
27532         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27533         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27534         var center = this.imageEl.OriginWidth / 2;
27535         
27536         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27537             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27538             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27539             center = this.imageEl.OriginHeight / 2;
27540         }
27541         
27542         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27543         
27544         contextEl.translate(center, center);
27545         contextEl.rotate(this.rotate * Math.PI / 180);
27546
27547         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27548         
27549         this.canvasEl = document.createElement("canvas");
27550         
27551         this.contextEl = this.canvasEl.getContext("2d");
27552         
27553         switch (this.rotate) {
27554             case 0 :
27555                 
27556                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27557                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27558                 
27559                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27560                 
27561                 break;
27562             case 90 : 
27563                 
27564                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27565                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27566                 
27567                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27568                     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);
27569                     break;
27570                 }
27571                 
27572                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27573                 
27574                 break;
27575             case 180 :
27576                 
27577                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27578                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27579                 
27580                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27581                     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);
27582                     break;
27583                 }
27584                 
27585                 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);
27586                 
27587                 break;
27588             case 270 :
27589                 
27590                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27591                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27592         
27593                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27594                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27595                     break;
27596                 }
27597                 
27598                 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);
27599                 
27600                 break;
27601             default : 
27602                 break;
27603         }
27604         
27605         this.previewEl.appendChild(this.canvasEl);
27606         
27607         this.setCanvasPosition();
27608     },
27609     
27610     crop : function()
27611     {
27612         if(!this.canvasLoaded){
27613             return;
27614         }
27615         
27616         var imageCanvas = document.createElement("canvas");
27617         
27618         var imageContext = imageCanvas.getContext("2d");
27619         
27620         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27621         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27622         
27623         var center = imageCanvas.width / 2;
27624         
27625         imageContext.translate(center, center);
27626         
27627         imageContext.rotate(this.rotate * Math.PI / 180);
27628         
27629         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27630         
27631         var canvas = document.createElement("canvas");
27632         
27633         var context = canvas.getContext("2d");
27634                 
27635         canvas.width = this.minWidth;
27636         canvas.height = this.minHeight;
27637
27638         switch (this.rotate) {
27639             case 0 :
27640                 
27641                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27642                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27643                 
27644                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27645                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27646                 
27647                 var targetWidth = this.minWidth - 2 * x;
27648                 var targetHeight = this.minHeight - 2 * y;
27649                 
27650                 var scale = 1;
27651                 
27652                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27653                     scale = targetWidth / width;
27654                 }
27655                 
27656                 if(x > 0 && y == 0){
27657                     scale = targetHeight / height;
27658                 }
27659                 
27660                 if(x > 0 && y > 0){
27661                     scale = targetWidth / width;
27662                     
27663                     if(width < height){
27664                         scale = targetHeight / height;
27665                     }
27666                 }
27667                 
27668                 context.scale(scale, scale);
27669                 
27670                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27671                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27672
27673                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27674                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27675
27676                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27677                 
27678                 break;
27679             case 90 : 
27680                 
27681                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27682                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27683                 
27684                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27685                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27686                 
27687                 var targetWidth = this.minWidth - 2 * x;
27688                 var targetHeight = this.minHeight - 2 * y;
27689                 
27690                 var scale = 1;
27691                 
27692                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27693                     scale = targetWidth / width;
27694                 }
27695                 
27696                 if(x > 0 && y == 0){
27697                     scale = targetHeight / height;
27698                 }
27699                 
27700                 if(x > 0 && y > 0){
27701                     scale = targetWidth / width;
27702                     
27703                     if(width < height){
27704                         scale = targetHeight / height;
27705                     }
27706                 }
27707                 
27708                 context.scale(scale, scale);
27709                 
27710                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27711                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27712
27713                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27714                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27715                 
27716                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27717                 
27718                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27719                 
27720                 break;
27721             case 180 :
27722                 
27723                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27724                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27725                 
27726                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27727                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27728                 
27729                 var targetWidth = this.minWidth - 2 * x;
27730                 var targetHeight = this.minHeight - 2 * y;
27731                 
27732                 var scale = 1;
27733                 
27734                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27735                     scale = targetWidth / width;
27736                 }
27737                 
27738                 if(x > 0 && y == 0){
27739                     scale = targetHeight / height;
27740                 }
27741                 
27742                 if(x > 0 && y > 0){
27743                     scale = targetWidth / width;
27744                     
27745                     if(width < height){
27746                         scale = targetHeight / height;
27747                     }
27748                 }
27749                 
27750                 context.scale(scale, scale);
27751                 
27752                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27753                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27754
27755                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27756                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27757
27758                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27759                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27760                 
27761                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27762                 
27763                 break;
27764             case 270 :
27765                 
27766                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27767                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27768                 
27769                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27770                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27771                 
27772                 var targetWidth = this.minWidth - 2 * x;
27773                 var targetHeight = this.minHeight - 2 * y;
27774                 
27775                 var scale = 1;
27776                 
27777                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27778                     scale = targetWidth / width;
27779                 }
27780                 
27781                 if(x > 0 && y == 0){
27782                     scale = targetHeight / height;
27783                 }
27784                 
27785                 if(x > 0 && y > 0){
27786                     scale = targetWidth / width;
27787                     
27788                     if(width < height){
27789                         scale = targetHeight / height;
27790                     }
27791                 }
27792                 
27793                 context.scale(scale, scale);
27794                 
27795                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27796                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27797
27798                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27799                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27800                 
27801                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27802                 
27803                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27804                 
27805                 break;
27806             default : 
27807                 break;
27808         }
27809         
27810         this.cropData = canvas.toDataURL(this.cropType);
27811         
27812         if(this.fireEvent('crop', this, this.cropData) !== false){
27813             this.process(this.file, this.cropData);
27814         }
27815         
27816         return;
27817         
27818     },
27819     
27820     setThumbBoxSize : function()
27821     {
27822         var width, height;
27823         
27824         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27825             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27826             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27827             
27828             this.minWidth = width;
27829             this.minHeight = height;
27830             
27831             if(this.rotate == 90 || this.rotate == 270){
27832                 this.minWidth = height;
27833                 this.minHeight = width;
27834             }
27835         }
27836         
27837         height = 300;
27838         width = Math.ceil(this.minWidth * height / this.minHeight);
27839         
27840         if(this.minWidth > this.minHeight){
27841             width = 300;
27842             height = Math.ceil(this.minHeight * width / this.minWidth);
27843         }
27844         
27845         this.thumbEl.setStyle({
27846             width : width + 'px',
27847             height : height + 'px'
27848         });
27849
27850         return;
27851             
27852     },
27853     
27854     setThumbBoxPosition : function()
27855     {
27856         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27857         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27858         
27859         this.thumbEl.setLeft(x);
27860         this.thumbEl.setTop(y);
27861         
27862     },
27863     
27864     baseRotateLevel : function()
27865     {
27866         this.baseRotate = 1;
27867         
27868         if(
27869                 typeof(this.exif) != 'undefined' &&
27870                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27871                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27872         ){
27873             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27874         }
27875         
27876         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27877         
27878     },
27879     
27880     baseScaleLevel : function()
27881     {
27882         var width, height;
27883         
27884         if(this.isDocument){
27885             
27886             if(this.baseRotate == 6 || this.baseRotate == 8){
27887             
27888                 height = this.thumbEl.getHeight();
27889                 this.baseScale = height / this.imageEl.OriginWidth;
27890
27891                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27892                     width = this.thumbEl.getWidth();
27893                     this.baseScale = width / this.imageEl.OriginHeight;
27894                 }
27895
27896                 return;
27897             }
27898
27899             height = this.thumbEl.getHeight();
27900             this.baseScale = height / this.imageEl.OriginHeight;
27901
27902             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27903                 width = this.thumbEl.getWidth();
27904                 this.baseScale = width / this.imageEl.OriginWidth;
27905             }
27906
27907             return;
27908         }
27909         
27910         if(this.baseRotate == 6 || this.baseRotate == 8){
27911             
27912             width = this.thumbEl.getHeight();
27913             this.baseScale = width / this.imageEl.OriginHeight;
27914             
27915             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27916                 height = this.thumbEl.getWidth();
27917                 this.baseScale = height / this.imageEl.OriginHeight;
27918             }
27919             
27920             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27921                 height = this.thumbEl.getWidth();
27922                 this.baseScale = height / this.imageEl.OriginHeight;
27923                 
27924                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27925                     width = this.thumbEl.getHeight();
27926                     this.baseScale = width / this.imageEl.OriginWidth;
27927                 }
27928             }
27929             
27930             return;
27931         }
27932         
27933         width = this.thumbEl.getWidth();
27934         this.baseScale = width / this.imageEl.OriginWidth;
27935         
27936         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27937             height = this.thumbEl.getHeight();
27938             this.baseScale = height / this.imageEl.OriginHeight;
27939         }
27940         
27941         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27942             
27943             height = this.thumbEl.getHeight();
27944             this.baseScale = height / this.imageEl.OriginHeight;
27945             
27946             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27947                 width = this.thumbEl.getWidth();
27948                 this.baseScale = width / this.imageEl.OriginWidth;
27949             }
27950             
27951         }
27952         
27953         return;
27954     },
27955     
27956     getScaleLevel : function()
27957     {
27958         return this.baseScale * Math.pow(1.1, this.scale);
27959     },
27960     
27961     onTouchStart : function(e)
27962     {
27963         if(!this.canvasLoaded){
27964             this.beforeSelectFile(e);
27965             return;
27966         }
27967         
27968         var touches = e.browserEvent.touches;
27969         
27970         if(!touches){
27971             return;
27972         }
27973         
27974         if(touches.length == 1){
27975             this.onMouseDown(e);
27976             return;
27977         }
27978         
27979         if(touches.length != 2){
27980             return;
27981         }
27982         
27983         var coords = [];
27984         
27985         for(var i = 0, finger; finger = touches[i]; i++){
27986             coords.push(finger.pageX, finger.pageY);
27987         }
27988         
27989         var x = Math.pow(coords[0] - coords[2], 2);
27990         var y = Math.pow(coords[1] - coords[3], 2);
27991         
27992         this.startDistance = Math.sqrt(x + y);
27993         
27994         this.startScale = this.scale;
27995         
27996         this.pinching = true;
27997         this.dragable = false;
27998         
27999     },
28000     
28001     onTouchMove : function(e)
28002     {
28003         if(!this.pinching && !this.dragable){
28004             return;
28005         }
28006         
28007         var touches = e.browserEvent.touches;
28008         
28009         if(!touches){
28010             return;
28011         }
28012         
28013         if(this.dragable){
28014             this.onMouseMove(e);
28015             return;
28016         }
28017         
28018         var coords = [];
28019         
28020         for(var i = 0, finger; finger = touches[i]; i++){
28021             coords.push(finger.pageX, finger.pageY);
28022         }
28023         
28024         var x = Math.pow(coords[0] - coords[2], 2);
28025         var y = Math.pow(coords[1] - coords[3], 2);
28026         
28027         this.endDistance = Math.sqrt(x + y);
28028         
28029         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28030         
28031         if(!this.zoomable()){
28032             this.scale = this.startScale;
28033             return;
28034         }
28035         
28036         this.draw();
28037         
28038     },
28039     
28040     onTouchEnd : function(e)
28041     {
28042         this.pinching = false;
28043         this.dragable = false;
28044         
28045     },
28046     
28047     process : function(file, crop)
28048     {
28049         if(this.loadMask){
28050             this.maskEl.mask(this.loadingText);
28051         }
28052         
28053         this.xhr = new XMLHttpRequest();
28054         
28055         file.xhr = this.xhr;
28056
28057         this.xhr.open(this.method, this.url, true);
28058         
28059         var headers = {
28060             "Accept": "application/json",
28061             "Cache-Control": "no-cache",
28062             "X-Requested-With": "XMLHttpRequest"
28063         };
28064         
28065         for (var headerName in headers) {
28066             var headerValue = headers[headerName];
28067             if (headerValue) {
28068                 this.xhr.setRequestHeader(headerName, headerValue);
28069             }
28070         }
28071         
28072         var _this = this;
28073         
28074         this.xhr.onload = function()
28075         {
28076             _this.xhrOnLoad(_this.xhr);
28077         }
28078         
28079         this.xhr.onerror = function()
28080         {
28081             _this.xhrOnError(_this.xhr);
28082         }
28083         
28084         var formData = new FormData();
28085
28086         formData.append('returnHTML', 'NO');
28087         
28088         if(crop){
28089             formData.append('crop', crop);
28090         }
28091         
28092         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28093             formData.append(this.paramName, file, file.name);
28094         }
28095         
28096         if(typeof(file.filename) != 'undefined'){
28097             formData.append('filename', file.filename);
28098         }
28099         
28100         if(typeof(file.mimetype) != 'undefined'){
28101             formData.append('mimetype', file.mimetype);
28102         }
28103         
28104         if(this.fireEvent('arrange', this, formData) != false){
28105             this.xhr.send(formData);
28106         };
28107     },
28108     
28109     xhrOnLoad : function(xhr)
28110     {
28111         if(this.loadMask){
28112             this.maskEl.unmask();
28113         }
28114         
28115         if (xhr.readyState !== 4) {
28116             this.fireEvent('exception', this, xhr);
28117             return;
28118         }
28119
28120         var response = Roo.decode(xhr.responseText);
28121         
28122         if(!response.success){
28123             this.fireEvent('exception', this, xhr);
28124             return;
28125         }
28126         
28127         var response = Roo.decode(xhr.responseText);
28128         
28129         this.fireEvent('upload', this, response);
28130         
28131     },
28132     
28133     xhrOnError : function()
28134     {
28135         if(this.loadMask){
28136             this.maskEl.unmask();
28137         }
28138         
28139         Roo.log('xhr on error');
28140         
28141         var response = Roo.decode(xhr.responseText);
28142           
28143         Roo.log(response);
28144         
28145     },
28146     
28147     prepare : function(file)
28148     {   
28149         if(this.loadMask){
28150             this.maskEl.mask(this.loadingText);
28151         }
28152         
28153         this.file = false;
28154         this.exif = {};
28155         
28156         if(typeof(file) === 'string'){
28157             this.loadCanvas(file);
28158             return;
28159         }
28160         
28161         if(!file || !this.urlAPI){
28162             return;
28163         }
28164         
28165         this.file = file;
28166         this.cropType = file.type;
28167         
28168         var _this = this;
28169         
28170         if(this.fireEvent('prepare', this, this.file) != false){
28171             
28172             var reader = new FileReader();
28173             
28174             reader.onload = function (e) {
28175                 if (e.target.error) {
28176                     Roo.log(e.target.error);
28177                     return;
28178                 }
28179                 
28180                 var buffer = e.target.result,
28181                     dataView = new DataView(buffer),
28182                     offset = 2,
28183                     maxOffset = dataView.byteLength - 4,
28184                     markerBytes,
28185                     markerLength;
28186                 
28187                 if (dataView.getUint16(0) === 0xffd8) {
28188                     while (offset < maxOffset) {
28189                         markerBytes = dataView.getUint16(offset);
28190                         
28191                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28192                             markerLength = dataView.getUint16(offset + 2) + 2;
28193                             if (offset + markerLength > dataView.byteLength) {
28194                                 Roo.log('Invalid meta data: Invalid segment size.');
28195                                 break;
28196                             }
28197                             
28198                             if(markerBytes == 0xffe1){
28199                                 _this.parseExifData(
28200                                     dataView,
28201                                     offset,
28202                                     markerLength
28203                                 );
28204                             }
28205                             
28206                             offset += markerLength;
28207                             
28208                             continue;
28209                         }
28210                         
28211                         break;
28212                     }
28213                     
28214                 }
28215                 
28216                 var url = _this.urlAPI.createObjectURL(_this.file);
28217                 
28218                 _this.loadCanvas(url);
28219                 
28220                 return;
28221             }
28222             
28223             reader.readAsArrayBuffer(this.file);
28224             
28225         }
28226         
28227     },
28228     
28229     parseExifData : function(dataView, offset, length)
28230     {
28231         var tiffOffset = offset + 10,
28232             littleEndian,
28233             dirOffset;
28234     
28235         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28236             // No Exif data, might be XMP data instead
28237             return;
28238         }
28239         
28240         // Check for the ASCII code for "Exif" (0x45786966):
28241         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28242             // No Exif data, might be XMP data instead
28243             return;
28244         }
28245         if (tiffOffset + 8 > dataView.byteLength) {
28246             Roo.log('Invalid Exif data: Invalid segment size.');
28247             return;
28248         }
28249         // Check for the two null bytes:
28250         if (dataView.getUint16(offset + 8) !== 0x0000) {
28251             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28252             return;
28253         }
28254         // Check the byte alignment:
28255         switch (dataView.getUint16(tiffOffset)) {
28256         case 0x4949:
28257             littleEndian = true;
28258             break;
28259         case 0x4D4D:
28260             littleEndian = false;
28261             break;
28262         default:
28263             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28264             return;
28265         }
28266         // Check for the TIFF tag marker (0x002A):
28267         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28268             Roo.log('Invalid Exif data: Missing TIFF marker.');
28269             return;
28270         }
28271         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28272         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28273         
28274         this.parseExifTags(
28275             dataView,
28276             tiffOffset,
28277             tiffOffset + dirOffset,
28278             littleEndian
28279         );
28280     },
28281     
28282     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28283     {
28284         var tagsNumber,
28285             dirEndOffset,
28286             i;
28287         if (dirOffset + 6 > dataView.byteLength) {
28288             Roo.log('Invalid Exif data: Invalid directory offset.');
28289             return;
28290         }
28291         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28292         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28293         if (dirEndOffset + 4 > dataView.byteLength) {
28294             Roo.log('Invalid Exif data: Invalid directory size.');
28295             return;
28296         }
28297         for (i = 0; i < tagsNumber; i += 1) {
28298             this.parseExifTag(
28299                 dataView,
28300                 tiffOffset,
28301                 dirOffset + 2 + 12 * i, // tag offset
28302                 littleEndian
28303             );
28304         }
28305         // Return the offset to the next directory:
28306         return dataView.getUint32(dirEndOffset, littleEndian);
28307     },
28308     
28309     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28310     {
28311         var tag = dataView.getUint16(offset, littleEndian);
28312         
28313         this.exif[tag] = this.getExifValue(
28314             dataView,
28315             tiffOffset,
28316             offset,
28317             dataView.getUint16(offset + 2, littleEndian), // tag type
28318             dataView.getUint32(offset + 4, littleEndian), // tag length
28319             littleEndian
28320         );
28321     },
28322     
28323     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28324     {
28325         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28326             tagSize,
28327             dataOffset,
28328             values,
28329             i,
28330             str,
28331             c;
28332     
28333         if (!tagType) {
28334             Roo.log('Invalid Exif data: Invalid tag type.');
28335             return;
28336         }
28337         
28338         tagSize = tagType.size * length;
28339         // Determine if the value is contained in the dataOffset bytes,
28340         // or if the value at the dataOffset is a pointer to the actual data:
28341         dataOffset = tagSize > 4 ?
28342                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28343         if (dataOffset + tagSize > dataView.byteLength) {
28344             Roo.log('Invalid Exif data: Invalid data offset.');
28345             return;
28346         }
28347         if (length === 1) {
28348             return tagType.getValue(dataView, dataOffset, littleEndian);
28349         }
28350         values = [];
28351         for (i = 0; i < length; i += 1) {
28352             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28353         }
28354         
28355         if (tagType.ascii) {
28356             str = '';
28357             // Concatenate the chars:
28358             for (i = 0; i < values.length; i += 1) {
28359                 c = values[i];
28360                 // Ignore the terminating NULL byte(s):
28361                 if (c === '\u0000') {
28362                     break;
28363                 }
28364                 str += c;
28365             }
28366             return str;
28367         }
28368         return values;
28369     }
28370     
28371 });
28372
28373 Roo.apply(Roo.bootstrap.UploadCropbox, {
28374     tags : {
28375         'Orientation': 0x0112
28376     },
28377     
28378     Orientation: {
28379             1: 0, //'top-left',
28380 //            2: 'top-right',
28381             3: 180, //'bottom-right',
28382 //            4: 'bottom-left',
28383 //            5: 'left-top',
28384             6: 90, //'right-top',
28385 //            7: 'right-bottom',
28386             8: 270 //'left-bottom'
28387     },
28388     
28389     exifTagTypes : {
28390         // byte, 8-bit unsigned int:
28391         1: {
28392             getValue: function (dataView, dataOffset) {
28393                 return dataView.getUint8(dataOffset);
28394             },
28395             size: 1
28396         },
28397         // ascii, 8-bit byte:
28398         2: {
28399             getValue: function (dataView, dataOffset) {
28400                 return String.fromCharCode(dataView.getUint8(dataOffset));
28401             },
28402             size: 1,
28403             ascii: true
28404         },
28405         // short, 16 bit int:
28406         3: {
28407             getValue: function (dataView, dataOffset, littleEndian) {
28408                 return dataView.getUint16(dataOffset, littleEndian);
28409             },
28410             size: 2
28411         },
28412         // long, 32 bit int:
28413         4: {
28414             getValue: function (dataView, dataOffset, littleEndian) {
28415                 return dataView.getUint32(dataOffset, littleEndian);
28416             },
28417             size: 4
28418         },
28419         // rational = two long values, first is numerator, second is denominator:
28420         5: {
28421             getValue: function (dataView, dataOffset, littleEndian) {
28422                 return dataView.getUint32(dataOffset, littleEndian) /
28423                     dataView.getUint32(dataOffset + 4, littleEndian);
28424             },
28425             size: 8
28426         },
28427         // slong, 32 bit signed int:
28428         9: {
28429             getValue: function (dataView, dataOffset, littleEndian) {
28430                 return dataView.getInt32(dataOffset, littleEndian);
28431             },
28432             size: 4
28433         },
28434         // srational, two slongs, first is numerator, second is denominator:
28435         10: {
28436             getValue: function (dataView, dataOffset, littleEndian) {
28437                 return dataView.getInt32(dataOffset, littleEndian) /
28438                     dataView.getInt32(dataOffset + 4, littleEndian);
28439             },
28440             size: 8
28441         }
28442     },
28443     
28444     footer : {
28445         STANDARD : [
28446             {
28447                 tag : 'div',
28448                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28449                 action : 'rotate-left',
28450                 cn : [
28451                     {
28452                         tag : 'button',
28453                         cls : 'btn btn-default',
28454                         html : '<i class="fa fa-undo"></i>'
28455                     }
28456                 ]
28457             },
28458             {
28459                 tag : 'div',
28460                 cls : 'btn-group roo-upload-cropbox-picture',
28461                 action : 'picture',
28462                 cn : [
28463                     {
28464                         tag : 'button',
28465                         cls : 'btn btn-default',
28466                         html : '<i class="fa fa-picture-o"></i>'
28467                     }
28468                 ]
28469             },
28470             {
28471                 tag : 'div',
28472                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28473                 action : 'rotate-right',
28474                 cn : [
28475                     {
28476                         tag : 'button',
28477                         cls : 'btn btn-default',
28478                         html : '<i class="fa fa-repeat"></i>'
28479                     }
28480                 ]
28481             }
28482         ],
28483         DOCUMENT : [
28484             {
28485                 tag : 'div',
28486                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28487                 action : 'rotate-left',
28488                 cn : [
28489                     {
28490                         tag : 'button',
28491                         cls : 'btn btn-default',
28492                         html : '<i class="fa fa-undo"></i>'
28493                     }
28494                 ]
28495             },
28496             {
28497                 tag : 'div',
28498                 cls : 'btn-group roo-upload-cropbox-download',
28499                 action : 'download',
28500                 cn : [
28501                     {
28502                         tag : 'button',
28503                         cls : 'btn btn-default',
28504                         html : '<i class="fa fa-download"></i>'
28505                     }
28506                 ]
28507             },
28508             {
28509                 tag : 'div',
28510                 cls : 'btn-group roo-upload-cropbox-crop',
28511                 action : 'crop',
28512                 cn : [
28513                     {
28514                         tag : 'button',
28515                         cls : 'btn btn-default',
28516                         html : '<i class="fa fa-crop"></i>'
28517                     }
28518                 ]
28519             },
28520             {
28521                 tag : 'div',
28522                 cls : 'btn-group roo-upload-cropbox-trash',
28523                 action : 'trash',
28524                 cn : [
28525                     {
28526                         tag : 'button',
28527                         cls : 'btn btn-default',
28528                         html : '<i class="fa fa-trash"></i>'
28529                     }
28530                 ]
28531             },
28532             {
28533                 tag : 'div',
28534                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28535                 action : 'rotate-right',
28536                 cn : [
28537                     {
28538                         tag : 'button',
28539                         cls : 'btn btn-default',
28540                         html : '<i class="fa fa-repeat"></i>'
28541                     }
28542                 ]
28543             }
28544         ],
28545         ROTATOR : [
28546             {
28547                 tag : 'div',
28548                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28549                 action : 'rotate-left',
28550                 cn : [
28551                     {
28552                         tag : 'button',
28553                         cls : 'btn btn-default',
28554                         html : '<i class="fa fa-undo"></i>'
28555                     }
28556                 ]
28557             },
28558             {
28559                 tag : 'div',
28560                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28561                 action : 'rotate-right',
28562                 cn : [
28563                     {
28564                         tag : 'button',
28565                         cls : 'btn btn-default',
28566                         html : '<i class="fa fa-repeat"></i>'
28567                     }
28568                 ]
28569             }
28570         ]
28571     }
28572 });
28573
28574 /*
28575 * Licence: LGPL
28576 */
28577
28578 /**
28579  * @class Roo.bootstrap.DocumentManager
28580  * @extends Roo.bootstrap.Component
28581  * Bootstrap DocumentManager class
28582  * @cfg {String} paramName default 'imageUpload'
28583  * @cfg {String} toolTipName default 'filename'
28584  * @cfg {String} method default POST
28585  * @cfg {String} url action url
28586  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28587  * @cfg {Boolean} multiple multiple upload default true
28588  * @cfg {Number} thumbSize default 300
28589  * @cfg {String} fieldLabel
28590  * @cfg {Number} labelWidth default 4
28591  * @cfg {String} labelAlign (left|top) default left
28592  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28593 * @cfg {Number} labellg set the width of label (1-12)
28594  * @cfg {Number} labelmd set the width of label (1-12)
28595  * @cfg {Number} labelsm set the width of label (1-12)
28596  * @cfg {Number} labelxs set the width of label (1-12)
28597  * 
28598  * @constructor
28599  * Create a new DocumentManager
28600  * @param {Object} config The config object
28601  */
28602
28603 Roo.bootstrap.DocumentManager = function(config){
28604     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28605     
28606     this.files = [];
28607     this.delegates = [];
28608     
28609     this.addEvents({
28610         /**
28611          * @event initial
28612          * Fire when initial the DocumentManager
28613          * @param {Roo.bootstrap.DocumentManager} this
28614          */
28615         "initial" : true,
28616         /**
28617          * @event inspect
28618          * inspect selected file
28619          * @param {Roo.bootstrap.DocumentManager} this
28620          * @param {File} file
28621          */
28622         "inspect" : true,
28623         /**
28624          * @event exception
28625          * Fire when xhr load exception
28626          * @param {Roo.bootstrap.DocumentManager} this
28627          * @param {XMLHttpRequest} xhr
28628          */
28629         "exception" : true,
28630         /**
28631          * @event afterupload
28632          * Fire when xhr load exception
28633          * @param {Roo.bootstrap.DocumentManager} this
28634          * @param {XMLHttpRequest} xhr
28635          */
28636         "afterupload" : true,
28637         /**
28638          * @event prepare
28639          * prepare the form data
28640          * @param {Roo.bootstrap.DocumentManager} this
28641          * @param {Object} formData
28642          */
28643         "prepare" : true,
28644         /**
28645          * @event remove
28646          * Fire when remove the file
28647          * @param {Roo.bootstrap.DocumentManager} this
28648          * @param {Object} file
28649          */
28650         "remove" : true,
28651         /**
28652          * @event refresh
28653          * Fire after refresh the file
28654          * @param {Roo.bootstrap.DocumentManager} this
28655          */
28656         "refresh" : true,
28657         /**
28658          * @event click
28659          * Fire after click the image
28660          * @param {Roo.bootstrap.DocumentManager} this
28661          * @param {Object} file
28662          */
28663         "click" : true,
28664         /**
28665          * @event edit
28666          * Fire when upload a image and editable set to true
28667          * @param {Roo.bootstrap.DocumentManager} this
28668          * @param {Object} file
28669          */
28670         "edit" : true,
28671         /**
28672          * @event beforeselectfile
28673          * Fire before select file
28674          * @param {Roo.bootstrap.DocumentManager} this
28675          */
28676         "beforeselectfile" : true,
28677         /**
28678          * @event process
28679          * Fire before process file
28680          * @param {Roo.bootstrap.DocumentManager} this
28681          * @param {Object} file
28682          */
28683         "process" : true,
28684         /**
28685          * @event previewrendered
28686          * Fire when preview rendered
28687          * @param {Roo.bootstrap.DocumentManager} this
28688          * @param {Object} file
28689          */
28690         "previewrendered" : true
28691         
28692     });
28693 };
28694
28695 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28696     
28697     boxes : 0,
28698     inputName : '',
28699     thumbSize : 300,
28700     multiple : true,
28701     files : false,
28702     method : 'POST',
28703     url : '',
28704     paramName : 'imageUpload',
28705     toolTipName : 'filename',
28706     fieldLabel : '',
28707     labelWidth : 4,
28708     labelAlign : 'left',
28709     editable : true,
28710     delegates : false,
28711     xhr : false, 
28712     
28713     labellg : 0,
28714     labelmd : 0,
28715     labelsm : 0,
28716     labelxs : 0,
28717     
28718     getAutoCreate : function()
28719     {   
28720         var managerWidget = {
28721             tag : 'div',
28722             cls : 'roo-document-manager',
28723             cn : [
28724                 {
28725                     tag : 'input',
28726                     cls : 'roo-document-manager-selector',
28727                     type : 'file'
28728                 },
28729                 {
28730                     tag : 'div',
28731                     cls : 'roo-document-manager-uploader',
28732                     cn : [
28733                         {
28734                             tag : 'div',
28735                             cls : 'roo-document-manager-upload-btn',
28736                             html : '<i class="fa fa-plus"></i>'
28737                         }
28738                     ]
28739                     
28740                 }
28741             ]
28742         };
28743         
28744         var content = [
28745             {
28746                 tag : 'div',
28747                 cls : 'column col-md-12',
28748                 cn : managerWidget
28749             }
28750         ];
28751         
28752         if(this.fieldLabel.length){
28753             
28754             content = [
28755                 {
28756                     tag : 'div',
28757                     cls : 'column col-md-12',
28758                     html : this.fieldLabel
28759                 },
28760                 {
28761                     tag : 'div',
28762                     cls : 'column col-md-12',
28763                     cn : managerWidget
28764                 }
28765             ];
28766
28767             if(this.labelAlign == 'left'){
28768                 content = [
28769                     {
28770                         tag : 'div',
28771                         cls : 'column',
28772                         html : this.fieldLabel
28773                     },
28774                     {
28775                         tag : 'div',
28776                         cls : 'column',
28777                         cn : managerWidget
28778                     }
28779                 ];
28780                 
28781                 if(this.labelWidth > 12){
28782                     content[0].style = "width: " + this.labelWidth + 'px';
28783                 }
28784
28785                 if(this.labelWidth < 13 && this.labelmd == 0){
28786                     this.labelmd = this.labelWidth;
28787                 }
28788
28789                 if(this.labellg > 0){
28790                     content[0].cls += ' col-lg-' + this.labellg;
28791                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28792                 }
28793
28794                 if(this.labelmd > 0){
28795                     content[0].cls += ' col-md-' + this.labelmd;
28796                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28797                 }
28798
28799                 if(this.labelsm > 0){
28800                     content[0].cls += ' col-sm-' + this.labelsm;
28801                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28802                 }
28803
28804                 if(this.labelxs > 0){
28805                     content[0].cls += ' col-xs-' + this.labelxs;
28806                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28807                 }
28808                 
28809             }
28810         }
28811         
28812         var cfg = {
28813             tag : 'div',
28814             cls : 'row clearfix',
28815             cn : content
28816         };
28817         
28818         return cfg;
28819         
28820     },
28821     
28822     initEvents : function()
28823     {
28824         this.managerEl = this.el.select('.roo-document-manager', true).first();
28825         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28826         
28827         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28828         this.selectorEl.hide();
28829         
28830         if(this.multiple){
28831             this.selectorEl.attr('multiple', 'multiple');
28832         }
28833         
28834         this.selectorEl.on('change', this.onFileSelected, this);
28835         
28836         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28837         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28838         
28839         this.uploader.on('click', this.onUploaderClick, this);
28840         
28841         this.renderProgressDialog();
28842         
28843         var _this = this;
28844         
28845         window.addEventListener("resize", function() { _this.refresh(); } );
28846         
28847         this.fireEvent('initial', this);
28848     },
28849     
28850     renderProgressDialog : function()
28851     {
28852         var _this = this;
28853         
28854         this.progressDialog = new Roo.bootstrap.Modal({
28855             cls : 'roo-document-manager-progress-dialog',
28856             allow_close : false,
28857             title : '',
28858             buttons : [
28859                 {
28860                     name  :'cancel',
28861                     weight : 'danger',
28862                     html : 'Cancel'
28863                 }
28864             ], 
28865             listeners : { 
28866                 btnclick : function() {
28867                     _this.uploadCancel();
28868                     this.hide();
28869                 }
28870             }
28871         });
28872          
28873         this.progressDialog.render(Roo.get(document.body));
28874          
28875         this.progress = new Roo.bootstrap.Progress({
28876             cls : 'roo-document-manager-progress',
28877             active : true,
28878             striped : true
28879         });
28880         
28881         this.progress.render(this.progressDialog.getChildContainer());
28882         
28883         this.progressBar = new Roo.bootstrap.ProgressBar({
28884             cls : 'roo-document-manager-progress-bar',
28885             aria_valuenow : 0,
28886             aria_valuemin : 0,
28887             aria_valuemax : 12,
28888             panel : 'success'
28889         });
28890         
28891         this.progressBar.render(this.progress.getChildContainer());
28892     },
28893     
28894     onUploaderClick : function(e)
28895     {
28896         e.preventDefault();
28897      
28898         if(this.fireEvent('beforeselectfile', this) != false){
28899             this.selectorEl.dom.click();
28900         }
28901         
28902     },
28903     
28904     onFileSelected : function(e)
28905     {
28906         e.preventDefault();
28907         
28908         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28909             return;
28910         }
28911         
28912         Roo.each(this.selectorEl.dom.files, function(file){
28913             if(this.fireEvent('inspect', this, file) != false){
28914                 this.files.push(file);
28915             }
28916         }, this);
28917         
28918         this.queue();
28919         
28920     },
28921     
28922     queue : function()
28923     {
28924         this.selectorEl.dom.value = '';
28925         
28926         if(!this.files || !this.files.length){
28927             return;
28928         }
28929         
28930         if(this.boxes > 0 && this.files.length > this.boxes){
28931             this.files = this.files.slice(0, this.boxes);
28932         }
28933         
28934         this.uploader.show();
28935         
28936         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28937             this.uploader.hide();
28938         }
28939         
28940         var _this = this;
28941         
28942         var files = [];
28943         
28944         var docs = [];
28945         
28946         Roo.each(this.files, function(file){
28947             
28948             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28949                 var f = this.renderPreview(file);
28950                 files.push(f);
28951                 return;
28952             }
28953             
28954             if(file.type.indexOf('image') != -1){
28955                 this.delegates.push(
28956                     (function(){
28957                         _this.process(file);
28958                     }).createDelegate(this)
28959                 );
28960         
28961                 return;
28962             }
28963             
28964             docs.push(
28965                 (function(){
28966                     _this.process(file);
28967                 }).createDelegate(this)
28968             );
28969             
28970         }, this);
28971         
28972         this.files = files;
28973         
28974         this.delegates = this.delegates.concat(docs);
28975         
28976         if(!this.delegates.length){
28977             this.refresh();
28978             return;
28979         }
28980         
28981         this.progressBar.aria_valuemax = this.delegates.length;
28982         
28983         this.arrange();
28984         
28985         return;
28986     },
28987     
28988     arrange : function()
28989     {
28990         if(!this.delegates.length){
28991             this.progressDialog.hide();
28992             this.refresh();
28993             return;
28994         }
28995         
28996         var delegate = this.delegates.shift();
28997         
28998         this.progressDialog.show();
28999         
29000         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29001         
29002         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29003         
29004         delegate();
29005     },
29006     
29007     refresh : function()
29008     {
29009         this.uploader.show();
29010         
29011         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29012             this.uploader.hide();
29013         }
29014         
29015         Roo.isTouch ? this.closable(false) : this.closable(true);
29016         
29017         this.fireEvent('refresh', this);
29018     },
29019     
29020     onRemove : function(e, el, o)
29021     {
29022         e.preventDefault();
29023         
29024         this.fireEvent('remove', this, o);
29025         
29026     },
29027     
29028     remove : function(o)
29029     {
29030         var files = [];
29031         
29032         Roo.each(this.files, function(file){
29033             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29034                 files.push(file);
29035                 return;
29036             }
29037
29038             o.target.remove();
29039
29040         }, this);
29041         
29042         this.files = files;
29043         
29044         this.refresh();
29045     },
29046     
29047     clear : function()
29048     {
29049         Roo.each(this.files, function(file){
29050             if(!file.target){
29051                 return;
29052             }
29053             
29054             file.target.remove();
29055
29056         }, this);
29057         
29058         this.files = [];
29059         
29060         this.refresh();
29061     },
29062     
29063     onClick : function(e, el, o)
29064     {
29065         e.preventDefault();
29066         
29067         this.fireEvent('click', this, o);
29068         
29069     },
29070     
29071     closable : function(closable)
29072     {
29073         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29074             
29075             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29076             
29077             if(closable){
29078                 el.show();
29079                 return;
29080             }
29081             
29082             el.hide();
29083             
29084         }, this);
29085     },
29086     
29087     xhrOnLoad : function(xhr)
29088     {
29089         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29090             el.remove();
29091         }, this);
29092         
29093         if (xhr.readyState !== 4) {
29094             this.arrange();
29095             this.fireEvent('exception', this, xhr);
29096             return;
29097         }
29098
29099         var response = Roo.decode(xhr.responseText);
29100         
29101         if(!response.success){
29102             this.arrange();
29103             this.fireEvent('exception', this, xhr);
29104             return;
29105         }
29106         
29107         var file = this.renderPreview(response.data);
29108         
29109         this.files.push(file);
29110         
29111         this.arrange();
29112         
29113         this.fireEvent('afterupload', this, xhr);
29114         
29115     },
29116     
29117     xhrOnError : function(xhr)
29118     {
29119         Roo.log('xhr on error');
29120         
29121         var response = Roo.decode(xhr.responseText);
29122           
29123         Roo.log(response);
29124         
29125         this.arrange();
29126     },
29127     
29128     process : function(file)
29129     {
29130         if(this.fireEvent('process', this, file) !== false){
29131             if(this.editable && file.type.indexOf('image') != -1){
29132                 this.fireEvent('edit', this, file);
29133                 return;
29134             }
29135
29136             this.uploadStart(file, false);
29137
29138             return;
29139         }
29140         
29141     },
29142     
29143     uploadStart : function(file, crop)
29144     {
29145         this.xhr = new XMLHttpRequest();
29146         
29147         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29148             this.arrange();
29149             return;
29150         }
29151         
29152         file.xhr = this.xhr;
29153             
29154         this.managerEl.createChild({
29155             tag : 'div',
29156             cls : 'roo-document-manager-loading',
29157             cn : [
29158                 {
29159                     tag : 'div',
29160                     tooltip : file.name,
29161                     cls : 'roo-document-manager-thumb',
29162                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29163                 }
29164             ]
29165
29166         });
29167
29168         this.xhr.open(this.method, this.url, true);
29169         
29170         var headers = {
29171             "Accept": "application/json",
29172             "Cache-Control": "no-cache",
29173             "X-Requested-With": "XMLHttpRequest"
29174         };
29175         
29176         for (var headerName in headers) {
29177             var headerValue = headers[headerName];
29178             if (headerValue) {
29179                 this.xhr.setRequestHeader(headerName, headerValue);
29180             }
29181         }
29182         
29183         var _this = this;
29184         
29185         this.xhr.onload = function()
29186         {
29187             _this.xhrOnLoad(_this.xhr);
29188         }
29189         
29190         this.xhr.onerror = function()
29191         {
29192             _this.xhrOnError(_this.xhr);
29193         }
29194         
29195         var formData = new FormData();
29196
29197         formData.append('returnHTML', 'NO');
29198         
29199         if(crop){
29200             formData.append('crop', crop);
29201         }
29202         
29203         formData.append(this.paramName, file, file.name);
29204         
29205         var options = {
29206             file : file, 
29207             manually : false
29208         };
29209         
29210         if(this.fireEvent('prepare', this, formData, options) != false){
29211             
29212             if(options.manually){
29213                 return;
29214             }
29215             
29216             this.xhr.send(formData);
29217             return;
29218         };
29219         
29220         this.uploadCancel();
29221     },
29222     
29223     uploadCancel : function()
29224     {
29225         if (this.xhr) {
29226             this.xhr.abort();
29227         }
29228         
29229         this.delegates = [];
29230         
29231         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29232             el.remove();
29233         }, this);
29234         
29235         this.arrange();
29236     },
29237     
29238     renderPreview : function(file)
29239     {
29240         if(typeof(file.target) != 'undefined' && file.target){
29241             return file;
29242         }
29243         
29244         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29245         
29246         var previewEl = this.managerEl.createChild({
29247             tag : 'div',
29248             cls : 'roo-document-manager-preview',
29249             cn : [
29250                 {
29251                     tag : 'div',
29252                     tooltip : file[this.toolTipName],
29253                     cls : 'roo-document-manager-thumb',
29254                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29255                 },
29256                 {
29257                     tag : 'button',
29258                     cls : 'close',
29259                     html : '<i class="fa fa-times-circle"></i>'
29260                 }
29261             ]
29262         });
29263
29264         var close = previewEl.select('button.close', true).first();
29265
29266         close.on('click', this.onRemove, this, file);
29267
29268         file.target = previewEl;
29269
29270         var image = previewEl.select('img', true).first();
29271         
29272         var _this = this;
29273         
29274         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29275         
29276         image.on('click', this.onClick, this, file);
29277         
29278         this.fireEvent('previewrendered', this, file);
29279         
29280         return file;
29281         
29282     },
29283     
29284     onPreviewLoad : function(file, image)
29285     {
29286         if(typeof(file.target) == 'undefined' || !file.target){
29287             return;
29288         }
29289         
29290         var width = image.dom.naturalWidth || image.dom.width;
29291         var height = image.dom.naturalHeight || image.dom.height;
29292         
29293         if(width > height){
29294             file.target.addClass('wide');
29295             return;
29296         }
29297         
29298         file.target.addClass('tall');
29299         return;
29300         
29301     },
29302     
29303     uploadFromSource : function(file, crop)
29304     {
29305         this.xhr = new XMLHttpRequest();
29306         
29307         this.managerEl.createChild({
29308             tag : 'div',
29309             cls : 'roo-document-manager-loading',
29310             cn : [
29311                 {
29312                     tag : 'div',
29313                     tooltip : file.name,
29314                     cls : 'roo-document-manager-thumb',
29315                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29316                 }
29317             ]
29318
29319         });
29320
29321         this.xhr.open(this.method, this.url, true);
29322         
29323         var headers = {
29324             "Accept": "application/json",
29325             "Cache-Control": "no-cache",
29326             "X-Requested-With": "XMLHttpRequest"
29327         };
29328         
29329         for (var headerName in headers) {
29330             var headerValue = headers[headerName];
29331             if (headerValue) {
29332                 this.xhr.setRequestHeader(headerName, headerValue);
29333             }
29334         }
29335         
29336         var _this = this;
29337         
29338         this.xhr.onload = function()
29339         {
29340             _this.xhrOnLoad(_this.xhr);
29341         }
29342         
29343         this.xhr.onerror = function()
29344         {
29345             _this.xhrOnError(_this.xhr);
29346         }
29347         
29348         var formData = new FormData();
29349
29350         formData.append('returnHTML', 'NO');
29351         
29352         formData.append('crop', crop);
29353         
29354         if(typeof(file.filename) != 'undefined'){
29355             formData.append('filename', file.filename);
29356         }
29357         
29358         if(typeof(file.mimetype) != 'undefined'){
29359             formData.append('mimetype', file.mimetype);
29360         }
29361         
29362         Roo.log(formData);
29363         
29364         if(this.fireEvent('prepare', this, formData) != false){
29365             this.xhr.send(formData);
29366         };
29367     }
29368 });
29369
29370 /*
29371 * Licence: LGPL
29372 */
29373
29374 /**
29375  * @class Roo.bootstrap.DocumentViewer
29376  * @extends Roo.bootstrap.Component
29377  * Bootstrap DocumentViewer class
29378  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29379  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29380  * 
29381  * @constructor
29382  * Create a new DocumentViewer
29383  * @param {Object} config The config object
29384  */
29385
29386 Roo.bootstrap.DocumentViewer = function(config){
29387     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29388     
29389     this.addEvents({
29390         /**
29391          * @event initial
29392          * Fire after initEvent
29393          * @param {Roo.bootstrap.DocumentViewer} this
29394          */
29395         "initial" : true,
29396         /**
29397          * @event click
29398          * Fire after click
29399          * @param {Roo.bootstrap.DocumentViewer} this
29400          */
29401         "click" : true,
29402         /**
29403          * @event download
29404          * Fire after download button
29405          * @param {Roo.bootstrap.DocumentViewer} this
29406          */
29407         "download" : true,
29408         /**
29409          * @event trash
29410          * Fire after trash button
29411          * @param {Roo.bootstrap.DocumentViewer} this
29412          */
29413         "trash" : true
29414         
29415     });
29416 };
29417
29418 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29419     
29420     showDownload : true,
29421     
29422     showTrash : true,
29423     
29424     getAutoCreate : function()
29425     {
29426         var cfg = {
29427             tag : 'div',
29428             cls : 'roo-document-viewer',
29429             cn : [
29430                 {
29431                     tag : 'div',
29432                     cls : 'roo-document-viewer-body',
29433                     cn : [
29434                         {
29435                             tag : 'div',
29436                             cls : 'roo-document-viewer-thumb',
29437                             cn : [
29438                                 {
29439                                     tag : 'img',
29440                                     cls : 'roo-document-viewer-image'
29441                                 }
29442                             ]
29443                         }
29444                     ]
29445                 },
29446                 {
29447                     tag : 'div',
29448                     cls : 'roo-document-viewer-footer',
29449                     cn : {
29450                         tag : 'div',
29451                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29452                         cn : [
29453                             {
29454                                 tag : 'div',
29455                                 cls : 'btn-group roo-document-viewer-download',
29456                                 cn : [
29457                                     {
29458                                         tag : 'button',
29459                                         cls : 'btn btn-default',
29460                                         html : '<i class="fa fa-download"></i>'
29461                                     }
29462                                 ]
29463                             },
29464                             {
29465                                 tag : 'div',
29466                                 cls : 'btn-group roo-document-viewer-trash',
29467                                 cn : [
29468                                     {
29469                                         tag : 'button',
29470                                         cls : 'btn btn-default',
29471                                         html : '<i class="fa fa-trash"></i>'
29472                                     }
29473                                 ]
29474                             }
29475                         ]
29476                     }
29477                 }
29478             ]
29479         };
29480         
29481         return cfg;
29482     },
29483     
29484     initEvents : function()
29485     {
29486         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29487         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29488         
29489         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29490         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29491         
29492         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29493         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29494         
29495         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29496         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29497         
29498         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29499         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29500         
29501         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29502         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29503         
29504         this.bodyEl.on('click', this.onClick, this);
29505         this.downloadBtn.on('click', this.onDownload, this);
29506         this.trashBtn.on('click', this.onTrash, this);
29507         
29508         this.downloadBtn.hide();
29509         this.trashBtn.hide();
29510         
29511         if(this.showDownload){
29512             this.downloadBtn.show();
29513         }
29514         
29515         if(this.showTrash){
29516             this.trashBtn.show();
29517         }
29518         
29519         if(!this.showDownload && !this.showTrash) {
29520             this.footerEl.hide();
29521         }
29522         
29523     },
29524     
29525     initial : function()
29526     {
29527         this.fireEvent('initial', this);
29528         
29529     },
29530     
29531     onClick : function(e)
29532     {
29533         e.preventDefault();
29534         
29535         this.fireEvent('click', this);
29536     },
29537     
29538     onDownload : function(e)
29539     {
29540         e.preventDefault();
29541         
29542         this.fireEvent('download', this);
29543     },
29544     
29545     onTrash : function(e)
29546     {
29547         e.preventDefault();
29548         
29549         this.fireEvent('trash', this);
29550     }
29551     
29552 });
29553 /*
29554  * - LGPL
29555  *
29556  * nav progress bar
29557  * 
29558  */
29559
29560 /**
29561  * @class Roo.bootstrap.NavProgressBar
29562  * @extends Roo.bootstrap.Component
29563  * Bootstrap NavProgressBar class
29564  * 
29565  * @constructor
29566  * Create a new nav progress bar
29567  * @param {Object} config The config object
29568  */
29569
29570 Roo.bootstrap.NavProgressBar = function(config){
29571     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29572
29573     this.bullets = this.bullets || [];
29574    
29575 //    Roo.bootstrap.NavProgressBar.register(this);
29576      this.addEvents({
29577         /**
29578              * @event changed
29579              * Fires when the active item changes
29580              * @param {Roo.bootstrap.NavProgressBar} this
29581              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29582              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29583          */
29584         'changed': true
29585      });
29586     
29587 };
29588
29589 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29590     
29591     bullets : [],
29592     barItems : [],
29593     
29594     getAutoCreate : function()
29595     {
29596         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29597         
29598         cfg = {
29599             tag : 'div',
29600             cls : 'roo-navigation-bar-group',
29601             cn : [
29602                 {
29603                     tag : 'div',
29604                     cls : 'roo-navigation-top-bar'
29605                 },
29606                 {
29607                     tag : 'div',
29608                     cls : 'roo-navigation-bullets-bar',
29609                     cn : [
29610                         {
29611                             tag : 'ul',
29612                             cls : 'roo-navigation-bar'
29613                         }
29614                     ]
29615                 },
29616                 
29617                 {
29618                     tag : 'div',
29619                     cls : 'roo-navigation-bottom-bar'
29620                 }
29621             ]
29622             
29623         };
29624         
29625         return cfg;
29626         
29627     },
29628     
29629     initEvents: function() 
29630     {
29631         
29632     },
29633     
29634     onRender : function(ct, position) 
29635     {
29636         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29637         
29638         if(this.bullets.length){
29639             Roo.each(this.bullets, function(b){
29640                this.addItem(b);
29641             }, this);
29642         }
29643         
29644         this.format();
29645         
29646     },
29647     
29648     addItem : function(cfg)
29649     {
29650         var item = new Roo.bootstrap.NavProgressItem(cfg);
29651         
29652         item.parentId = this.id;
29653         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29654         
29655         if(cfg.html){
29656             var top = new Roo.bootstrap.Element({
29657                 tag : 'div',
29658                 cls : 'roo-navigation-bar-text'
29659             });
29660             
29661             var bottom = new Roo.bootstrap.Element({
29662                 tag : 'div',
29663                 cls : 'roo-navigation-bar-text'
29664             });
29665             
29666             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29667             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29668             
29669             var topText = new Roo.bootstrap.Element({
29670                 tag : 'span',
29671                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29672             });
29673             
29674             var bottomText = new Roo.bootstrap.Element({
29675                 tag : 'span',
29676                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29677             });
29678             
29679             topText.onRender(top.el, null);
29680             bottomText.onRender(bottom.el, null);
29681             
29682             item.topEl = top;
29683             item.bottomEl = bottom;
29684         }
29685         
29686         this.barItems.push(item);
29687         
29688         return item;
29689     },
29690     
29691     getActive : function()
29692     {
29693         var active = false;
29694         
29695         Roo.each(this.barItems, function(v){
29696             
29697             if (!v.isActive()) {
29698                 return;
29699             }
29700             
29701             active = v;
29702             return false;
29703             
29704         });
29705         
29706         return active;
29707     },
29708     
29709     setActiveItem : function(item)
29710     {
29711         var prev = false;
29712         
29713         Roo.each(this.barItems, function(v){
29714             if (v.rid == item.rid) {
29715                 return ;
29716             }
29717             
29718             if (v.isActive()) {
29719                 v.setActive(false);
29720                 prev = v;
29721             }
29722         });
29723
29724         item.setActive(true);
29725         
29726         this.fireEvent('changed', this, item, prev);
29727     },
29728     
29729     getBarItem: function(rid)
29730     {
29731         var ret = false;
29732         
29733         Roo.each(this.barItems, function(e) {
29734             if (e.rid != rid) {
29735                 return;
29736             }
29737             
29738             ret =  e;
29739             return false;
29740         });
29741         
29742         return ret;
29743     },
29744     
29745     indexOfItem : function(item)
29746     {
29747         var index = false;
29748         
29749         Roo.each(this.barItems, function(v, i){
29750             
29751             if (v.rid != item.rid) {
29752                 return;
29753             }
29754             
29755             index = i;
29756             return false
29757         });
29758         
29759         return index;
29760     },
29761     
29762     setActiveNext : function()
29763     {
29764         var i = this.indexOfItem(this.getActive());
29765         
29766         if (i > this.barItems.length) {
29767             return;
29768         }
29769         
29770         this.setActiveItem(this.barItems[i+1]);
29771     },
29772     
29773     setActivePrev : function()
29774     {
29775         var i = this.indexOfItem(this.getActive());
29776         
29777         if (i  < 1) {
29778             return;
29779         }
29780         
29781         this.setActiveItem(this.barItems[i-1]);
29782     },
29783     
29784     format : function()
29785     {
29786         if(!this.barItems.length){
29787             return;
29788         }
29789      
29790         var width = 100 / this.barItems.length;
29791         
29792         Roo.each(this.barItems, function(i){
29793             i.el.setStyle('width', width + '%');
29794             i.topEl.el.setStyle('width', width + '%');
29795             i.bottomEl.el.setStyle('width', width + '%');
29796         }, this);
29797         
29798     }
29799     
29800 });
29801 /*
29802  * - LGPL
29803  *
29804  * Nav Progress Item
29805  * 
29806  */
29807
29808 /**
29809  * @class Roo.bootstrap.NavProgressItem
29810  * @extends Roo.bootstrap.Component
29811  * Bootstrap NavProgressItem class
29812  * @cfg {String} rid the reference id
29813  * @cfg {Boolean} active (true|false) Is item active default false
29814  * @cfg {Boolean} disabled (true|false) Is item active default false
29815  * @cfg {String} html
29816  * @cfg {String} position (top|bottom) text position default bottom
29817  * @cfg {String} icon show icon instead of number
29818  * 
29819  * @constructor
29820  * Create a new NavProgressItem
29821  * @param {Object} config The config object
29822  */
29823 Roo.bootstrap.NavProgressItem = function(config){
29824     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29825     this.addEvents({
29826         // raw events
29827         /**
29828          * @event click
29829          * The raw click event for the entire grid.
29830          * @param {Roo.bootstrap.NavProgressItem} this
29831          * @param {Roo.EventObject} e
29832          */
29833         "click" : true
29834     });
29835    
29836 };
29837
29838 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29839     
29840     rid : '',
29841     active : false,
29842     disabled : false,
29843     html : '',
29844     position : 'bottom',
29845     icon : false,
29846     
29847     getAutoCreate : function()
29848     {
29849         var iconCls = 'roo-navigation-bar-item-icon';
29850         
29851         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29852         
29853         var cfg = {
29854             tag: 'li',
29855             cls: 'roo-navigation-bar-item',
29856             cn : [
29857                 {
29858                     tag : 'i',
29859                     cls : iconCls
29860                 }
29861             ]
29862         };
29863         
29864         if(this.active){
29865             cfg.cls += ' active';
29866         }
29867         if(this.disabled){
29868             cfg.cls += ' disabled';
29869         }
29870         
29871         return cfg;
29872     },
29873     
29874     disable : function()
29875     {
29876         this.setDisabled(true);
29877     },
29878     
29879     enable : function()
29880     {
29881         this.setDisabled(false);
29882     },
29883     
29884     initEvents: function() 
29885     {
29886         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29887         
29888         this.iconEl.on('click', this.onClick, this);
29889     },
29890     
29891     onClick : function(e)
29892     {
29893         e.preventDefault();
29894         
29895         if(this.disabled){
29896             return;
29897         }
29898         
29899         if(this.fireEvent('click', this, e) === false){
29900             return;
29901         };
29902         
29903         this.parent().setActiveItem(this);
29904     },
29905     
29906     isActive: function () 
29907     {
29908         return this.active;
29909     },
29910     
29911     setActive : function(state)
29912     {
29913         if(this.active == state){
29914             return;
29915         }
29916         
29917         this.active = state;
29918         
29919         if (state) {
29920             this.el.addClass('active');
29921             return;
29922         }
29923         
29924         this.el.removeClass('active');
29925         
29926         return;
29927     },
29928     
29929     setDisabled : function(state)
29930     {
29931         if(this.disabled == state){
29932             return;
29933         }
29934         
29935         this.disabled = state;
29936         
29937         if (state) {
29938             this.el.addClass('disabled');
29939             return;
29940         }
29941         
29942         this.el.removeClass('disabled');
29943     },
29944     
29945     tooltipEl : function()
29946     {
29947         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29948     }
29949 });
29950  
29951
29952  /*
29953  * - LGPL
29954  *
29955  * FieldLabel
29956  * 
29957  */
29958
29959 /**
29960  * @class Roo.bootstrap.FieldLabel
29961  * @extends Roo.bootstrap.Component
29962  * Bootstrap FieldLabel class
29963  * @cfg {String} html contents of the element
29964  * @cfg {String} tag tag of the element default label
29965  * @cfg {String} cls class of the element
29966  * @cfg {String} target label target 
29967  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29968  * @cfg {String} invalidClass default "text-warning"
29969  * @cfg {String} validClass default "text-success"
29970  * @cfg {String} iconTooltip default "This field is required"
29971  * @cfg {String} indicatorpos (left|right) default left
29972  * 
29973  * @constructor
29974  * Create a new FieldLabel
29975  * @param {Object} config The config object
29976  */
29977
29978 Roo.bootstrap.FieldLabel = function(config){
29979     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29980     
29981     this.addEvents({
29982             /**
29983              * @event invalid
29984              * Fires after the field has been marked as invalid.
29985              * @param {Roo.form.FieldLabel} this
29986              * @param {String} msg The validation message
29987              */
29988             invalid : true,
29989             /**
29990              * @event valid
29991              * Fires after the field has been validated with no errors.
29992              * @param {Roo.form.FieldLabel} this
29993              */
29994             valid : true
29995         });
29996 };
29997
29998 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29999     
30000     tag: 'label',
30001     cls: '',
30002     html: '',
30003     target: '',
30004     allowBlank : true,
30005     invalidClass : 'has-warning',
30006     validClass : 'has-success',
30007     iconTooltip : 'This field is required',
30008     indicatorpos : 'left',
30009     
30010     getAutoCreate : function(){
30011         
30012         var cfg = {
30013             tag : this.tag,
30014             cls : 'roo-bootstrap-field-label ' + this.cls,
30015             for : this.target,
30016             cn : [
30017                 {
30018                     tag : 'i',
30019                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
30020                     tooltip : this.iconTooltip
30021                 },
30022                 {
30023                     tag : 'span',
30024                     html : this.html
30025                 }
30026             ] 
30027         };
30028         
30029         if(this.indicatorpos == 'right'){
30030             var cfg = {
30031                 tag : this.tag,
30032                 cls : 'roo-bootstrap-field-label ' + this.cls,
30033                 for : this.target,
30034                 cn : [
30035                     {
30036                         tag : 'span',
30037                         html : this.html
30038                     },
30039                     {
30040                         tag : 'i',
30041                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30042                         tooltip : this.iconTooltip
30043                     }
30044                 ] 
30045             };
30046         }
30047         
30048         return cfg;
30049     },
30050     
30051     initEvents: function() 
30052     {
30053         Roo.bootstrap.Element.superclass.initEvents.call(this);
30054         
30055         this.indicator = this.indicatorEl();
30056         
30057         if(this.indicator){
30058             this.indicator.removeClass('visible');
30059             this.indicator.addClass('invisible');
30060         }
30061         
30062         Roo.bootstrap.FieldLabel.register(this);
30063     },
30064     
30065     indicatorEl : function()
30066     {
30067         var indicator = this.el.select('i.roo-required-indicator',true).first();
30068         
30069         if(!indicator){
30070             return false;
30071         }
30072         
30073         return indicator;
30074         
30075     },
30076     
30077     /**
30078      * Mark this field as valid
30079      */
30080     markValid : function()
30081     {
30082         if(this.indicator){
30083             this.indicator.removeClass('visible');
30084             this.indicator.addClass('invisible');
30085         }
30086         
30087         this.el.removeClass(this.invalidClass);
30088         
30089         this.el.addClass(this.validClass);
30090         
30091         this.fireEvent('valid', this);
30092     },
30093     
30094     /**
30095      * Mark this field as invalid
30096      * @param {String} msg The validation message
30097      */
30098     markInvalid : function(msg)
30099     {
30100         if(this.indicator){
30101             this.indicator.removeClass('invisible');
30102             this.indicator.addClass('visible');
30103         }
30104         
30105         this.el.removeClass(this.validClass);
30106         
30107         this.el.addClass(this.invalidClass);
30108         
30109         this.fireEvent('invalid', this, msg);
30110     }
30111     
30112    
30113 });
30114
30115 Roo.apply(Roo.bootstrap.FieldLabel, {
30116     
30117     groups: {},
30118     
30119      /**
30120     * register a FieldLabel Group
30121     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30122     */
30123     register : function(label)
30124     {
30125         if(this.groups.hasOwnProperty(label.target)){
30126             return;
30127         }
30128      
30129         this.groups[label.target] = label;
30130         
30131     },
30132     /**
30133     * fetch a FieldLabel Group based on the target
30134     * @param {string} target
30135     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30136     */
30137     get: function(target) {
30138         if (typeof(this.groups[target]) == 'undefined') {
30139             return false;
30140         }
30141         
30142         return this.groups[target] ;
30143     }
30144 });
30145
30146  
30147
30148  /*
30149  * - LGPL
30150  *
30151  * page DateSplitField.
30152  * 
30153  */
30154
30155
30156 /**
30157  * @class Roo.bootstrap.DateSplitField
30158  * @extends Roo.bootstrap.Component
30159  * Bootstrap DateSplitField class
30160  * @cfg {string} fieldLabel - the label associated
30161  * @cfg {Number} labelWidth set the width of label (0-12)
30162  * @cfg {String} labelAlign (top|left)
30163  * @cfg {Boolean} dayAllowBlank (true|false) default false
30164  * @cfg {Boolean} monthAllowBlank (true|false) default false
30165  * @cfg {Boolean} yearAllowBlank (true|false) default false
30166  * @cfg {string} dayPlaceholder 
30167  * @cfg {string} monthPlaceholder
30168  * @cfg {string} yearPlaceholder
30169  * @cfg {string} dayFormat default 'd'
30170  * @cfg {string} monthFormat default 'm'
30171  * @cfg {string} yearFormat default 'Y'
30172  * @cfg {Number} labellg set the width of label (1-12)
30173  * @cfg {Number} labelmd set the width of label (1-12)
30174  * @cfg {Number} labelsm set the width of label (1-12)
30175  * @cfg {Number} labelxs set the width of label (1-12)
30176
30177  *     
30178  * @constructor
30179  * Create a new DateSplitField
30180  * @param {Object} config The config object
30181  */
30182
30183 Roo.bootstrap.DateSplitField = function(config){
30184     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30185     
30186     this.addEvents({
30187         // raw events
30188          /**
30189          * @event years
30190          * getting the data of years
30191          * @param {Roo.bootstrap.DateSplitField} this
30192          * @param {Object} years
30193          */
30194         "years" : true,
30195         /**
30196          * @event days
30197          * getting the data of days
30198          * @param {Roo.bootstrap.DateSplitField} this
30199          * @param {Object} days
30200          */
30201         "days" : true,
30202         /**
30203          * @event invalid
30204          * Fires after the field has been marked as invalid.
30205          * @param {Roo.form.Field} this
30206          * @param {String} msg The validation message
30207          */
30208         invalid : true,
30209        /**
30210          * @event valid
30211          * Fires after the field has been validated with no errors.
30212          * @param {Roo.form.Field} this
30213          */
30214         valid : true
30215     });
30216 };
30217
30218 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30219     
30220     fieldLabel : '',
30221     labelAlign : 'top',
30222     labelWidth : 3,
30223     dayAllowBlank : false,
30224     monthAllowBlank : false,
30225     yearAllowBlank : false,
30226     dayPlaceholder : '',
30227     monthPlaceholder : '',
30228     yearPlaceholder : '',
30229     dayFormat : 'd',
30230     monthFormat : 'm',
30231     yearFormat : 'Y',
30232     isFormField : true,
30233     labellg : 0,
30234     labelmd : 0,
30235     labelsm : 0,
30236     labelxs : 0,
30237     
30238     getAutoCreate : function()
30239     {
30240         var cfg = {
30241             tag : 'div',
30242             cls : 'row roo-date-split-field-group',
30243             cn : [
30244                 {
30245                     tag : 'input',
30246                     type : 'hidden',
30247                     cls : 'form-hidden-field roo-date-split-field-group-value',
30248                     name : this.name
30249                 }
30250             ]
30251         };
30252         
30253         var labelCls = 'col-md-12';
30254         var contentCls = 'col-md-4';
30255         
30256         if(this.fieldLabel){
30257             
30258             var label = {
30259                 tag : 'div',
30260                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30261                 cn : [
30262                     {
30263                         tag : 'label',
30264                         html : this.fieldLabel
30265                     }
30266                 ]
30267             };
30268             
30269             if(this.labelAlign == 'left'){
30270             
30271                 if(this.labelWidth > 12){
30272                     label.style = "width: " + this.labelWidth + 'px';
30273                 }
30274
30275                 if(this.labelWidth < 13 && this.labelmd == 0){
30276                     this.labelmd = this.labelWidth;
30277                 }
30278
30279                 if(this.labellg > 0){
30280                     labelCls = ' col-lg-' + this.labellg;
30281                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30282                 }
30283
30284                 if(this.labelmd > 0){
30285                     labelCls = ' col-md-' + this.labelmd;
30286                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30287                 }
30288
30289                 if(this.labelsm > 0){
30290                     labelCls = ' col-sm-' + this.labelsm;
30291                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30292                 }
30293
30294                 if(this.labelxs > 0){
30295                     labelCls = ' col-xs-' + this.labelxs;
30296                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30297                 }
30298             }
30299             
30300             label.cls += ' ' + labelCls;
30301             
30302             cfg.cn.push(label);
30303         }
30304         
30305         Roo.each(['day', 'month', 'year'], function(t){
30306             cfg.cn.push({
30307                 tag : 'div',
30308                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30309             });
30310         }, this);
30311         
30312         return cfg;
30313     },
30314     
30315     inputEl: function ()
30316     {
30317         return this.el.select('.roo-date-split-field-group-value', true).first();
30318     },
30319     
30320     onRender : function(ct, position) 
30321     {
30322         var _this = this;
30323         
30324         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30325         
30326         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30327         
30328         this.dayField = new Roo.bootstrap.ComboBox({
30329             allowBlank : this.dayAllowBlank,
30330             alwaysQuery : true,
30331             displayField : 'value',
30332             editable : false,
30333             fieldLabel : '',
30334             forceSelection : true,
30335             mode : 'local',
30336             placeholder : this.dayPlaceholder,
30337             selectOnFocus : true,
30338             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30339             triggerAction : 'all',
30340             typeAhead : true,
30341             valueField : 'value',
30342             store : new Roo.data.SimpleStore({
30343                 data : (function() {    
30344                     var days = [];
30345                     _this.fireEvent('days', _this, days);
30346                     return days;
30347                 })(),
30348                 fields : [ 'value' ]
30349             }),
30350             listeners : {
30351                 select : function (_self, record, index)
30352                 {
30353                     _this.setValue(_this.getValue());
30354                 }
30355             }
30356         });
30357
30358         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30359         
30360         this.monthField = new Roo.bootstrap.MonthField({
30361             after : '<i class=\"fa fa-calendar\"></i>',
30362             allowBlank : this.monthAllowBlank,
30363             placeholder : this.monthPlaceholder,
30364             readOnly : true,
30365             listeners : {
30366                 render : function (_self)
30367                 {
30368                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30369                         e.preventDefault();
30370                         _self.focus();
30371                     });
30372                 },
30373                 select : function (_self, oldvalue, newvalue)
30374                 {
30375                     _this.setValue(_this.getValue());
30376                 }
30377             }
30378         });
30379         
30380         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30381         
30382         this.yearField = new Roo.bootstrap.ComboBox({
30383             allowBlank : this.yearAllowBlank,
30384             alwaysQuery : true,
30385             displayField : 'value',
30386             editable : false,
30387             fieldLabel : '',
30388             forceSelection : true,
30389             mode : 'local',
30390             placeholder : this.yearPlaceholder,
30391             selectOnFocus : true,
30392             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30393             triggerAction : 'all',
30394             typeAhead : true,
30395             valueField : 'value',
30396             store : new Roo.data.SimpleStore({
30397                 data : (function() {
30398                     var years = [];
30399                     _this.fireEvent('years', _this, years);
30400                     return years;
30401                 })(),
30402                 fields : [ 'value' ]
30403             }),
30404             listeners : {
30405                 select : function (_self, record, index)
30406                 {
30407                     _this.setValue(_this.getValue());
30408                 }
30409             }
30410         });
30411
30412         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30413     },
30414     
30415     setValue : function(v, format)
30416     {
30417         this.inputEl.dom.value = v;
30418         
30419         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30420         
30421         var d = Date.parseDate(v, f);
30422         
30423         if(!d){
30424             this.validate();
30425             return;
30426         }
30427         
30428         this.setDay(d.format(this.dayFormat));
30429         this.setMonth(d.format(this.monthFormat));
30430         this.setYear(d.format(this.yearFormat));
30431         
30432         this.validate();
30433         
30434         return;
30435     },
30436     
30437     setDay : function(v)
30438     {
30439         this.dayField.setValue(v);
30440         this.inputEl.dom.value = this.getValue();
30441         this.validate();
30442         return;
30443     },
30444     
30445     setMonth : function(v)
30446     {
30447         this.monthField.setValue(v, true);
30448         this.inputEl.dom.value = this.getValue();
30449         this.validate();
30450         return;
30451     },
30452     
30453     setYear : function(v)
30454     {
30455         this.yearField.setValue(v);
30456         this.inputEl.dom.value = this.getValue();
30457         this.validate();
30458         return;
30459     },
30460     
30461     getDay : function()
30462     {
30463         return this.dayField.getValue();
30464     },
30465     
30466     getMonth : function()
30467     {
30468         return this.monthField.getValue();
30469     },
30470     
30471     getYear : function()
30472     {
30473         return this.yearField.getValue();
30474     },
30475     
30476     getValue : function()
30477     {
30478         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30479         
30480         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30481         
30482         return date;
30483     },
30484     
30485     reset : function()
30486     {
30487         this.setDay('');
30488         this.setMonth('');
30489         this.setYear('');
30490         this.inputEl.dom.value = '';
30491         this.validate();
30492         return;
30493     },
30494     
30495     validate : function()
30496     {
30497         var d = this.dayField.validate();
30498         var m = this.monthField.validate();
30499         var y = this.yearField.validate();
30500         
30501         var valid = true;
30502         
30503         if(
30504                 (!this.dayAllowBlank && !d) ||
30505                 (!this.monthAllowBlank && !m) ||
30506                 (!this.yearAllowBlank && !y)
30507         ){
30508             valid = false;
30509         }
30510         
30511         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30512             return valid;
30513         }
30514         
30515         if(valid){
30516             this.markValid();
30517             return valid;
30518         }
30519         
30520         this.markInvalid();
30521         
30522         return valid;
30523     },
30524     
30525     markValid : function()
30526     {
30527         
30528         var label = this.el.select('label', true).first();
30529         var icon = this.el.select('i.fa-star', true).first();
30530
30531         if(label && icon){
30532             icon.remove();
30533         }
30534         
30535         this.fireEvent('valid', this);
30536     },
30537     
30538      /**
30539      * Mark this field as invalid
30540      * @param {String} msg The validation message
30541      */
30542     markInvalid : function(msg)
30543     {
30544         
30545         var label = this.el.select('label', true).first();
30546         var icon = this.el.select('i.fa-star', true).first();
30547
30548         if(label && !icon){
30549             this.el.select('.roo-date-split-field-label', true).createChild({
30550                 tag : 'i',
30551                 cls : 'text-danger fa fa-lg fa-star',
30552                 tooltip : 'This field is required',
30553                 style : 'margin-right:5px;'
30554             }, label, true);
30555         }
30556         
30557         this.fireEvent('invalid', this, msg);
30558     },
30559     
30560     clearInvalid : function()
30561     {
30562         var label = this.el.select('label', true).first();
30563         var icon = this.el.select('i.fa-star', true).first();
30564
30565         if(label && icon){
30566             icon.remove();
30567         }
30568         
30569         this.fireEvent('valid', this);
30570     },
30571     
30572     getName: function()
30573     {
30574         return this.name;
30575     }
30576     
30577 });
30578
30579  /**
30580  *
30581  * This is based on 
30582  * http://masonry.desandro.com
30583  *
30584  * The idea is to render all the bricks based on vertical width...
30585  *
30586  * The original code extends 'outlayer' - we might need to use that....
30587  * 
30588  */
30589
30590
30591 /**
30592  * @class Roo.bootstrap.LayoutMasonry
30593  * @extends Roo.bootstrap.Component
30594  * Bootstrap Layout Masonry class
30595  * 
30596  * @constructor
30597  * Create a new Element
30598  * @param {Object} config The config object
30599  */
30600
30601 Roo.bootstrap.LayoutMasonry = function(config){
30602     
30603     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30604     
30605     this.bricks = [];
30606     
30607     Roo.bootstrap.LayoutMasonry.register(this);
30608     
30609     this.addEvents({
30610         // raw events
30611         /**
30612          * @event layout
30613          * Fire after layout the items
30614          * @param {Roo.bootstrap.LayoutMasonry} this
30615          * @param {Roo.EventObject} e
30616          */
30617         "layout" : true
30618     });
30619     
30620 };
30621
30622 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30623     
30624     /**
30625      * @cfg {Boolean} isLayoutInstant = no animation?
30626      */   
30627     isLayoutInstant : false, // needed?
30628    
30629     /**
30630      * @cfg {Number} boxWidth  width of the columns
30631      */   
30632     boxWidth : 450,
30633     
30634       /**
30635      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30636      */   
30637     boxHeight : 0,
30638     
30639     /**
30640      * @cfg {Number} padWidth padding below box..
30641      */   
30642     padWidth : 10, 
30643     
30644     /**
30645      * @cfg {Number} gutter gutter width..
30646      */   
30647     gutter : 10,
30648     
30649      /**
30650      * @cfg {Number} maxCols maximum number of columns
30651      */   
30652     
30653     maxCols: 0,
30654     
30655     /**
30656      * @cfg {Boolean} isAutoInitial defalut true
30657      */   
30658     isAutoInitial : true, 
30659     
30660     containerWidth: 0,
30661     
30662     /**
30663      * @cfg {Boolean} isHorizontal defalut false
30664      */   
30665     isHorizontal : false, 
30666
30667     currentSize : null,
30668     
30669     tag: 'div',
30670     
30671     cls: '',
30672     
30673     bricks: null, //CompositeElement
30674     
30675     cols : 1,
30676     
30677     _isLayoutInited : false,
30678     
30679 //    isAlternative : false, // only use for vertical layout...
30680     
30681     /**
30682      * @cfg {Number} alternativePadWidth padding below box..
30683      */   
30684     alternativePadWidth : 50,
30685     
30686     selectedBrick : [],
30687     
30688     getAutoCreate : function(){
30689         
30690         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30691         
30692         var cfg = {
30693             tag: this.tag,
30694             cls: 'blog-masonary-wrapper ' + this.cls,
30695             cn : {
30696                 cls : 'mas-boxes masonary'
30697             }
30698         };
30699         
30700         return cfg;
30701     },
30702     
30703     getChildContainer: function( )
30704     {
30705         if (this.boxesEl) {
30706             return this.boxesEl;
30707         }
30708         
30709         this.boxesEl = this.el.select('.mas-boxes').first();
30710         
30711         return this.boxesEl;
30712     },
30713     
30714     
30715     initEvents : function()
30716     {
30717         var _this = this;
30718         
30719         if(this.isAutoInitial){
30720             Roo.log('hook children rendered');
30721             this.on('childrenrendered', function() {
30722                 Roo.log('children rendered');
30723                 _this.initial();
30724             } ,this);
30725         }
30726     },
30727     
30728     initial : function()
30729     {
30730         this.selectedBrick = [];
30731         
30732         this.currentSize = this.el.getBox(true);
30733         
30734         Roo.EventManager.onWindowResize(this.resize, this); 
30735
30736         if(!this.isAutoInitial){
30737             this.layout();
30738             return;
30739         }
30740         
30741         this.layout();
30742         
30743         return;
30744         //this.layout.defer(500,this);
30745         
30746     },
30747     
30748     resize : function()
30749     {
30750         var cs = this.el.getBox(true);
30751         
30752         if (
30753                 this.currentSize.width == cs.width && 
30754                 this.currentSize.x == cs.x && 
30755                 this.currentSize.height == cs.height && 
30756                 this.currentSize.y == cs.y 
30757         ) {
30758             Roo.log("no change in with or X or Y");
30759             return;
30760         }
30761         
30762         this.currentSize = cs;
30763         
30764         this.layout();
30765         
30766     },
30767     
30768     layout : function()
30769     {   
30770         this._resetLayout();
30771         
30772         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30773         
30774         this.layoutItems( isInstant );
30775       
30776         this._isLayoutInited = true;
30777         
30778         this.fireEvent('layout', this);
30779         
30780     },
30781     
30782     _resetLayout : function()
30783     {
30784         if(this.isHorizontal){
30785             this.horizontalMeasureColumns();
30786             return;
30787         }
30788         
30789         this.verticalMeasureColumns();
30790         
30791     },
30792     
30793     verticalMeasureColumns : function()
30794     {
30795         this.getContainerWidth();
30796         
30797 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30798 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30799 //            return;
30800 //        }
30801         
30802         var boxWidth = this.boxWidth + this.padWidth;
30803         
30804         if(this.containerWidth < this.boxWidth){
30805             boxWidth = this.containerWidth
30806         }
30807         
30808         var containerWidth = this.containerWidth;
30809         
30810         var cols = Math.floor(containerWidth / boxWidth);
30811         
30812         this.cols = Math.max( cols, 1 );
30813         
30814         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30815         
30816         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30817         
30818         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30819         
30820         this.colWidth = boxWidth + avail - this.padWidth;
30821         
30822         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30823         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30824     },
30825     
30826     horizontalMeasureColumns : function()
30827     {
30828         this.getContainerWidth();
30829         
30830         var boxWidth = this.boxWidth;
30831         
30832         if(this.containerWidth < boxWidth){
30833             boxWidth = this.containerWidth;
30834         }
30835         
30836         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30837         
30838         this.el.setHeight(boxWidth);
30839         
30840     },
30841     
30842     getContainerWidth : function()
30843     {
30844         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30845     },
30846     
30847     layoutItems : function( isInstant )
30848     {
30849         Roo.log(this.bricks);
30850         
30851         var items = Roo.apply([], this.bricks);
30852         
30853         if(this.isHorizontal){
30854             this._horizontalLayoutItems( items , isInstant );
30855             return;
30856         }
30857         
30858 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30859 //            this._verticalAlternativeLayoutItems( items , isInstant );
30860 //            return;
30861 //        }
30862         
30863         this._verticalLayoutItems( items , isInstant );
30864         
30865     },
30866     
30867     _verticalLayoutItems : function ( items , isInstant)
30868     {
30869         if ( !items || !items.length ) {
30870             return;
30871         }
30872         
30873         var standard = [
30874             ['xs', 'xs', 'xs', 'tall'],
30875             ['xs', 'xs', 'tall'],
30876             ['xs', 'xs', 'sm'],
30877             ['xs', 'xs', 'xs'],
30878             ['xs', 'tall'],
30879             ['xs', 'sm'],
30880             ['xs', 'xs'],
30881             ['xs'],
30882             
30883             ['sm', 'xs', 'xs'],
30884             ['sm', 'xs'],
30885             ['sm'],
30886             
30887             ['tall', 'xs', 'xs', 'xs'],
30888             ['tall', 'xs', 'xs'],
30889             ['tall', 'xs'],
30890             ['tall']
30891             
30892         ];
30893         
30894         var queue = [];
30895         
30896         var boxes = [];
30897         
30898         var box = [];
30899         
30900         Roo.each(items, function(item, k){
30901             
30902             switch (item.size) {
30903                 // these layouts take up a full box,
30904                 case 'md' :
30905                 case 'md-left' :
30906                 case 'md-right' :
30907                 case 'wide' :
30908                     
30909                     if(box.length){
30910                         boxes.push(box);
30911                         box = [];
30912                     }
30913                     
30914                     boxes.push([item]);
30915                     
30916                     break;
30917                     
30918                 case 'xs' :
30919                 case 'sm' :
30920                 case 'tall' :
30921                     
30922                     box.push(item);
30923                     
30924                     break;
30925                 default :
30926                     break;
30927                     
30928             }
30929             
30930         }, this);
30931         
30932         if(box.length){
30933             boxes.push(box);
30934             box = [];
30935         }
30936         
30937         var filterPattern = function(box, length)
30938         {
30939             if(!box.length){
30940                 return;
30941             }
30942             
30943             var match = false;
30944             
30945             var pattern = box.slice(0, length);
30946             
30947             var format = [];
30948             
30949             Roo.each(pattern, function(i){
30950                 format.push(i.size);
30951             }, this);
30952             
30953             Roo.each(standard, function(s){
30954                 
30955                 if(String(s) != String(format)){
30956                     return;
30957                 }
30958                 
30959                 match = true;
30960                 return false;
30961                 
30962             }, this);
30963             
30964             if(!match && length == 1){
30965                 return;
30966             }
30967             
30968             if(!match){
30969                 filterPattern(box, length - 1);
30970                 return;
30971             }
30972                 
30973             queue.push(pattern);
30974
30975             box = box.slice(length, box.length);
30976
30977             filterPattern(box, 4);
30978
30979             return;
30980             
30981         }
30982         
30983         Roo.each(boxes, function(box, k){
30984             
30985             if(!box.length){
30986                 return;
30987             }
30988             
30989             if(box.length == 1){
30990                 queue.push(box);
30991                 return;
30992             }
30993             
30994             filterPattern(box, 4);
30995             
30996         }, this);
30997         
30998         this._processVerticalLayoutQueue( queue, isInstant );
30999         
31000     },
31001     
31002 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31003 //    {
31004 //        if ( !items || !items.length ) {
31005 //            return;
31006 //        }
31007 //
31008 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31009 //        
31010 //    },
31011     
31012     _horizontalLayoutItems : function ( items , isInstant)
31013     {
31014         if ( !items || !items.length || items.length < 3) {
31015             return;
31016         }
31017         
31018         items.reverse();
31019         
31020         var eItems = items.slice(0, 3);
31021         
31022         items = items.slice(3, items.length);
31023         
31024         var standard = [
31025             ['xs', 'xs', 'xs', 'wide'],
31026             ['xs', 'xs', 'wide'],
31027             ['xs', 'xs', 'sm'],
31028             ['xs', 'xs', 'xs'],
31029             ['xs', 'wide'],
31030             ['xs', 'sm'],
31031             ['xs', 'xs'],
31032             ['xs'],
31033             
31034             ['sm', 'xs', 'xs'],
31035             ['sm', 'xs'],
31036             ['sm'],
31037             
31038             ['wide', 'xs', 'xs', 'xs'],
31039             ['wide', 'xs', 'xs'],
31040             ['wide', 'xs'],
31041             ['wide'],
31042             
31043             ['wide-thin']
31044         ];
31045         
31046         var queue = [];
31047         
31048         var boxes = [];
31049         
31050         var box = [];
31051         
31052         Roo.each(items, function(item, k){
31053             
31054             switch (item.size) {
31055                 case 'md' :
31056                 case 'md-left' :
31057                 case 'md-right' :
31058                 case 'tall' :
31059                     
31060                     if(box.length){
31061                         boxes.push(box);
31062                         box = [];
31063                     }
31064                     
31065                     boxes.push([item]);
31066                     
31067                     break;
31068                     
31069                 case 'xs' :
31070                 case 'sm' :
31071                 case 'wide' :
31072                 case 'wide-thin' :
31073                     
31074                     box.push(item);
31075                     
31076                     break;
31077                 default :
31078                     break;
31079                     
31080             }
31081             
31082         }, this);
31083         
31084         if(box.length){
31085             boxes.push(box);
31086             box = [];
31087         }
31088         
31089         var filterPattern = function(box, length)
31090         {
31091             if(!box.length){
31092                 return;
31093             }
31094             
31095             var match = false;
31096             
31097             var pattern = box.slice(0, length);
31098             
31099             var format = [];
31100             
31101             Roo.each(pattern, function(i){
31102                 format.push(i.size);
31103             }, this);
31104             
31105             Roo.each(standard, function(s){
31106                 
31107                 if(String(s) != String(format)){
31108                     return;
31109                 }
31110                 
31111                 match = true;
31112                 return false;
31113                 
31114             }, this);
31115             
31116             if(!match && length == 1){
31117                 return;
31118             }
31119             
31120             if(!match){
31121                 filterPattern(box, length - 1);
31122                 return;
31123             }
31124                 
31125             queue.push(pattern);
31126
31127             box = box.slice(length, box.length);
31128
31129             filterPattern(box, 4);
31130
31131             return;
31132             
31133         }
31134         
31135         Roo.each(boxes, function(box, k){
31136             
31137             if(!box.length){
31138                 return;
31139             }
31140             
31141             if(box.length == 1){
31142                 queue.push(box);
31143                 return;
31144             }
31145             
31146             filterPattern(box, 4);
31147             
31148         }, this);
31149         
31150         
31151         var prune = [];
31152         
31153         var pos = this.el.getBox(true);
31154         
31155         var minX = pos.x;
31156         
31157         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31158         
31159         var hit_end = false;
31160         
31161         Roo.each(queue, function(box){
31162             
31163             if(hit_end){
31164                 
31165                 Roo.each(box, function(b){
31166                 
31167                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31168                     b.el.hide();
31169
31170                 }, this);
31171
31172                 return;
31173             }
31174             
31175             var mx = 0;
31176             
31177             Roo.each(box, function(b){
31178                 
31179                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31180                 b.el.show();
31181
31182                 mx = Math.max(mx, b.x);
31183                 
31184             }, this);
31185             
31186             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31187             
31188             if(maxX < minX){
31189                 
31190                 Roo.each(box, function(b){
31191                 
31192                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31193                     b.el.hide();
31194                     
31195                 }, this);
31196                 
31197                 hit_end = true;
31198                 
31199                 return;
31200             }
31201             
31202             prune.push(box);
31203             
31204         }, this);
31205         
31206         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31207     },
31208     
31209     /** Sets position of item in DOM
31210     * @param {Element} item
31211     * @param {Number} x - horizontal position
31212     * @param {Number} y - vertical position
31213     * @param {Boolean} isInstant - disables transitions
31214     */
31215     _processVerticalLayoutQueue : function( queue, isInstant )
31216     {
31217         var pos = this.el.getBox(true);
31218         var x = pos.x;
31219         var y = pos.y;
31220         var maxY = [];
31221         
31222         for (var i = 0; i < this.cols; i++){
31223             maxY[i] = pos.y;
31224         }
31225         
31226         Roo.each(queue, function(box, k){
31227             
31228             var col = k % this.cols;
31229             
31230             Roo.each(box, function(b,kk){
31231                 
31232                 b.el.position('absolute');
31233                 
31234                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31235                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31236                 
31237                 if(b.size == 'md-left' || b.size == 'md-right'){
31238                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31239                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31240                 }
31241                 
31242                 b.el.setWidth(width);
31243                 b.el.setHeight(height);
31244                 // iframe?
31245                 b.el.select('iframe',true).setSize(width,height);
31246                 
31247             }, this);
31248             
31249             for (var i = 0; i < this.cols; i++){
31250                 
31251                 if(maxY[i] < maxY[col]){
31252                     col = i;
31253                     continue;
31254                 }
31255                 
31256                 col = Math.min(col, i);
31257                 
31258             }
31259             
31260             x = pos.x + col * (this.colWidth + this.padWidth);
31261             
31262             y = maxY[col];
31263             
31264             var positions = [];
31265             
31266             switch (box.length){
31267                 case 1 :
31268                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31269                     break;
31270                 case 2 :
31271                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31272                     break;
31273                 case 3 :
31274                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31275                     break;
31276                 case 4 :
31277                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31278                     break;
31279                 default :
31280                     break;
31281             }
31282             
31283             Roo.each(box, function(b,kk){
31284                 
31285                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31286                 
31287                 var sz = b.el.getSize();
31288                 
31289                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31290                 
31291             }, this);
31292             
31293         }, this);
31294         
31295         var mY = 0;
31296         
31297         for (var i = 0; i < this.cols; i++){
31298             mY = Math.max(mY, maxY[i]);
31299         }
31300         
31301         this.el.setHeight(mY - pos.y);
31302         
31303     },
31304     
31305 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31306 //    {
31307 //        var pos = this.el.getBox(true);
31308 //        var x = pos.x;
31309 //        var y = pos.y;
31310 //        var maxX = pos.right;
31311 //        
31312 //        var maxHeight = 0;
31313 //        
31314 //        Roo.each(items, function(item, k){
31315 //            
31316 //            var c = k % 2;
31317 //            
31318 //            item.el.position('absolute');
31319 //                
31320 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31321 //
31322 //            item.el.setWidth(width);
31323 //
31324 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31325 //
31326 //            item.el.setHeight(height);
31327 //            
31328 //            if(c == 0){
31329 //                item.el.setXY([x, y], isInstant ? false : true);
31330 //            } else {
31331 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31332 //            }
31333 //            
31334 //            y = y + height + this.alternativePadWidth;
31335 //            
31336 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31337 //            
31338 //        }, this);
31339 //        
31340 //        this.el.setHeight(maxHeight);
31341 //        
31342 //    },
31343     
31344     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31345     {
31346         var pos = this.el.getBox(true);
31347         
31348         var minX = pos.x;
31349         var minY = pos.y;
31350         
31351         var maxX = pos.right;
31352         
31353         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31354         
31355         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31356         
31357         Roo.each(queue, function(box, k){
31358             
31359             Roo.each(box, function(b, kk){
31360                 
31361                 b.el.position('absolute');
31362                 
31363                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31364                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31365                 
31366                 if(b.size == 'md-left' || b.size == 'md-right'){
31367                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31368                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31369                 }
31370                 
31371                 b.el.setWidth(width);
31372                 b.el.setHeight(height);
31373                 
31374             }, this);
31375             
31376             if(!box.length){
31377                 return;
31378             }
31379             
31380             var positions = [];
31381             
31382             switch (box.length){
31383                 case 1 :
31384                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31385                     break;
31386                 case 2 :
31387                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31388                     break;
31389                 case 3 :
31390                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31391                     break;
31392                 case 4 :
31393                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31394                     break;
31395                 default :
31396                     break;
31397             }
31398             
31399             Roo.each(box, function(b,kk){
31400                 
31401                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31402                 
31403                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31404                 
31405             }, this);
31406             
31407         }, this);
31408         
31409     },
31410     
31411     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31412     {
31413         Roo.each(eItems, function(b,k){
31414             
31415             b.size = (k == 0) ? 'sm' : 'xs';
31416             b.x = (k == 0) ? 2 : 1;
31417             b.y = (k == 0) ? 2 : 1;
31418             
31419             b.el.position('absolute');
31420             
31421             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31422                 
31423             b.el.setWidth(width);
31424             
31425             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31426             
31427             b.el.setHeight(height);
31428             
31429         }, this);
31430
31431         var positions = [];
31432         
31433         positions.push({
31434             x : maxX - this.unitWidth * 2 - this.gutter,
31435             y : minY
31436         });
31437         
31438         positions.push({
31439             x : maxX - this.unitWidth,
31440             y : minY + (this.unitWidth + this.gutter) * 2
31441         });
31442         
31443         positions.push({
31444             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31445             y : minY
31446         });
31447         
31448         Roo.each(eItems, function(b,k){
31449             
31450             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31451
31452         }, this);
31453         
31454     },
31455     
31456     getVerticalOneBoxColPositions : function(x, y, box)
31457     {
31458         var pos = [];
31459         
31460         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31461         
31462         if(box[0].size == 'md-left'){
31463             rand = 0;
31464         }
31465         
31466         if(box[0].size == 'md-right'){
31467             rand = 1;
31468         }
31469         
31470         pos.push({
31471             x : x + (this.unitWidth + this.gutter) * rand,
31472             y : y
31473         });
31474         
31475         return pos;
31476     },
31477     
31478     getVerticalTwoBoxColPositions : function(x, y, box)
31479     {
31480         var pos = [];
31481         
31482         if(box[0].size == 'xs'){
31483             
31484             pos.push({
31485                 x : x,
31486                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31487             });
31488
31489             pos.push({
31490                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31491                 y : y
31492             });
31493             
31494             return pos;
31495             
31496         }
31497         
31498         pos.push({
31499             x : x,
31500             y : y
31501         });
31502
31503         pos.push({
31504             x : x + (this.unitWidth + this.gutter) * 2,
31505             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31506         });
31507         
31508         return pos;
31509         
31510     },
31511     
31512     getVerticalThreeBoxColPositions : function(x, y, box)
31513     {
31514         var pos = [];
31515         
31516         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31517             
31518             pos.push({
31519                 x : x,
31520                 y : y
31521             });
31522
31523             pos.push({
31524                 x : x + (this.unitWidth + this.gutter) * 1,
31525                 y : y
31526             });
31527             
31528             pos.push({
31529                 x : x + (this.unitWidth + this.gutter) * 2,
31530                 y : y
31531             });
31532             
31533             return pos;
31534             
31535         }
31536         
31537         if(box[0].size == 'xs' && box[1].size == 'xs'){
31538             
31539             pos.push({
31540                 x : x,
31541                 y : y
31542             });
31543
31544             pos.push({
31545                 x : x,
31546                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31547             });
31548             
31549             pos.push({
31550                 x : x + (this.unitWidth + this.gutter) * 1,
31551                 y : y
31552             });
31553             
31554             return pos;
31555             
31556         }
31557         
31558         pos.push({
31559             x : x,
31560             y : y
31561         });
31562
31563         pos.push({
31564             x : x + (this.unitWidth + this.gutter) * 2,
31565             y : y
31566         });
31567
31568         pos.push({
31569             x : x + (this.unitWidth + this.gutter) * 2,
31570             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31571         });
31572             
31573         return pos;
31574         
31575     },
31576     
31577     getVerticalFourBoxColPositions : function(x, y, box)
31578     {
31579         var pos = [];
31580         
31581         if(box[0].size == 'xs'){
31582             
31583             pos.push({
31584                 x : x,
31585                 y : y
31586             });
31587
31588             pos.push({
31589                 x : x,
31590                 y : y + (this.unitHeight + this.gutter) * 1
31591             });
31592             
31593             pos.push({
31594                 x : x,
31595                 y : y + (this.unitHeight + this.gutter) * 2
31596             });
31597             
31598             pos.push({
31599                 x : x + (this.unitWidth + this.gutter) * 1,
31600                 y : y
31601             });
31602             
31603             return pos;
31604             
31605         }
31606         
31607         pos.push({
31608             x : x,
31609             y : y
31610         });
31611
31612         pos.push({
31613             x : x + (this.unitWidth + this.gutter) * 2,
31614             y : y
31615         });
31616
31617         pos.push({
31618             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31619             y : y + (this.unitHeight + this.gutter) * 1
31620         });
31621
31622         pos.push({
31623             x : x + (this.unitWidth + this.gutter) * 2,
31624             y : y + (this.unitWidth + this.gutter) * 2
31625         });
31626
31627         return pos;
31628         
31629     },
31630     
31631     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31632     {
31633         var pos = [];
31634         
31635         if(box[0].size == 'md-left'){
31636             pos.push({
31637                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31638                 y : minY
31639             });
31640             
31641             return pos;
31642         }
31643         
31644         if(box[0].size == 'md-right'){
31645             pos.push({
31646                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31647                 y : minY + (this.unitWidth + this.gutter) * 1
31648             });
31649             
31650             return pos;
31651         }
31652         
31653         var rand = Math.floor(Math.random() * (4 - box[0].y));
31654         
31655         pos.push({
31656             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31657             y : minY + (this.unitWidth + this.gutter) * rand
31658         });
31659         
31660         return pos;
31661         
31662     },
31663     
31664     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31665     {
31666         var pos = [];
31667         
31668         if(box[0].size == 'xs'){
31669             
31670             pos.push({
31671                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31672                 y : minY
31673             });
31674
31675             pos.push({
31676                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31677                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31678             });
31679             
31680             return pos;
31681             
31682         }
31683         
31684         pos.push({
31685             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31686             y : minY
31687         });
31688
31689         pos.push({
31690             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31691             y : minY + (this.unitWidth + this.gutter) * 2
31692         });
31693         
31694         return pos;
31695         
31696     },
31697     
31698     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31699     {
31700         var pos = [];
31701         
31702         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31703             
31704             pos.push({
31705                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31706                 y : minY
31707             });
31708
31709             pos.push({
31710                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31711                 y : minY + (this.unitWidth + this.gutter) * 1
31712             });
31713             
31714             pos.push({
31715                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31716                 y : minY + (this.unitWidth + this.gutter) * 2
31717             });
31718             
31719             return pos;
31720             
31721         }
31722         
31723         if(box[0].size == 'xs' && box[1].size == 'xs'){
31724             
31725             pos.push({
31726                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31727                 y : minY
31728             });
31729
31730             pos.push({
31731                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31732                 y : minY
31733             });
31734             
31735             pos.push({
31736                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31737                 y : minY + (this.unitWidth + this.gutter) * 1
31738             });
31739             
31740             return pos;
31741             
31742         }
31743         
31744         pos.push({
31745             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31746             y : minY
31747         });
31748
31749         pos.push({
31750             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31751             y : minY + (this.unitWidth + this.gutter) * 2
31752         });
31753
31754         pos.push({
31755             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31756             y : minY + (this.unitWidth + this.gutter) * 2
31757         });
31758             
31759         return pos;
31760         
31761     },
31762     
31763     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31764     {
31765         var pos = [];
31766         
31767         if(box[0].size == 'xs'){
31768             
31769             pos.push({
31770                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31771                 y : minY
31772             });
31773
31774             pos.push({
31775                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31776                 y : minY
31777             });
31778             
31779             pos.push({
31780                 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),
31781                 y : minY
31782             });
31783             
31784             pos.push({
31785                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31786                 y : minY + (this.unitWidth + this.gutter) * 1
31787             });
31788             
31789             return pos;
31790             
31791         }
31792         
31793         pos.push({
31794             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31795             y : minY
31796         });
31797         
31798         pos.push({
31799             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31800             y : minY + (this.unitWidth + this.gutter) * 2
31801         });
31802         
31803         pos.push({
31804             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31805             y : minY + (this.unitWidth + this.gutter) * 2
31806         });
31807         
31808         pos.push({
31809             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),
31810             y : minY + (this.unitWidth + this.gutter) * 2
31811         });
31812
31813         return pos;
31814         
31815     },
31816     
31817     /**
31818     * remove a Masonry Brick
31819     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31820     */
31821     removeBrick : function(brick_id)
31822     {
31823         if (!brick_id) {
31824             return;
31825         }
31826         
31827         for (var i = 0; i<this.bricks.length; i++) {
31828             if (this.bricks[i].id == brick_id) {
31829                 this.bricks.splice(i,1);
31830                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31831                 this.initial();
31832             }
31833         }
31834     },
31835     
31836     /**
31837     * adds a Masonry Brick
31838     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31839     */
31840     addBrick : function(cfg)
31841     {
31842         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31843         //this.register(cn);
31844         cn.parentId = this.id;
31845         cn.onRender(this.el, null);
31846         return cn;
31847     },
31848     
31849     /**
31850     * register a Masonry Brick
31851     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31852     */
31853     
31854     register : function(brick)
31855     {
31856         this.bricks.push(brick);
31857         brick.masonryId = this.id;
31858     },
31859     
31860     /**
31861     * clear all the Masonry Brick
31862     */
31863     clearAll : function()
31864     {
31865         this.bricks = [];
31866         //this.getChildContainer().dom.innerHTML = "";
31867         this.el.dom.innerHTML = '';
31868     },
31869     
31870     getSelected : function()
31871     {
31872         if (!this.selectedBrick) {
31873             return false;
31874         }
31875         
31876         return this.selectedBrick;
31877     }
31878 });
31879
31880 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31881     
31882     groups: {},
31883      /**
31884     * register a Masonry Layout
31885     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31886     */
31887     
31888     register : function(layout)
31889     {
31890         this.groups[layout.id] = layout;
31891     },
31892     /**
31893     * fetch a  Masonry Layout based on the masonry layout ID
31894     * @param {string} the masonry layout to add
31895     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31896     */
31897     
31898     get: function(layout_id) {
31899         if (typeof(this.groups[layout_id]) == 'undefined') {
31900             return false;
31901         }
31902         return this.groups[layout_id] ;
31903     }
31904     
31905     
31906     
31907 });
31908
31909  
31910
31911  /**
31912  *
31913  * This is based on 
31914  * http://masonry.desandro.com
31915  *
31916  * The idea is to render all the bricks based on vertical width...
31917  *
31918  * The original code extends 'outlayer' - we might need to use that....
31919  * 
31920  */
31921
31922
31923 /**
31924  * @class Roo.bootstrap.LayoutMasonryAuto
31925  * @extends Roo.bootstrap.Component
31926  * Bootstrap Layout Masonry class
31927  * 
31928  * @constructor
31929  * Create a new Element
31930  * @param {Object} config The config object
31931  */
31932
31933 Roo.bootstrap.LayoutMasonryAuto = function(config){
31934     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31935 };
31936
31937 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31938     
31939       /**
31940      * @cfg {Boolean} isFitWidth  - resize the width..
31941      */   
31942     isFitWidth : false,  // options..
31943     /**
31944      * @cfg {Boolean} isOriginLeft = left align?
31945      */   
31946     isOriginLeft : true,
31947     /**
31948      * @cfg {Boolean} isOriginTop = top align?
31949      */   
31950     isOriginTop : false,
31951     /**
31952      * @cfg {Boolean} isLayoutInstant = no animation?
31953      */   
31954     isLayoutInstant : false, // needed?
31955     /**
31956      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31957      */   
31958     isResizingContainer : true,
31959     /**
31960      * @cfg {Number} columnWidth  width of the columns 
31961      */   
31962     
31963     columnWidth : 0,
31964     
31965     /**
31966      * @cfg {Number} maxCols maximum number of columns
31967      */   
31968     
31969     maxCols: 0,
31970     /**
31971      * @cfg {Number} padHeight padding below box..
31972      */   
31973     
31974     padHeight : 10, 
31975     
31976     /**
31977      * @cfg {Boolean} isAutoInitial defalut true
31978      */   
31979     
31980     isAutoInitial : true, 
31981     
31982     // private?
31983     gutter : 0,
31984     
31985     containerWidth: 0,
31986     initialColumnWidth : 0,
31987     currentSize : null,
31988     
31989     colYs : null, // array.
31990     maxY : 0,
31991     padWidth: 10,
31992     
31993     
31994     tag: 'div',
31995     cls: '',
31996     bricks: null, //CompositeElement
31997     cols : 0, // array?
31998     // element : null, // wrapped now this.el
31999     _isLayoutInited : null, 
32000     
32001     
32002     getAutoCreate : function(){
32003         
32004         var cfg = {
32005             tag: this.tag,
32006             cls: 'blog-masonary-wrapper ' + this.cls,
32007             cn : {
32008                 cls : 'mas-boxes masonary'
32009             }
32010         };
32011         
32012         return cfg;
32013     },
32014     
32015     getChildContainer: function( )
32016     {
32017         if (this.boxesEl) {
32018             return this.boxesEl;
32019         }
32020         
32021         this.boxesEl = this.el.select('.mas-boxes').first();
32022         
32023         return this.boxesEl;
32024     },
32025     
32026     
32027     initEvents : function()
32028     {
32029         var _this = this;
32030         
32031         if(this.isAutoInitial){
32032             Roo.log('hook children rendered');
32033             this.on('childrenrendered', function() {
32034                 Roo.log('children rendered');
32035                 _this.initial();
32036             } ,this);
32037         }
32038         
32039     },
32040     
32041     initial : function()
32042     {
32043         this.reloadItems();
32044
32045         this.currentSize = this.el.getBox(true);
32046
32047         /// was window resize... - let's see if this works..
32048         Roo.EventManager.onWindowResize(this.resize, this); 
32049
32050         if(!this.isAutoInitial){
32051             this.layout();
32052             return;
32053         }
32054         
32055         this.layout.defer(500,this);
32056     },
32057     
32058     reloadItems: function()
32059     {
32060         this.bricks = this.el.select('.masonry-brick', true);
32061         
32062         this.bricks.each(function(b) {
32063             //Roo.log(b.getSize());
32064             if (!b.attr('originalwidth')) {
32065                 b.attr('originalwidth',  b.getSize().width);
32066             }
32067             
32068         });
32069         
32070         Roo.log(this.bricks.elements.length);
32071     },
32072     
32073     resize : function()
32074     {
32075         Roo.log('resize');
32076         var cs = this.el.getBox(true);
32077         
32078         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32079             Roo.log("no change in with or X");
32080             return;
32081         }
32082         this.currentSize = cs;
32083         this.layout();
32084     },
32085     
32086     layout : function()
32087     {
32088          Roo.log('layout');
32089         this._resetLayout();
32090         //this._manageStamps();
32091       
32092         // don't animate first layout
32093         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32094         this.layoutItems( isInstant );
32095       
32096         // flag for initalized
32097         this._isLayoutInited = true;
32098     },
32099     
32100     layoutItems : function( isInstant )
32101     {
32102         //var items = this._getItemsForLayout( this.items );
32103         // original code supports filtering layout items.. we just ignore it..
32104         
32105         this._layoutItems( this.bricks , isInstant );
32106       
32107         this._postLayout();
32108     },
32109     _layoutItems : function ( items , isInstant)
32110     {
32111        //this.fireEvent( 'layout', this, items );
32112     
32113
32114         if ( !items || !items.elements.length ) {
32115           // no items, emit event with empty array
32116             return;
32117         }
32118
32119         var queue = [];
32120         items.each(function(item) {
32121             Roo.log("layout item");
32122             Roo.log(item);
32123             // get x/y object from method
32124             var position = this._getItemLayoutPosition( item );
32125             // enqueue
32126             position.item = item;
32127             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32128             queue.push( position );
32129         }, this);
32130       
32131         this._processLayoutQueue( queue );
32132     },
32133     /** Sets position of item in DOM
32134     * @param {Element} item
32135     * @param {Number} x - horizontal position
32136     * @param {Number} y - vertical position
32137     * @param {Boolean} isInstant - disables transitions
32138     */
32139     _processLayoutQueue : function( queue )
32140     {
32141         for ( var i=0, len = queue.length; i < len; i++ ) {
32142             var obj = queue[i];
32143             obj.item.position('absolute');
32144             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32145         }
32146     },
32147       
32148     
32149     /**
32150     * Any logic you want to do after each layout,
32151     * i.e. size the container
32152     */
32153     _postLayout : function()
32154     {
32155         this.resizeContainer();
32156     },
32157     
32158     resizeContainer : function()
32159     {
32160         if ( !this.isResizingContainer ) {
32161             return;
32162         }
32163         var size = this._getContainerSize();
32164         if ( size ) {
32165             this.el.setSize(size.width,size.height);
32166             this.boxesEl.setSize(size.width,size.height);
32167         }
32168     },
32169     
32170     
32171     
32172     _resetLayout : function()
32173     {
32174         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32175         this.colWidth = this.el.getWidth();
32176         //this.gutter = this.el.getWidth(); 
32177         
32178         this.measureColumns();
32179
32180         // reset column Y
32181         var i = this.cols;
32182         this.colYs = [];
32183         while (i--) {
32184             this.colYs.push( 0 );
32185         }
32186     
32187         this.maxY = 0;
32188     },
32189
32190     measureColumns : function()
32191     {
32192         this.getContainerWidth();
32193       // if columnWidth is 0, default to outerWidth of first item
32194         if ( !this.columnWidth ) {
32195             var firstItem = this.bricks.first();
32196             Roo.log(firstItem);
32197             this.columnWidth  = this.containerWidth;
32198             if (firstItem && firstItem.attr('originalwidth') ) {
32199                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32200             }
32201             // columnWidth fall back to item of first element
32202             Roo.log("set column width?");
32203                         this.initialColumnWidth = this.columnWidth  ;
32204
32205             // if first elem has no width, default to size of container
32206             
32207         }
32208         
32209         
32210         if (this.initialColumnWidth) {
32211             this.columnWidth = this.initialColumnWidth;
32212         }
32213         
32214         
32215             
32216         // column width is fixed at the top - however if container width get's smaller we should
32217         // reduce it...
32218         
32219         // this bit calcs how man columns..
32220             
32221         var columnWidth = this.columnWidth += this.gutter;
32222       
32223         // calculate columns
32224         var containerWidth = this.containerWidth + this.gutter;
32225         
32226         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32227         // fix rounding errors, typically with gutters
32228         var excess = columnWidth - containerWidth % columnWidth;
32229         
32230         
32231         // if overshoot is less than a pixel, round up, otherwise floor it
32232         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32233         cols = Math[ mathMethod ]( cols );
32234         this.cols = Math.max( cols, 1 );
32235         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32236         
32237          // padding positioning..
32238         var totalColWidth = this.cols * this.columnWidth;
32239         var padavail = this.containerWidth - totalColWidth;
32240         // so for 2 columns - we need 3 'pads'
32241         
32242         var padNeeded = (1+this.cols) * this.padWidth;
32243         
32244         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32245         
32246         this.columnWidth += padExtra
32247         //this.padWidth = Math.floor(padavail /  ( this.cols));
32248         
32249         // adjust colum width so that padding is fixed??
32250         
32251         // we have 3 columns ... total = width * 3
32252         // we have X left over... that should be used by 
32253         
32254         //if (this.expandC) {
32255             
32256         //}
32257         
32258         
32259         
32260     },
32261     
32262     getContainerWidth : function()
32263     {
32264        /* // container is parent if fit width
32265         var container = this.isFitWidth ? this.element.parentNode : this.element;
32266         // check that this.size and size are there
32267         // IE8 triggers resize on body size change, so they might not be
32268         
32269         var size = getSize( container );  //FIXME
32270         this.containerWidth = size && size.innerWidth; //FIXME
32271         */
32272          
32273         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32274         
32275     },
32276     
32277     _getItemLayoutPosition : function( item )  // what is item?
32278     {
32279         // we resize the item to our columnWidth..
32280       
32281         item.setWidth(this.columnWidth);
32282         item.autoBoxAdjust  = false;
32283         
32284         var sz = item.getSize();
32285  
32286         // how many columns does this brick span
32287         var remainder = this.containerWidth % this.columnWidth;
32288         
32289         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32290         // round if off by 1 pixel, otherwise use ceil
32291         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32292         colSpan = Math.min( colSpan, this.cols );
32293         
32294         // normally this should be '1' as we dont' currently allow multi width columns..
32295         
32296         var colGroup = this._getColGroup( colSpan );
32297         // get the minimum Y value from the columns
32298         var minimumY = Math.min.apply( Math, colGroup );
32299         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32300         
32301         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32302          
32303         // position the brick
32304         var position = {
32305             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32306             y: this.currentSize.y + minimumY + this.padHeight
32307         };
32308         
32309         Roo.log(position);
32310         // apply setHeight to necessary columns
32311         var setHeight = minimumY + sz.height + this.padHeight;
32312         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32313         
32314         var setSpan = this.cols + 1 - colGroup.length;
32315         for ( var i = 0; i < setSpan; i++ ) {
32316           this.colYs[ shortColIndex + i ] = setHeight ;
32317         }
32318       
32319         return position;
32320     },
32321     
32322     /**
32323      * @param {Number} colSpan - number of columns the element spans
32324      * @returns {Array} colGroup
32325      */
32326     _getColGroup : function( colSpan )
32327     {
32328         if ( colSpan < 2 ) {
32329           // if brick spans only one column, use all the column Ys
32330           return this.colYs;
32331         }
32332       
32333         var colGroup = [];
32334         // how many different places could this brick fit horizontally
32335         var groupCount = this.cols + 1 - colSpan;
32336         // for each group potential horizontal position
32337         for ( var i = 0; i < groupCount; i++ ) {
32338           // make an array of colY values for that one group
32339           var groupColYs = this.colYs.slice( i, i + colSpan );
32340           // and get the max value of the array
32341           colGroup[i] = Math.max.apply( Math, groupColYs );
32342         }
32343         return colGroup;
32344     },
32345     /*
32346     _manageStamp : function( stamp )
32347     {
32348         var stampSize =  stamp.getSize();
32349         var offset = stamp.getBox();
32350         // get the columns that this stamp affects
32351         var firstX = this.isOriginLeft ? offset.x : offset.right;
32352         var lastX = firstX + stampSize.width;
32353         var firstCol = Math.floor( firstX / this.columnWidth );
32354         firstCol = Math.max( 0, firstCol );
32355         
32356         var lastCol = Math.floor( lastX / this.columnWidth );
32357         // lastCol should not go over if multiple of columnWidth #425
32358         lastCol -= lastX % this.columnWidth ? 0 : 1;
32359         lastCol = Math.min( this.cols - 1, lastCol );
32360         
32361         // set colYs to bottom of the stamp
32362         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32363             stampSize.height;
32364             
32365         for ( var i = firstCol; i <= lastCol; i++ ) {
32366           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32367         }
32368     },
32369     */
32370     
32371     _getContainerSize : function()
32372     {
32373         this.maxY = Math.max.apply( Math, this.colYs );
32374         var size = {
32375             height: this.maxY
32376         };
32377       
32378         if ( this.isFitWidth ) {
32379             size.width = this._getContainerFitWidth();
32380         }
32381       
32382         return size;
32383     },
32384     
32385     _getContainerFitWidth : function()
32386     {
32387         var unusedCols = 0;
32388         // count unused columns
32389         var i = this.cols;
32390         while ( --i ) {
32391           if ( this.colYs[i] !== 0 ) {
32392             break;
32393           }
32394           unusedCols++;
32395         }
32396         // fit container to columns that have been used
32397         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32398     },
32399     
32400     needsResizeLayout : function()
32401     {
32402         var previousWidth = this.containerWidth;
32403         this.getContainerWidth();
32404         return previousWidth !== this.containerWidth;
32405     }
32406  
32407 });
32408
32409  
32410
32411  /*
32412  * - LGPL
32413  *
32414  * element
32415  * 
32416  */
32417
32418 /**
32419  * @class Roo.bootstrap.MasonryBrick
32420  * @extends Roo.bootstrap.Component
32421  * Bootstrap MasonryBrick class
32422  * 
32423  * @constructor
32424  * Create a new MasonryBrick
32425  * @param {Object} config The config object
32426  */
32427
32428 Roo.bootstrap.MasonryBrick = function(config){
32429     
32430     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32431     
32432     Roo.bootstrap.MasonryBrick.register(this);
32433     
32434     this.addEvents({
32435         // raw events
32436         /**
32437          * @event click
32438          * When a MasonryBrick is clcik
32439          * @param {Roo.bootstrap.MasonryBrick} this
32440          * @param {Roo.EventObject} e
32441          */
32442         "click" : true
32443     });
32444 };
32445
32446 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32447     
32448     /**
32449      * @cfg {String} title
32450      */   
32451     title : '',
32452     /**
32453      * @cfg {String} html
32454      */   
32455     html : '',
32456     /**
32457      * @cfg {String} bgimage
32458      */   
32459     bgimage : '',
32460     /**
32461      * @cfg {String} videourl
32462      */   
32463     videourl : '',
32464     /**
32465      * @cfg {String} cls
32466      */   
32467     cls : '',
32468     /**
32469      * @cfg {String} href
32470      */   
32471     href : '',
32472     /**
32473      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32474      */   
32475     size : 'xs',
32476     
32477     /**
32478      * @cfg {String} placetitle (center|bottom)
32479      */   
32480     placetitle : '',
32481     
32482     /**
32483      * @cfg {Boolean} isFitContainer defalut true
32484      */   
32485     isFitContainer : true, 
32486     
32487     /**
32488      * @cfg {Boolean} preventDefault defalut false
32489      */   
32490     preventDefault : false, 
32491     
32492     /**
32493      * @cfg {Boolean} inverse defalut false
32494      */   
32495     maskInverse : false, 
32496     
32497     getAutoCreate : function()
32498     {
32499         if(!this.isFitContainer){
32500             return this.getSplitAutoCreate();
32501         }
32502         
32503         var cls = 'masonry-brick masonry-brick-full';
32504         
32505         if(this.href.length){
32506             cls += ' masonry-brick-link';
32507         }
32508         
32509         if(this.bgimage.length){
32510             cls += ' masonry-brick-image';
32511         }
32512         
32513         if(this.maskInverse){
32514             cls += ' mask-inverse';
32515         }
32516         
32517         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32518             cls += ' enable-mask';
32519         }
32520         
32521         if(this.size){
32522             cls += ' masonry-' + this.size + '-brick';
32523         }
32524         
32525         if(this.placetitle.length){
32526             
32527             switch (this.placetitle) {
32528                 case 'center' :
32529                     cls += ' masonry-center-title';
32530                     break;
32531                 case 'bottom' :
32532                     cls += ' masonry-bottom-title';
32533                     break;
32534                 default:
32535                     break;
32536             }
32537             
32538         } else {
32539             if(!this.html.length && !this.bgimage.length){
32540                 cls += ' masonry-center-title';
32541             }
32542
32543             if(!this.html.length && this.bgimage.length){
32544                 cls += ' masonry-bottom-title';
32545             }
32546         }
32547         
32548         if(this.cls){
32549             cls += ' ' + this.cls;
32550         }
32551         
32552         var cfg = {
32553             tag: (this.href.length) ? 'a' : 'div',
32554             cls: cls,
32555             cn: [
32556                 {
32557                     tag: 'div',
32558                     cls: 'masonry-brick-mask'
32559                 },
32560                 {
32561                     tag: 'div',
32562                     cls: 'masonry-brick-paragraph',
32563                     cn: []
32564                 }
32565             ]
32566         };
32567         
32568         if(this.href.length){
32569             cfg.href = this.href;
32570         }
32571         
32572         var cn = cfg.cn[1].cn;
32573         
32574         if(this.title.length){
32575             cn.push({
32576                 tag: 'h4',
32577                 cls: 'masonry-brick-title',
32578                 html: this.title
32579             });
32580         }
32581         
32582         if(this.html.length){
32583             cn.push({
32584                 tag: 'p',
32585                 cls: 'masonry-brick-text',
32586                 html: this.html
32587             });
32588         }
32589         
32590         if (!this.title.length && !this.html.length) {
32591             cfg.cn[1].cls += ' hide';
32592         }
32593         
32594         if(this.bgimage.length){
32595             cfg.cn.push({
32596                 tag: 'img',
32597                 cls: 'masonry-brick-image-view',
32598                 src: this.bgimage
32599             });
32600         }
32601         
32602         if(this.videourl.length){
32603             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32604             // youtube support only?
32605             cfg.cn.push({
32606                 tag: 'iframe',
32607                 cls: 'masonry-brick-image-view',
32608                 src: vurl,
32609                 frameborder : 0,
32610                 allowfullscreen : true
32611             });
32612         }
32613         
32614         return cfg;
32615         
32616     },
32617     
32618     getSplitAutoCreate : function()
32619     {
32620         var cls = 'masonry-brick masonry-brick-split';
32621         
32622         if(this.href.length){
32623             cls += ' masonry-brick-link';
32624         }
32625         
32626         if(this.bgimage.length){
32627             cls += ' masonry-brick-image';
32628         }
32629         
32630         if(this.size){
32631             cls += ' masonry-' + this.size + '-brick';
32632         }
32633         
32634         switch (this.placetitle) {
32635             case 'center' :
32636                 cls += ' masonry-center-title';
32637                 break;
32638             case 'bottom' :
32639                 cls += ' masonry-bottom-title';
32640                 break;
32641             default:
32642                 if(!this.bgimage.length){
32643                     cls += ' masonry-center-title';
32644                 }
32645
32646                 if(this.bgimage.length){
32647                     cls += ' masonry-bottom-title';
32648                 }
32649                 break;
32650         }
32651         
32652         if(this.cls){
32653             cls += ' ' + this.cls;
32654         }
32655         
32656         var cfg = {
32657             tag: (this.href.length) ? 'a' : 'div',
32658             cls: cls,
32659             cn: [
32660                 {
32661                     tag: 'div',
32662                     cls: 'masonry-brick-split-head',
32663                     cn: [
32664                         {
32665                             tag: 'div',
32666                             cls: 'masonry-brick-paragraph',
32667                             cn: []
32668                         }
32669                     ]
32670                 },
32671                 {
32672                     tag: 'div',
32673                     cls: 'masonry-brick-split-body',
32674                     cn: []
32675                 }
32676             ]
32677         };
32678         
32679         if(this.href.length){
32680             cfg.href = this.href;
32681         }
32682         
32683         if(this.title.length){
32684             cfg.cn[0].cn[0].cn.push({
32685                 tag: 'h4',
32686                 cls: 'masonry-brick-title',
32687                 html: this.title
32688             });
32689         }
32690         
32691         if(this.html.length){
32692             cfg.cn[1].cn.push({
32693                 tag: 'p',
32694                 cls: 'masonry-brick-text',
32695                 html: this.html
32696             });
32697         }
32698
32699         if(this.bgimage.length){
32700             cfg.cn[0].cn.push({
32701                 tag: 'img',
32702                 cls: 'masonry-brick-image-view',
32703                 src: this.bgimage
32704             });
32705         }
32706         
32707         if(this.videourl.length){
32708             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32709             // youtube support only?
32710             cfg.cn[0].cn.cn.push({
32711                 tag: 'iframe',
32712                 cls: 'masonry-brick-image-view',
32713                 src: vurl,
32714                 frameborder : 0,
32715                 allowfullscreen : true
32716             });
32717         }
32718         
32719         return cfg;
32720     },
32721     
32722     initEvents: function() 
32723     {
32724         switch (this.size) {
32725             case 'xs' :
32726                 this.x = 1;
32727                 this.y = 1;
32728                 break;
32729             case 'sm' :
32730                 this.x = 2;
32731                 this.y = 2;
32732                 break;
32733             case 'md' :
32734             case 'md-left' :
32735             case 'md-right' :
32736                 this.x = 3;
32737                 this.y = 3;
32738                 break;
32739             case 'tall' :
32740                 this.x = 2;
32741                 this.y = 3;
32742                 break;
32743             case 'wide' :
32744                 this.x = 3;
32745                 this.y = 2;
32746                 break;
32747             case 'wide-thin' :
32748                 this.x = 3;
32749                 this.y = 1;
32750                 break;
32751                         
32752             default :
32753                 break;
32754         }
32755         
32756         if(Roo.isTouch){
32757             this.el.on('touchstart', this.onTouchStart, this);
32758             this.el.on('touchmove', this.onTouchMove, this);
32759             this.el.on('touchend', this.onTouchEnd, this);
32760             this.el.on('contextmenu', this.onContextMenu, this);
32761         } else {
32762             this.el.on('mouseenter'  ,this.enter, this);
32763             this.el.on('mouseleave', this.leave, this);
32764             this.el.on('click', this.onClick, this);
32765         }
32766         
32767         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32768             this.parent().bricks.push(this);   
32769         }
32770         
32771     },
32772     
32773     onClick: function(e, el)
32774     {
32775         var time = this.endTimer - this.startTimer;
32776         // Roo.log(e.preventDefault());
32777         if(Roo.isTouch){
32778             if(time > 1000){
32779                 e.preventDefault();
32780                 return;
32781             }
32782         }
32783         
32784         if(!this.preventDefault){
32785             return;
32786         }
32787         
32788         e.preventDefault();
32789         
32790         if (this.activeClass != '') {
32791             this.selectBrick();
32792         }
32793         
32794         this.fireEvent('click', this, e);
32795     },
32796     
32797     enter: function(e, el)
32798     {
32799         e.preventDefault();
32800         
32801         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32802             return;
32803         }
32804         
32805         if(this.bgimage.length && this.html.length){
32806             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32807         }
32808     },
32809     
32810     leave: function(e, el)
32811     {
32812         e.preventDefault();
32813         
32814         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32815             return;
32816         }
32817         
32818         if(this.bgimage.length && this.html.length){
32819             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32820         }
32821     },
32822     
32823     onTouchStart: function(e, el)
32824     {
32825 //        e.preventDefault();
32826         
32827         this.touchmoved = false;
32828         
32829         if(!this.isFitContainer){
32830             return;
32831         }
32832         
32833         if(!this.bgimage.length || !this.html.length){
32834             return;
32835         }
32836         
32837         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32838         
32839         this.timer = new Date().getTime();
32840         
32841     },
32842     
32843     onTouchMove: function(e, el)
32844     {
32845         this.touchmoved = true;
32846     },
32847     
32848     onContextMenu : function(e,el)
32849     {
32850         e.preventDefault();
32851         e.stopPropagation();
32852         return false;
32853     },
32854     
32855     onTouchEnd: function(e, el)
32856     {
32857 //        e.preventDefault();
32858         
32859         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32860         
32861             this.leave(e,el);
32862             
32863             return;
32864         }
32865         
32866         if(!this.bgimage.length || !this.html.length){
32867             
32868             if(this.href.length){
32869                 window.location.href = this.href;
32870             }
32871             
32872             return;
32873         }
32874         
32875         if(!this.isFitContainer){
32876             return;
32877         }
32878         
32879         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32880         
32881         window.location.href = this.href;
32882     },
32883     
32884     //selection on single brick only
32885     selectBrick : function() {
32886         
32887         if (!this.parentId) {
32888             return;
32889         }
32890         
32891         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32892         var index = m.selectedBrick.indexOf(this.id);
32893         
32894         if ( index > -1) {
32895             m.selectedBrick.splice(index,1);
32896             this.el.removeClass(this.activeClass);
32897             return;
32898         }
32899         
32900         for(var i = 0; i < m.selectedBrick.length; i++) {
32901             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32902             b.el.removeClass(b.activeClass);
32903         }
32904         
32905         m.selectedBrick = [];
32906         
32907         m.selectedBrick.push(this.id);
32908         this.el.addClass(this.activeClass);
32909         return;
32910     },
32911     
32912     isSelected : function(){
32913         return this.el.hasClass(this.activeClass);
32914         
32915     }
32916 });
32917
32918 Roo.apply(Roo.bootstrap.MasonryBrick, {
32919     
32920     //groups: {},
32921     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32922      /**
32923     * register a Masonry Brick
32924     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32925     */
32926     
32927     register : function(brick)
32928     {
32929         //this.groups[brick.id] = brick;
32930         this.groups.add(brick.id, brick);
32931     },
32932     /**
32933     * fetch a  masonry brick based on the masonry brick ID
32934     * @param {string} the masonry brick to add
32935     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32936     */
32937     
32938     get: function(brick_id) 
32939     {
32940         // if (typeof(this.groups[brick_id]) == 'undefined') {
32941         //     return false;
32942         // }
32943         // return this.groups[brick_id] ;
32944         
32945         if(this.groups.key(brick_id)) {
32946             return this.groups.key(brick_id);
32947         }
32948         
32949         return false;
32950     }
32951     
32952     
32953     
32954 });
32955
32956  /*
32957  * - LGPL
32958  *
32959  * element
32960  * 
32961  */
32962
32963 /**
32964  * @class Roo.bootstrap.Brick
32965  * @extends Roo.bootstrap.Component
32966  * Bootstrap Brick class
32967  * 
32968  * @constructor
32969  * Create a new Brick
32970  * @param {Object} config The config object
32971  */
32972
32973 Roo.bootstrap.Brick = function(config){
32974     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32975     
32976     this.addEvents({
32977         // raw events
32978         /**
32979          * @event click
32980          * When a Brick is click
32981          * @param {Roo.bootstrap.Brick} this
32982          * @param {Roo.EventObject} e
32983          */
32984         "click" : true
32985     });
32986 };
32987
32988 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32989     
32990     /**
32991      * @cfg {String} title
32992      */   
32993     title : '',
32994     /**
32995      * @cfg {String} html
32996      */   
32997     html : '',
32998     /**
32999      * @cfg {String} bgimage
33000      */   
33001     bgimage : '',
33002     /**
33003      * @cfg {String} cls
33004      */   
33005     cls : '',
33006     /**
33007      * @cfg {String} href
33008      */   
33009     href : '',
33010     /**
33011      * @cfg {String} video
33012      */   
33013     video : '',
33014     /**
33015      * @cfg {Boolean} square
33016      */   
33017     square : true,
33018     
33019     getAutoCreate : function()
33020     {
33021         var cls = 'roo-brick';
33022         
33023         if(this.href.length){
33024             cls += ' roo-brick-link';
33025         }
33026         
33027         if(this.bgimage.length){
33028             cls += ' roo-brick-image';
33029         }
33030         
33031         if(!this.html.length && !this.bgimage.length){
33032             cls += ' roo-brick-center-title';
33033         }
33034         
33035         if(!this.html.length && this.bgimage.length){
33036             cls += ' roo-brick-bottom-title';
33037         }
33038         
33039         if(this.cls){
33040             cls += ' ' + this.cls;
33041         }
33042         
33043         var cfg = {
33044             tag: (this.href.length) ? 'a' : 'div',
33045             cls: cls,
33046             cn: [
33047                 {
33048                     tag: 'div',
33049                     cls: 'roo-brick-paragraph',
33050                     cn: []
33051                 }
33052             ]
33053         };
33054         
33055         if(this.href.length){
33056             cfg.href = this.href;
33057         }
33058         
33059         var cn = cfg.cn[0].cn;
33060         
33061         if(this.title.length){
33062             cn.push({
33063                 tag: 'h4',
33064                 cls: 'roo-brick-title',
33065                 html: this.title
33066             });
33067         }
33068         
33069         if(this.html.length){
33070             cn.push({
33071                 tag: 'p',
33072                 cls: 'roo-brick-text',
33073                 html: this.html
33074             });
33075         } else {
33076             cn.cls += ' hide';
33077         }
33078         
33079         if(this.bgimage.length){
33080             cfg.cn.push({
33081                 tag: 'img',
33082                 cls: 'roo-brick-image-view',
33083                 src: this.bgimage
33084             });
33085         }
33086         
33087         return cfg;
33088     },
33089     
33090     initEvents: function() 
33091     {
33092         if(this.title.length || this.html.length){
33093             this.el.on('mouseenter'  ,this.enter, this);
33094             this.el.on('mouseleave', this.leave, this);
33095         }
33096         
33097         Roo.EventManager.onWindowResize(this.resize, this); 
33098         
33099         if(this.bgimage.length){
33100             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33101             this.imageEl.on('load', this.onImageLoad, this);
33102             return;
33103         }
33104         
33105         this.resize();
33106     },
33107     
33108     onImageLoad : function()
33109     {
33110         this.resize();
33111     },
33112     
33113     resize : function()
33114     {
33115         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33116         
33117         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33118         
33119         if(this.bgimage.length){
33120             var image = this.el.select('.roo-brick-image-view', true).first();
33121             
33122             image.setWidth(paragraph.getWidth());
33123             
33124             if(this.square){
33125                 image.setHeight(paragraph.getWidth());
33126             }
33127             
33128             this.el.setHeight(image.getHeight());
33129             paragraph.setHeight(image.getHeight());
33130             
33131         }
33132         
33133     },
33134     
33135     enter: function(e, el)
33136     {
33137         e.preventDefault();
33138         
33139         if(this.bgimage.length){
33140             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33141             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33142         }
33143     },
33144     
33145     leave: function(e, el)
33146     {
33147         e.preventDefault();
33148         
33149         if(this.bgimage.length){
33150             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33151             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33152         }
33153     }
33154     
33155 });
33156
33157  
33158
33159  /*
33160  * - LGPL
33161  *
33162  * Number field 
33163  */
33164
33165 /**
33166  * @class Roo.bootstrap.NumberField
33167  * @extends Roo.bootstrap.Input
33168  * Bootstrap NumberField class
33169  * 
33170  * 
33171  * 
33172  * 
33173  * @constructor
33174  * Create a new NumberField
33175  * @param {Object} config The config object
33176  */
33177
33178 Roo.bootstrap.NumberField = function(config){
33179     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33180 };
33181
33182 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33183     
33184     /**
33185      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33186      */
33187     allowDecimals : true,
33188     /**
33189      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33190      */
33191     decimalSeparator : ".",
33192     /**
33193      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33194      */
33195     decimalPrecision : 2,
33196     /**
33197      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33198      */
33199     allowNegative : true,
33200     
33201     /**
33202      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33203      */
33204     allowZero: true,
33205     /**
33206      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33207      */
33208     minValue : Number.NEGATIVE_INFINITY,
33209     /**
33210      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33211      */
33212     maxValue : Number.MAX_VALUE,
33213     /**
33214      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33215      */
33216     minText : "The minimum value for this field is {0}",
33217     /**
33218      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33219      */
33220     maxText : "The maximum value for this field is {0}",
33221     /**
33222      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33223      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33224      */
33225     nanText : "{0} is not a valid number",
33226     /**
33227      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33228      */
33229     castInt : true,
33230     /**
33231      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33232      */
33233     thousandsDelimiter : false,
33234     /**
33235      * @cfg {String} valueAlign alignment of value
33236      */
33237     valueAlign : "left",
33238
33239     getAutoCreate : function()
33240     {
33241         var hiddenInput = {
33242             tag: 'input',
33243             type: 'hidden',
33244             id: Roo.id(),
33245             cls: 'hidden-number-input'
33246         };
33247         
33248         if (this.name) {
33249             hiddenInput.name = this.name;
33250         }
33251         
33252         this.name = '';
33253         
33254         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33255         
33256         this.name = hiddenInput.name;
33257         
33258         if(cfg.cn.length > 0) {
33259             cfg.cn.push(hiddenInput);
33260         }
33261         
33262         return cfg;
33263     },
33264
33265     // private
33266     initEvents : function()
33267     {   
33268         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33269         
33270         var allowed = "0123456789";
33271         
33272         if(this.allowDecimals){
33273             allowed += this.decimalSeparator;
33274         }
33275         
33276         if(this.allowNegative){
33277             allowed += "-";
33278         }
33279         
33280         if(this.thousandsDelimiter) {
33281             allowed += ",";
33282         }
33283         
33284         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33285         
33286         var keyPress = function(e){
33287             
33288             var k = e.getKey();
33289             
33290             var c = e.getCharCode();
33291             
33292             if(
33293                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33294                     allowed.indexOf(String.fromCharCode(c)) === -1
33295             ){
33296                 e.stopEvent();
33297                 return;
33298             }
33299             
33300             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33301                 return;
33302             }
33303             
33304             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33305                 e.stopEvent();
33306             }
33307         };
33308         
33309         this.el.on("keypress", keyPress, this);
33310     },
33311     
33312     validateValue : function(value)
33313     {
33314         
33315         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33316             return false;
33317         }
33318         
33319         var num = this.parseValue(value);
33320         
33321         if(isNaN(num)){
33322             this.markInvalid(String.format(this.nanText, value));
33323             return false;
33324         }
33325         
33326         if(num < this.minValue){
33327             this.markInvalid(String.format(this.minText, this.minValue));
33328             return false;
33329         }
33330         
33331         if(num > this.maxValue){
33332             this.markInvalid(String.format(this.maxText, this.maxValue));
33333             return false;
33334         }
33335         
33336         return true;
33337     },
33338
33339     getValue : function()
33340     {
33341         var v = this.hiddenEl().getValue();
33342         
33343         return this.fixPrecision(this.parseValue(v));
33344     },
33345
33346     parseValue : function(value)
33347     {
33348         if(this.thousandsDelimiter) {
33349             value += "";
33350             r = new RegExp(",", "g");
33351             value = value.replace(r, "");
33352         }
33353         
33354         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33355         return isNaN(value) ? '' : value;
33356     },
33357
33358     fixPrecision : function(value)
33359     {
33360         if(this.thousandsDelimiter) {
33361             value += "";
33362             r = new RegExp(",", "g");
33363             value = value.replace(r, "");
33364         }
33365         
33366         var nan = isNaN(value);
33367         
33368         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33369             return nan ? '' : value;
33370         }
33371         return parseFloat(value).toFixed(this.decimalPrecision);
33372     },
33373
33374     setValue : function(v)
33375     {
33376         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33377         
33378         this.value = v;
33379         
33380         if(this.rendered){
33381             
33382             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33383             
33384             this.inputEl().dom.value = (v == '') ? '' :
33385                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33386             
33387             if(!this.allowZero && v === '0') {
33388                 this.hiddenEl().dom.value = '';
33389                 this.inputEl().dom.value = '';
33390             }
33391             
33392             this.validate();
33393         }
33394     },
33395
33396     decimalPrecisionFcn : function(v)
33397     {
33398         return Math.floor(v);
33399     },
33400
33401     beforeBlur : function()
33402     {
33403         if(!this.castInt){
33404             return;
33405         }
33406         
33407         var v = this.parseValue(this.getRawValue());
33408         
33409         if(v || v === 0){
33410             this.setValue(v);
33411         }
33412     },
33413     
33414     hiddenEl : function()
33415     {
33416         return this.el.select('input.hidden-number-input',true).first();
33417     }
33418     
33419 });
33420
33421  
33422
33423 /*
33424 * Licence: LGPL
33425 */
33426
33427 /**
33428  * @class Roo.bootstrap.DocumentSlider
33429  * @extends Roo.bootstrap.Component
33430  * Bootstrap DocumentSlider class
33431  * 
33432  * @constructor
33433  * Create a new DocumentViewer
33434  * @param {Object} config The config object
33435  */
33436
33437 Roo.bootstrap.DocumentSlider = function(config){
33438     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33439     
33440     this.files = [];
33441     
33442     this.addEvents({
33443         /**
33444          * @event initial
33445          * Fire after initEvent
33446          * @param {Roo.bootstrap.DocumentSlider} this
33447          */
33448         "initial" : true,
33449         /**
33450          * @event update
33451          * Fire after update
33452          * @param {Roo.bootstrap.DocumentSlider} this
33453          */
33454         "update" : true,
33455         /**
33456          * @event click
33457          * Fire after click
33458          * @param {Roo.bootstrap.DocumentSlider} this
33459          */
33460         "click" : true
33461     });
33462 };
33463
33464 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33465     
33466     files : false,
33467     
33468     indicator : 0,
33469     
33470     getAutoCreate : function()
33471     {
33472         var cfg = {
33473             tag : 'div',
33474             cls : 'roo-document-slider',
33475             cn : [
33476                 {
33477                     tag : 'div',
33478                     cls : 'roo-document-slider-header',
33479                     cn : [
33480                         {
33481                             tag : 'div',
33482                             cls : 'roo-document-slider-header-title'
33483                         }
33484                     ]
33485                 },
33486                 {
33487                     tag : 'div',
33488                     cls : 'roo-document-slider-body',
33489                     cn : [
33490                         {
33491                             tag : 'div',
33492                             cls : 'roo-document-slider-prev',
33493                             cn : [
33494                                 {
33495                                     tag : 'i',
33496                                     cls : 'fa fa-chevron-left'
33497                                 }
33498                             ]
33499                         },
33500                         {
33501                             tag : 'div',
33502                             cls : 'roo-document-slider-thumb',
33503                             cn : [
33504                                 {
33505                                     tag : 'img',
33506                                     cls : 'roo-document-slider-image'
33507                                 }
33508                             ]
33509                         },
33510                         {
33511                             tag : 'div',
33512                             cls : 'roo-document-slider-next',
33513                             cn : [
33514                                 {
33515                                     tag : 'i',
33516                                     cls : 'fa fa-chevron-right'
33517                                 }
33518                             ]
33519                         }
33520                     ]
33521                 }
33522             ]
33523         };
33524         
33525         return cfg;
33526     },
33527     
33528     initEvents : function()
33529     {
33530         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33531         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33532         
33533         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33534         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33535         
33536         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33537         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33538         
33539         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33540         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33541         
33542         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33543         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33544         
33545         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33546         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33547         
33548         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33549         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33550         
33551         this.thumbEl.on('click', this.onClick, this);
33552         
33553         this.prevIndicator.on('click', this.prev, this);
33554         
33555         this.nextIndicator.on('click', this.next, this);
33556         
33557     },
33558     
33559     initial : function()
33560     {
33561         if(this.files.length){
33562             this.indicator = 1;
33563             this.update()
33564         }
33565         
33566         this.fireEvent('initial', this);
33567     },
33568     
33569     update : function()
33570     {
33571         this.imageEl.attr('src', this.files[this.indicator - 1]);
33572         
33573         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33574         
33575         this.prevIndicator.show();
33576         
33577         if(this.indicator == 1){
33578             this.prevIndicator.hide();
33579         }
33580         
33581         this.nextIndicator.show();
33582         
33583         if(this.indicator == this.files.length){
33584             this.nextIndicator.hide();
33585         }
33586         
33587         this.thumbEl.scrollTo('top');
33588         
33589         this.fireEvent('update', this);
33590     },
33591     
33592     onClick : function(e)
33593     {
33594         e.preventDefault();
33595         
33596         this.fireEvent('click', this);
33597     },
33598     
33599     prev : function(e)
33600     {
33601         e.preventDefault();
33602         
33603         this.indicator = Math.max(1, this.indicator - 1);
33604         
33605         this.update();
33606     },
33607     
33608     next : function(e)
33609     {
33610         e.preventDefault();
33611         
33612         this.indicator = Math.min(this.files.length, this.indicator + 1);
33613         
33614         this.update();
33615     }
33616 });
33617 /*
33618  * - LGPL
33619  *
33620  * RadioSet
33621  *
33622  *
33623  */
33624
33625 /**
33626  * @class Roo.bootstrap.RadioSet
33627  * @extends Roo.bootstrap.Input
33628  * Bootstrap RadioSet class
33629  * @cfg {String} indicatorpos (left|right) default left
33630  * @cfg {Boolean} inline (true|false) inline the element (default true)
33631  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33632  * @constructor
33633  * Create a new RadioSet
33634  * @param {Object} config The config object
33635  */
33636
33637 Roo.bootstrap.RadioSet = function(config){
33638     
33639     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33640     
33641     this.radioes = [];
33642     
33643     Roo.bootstrap.RadioSet.register(this);
33644     
33645     this.addEvents({
33646         /**
33647         * @event check
33648         * Fires when the element is checked or unchecked.
33649         * @param {Roo.bootstrap.RadioSet} this This radio
33650         * @param {Roo.bootstrap.Radio} item The checked item
33651         */
33652        check : true,
33653        /**
33654         * @event click
33655         * Fires when the element is click.
33656         * @param {Roo.bootstrap.RadioSet} this This radio set
33657         * @param {Roo.bootstrap.Radio} item The checked item
33658         * @param {Roo.EventObject} e The event object
33659         */
33660        click : true
33661     });
33662     
33663 };
33664
33665 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33666
33667     radioes : false,
33668     
33669     inline : true,
33670     
33671     weight : '',
33672     
33673     indicatorpos : 'left',
33674     
33675     getAutoCreate : function()
33676     {
33677         var label = {
33678             tag : 'label',
33679             cls : 'roo-radio-set-label',
33680             cn : [
33681                 {
33682                     tag : 'span',
33683                     html : this.fieldLabel
33684                 }
33685             ]
33686         };
33687         
33688         if(this.indicatorpos == 'left'){
33689             label.cn.unshift({
33690                 tag : 'i',
33691                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33692                 tooltip : 'This field is required'
33693             });
33694         } else {
33695             label.cn.push({
33696                 tag : 'i',
33697                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33698                 tooltip : 'This field is required'
33699             });
33700         }
33701         
33702         var items = {
33703             tag : 'div',
33704             cls : 'roo-radio-set-items'
33705         };
33706         
33707         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33708         
33709         if (align === 'left' && this.fieldLabel.length) {
33710             
33711             items = {
33712                 cls : "roo-radio-set-right", 
33713                 cn: [
33714                     items
33715                 ]
33716             };
33717             
33718             if(this.labelWidth > 12){
33719                 label.style = "width: " + this.labelWidth + 'px';
33720             }
33721             
33722             if(this.labelWidth < 13 && this.labelmd == 0){
33723                 this.labelmd = this.labelWidth;
33724             }
33725             
33726             if(this.labellg > 0){
33727                 label.cls += ' col-lg-' + this.labellg;
33728                 items.cls += ' col-lg-' + (12 - this.labellg);
33729             }
33730             
33731             if(this.labelmd > 0){
33732                 label.cls += ' col-md-' + this.labelmd;
33733                 items.cls += ' col-md-' + (12 - this.labelmd);
33734             }
33735             
33736             if(this.labelsm > 0){
33737                 label.cls += ' col-sm-' + this.labelsm;
33738                 items.cls += ' col-sm-' + (12 - this.labelsm);
33739             }
33740             
33741             if(this.labelxs > 0){
33742                 label.cls += ' col-xs-' + this.labelxs;
33743                 items.cls += ' col-xs-' + (12 - this.labelxs);
33744             }
33745         }
33746         
33747         var cfg = {
33748             tag : 'div',
33749             cls : 'roo-radio-set',
33750             cn : [
33751                 {
33752                     tag : 'input',
33753                     cls : 'roo-radio-set-input',
33754                     type : 'hidden',
33755                     name : this.name,
33756                     value : this.value ? this.value :  ''
33757                 },
33758                 label,
33759                 items
33760             ]
33761         };
33762         
33763         if(this.weight.length){
33764             cfg.cls += ' roo-radio-' + this.weight;
33765         }
33766         
33767         if(this.inline) {
33768             cfg.cls += ' roo-radio-set-inline';
33769         }
33770         
33771         var settings=this;
33772         ['xs','sm','md','lg'].map(function(size){
33773             if (settings[size]) {
33774                 cfg.cls += ' col-' + size + '-' + settings[size];
33775             }
33776         });
33777         
33778         return cfg;
33779         
33780     },
33781
33782     initEvents : function()
33783     {
33784         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33785         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33786         
33787         if(!this.fieldLabel.length){
33788             this.labelEl.hide();
33789         }
33790         
33791         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33792         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33793         
33794         this.indicator = this.indicatorEl();
33795         
33796         if(this.indicator){
33797             this.indicator.addClass('invisible');
33798         }
33799         
33800         this.originalValue = this.getValue();
33801         
33802     },
33803     
33804     inputEl: function ()
33805     {
33806         return this.el.select('.roo-radio-set-input', true).first();
33807     },
33808     
33809     getChildContainer : function()
33810     {
33811         return this.itemsEl;
33812     },
33813     
33814     register : function(item)
33815     {
33816         this.radioes.push(item);
33817         
33818     },
33819     
33820     validate : function()
33821     {   
33822         if(this.getVisibilityEl().hasClass('hidden')){
33823             return true;
33824         }
33825         
33826         var valid = false;
33827         
33828         Roo.each(this.radioes, function(i){
33829             if(!i.checked){
33830                 return;
33831             }
33832             
33833             valid = true;
33834             return false;
33835         });
33836         
33837         if(this.allowBlank) {
33838             return true;
33839         }
33840         
33841         if(this.disabled || valid){
33842             this.markValid();
33843             return true;
33844         }
33845         
33846         this.markInvalid();
33847         return false;
33848         
33849     },
33850     
33851     markValid : function()
33852     {
33853         if(this.labelEl.isVisible(true)){
33854             this.indicatorEl().removeClass('visible');
33855             this.indicatorEl().addClass('invisible');
33856         }
33857         
33858         this.el.removeClass([this.invalidClass, this.validClass]);
33859         this.el.addClass(this.validClass);
33860         
33861         this.fireEvent('valid', this);
33862     },
33863     
33864     markInvalid : function(msg)
33865     {
33866         if(this.allowBlank || this.disabled){
33867             return;
33868         }
33869         
33870         if(this.labelEl.isVisible(true)){
33871             this.indicatorEl().removeClass('invisible');
33872             this.indicatorEl().addClass('visible');
33873         }
33874         
33875         this.el.removeClass([this.invalidClass, this.validClass]);
33876         this.el.addClass(this.invalidClass);
33877         
33878         this.fireEvent('invalid', this, msg);
33879         
33880     },
33881     
33882     setValue : function(v, suppressEvent)
33883     {   
33884         if(this.value === v){
33885             return;
33886         }
33887         
33888         this.value = v;
33889         
33890         if(this.rendered){
33891             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33892         }
33893         
33894         Roo.each(this.radioes, function(i){
33895             i.checked = false;
33896             i.el.removeClass('checked');
33897         });
33898         
33899         Roo.each(this.radioes, function(i){
33900             
33901             if(i.value === v || i.value.toString() === v.toString()){
33902                 i.checked = true;
33903                 i.el.addClass('checked');
33904                 
33905                 if(suppressEvent !== true){
33906                     this.fireEvent('check', this, i);
33907                 }
33908                 
33909                 return false;
33910             }
33911             
33912         }, this);
33913         
33914         this.validate();
33915     },
33916     
33917     clearInvalid : function(){
33918         
33919         if(!this.el || this.preventMark){
33920             return;
33921         }
33922         
33923         this.el.removeClass([this.invalidClass]);
33924         
33925         this.fireEvent('valid', this);
33926     }
33927     
33928 });
33929
33930 Roo.apply(Roo.bootstrap.RadioSet, {
33931     
33932     groups: {},
33933     
33934     register : function(set)
33935     {
33936         this.groups[set.name] = set;
33937     },
33938     
33939     get: function(name) 
33940     {
33941         if (typeof(this.groups[name]) == 'undefined') {
33942             return false;
33943         }
33944         
33945         return this.groups[name] ;
33946     }
33947     
33948 });
33949 /*
33950  * Based on:
33951  * Ext JS Library 1.1.1
33952  * Copyright(c) 2006-2007, Ext JS, LLC.
33953  *
33954  * Originally Released Under LGPL - original licence link has changed is not relivant.
33955  *
33956  * Fork - LGPL
33957  * <script type="text/javascript">
33958  */
33959
33960
33961 /**
33962  * @class Roo.bootstrap.SplitBar
33963  * @extends Roo.util.Observable
33964  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33965  * <br><br>
33966  * Usage:
33967  * <pre><code>
33968 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33969                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33970 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33971 split.minSize = 100;
33972 split.maxSize = 600;
33973 split.animate = true;
33974 split.on('moved', splitterMoved);
33975 </code></pre>
33976  * @constructor
33977  * Create a new SplitBar
33978  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33979  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33980  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33981  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33982                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33983                         position of the SplitBar).
33984  */
33985 Roo.bootstrap.SplitBar = function(cfg){
33986     
33987     /** @private */
33988     
33989     //{
33990     //  dragElement : elm
33991     //  resizingElement: el,
33992         // optional..
33993     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33994     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33995         // existingProxy ???
33996     //}
33997     
33998     this.el = Roo.get(cfg.dragElement, true);
33999     this.el.dom.unselectable = "on";
34000     /** @private */
34001     this.resizingEl = Roo.get(cfg.resizingElement, true);
34002
34003     /**
34004      * @private
34005      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34006      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34007      * @type Number
34008      */
34009     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34010     
34011     /**
34012      * The minimum size of the resizing element. (Defaults to 0)
34013      * @type Number
34014      */
34015     this.minSize = 0;
34016     
34017     /**
34018      * The maximum size of the resizing element. (Defaults to 2000)
34019      * @type Number
34020      */
34021     this.maxSize = 2000;
34022     
34023     /**
34024      * Whether to animate the transition to the new size
34025      * @type Boolean
34026      */
34027     this.animate = false;
34028     
34029     /**
34030      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34031      * @type Boolean
34032      */
34033     this.useShim = false;
34034     
34035     /** @private */
34036     this.shim = null;
34037     
34038     if(!cfg.existingProxy){
34039         /** @private */
34040         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34041     }else{
34042         this.proxy = Roo.get(cfg.existingProxy).dom;
34043     }
34044     /** @private */
34045     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34046     
34047     /** @private */
34048     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34049     
34050     /** @private */
34051     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34052     
34053     /** @private */
34054     this.dragSpecs = {};
34055     
34056     /**
34057      * @private The adapter to use to positon and resize elements
34058      */
34059     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34060     this.adapter.init(this);
34061     
34062     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34063         /** @private */
34064         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34065         this.el.addClass("roo-splitbar-h");
34066     }else{
34067         /** @private */
34068         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34069         this.el.addClass("roo-splitbar-v");
34070     }
34071     
34072     this.addEvents({
34073         /**
34074          * @event resize
34075          * Fires when the splitter is moved (alias for {@link #event-moved})
34076          * @param {Roo.bootstrap.SplitBar} this
34077          * @param {Number} newSize the new width or height
34078          */
34079         "resize" : true,
34080         /**
34081          * @event moved
34082          * Fires when the splitter is moved
34083          * @param {Roo.bootstrap.SplitBar} this
34084          * @param {Number} newSize the new width or height
34085          */
34086         "moved" : true,
34087         /**
34088          * @event beforeresize
34089          * Fires before the splitter is dragged
34090          * @param {Roo.bootstrap.SplitBar} this
34091          */
34092         "beforeresize" : true,
34093
34094         "beforeapply" : true
34095     });
34096
34097     Roo.util.Observable.call(this);
34098 };
34099
34100 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34101     onStartProxyDrag : function(x, y){
34102         this.fireEvent("beforeresize", this);
34103         if(!this.overlay){
34104             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34105             o.unselectable();
34106             o.enableDisplayMode("block");
34107             // all splitbars share the same overlay
34108             Roo.bootstrap.SplitBar.prototype.overlay = o;
34109         }
34110         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34111         this.overlay.show();
34112         Roo.get(this.proxy).setDisplayed("block");
34113         var size = this.adapter.getElementSize(this);
34114         this.activeMinSize = this.getMinimumSize();;
34115         this.activeMaxSize = this.getMaximumSize();;
34116         var c1 = size - this.activeMinSize;
34117         var c2 = Math.max(this.activeMaxSize - size, 0);
34118         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34119             this.dd.resetConstraints();
34120             this.dd.setXConstraint(
34121                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34122                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34123             );
34124             this.dd.setYConstraint(0, 0);
34125         }else{
34126             this.dd.resetConstraints();
34127             this.dd.setXConstraint(0, 0);
34128             this.dd.setYConstraint(
34129                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34130                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34131             );
34132          }
34133         this.dragSpecs.startSize = size;
34134         this.dragSpecs.startPoint = [x, y];
34135         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34136     },
34137     
34138     /** 
34139      * @private Called after the drag operation by the DDProxy
34140      */
34141     onEndProxyDrag : function(e){
34142         Roo.get(this.proxy).setDisplayed(false);
34143         var endPoint = Roo.lib.Event.getXY(e);
34144         if(this.overlay){
34145             this.overlay.hide();
34146         }
34147         var newSize;
34148         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34149             newSize = this.dragSpecs.startSize + 
34150                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34151                     endPoint[0] - this.dragSpecs.startPoint[0] :
34152                     this.dragSpecs.startPoint[0] - endPoint[0]
34153                 );
34154         }else{
34155             newSize = this.dragSpecs.startSize + 
34156                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34157                     endPoint[1] - this.dragSpecs.startPoint[1] :
34158                     this.dragSpecs.startPoint[1] - endPoint[1]
34159                 );
34160         }
34161         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34162         if(newSize != this.dragSpecs.startSize){
34163             if(this.fireEvent('beforeapply', this, newSize) !== false){
34164                 this.adapter.setElementSize(this, newSize);
34165                 this.fireEvent("moved", this, newSize);
34166                 this.fireEvent("resize", this, newSize);
34167             }
34168         }
34169     },
34170     
34171     /**
34172      * Get the adapter this SplitBar uses
34173      * @return The adapter object
34174      */
34175     getAdapter : function(){
34176         return this.adapter;
34177     },
34178     
34179     /**
34180      * Set the adapter this SplitBar uses
34181      * @param {Object} adapter A SplitBar adapter object
34182      */
34183     setAdapter : function(adapter){
34184         this.adapter = adapter;
34185         this.adapter.init(this);
34186     },
34187     
34188     /**
34189      * Gets the minimum size for the resizing element
34190      * @return {Number} The minimum size
34191      */
34192     getMinimumSize : function(){
34193         return this.minSize;
34194     },
34195     
34196     /**
34197      * Sets the minimum size for the resizing element
34198      * @param {Number} minSize The minimum size
34199      */
34200     setMinimumSize : function(minSize){
34201         this.minSize = minSize;
34202     },
34203     
34204     /**
34205      * Gets the maximum size for the resizing element
34206      * @return {Number} The maximum size
34207      */
34208     getMaximumSize : function(){
34209         return this.maxSize;
34210     },
34211     
34212     /**
34213      * Sets the maximum size for the resizing element
34214      * @param {Number} maxSize The maximum size
34215      */
34216     setMaximumSize : function(maxSize){
34217         this.maxSize = maxSize;
34218     },
34219     
34220     /**
34221      * Sets the initialize size for the resizing element
34222      * @param {Number} size The initial size
34223      */
34224     setCurrentSize : function(size){
34225         var oldAnimate = this.animate;
34226         this.animate = false;
34227         this.adapter.setElementSize(this, size);
34228         this.animate = oldAnimate;
34229     },
34230     
34231     /**
34232      * Destroy this splitbar. 
34233      * @param {Boolean} removeEl True to remove the element
34234      */
34235     destroy : function(removeEl){
34236         if(this.shim){
34237             this.shim.remove();
34238         }
34239         this.dd.unreg();
34240         this.proxy.parentNode.removeChild(this.proxy);
34241         if(removeEl){
34242             this.el.remove();
34243         }
34244     }
34245 });
34246
34247 /**
34248  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
34249  */
34250 Roo.bootstrap.SplitBar.createProxy = function(dir){
34251     var proxy = new Roo.Element(document.createElement("div"));
34252     proxy.unselectable();
34253     var cls = 'roo-splitbar-proxy';
34254     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34255     document.body.appendChild(proxy.dom);
34256     return proxy.dom;
34257 };
34258
34259 /** 
34260  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34261  * Default Adapter. It assumes the splitter and resizing element are not positioned
34262  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34263  */
34264 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34265 };
34266
34267 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34268     // do nothing for now
34269     init : function(s){
34270     
34271     },
34272     /**
34273      * Called before drag operations to get the current size of the resizing element. 
34274      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34275      */
34276      getElementSize : function(s){
34277         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34278             return s.resizingEl.getWidth();
34279         }else{
34280             return s.resizingEl.getHeight();
34281         }
34282     },
34283     
34284     /**
34285      * Called after drag operations to set the size of the resizing element.
34286      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34287      * @param {Number} newSize The new size to set
34288      * @param {Function} onComplete A function to be invoked when resizing is complete
34289      */
34290     setElementSize : function(s, newSize, onComplete){
34291         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34292             if(!s.animate){
34293                 s.resizingEl.setWidth(newSize);
34294                 if(onComplete){
34295                     onComplete(s, newSize);
34296                 }
34297             }else{
34298                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34299             }
34300         }else{
34301             
34302             if(!s.animate){
34303                 s.resizingEl.setHeight(newSize);
34304                 if(onComplete){
34305                     onComplete(s, newSize);
34306                 }
34307             }else{
34308                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34309             }
34310         }
34311     }
34312 };
34313
34314 /** 
34315  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34316  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34317  * Adapter that  moves the splitter element to align with the resized sizing element. 
34318  * Used with an absolute positioned SplitBar.
34319  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34320  * document.body, make sure you assign an id to the body element.
34321  */
34322 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34323     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34324     this.container = Roo.get(container);
34325 };
34326
34327 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34328     init : function(s){
34329         this.basic.init(s);
34330     },
34331     
34332     getElementSize : function(s){
34333         return this.basic.getElementSize(s);
34334     },
34335     
34336     setElementSize : function(s, newSize, onComplete){
34337         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34338     },
34339     
34340     moveSplitter : function(s){
34341         var yes = Roo.bootstrap.SplitBar;
34342         switch(s.placement){
34343             case yes.LEFT:
34344                 s.el.setX(s.resizingEl.getRight());
34345                 break;
34346             case yes.RIGHT:
34347                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34348                 break;
34349             case yes.TOP:
34350                 s.el.setY(s.resizingEl.getBottom());
34351                 break;
34352             case yes.BOTTOM:
34353                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34354                 break;
34355         }
34356     }
34357 };
34358
34359 /**
34360  * Orientation constant - Create a vertical SplitBar
34361  * @static
34362  * @type Number
34363  */
34364 Roo.bootstrap.SplitBar.VERTICAL = 1;
34365
34366 /**
34367  * Orientation constant - Create a horizontal SplitBar
34368  * @static
34369  * @type Number
34370  */
34371 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34372
34373 /**
34374  * Placement constant - The resizing element is to the left of the splitter element
34375  * @static
34376  * @type Number
34377  */
34378 Roo.bootstrap.SplitBar.LEFT = 1;
34379
34380 /**
34381  * Placement constant - The resizing element is to the right of the splitter element
34382  * @static
34383  * @type Number
34384  */
34385 Roo.bootstrap.SplitBar.RIGHT = 2;
34386
34387 /**
34388  * Placement constant - The resizing element is positioned above the splitter element
34389  * @static
34390  * @type Number
34391  */
34392 Roo.bootstrap.SplitBar.TOP = 3;
34393
34394 /**
34395  * Placement constant - The resizing element is positioned under splitter element
34396  * @static
34397  * @type Number
34398  */
34399 Roo.bootstrap.SplitBar.BOTTOM = 4;
34400 Roo.namespace("Roo.bootstrap.layout");/*
34401  * Based on:
34402  * Ext JS Library 1.1.1
34403  * Copyright(c) 2006-2007, Ext JS, LLC.
34404  *
34405  * Originally Released Under LGPL - original licence link has changed is not relivant.
34406  *
34407  * Fork - LGPL
34408  * <script type="text/javascript">
34409  */
34410
34411 /**
34412  * @class Roo.bootstrap.layout.Manager
34413  * @extends Roo.bootstrap.Component
34414  * Base class for layout managers.
34415  */
34416 Roo.bootstrap.layout.Manager = function(config)
34417 {
34418     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34419
34420
34421
34422
34423
34424     /** false to disable window resize monitoring @type Boolean */
34425     this.monitorWindowResize = true;
34426     this.regions = {};
34427     this.addEvents({
34428         /**
34429          * @event layout
34430          * Fires when a layout is performed.
34431          * @param {Roo.LayoutManager} this
34432          */
34433         "layout" : true,
34434         /**
34435          * @event regionresized
34436          * Fires when the user resizes a region.
34437          * @param {Roo.LayoutRegion} region The resized region
34438          * @param {Number} newSize The new size (width for east/west, height for north/south)
34439          */
34440         "regionresized" : true,
34441         /**
34442          * @event regioncollapsed
34443          * Fires when a region is collapsed.
34444          * @param {Roo.LayoutRegion} region The collapsed region
34445          */
34446         "regioncollapsed" : true,
34447         /**
34448          * @event regionexpanded
34449          * Fires when a region is expanded.
34450          * @param {Roo.LayoutRegion} region The expanded region
34451          */
34452         "regionexpanded" : true
34453     });
34454     this.updating = false;
34455
34456     if (config.el) {
34457         this.el = Roo.get(config.el);
34458         this.initEvents();
34459     }
34460
34461 };
34462
34463 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34464
34465
34466     regions : null,
34467
34468     monitorWindowResize : true,
34469
34470
34471     updating : false,
34472
34473
34474     onRender : function(ct, position)
34475     {
34476         if(!this.el){
34477             this.el = Roo.get(ct);
34478             this.initEvents();
34479         }
34480         //this.fireEvent('render',this);
34481     },
34482
34483
34484     initEvents: function()
34485     {
34486
34487
34488         // ie scrollbar fix
34489         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34490             document.body.scroll = "no";
34491         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34492             this.el.position('relative');
34493         }
34494         this.id = this.el.id;
34495         this.el.addClass("roo-layout-container");
34496         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34497         if(this.el.dom != document.body ) {
34498             this.el.on('resize', this.layout,this);
34499             this.el.on('show', this.layout,this);
34500         }
34501
34502     },
34503
34504     /**
34505      * Returns true if this layout is currently being updated
34506      * @return {Boolean}
34507      */
34508     isUpdating : function(){
34509         return this.updating;
34510     },
34511
34512     /**
34513      * Suspend the LayoutManager from doing auto-layouts while
34514      * making multiple add or remove calls
34515      */
34516     beginUpdate : function(){
34517         this.updating = true;
34518     },
34519
34520     /**
34521      * Restore auto-layouts and optionally disable the manager from performing a layout
34522      * @param {Boolean} noLayout true to disable a layout update
34523      */
34524     endUpdate : function(noLayout){
34525         this.updating = false;
34526         if(!noLayout){
34527             this.layout();
34528         }
34529     },
34530
34531     layout: function(){
34532         // abstract...
34533     },
34534
34535     onRegionResized : function(region, newSize){
34536         this.fireEvent("regionresized", region, newSize);
34537         this.layout();
34538     },
34539
34540     onRegionCollapsed : function(region){
34541         this.fireEvent("regioncollapsed", region);
34542     },
34543
34544     onRegionExpanded : function(region){
34545         this.fireEvent("regionexpanded", region);
34546     },
34547
34548     /**
34549      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34550      * performs box-model adjustments.
34551      * @return {Object} The size as an object {width: (the width), height: (the height)}
34552      */
34553     getViewSize : function()
34554     {
34555         var size;
34556         if(this.el.dom != document.body){
34557             size = this.el.getSize();
34558         }else{
34559             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34560         }
34561         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34562         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34563         return size;
34564     },
34565
34566     /**
34567      * Returns the Element this layout is bound to.
34568      * @return {Roo.Element}
34569      */
34570     getEl : function(){
34571         return this.el;
34572     },
34573
34574     /**
34575      * Returns the specified region.
34576      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34577      * @return {Roo.LayoutRegion}
34578      */
34579     getRegion : function(target){
34580         return this.regions[target.toLowerCase()];
34581     },
34582
34583     onWindowResize : function(){
34584         if(this.monitorWindowResize){
34585             this.layout();
34586         }
34587     }
34588 });
34589 /*
34590  * Based on:
34591  * Ext JS Library 1.1.1
34592  * Copyright(c) 2006-2007, Ext JS, LLC.
34593  *
34594  * Originally Released Under LGPL - original licence link has changed is not relivant.
34595  *
34596  * Fork - LGPL
34597  * <script type="text/javascript">
34598  */
34599 /**
34600  * @class Roo.bootstrap.layout.Border
34601  * @extends Roo.bootstrap.layout.Manager
34602  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34603  * please see: examples/bootstrap/nested.html<br><br>
34604  
34605 <b>The container the layout is rendered into can be either the body element or any other element.
34606 If it is not the body element, the container needs to either be an absolute positioned element,
34607 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34608 the container size if it is not the body element.</b>
34609
34610 * @constructor
34611 * Create a new Border
34612 * @param {Object} config Configuration options
34613  */
34614 Roo.bootstrap.layout.Border = function(config){
34615     config = config || {};
34616     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34617     
34618     
34619     
34620     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34621         if(config[region]){
34622             config[region].region = region;
34623             this.addRegion(config[region]);
34624         }
34625     },this);
34626     
34627 };
34628
34629 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34630
34631 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34632     /**
34633      * Creates and adds a new region if it doesn't already exist.
34634      * @param {String} target The target region key (north, south, east, west or center).
34635      * @param {Object} config The regions config object
34636      * @return {BorderLayoutRegion} The new region
34637      */
34638     addRegion : function(config)
34639     {
34640         if(!this.regions[config.region]){
34641             var r = this.factory(config);
34642             this.bindRegion(r);
34643         }
34644         return this.regions[config.region];
34645     },
34646
34647     // private (kinda)
34648     bindRegion : function(r){
34649         this.regions[r.config.region] = r;
34650         
34651         r.on("visibilitychange",    this.layout, this);
34652         r.on("paneladded",          this.layout, this);
34653         r.on("panelremoved",        this.layout, this);
34654         r.on("invalidated",         this.layout, this);
34655         r.on("resized",             this.onRegionResized, this);
34656         r.on("collapsed",           this.onRegionCollapsed, this);
34657         r.on("expanded",            this.onRegionExpanded, this);
34658     },
34659
34660     /**
34661      * Performs a layout update.
34662      */
34663     layout : function()
34664     {
34665         if(this.updating) {
34666             return;
34667         }
34668         
34669         // render all the rebions if they have not been done alreayd?
34670         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34671             if(this.regions[region] && !this.regions[region].bodyEl){
34672                 this.regions[region].onRender(this.el)
34673             }
34674         },this);
34675         
34676         var size = this.getViewSize();
34677         var w = size.width;
34678         var h = size.height;
34679         var centerW = w;
34680         var centerH = h;
34681         var centerY = 0;
34682         var centerX = 0;
34683         //var x = 0, y = 0;
34684
34685         var rs = this.regions;
34686         var north = rs["north"];
34687         var south = rs["south"]; 
34688         var west = rs["west"];
34689         var east = rs["east"];
34690         var center = rs["center"];
34691         //if(this.hideOnLayout){ // not supported anymore
34692             //c.el.setStyle("display", "none");
34693         //}
34694         if(north && north.isVisible()){
34695             var b = north.getBox();
34696             var m = north.getMargins();
34697             b.width = w - (m.left+m.right);
34698             b.x = m.left;
34699             b.y = m.top;
34700             centerY = b.height + b.y + m.bottom;
34701             centerH -= centerY;
34702             north.updateBox(this.safeBox(b));
34703         }
34704         if(south && south.isVisible()){
34705             var b = south.getBox();
34706             var m = south.getMargins();
34707             b.width = w - (m.left+m.right);
34708             b.x = m.left;
34709             var totalHeight = (b.height + m.top + m.bottom);
34710             b.y = h - totalHeight + m.top;
34711             centerH -= totalHeight;
34712             south.updateBox(this.safeBox(b));
34713         }
34714         if(west && west.isVisible()){
34715             var b = west.getBox();
34716             var m = west.getMargins();
34717             b.height = centerH - (m.top+m.bottom);
34718             b.x = m.left;
34719             b.y = centerY + m.top;
34720             var totalWidth = (b.width + m.left + m.right);
34721             centerX += totalWidth;
34722             centerW -= totalWidth;
34723             west.updateBox(this.safeBox(b));
34724         }
34725         if(east && east.isVisible()){
34726             var b = east.getBox();
34727             var m = east.getMargins();
34728             b.height = centerH - (m.top+m.bottom);
34729             var totalWidth = (b.width + m.left + m.right);
34730             b.x = w - totalWidth + m.left;
34731             b.y = centerY + m.top;
34732             centerW -= totalWidth;
34733             east.updateBox(this.safeBox(b));
34734         }
34735         if(center){
34736             var m = center.getMargins();
34737             var centerBox = {
34738                 x: centerX + m.left,
34739                 y: centerY + m.top,
34740                 width: centerW - (m.left+m.right),
34741                 height: centerH - (m.top+m.bottom)
34742             };
34743             //if(this.hideOnLayout){
34744                 //center.el.setStyle("display", "block");
34745             //}
34746             center.updateBox(this.safeBox(centerBox));
34747         }
34748         this.el.repaint();
34749         this.fireEvent("layout", this);
34750     },
34751
34752     // private
34753     safeBox : function(box){
34754         box.width = Math.max(0, box.width);
34755         box.height = Math.max(0, box.height);
34756         return box;
34757     },
34758
34759     /**
34760      * Adds a ContentPanel (or subclass) to this layout.
34761      * @param {String} target The target region key (north, south, east, west or center).
34762      * @param {Roo.ContentPanel} panel The panel to add
34763      * @return {Roo.ContentPanel} The added panel
34764      */
34765     add : function(target, panel){
34766          
34767         target = target.toLowerCase();
34768         return this.regions[target].add(panel);
34769     },
34770
34771     /**
34772      * Remove a ContentPanel (or subclass) to this layout.
34773      * @param {String} target The target region key (north, south, east, west or center).
34774      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34775      * @return {Roo.ContentPanel} The removed panel
34776      */
34777     remove : function(target, panel){
34778         target = target.toLowerCase();
34779         return this.regions[target].remove(panel);
34780     },
34781
34782     /**
34783      * Searches all regions for a panel with the specified id
34784      * @param {String} panelId
34785      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34786      */
34787     findPanel : function(panelId){
34788         var rs = this.regions;
34789         for(var target in rs){
34790             if(typeof rs[target] != "function"){
34791                 var p = rs[target].getPanel(panelId);
34792                 if(p){
34793                     return p;
34794                 }
34795             }
34796         }
34797         return null;
34798     },
34799
34800     /**
34801      * Searches all regions for a panel with the specified id and activates (shows) it.
34802      * @param {String/ContentPanel} panelId The panels id or the panel itself
34803      * @return {Roo.ContentPanel} The shown panel or null
34804      */
34805     showPanel : function(panelId) {
34806       var rs = this.regions;
34807       for(var target in rs){
34808          var r = rs[target];
34809          if(typeof r != "function"){
34810             if(r.hasPanel(panelId)){
34811                return r.showPanel(panelId);
34812             }
34813          }
34814       }
34815       return null;
34816    },
34817
34818    /**
34819      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34820      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34821      */
34822    /*
34823     restoreState : function(provider){
34824         if(!provider){
34825             provider = Roo.state.Manager;
34826         }
34827         var sm = new Roo.LayoutStateManager();
34828         sm.init(this, provider);
34829     },
34830 */
34831  
34832  
34833     /**
34834      * Adds a xtype elements to the layout.
34835      * <pre><code>
34836
34837 layout.addxtype({
34838        xtype : 'ContentPanel',
34839        region: 'west',
34840        items: [ .... ]
34841    }
34842 );
34843
34844 layout.addxtype({
34845         xtype : 'NestedLayoutPanel',
34846         region: 'west',
34847         layout: {
34848            center: { },
34849            west: { }   
34850         },
34851         items : [ ... list of content panels or nested layout panels.. ]
34852    }
34853 );
34854 </code></pre>
34855      * @param {Object} cfg Xtype definition of item to add.
34856      */
34857     addxtype : function(cfg)
34858     {
34859         // basically accepts a pannel...
34860         // can accept a layout region..!?!?
34861         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34862         
34863         
34864         // theory?  children can only be panels??
34865         
34866         //if (!cfg.xtype.match(/Panel$/)) {
34867         //    return false;
34868         //}
34869         var ret = false;
34870         
34871         if (typeof(cfg.region) == 'undefined') {
34872             Roo.log("Failed to add Panel, region was not set");
34873             Roo.log(cfg);
34874             return false;
34875         }
34876         var region = cfg.region;
34877         delete cfg.region;
34878         
34879           
34880         var xitems = [];
34881         if (cfg.items) {
34882             xitems = cfg.items;
34883             delete cfg.items;
34884         }
34885         var nb = false;
34886         
34887         switch(cfg.xtype) 
34888         {
34889             case 'Content':  // ContentPanel (el, cfg)
34890             case 'Scroll':  // ContentPanel (el, cfg)
34891             case 'View': 
34892                 cfg.autoCreate = true;
34893                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34894                 //} else {
34895                 //    var el = this.el.createChild();
34896                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34897                 //}
34898                 
34899                 this.add(region, ret);
34900                 break;
34901             
34902             /*
34903             case 'TreePanel': // our new panel!
34904                 cfg.el = this.el.createChild();
34905                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34906                 this.add(region, ret);
34907                 break;
34908             */
34909             
34910             case 'Nest': 
34911                 // create a new Layout (which is  a Border Layout...
34912                 
34913                 var clayout = cfg.layout;
34914                 clayout.el  = this.el.createChild();
34915                 clayout.items   = clayout.items  || [];
34916                 
34917                 delete cfg.layout;
34918                 
34919                 // replace this exitems with the clayout ones..
34920                 xitems = clayout.items;
34921                  
34922                 // force background off if it's in center...
34923                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34924                     cfg.background = false;
34925                 }
34926                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34927                 
34928                 
34929                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34930                 //console.log('adding nested layout panel '  + cfg.toSource());
34931                 this.add(region, ret);
34932                 nb = {}; /// find first...
34933                 break;
34934             
34935             case 'Grid':
34936                 
34937                 // needs grid and region
34938                 
34939                 //var el = this.getRegion(region).el.createChild();
34940                 /*
34941                  *var el = this.el.createChild();
34942                 // create the grid first...
34943                 cfg.grid.container = el;
34944                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34945                 */
34946                 
34947                 if (region == 'center' && this.active ) {
34948                     cfg.background = false;
34949                 }
34950                 
34951                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34952                 
34953                 this.add(region, ret);
34954                 /*
34955                 if (cfg.background) {
34956                     // render grid on panel activation (if panel background)
34957                     ret.on('activate', function(gp) {
34958                         if (!gp.grid.rendered) {
34959                     //        gp.grid.render(el);
34960                         }
34961                     });
34962                 } else {
34963                   //  cfg.grid.render(el);
34964                 }
34965                 */
34966                 break;
34967            
34968            
34969             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34970                 // it was the old xcomponent building that caused this before.
34971                 // espeically if border is the top element in the tree.
34972                 ret = this;
34973                 break; 
34974                 
34975                     
34976                 
34977                 
34978                 
34979             default:
34980                 /*
34981                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34982                     
34983                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34984                     this.add(region, ret);
34985                 } else {
34986                 */
34987                     Roo.log(cfg);
34988                     throw "Can not add '" + cfg.xtype + "' to Border";
34989                     return null;
34990              
34991                                 
34992              
34993         }
34994         this.beginUpdate();
34995         // add children..
34996         var region = '';
34997         var abn = {};
34998         Roo.each(xitems, function(i)  {
34999             region = nb && i.region ? i.region : false;
35000             
35001             var add = ret.addxtype(i);
35002            
35003             if (region) {
35004                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35005                 if (!i.background) {
35006                     abn[region] = nb[region] ;
35007                 }
35008             }
35009             
35010         });
35011         this.endUpdate();
35012
35013         // make the last non-background panel active..
35014         //if (nb) { Roo.log(abn); }
35015         if (nb) {
35016             
35017             for(var r in abn) {
35018                 region = this.getRegion(r);
35019                 if (region) {
35020                     // tried using nb[r], but it does not work..
35021                      
35022                     region.showPanel(abn[r]);
35023                    
35024                 }
35025             }
35026         }
35027         return ret;
35028         
35029     },
35030     
35031     
35032 // private
35033     factory : function(cfg)
35034     {
35035         
35036         var validRegions = Roo.bootstrap.layout.Border.regions;
35037
35038         var target = cfg.region;
35039         cfg.mgr = this;
35040         
35041         var r = Roo.bootstrap.layout;
35042         Roo.log(target);
35043         switch(target){
35044             case "north":
35045                 return new r.North(cfg);
35046             case "south":
35047                 return new r.South(cfg);
35048             case "east":
35049                 return new r.East(cfg);
35050             case "west":
35051                 return new r.West(cfg);
35052             case "center":
35053                 return new r.Center(cfg);
35054         }
35055         throw 'Layout region "'+target+'" not supported.';
35056     }
35057     
35058     
35059 });
35060  /*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070  
35071 /**
35072  * @class Roo.bootstrap.layout.Basic
35073  * @extends Roo.util.Observable
35074  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35075  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35076  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35077  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35078  * @cfg {string}   region  the region that it inhabits..
35079  * @cfg {bool}   skipConfig skip config?
35080  * 
35081
35082  */
35083 Roo.bootstrap.layout.Basic = function(config){
35084     
35085     this.mgr = config.mgr;
35086     
35087     this.position = config.region;
35088     
35089     var skipConfig = config.skipConfig;
35090     
35091     this.events = {
35092         /**
35093          * @scope Roo.BasicLayoutRegion
35094          */
35095         
35096         /**
35097          * @event beforeremove
35098          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35099          * @param {Roo.LayoutRegion} this
35100          * @param {Roo.ContentPanel} panel The panel
35101          * @param {Object} e The cancel event object
35102          */
35103         "beforeremove" : true,
35104         /**
35105          * @event invalidated
35106          * Fires when the layout for this region is changed.
35107          * @param {Roo.LayoutRegion} this
35108          */
35109         "invalidated" : true,
35110         /**
35111          * @event visibilitychange
35112          * Fires when this region is shown or hidden 
35113          * @param {Roo.LayoutRegion} this
35114          * @param {Boolean} visibility true or false
35115          */
35116         "visibilitychange" : true,
35117         /**
35118          * @event paneladded
35119          * Fires when a panel is added. 
35120          * @param {Roo.LayoutRegion} this
35121          * @param {Roo.ContentPanel} panel The panel
35122          */
35123         "paneladded" : true,
35124         /**
35125          * @event panelremoved
35126          * Fires when a panel is removed. 
35127          * @param {Roo.LayoutRegion} this
35128          * @param {Roo.ContentPanel} panel The panel
35129          */
35130         "panelremoved" : true,
35131         /**
35132          * @event beforecollapse
35133          * Fires when this region before collapse.
35134          * @param {Roo.LayoutRegion} this
35135          */
35136         "beforecollapse" : true,
35137         /**
35138          * @event collapsed
35139          * Fires when this region is collapsed.
35140          * @param {Roo.LayoutRegion} this
35141          */
35142         "collapsed" : true,
35143         /**
35144          * @event expanded
35145          * Fires when this region is expanded.
35146          * @param {Roo.LayoutRegion} this
35147          */
35148         "expanded" : true,
35149         /**
35150          * @event slideshow
35151          * Fires when this region is slid into view.
35152          * @param {Roo.LayoutRegion} this
35153          */
35154         "slideshow" : true,
35155         /**
35156          * @event slidehide
35157          * Fires when this region slides out of view. 
35158          * @param {Roo.LayoutRegion} this
35159          */
35160         "slidehide" : true,
35161         /**
35162          * @event panelactivated
35163          * Fires when a panel is activated. 
35164          * @param {Roo.LayoutRegion} this
35165          * @param {Roo.ContentPanel} panel The activated panel
35166          */
35167         "panelactivated" : true,
35168         /**
35169          * @event resized
35170          * Fires when the user resizes this region. 
35171          * @param {Roo.LayoutRegion} this
35172          * @param {Number} newSize The new size (width for east/west, height for north/south)
35173          */
35174         "resized" : true
35175     };
35176     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35177     this.panels = new Roo.util.MixedCollection();
35178     this.panels.getKey = this.getPanelId.createDelegate(this);
35179     this.box = null;
35180     this.activePanel = null;
35181     // ensure listeners are added...
35182     
35183     if (config.listeners || config.events) {
35184         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35185             listeners : config.listeners || {},
35186             events : config.events || {}
35187         });
35188     }
35189     
35190     if(skipConfig !== true){
35191         this.applyConfig(config);
35192     }
35193 };
35194
35195 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35196 {
35197     getPanelId : function(p){
35198         return p.getId();
35199     },
35200     
35201     applyConfig : function(config){
35202         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35203         this.config = config;
35204         
35205     },
35206     
35207     /**
35208      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35209      * the width, for horizontal (north, south) the height.
35210      * @param {Number} newSize The new width or height
35211      */
35212     resizeTo : function(newSize){
35213         var el = this.el ? this.el :
35214                  (this.activePanel ? this.activePanel.getEl() : null);
35215         if(el){
35216             switch(this.position){
35217                 case "east":
35218                 case "west":
35219                     el.setWidth(newSize);
35220                     this.fireEvent("resized", this, newSize);
35221                 break;
35222                 case "north":
35223                 case "south":
35224                     el.setHeight(newSize);
35225                     this.fireEvent("resized", this, newSize);
35226                 break;                
35227             }
35228         }
35229     },
35230     
35231     getBox : function(){
35232         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35233     },
35234     
35235     getMargins : function(){
35236         return this.margins;
35237     },
35238     
35239     updateBox : function(box){
35240         this.box = box;
35241         var el = this.activePanel.getEl();
35242         el.dom.style.left = box.x + "px";
35243         el.dom.style.top = box.y + "px";
35244         this.activePanel.setSize(box.width, box.height);
35245     },
35246     
35247     /**
35248      * Returns the container element for this region.
35249      * @return {Roo.Element}
35250      */
35251     getEl : function(){
35252         return this.activePanel;
35253     },
35254     
35255     /**
35256      * Returns true if this region is currently visible.
35257      * @return {Boolean}
35258      */
35259     isVisible : function(){
35260         return this.activePanel ? true : false;
35261     },
35262     
35263     setActivePanel : function(panel){
35264         panel = this.getPanel(panel);
35265         if(this.activePanel && this.activePanel != panel){
35266             this.activePanel.setActiveState(false);
35267             this.activePanel.getEl().setLeftTop(-10000,-10000);
35268         }
35269         this.activePanel = panel;
35270         panel.setActiveState(true);
35271         if(this.box){
35272             panel.setSize(this.box.width, this.box.height);
35273         }
35274         this.fireEvent("panelactivated", this, panel);
35275         this.fireEvent("invalidated");
35276     },
35277     
35278     /**
35279      * Show the specified panel.
35280      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35281      * @return {Roo.ContentPanel} The shown panel or null
35282      */
35283     showPanel : function(panel){
35284         panel = this.getPanel(panel);
35285         if(panel){
35286             this.setActivePanel(panel);
35287         }
35288         return panel;
35289     },
35290     
35291     /**
35292      * Get the active panel for this region.
35293      * @return {Roo.ContentPanel} The active panel or null
35294      */
35295     getActivePanel : function(){
35296         return this.activePanel;
35297     },
35298     
35299     /**
35300      * Add the passed ContentPanel(s)
35301      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35302      * @return {Roo.ContentPanel} The panel added (if only one was added)
35303      */
35304     add : function(panel){
35305         if(arguments.length > 1){
35306             for(var i = 0, len = arguments.length; i < len; i++) {
35307                 this.add(arguments[i]);
35308             }
35309             return null;
35310         }
35311         if(this.hasPanel(panel)){
35312             this.showPanel(panel);
35313             return panel;
35314         }
35315         var el = panel.getEl();
35316         if(el.dom.parentNode != this.mgr.el.dom){
35317             this.mgr.el.dom.appendChild(el.dom);
35318         }
35319         if(panel.setRegion){
35320             panel.setRegion(this);
35321         }
35322         this.panels.add(panel);
35323         el.setStyle("position", "absolute");
35324         if(!panel.background){
35325             this.setActivePanel(panel);
35326             if(this.config.initialSize && this.panels.getCount()==1){
35327                 this.resizeTo(this.config.initialSize);
35328             }
35329         }
35330         this.fireEvent("paneladded", this, panel);
35331         return panel;
35332     },
35333     
35334     /**
35335      * Returns true if the panel is in this region.
35336      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35337      * @return {Boolean}
35338      */
35339     hasPanel : function(panel){
35340         if(typeof panel == "object"){ // must be panel obj
35341             panel = panel.getId();
35342         }
35343         return this.getPanel(panel) ? true : false;
35344     },
35345     
35346     /**
35347      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35348      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35349      * @param {Boolean} preservePanel Overrides the config preservePanel option
35350      * @return {Roo.ContentPanel} The panel that was removed
35351      */
35352     remove : function(panel, preservePanel){
35353         panel = this.getPanel(panel);
35354         if(!panel){
35355             return null;
35356         }
35357         var e = {};
35358         this.fireEvent("beforeremove", this, panel, e);
35359         if(e.cancel === true){
35360             return null;
35361         }
35362         var panelId = panel.getId();
35363         this.panels.removeKey(panelId);
35364         return panel;
35365     },
35366     
35367     /**
35368      * Returns the panel specified or null if it's not in this region.
35369      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35370      * @return {Roo.ContentPanel}
35371      */
35372     getPanel : function(id){
35373         if(typeof id == "object"){ // must be panel obj
35374             return id;
35375         }
35376         return this.panels.get(id);
35377     },
35378     
35379     /**
35380      * Returns this regions position (north/south/east/west/center).
35381      * @return {String} 
35382      */
35383     getPosition: function(){
35384         return this.position;    
35385     }
35386 });/*
35387  * Based on:
35388  * Ext JS Library 1.1.1
35389  * Copyright(c) 2006-2007, Ext JS, LLC.
35390  *
35391  * Originally Released Under LGPL - original licence link has changed is not relivant.
35392  *
35393  * Fork - LGPL
35394  * <script type="text/javascript">
35395  */
35396  
35397 /**
35398  * @class Roo.bootstrap.layout.Region
35399  * @extends Roo.bootstrap.layout.Basic
35400  * This class represents a region in a layout manager.
35401  
35402  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35403  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
35404  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35405  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35406  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35407  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35408  * @cfg {String}    title           The title for the region (overrides panel titles)
35409  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35410  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35411  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35412  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35413  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35414  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35415  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35416  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35417  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35418  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35419
35420  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35421  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35422  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35423  * @cfg {Number}    width           For East/West panels
35424  * @cfg {Number}    height          For North/South panels
35425  * @cfg {Boolean}   split           To show the splitter
35426  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35427  * 
35428  * @cfg {string}   cls             Extra CSS classes to add to region
35429  * 
35430  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35431  * @cfg {string}   region  the region that it inhabits..
35432  *
35433
35434  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35435  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35436
35437  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35438  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35439  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35440  */
35441 Roo.bootstrap.layout.Region = function(config)
35442 {
35443     this.applyConfig(config);
35444
35445     var mgr = config.mgr;
35446     var pos = config.region;
35447     config.skipConfig = true;
35448     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35449     
35450     if (mgr.el) {
35451         this.onRender(mgr.el);   
35452     }
35453      
35454     this.visible = true;
35455     this.collapsed = false;
35456     this.unrendered_panels = [];
35457 };
35458
35459 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35460
35461     position: '', // set by wrapper (eg. north/south etc..)
35462     unrendered_panels : null,  // unrendered panels.
35463     createBody : function(){
35464         /** This region's body element 
35465         * @type Roo.Element */
35466         this.bodyEl = this.el.createChild({
35467                 tag: "div",
35468                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35469         });
35470     },
35471
35472     onRender: function(ctr, pos)
35473     {
35474         var dh = Roo.DomHelper;
35475         /** This region's container element 
35476         * @type Roo.Element */
35477         this.el = dh.append(ctr.dom, {
35478                 tag: "div",
35479                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35480             }, true);
35481         /** This region's title element 
35482         * @type Roo.Element */
35483     
35484         this.titleEl = dh.append(this.el.dom,
35485             {
35486                     tag: "div",
35487                     unselectable: "on",
35488                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35489                     children:[
35490                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35491                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35492                     ]}, true);
35493         
35494         this.titleEl.enableDisplayMode();
35495         /** This region's title text element 
35496         * @type HTMLElement */
35497         this.titleTextEl = this.titleEl.dom.firstChild;
35498         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35499         /*
35500         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35501         this.closeBtn.enableDisplayMode();
35502         this.closeBtn.on("click", this.closeClicked, this);
35503         this.closeBtn.hide();
35504     */
35505         this.createBody(this.config);
35506         if(this.config.hideWhenEmpty){
35507             this.hide();
35508             this.on("paneladded", this.validateVisibility, this);
35509             this.on("panelremoved", this.validateVisibility, this);
35510         }
35511         if(this.autoScroll){
35512             this.bodyEl.setStyle("overflow", "auto");
35513         }else{
35514             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35515         }
35516         //if(c.titlebar !== false){
35517             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35518                 this.titleEl.hide();
35519             }else{
35520                 this.titleEl.show();
35521                 if(this.config.title){
35522                     this.titleTextEl.innerHTML = this.config.title;
35523                 }
35524             }
35525         //}
35526         if(this.config.collapsed){
35527             this.collapse(true);
35528         }
35529         if(this.config.hidden){
35530             this.hide();
35531         }
35532         
35533         if (this.unrendered_panels && this.unrendered_panels.length) {
35534             for (var i =0;i< this.unrendered_panels.length; i++) {
35535                 this.add(this.unrendered_panels[i]);
35536             }
35537             this.unrendered_panels = null;
35538             
35539         }
35540         
35541     },
35542     
35543     applyConfig : function(c)
35544     {
35545         /*
35546          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35547             var dh = Roo.DomHelper;
35548             if(c.titlebar !== false){
35549                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35550                 this.collapseBtn.on("click", this.collapse, this);
35551                 this.collapseBtn.enableDisplayMode();
35552                 /*
35553                 if(c.showPin === true || this.showPin){
35554                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35555                     this.stickBtn.enableDisplayMode();
35556                     this.stickBtn.on("click", this.expand, this);
35557                     this.stickBtn.hide();
35558                 }
35559                 
35560             }
35561             */
35562             /** This region's collapsed element
35563             * @type Roo.Element */
35564             /*
35565              *
35566             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35567                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35568             ]}, true);
35569             
35570             if(c.floatable !== false){
35571                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35572                this.collapsedEl.on("click", this.collapseClick, this);
35573             }
35574
35575             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35576                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35577                    id: "message", unselectable: "on", style:{"float":"left"}});
35578                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35579              }
35580             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35581             this.expandBtn.on("click", this.expand, this);
35582             
35583         }
35584         
35585         if(this.collapseBtn){
35586             this.collapseBtn.setVisible(c.collapsible == true);
35587         }
35588         
35589         this.cmargins = c.cmargins || this.cmargins ||
35590                          (this.position == "west" || this.position == "east" ?
35591                              {top: 0, left: 2, right:2, bottom: 0} :
35592                              {top: 2, left: 0, right:0, bottom: 2});
35593         */
35594         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35595         
35596         
35597         this.bottomTabs = c.tabPosition != "top";
35598         
35599         this.autoScroll = c.autoScroll || false;
35600         
35601         
35602        
35603         
35604         this.duration = c.duration || .30;
35605         this.slideDuration = c.slideDuration || .45;
35606         this.config = c;
35607        
35608     },
35609     /**
35610      * Returns true if this region is currently visible.
35611      * @return {Boolean}
35612      */
35613     isVisible : function(){
35614         return this.visible;
35615     },
35616
35617     /**
35618      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35619      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35620      */
35621     //setCollapsedTitle : function(title){
35622     //    title = title || "&#160;";
35623      //   if(this.collapsedTitleTextEl){
35624       //      this.collapsedTitleTextEl.innerHTML = title;
35625        // }
35626     //},
35627
35628     getBox : function(){
35629         var b;
35630       //  if(!this.collapsed){
35631             b = this.el.getBox(false, true);
35632        // }else{
35633           //  b = this.collapsedEl.getBox(false, true);
35634         //}
35635         return b;
35636     },
35637
35638     getMargins : function(){
35639         return this.margins;
35640         //return this.collapsed ? this.cmargins : this.margins;
35641     },
35642 /*
35643     highlight : function(){
35644         this.el.addClass("x-layout-panel-dragover");
35645     },
35646
35647     unhighlight : function(){
35648         this.el.removeClass("x-layout-panel-dragover");
35649     },
35650 */
35651     updateBox : function(box)
35652     {
35653         if (!this.bodyEl) {
35654             return; // not rendered yet..
35655         }
35656         
35657         this.box = box;
35658         if(!this.collapsed){
35659             this.el.dom.style.left = box.x + "px";
35660             this.el.dom.style.top = box.y + "px";
35661             this.updateBody(box.width, box.height);
35662         }else{
35663             this.collapsedEl.dom.style.left = box.x + "px";
35664             this.collapsedEl.dom.style.top = box.y + "px";
35665             this.collapsedEl.setSize(box.width, box.height);
35666         }
35667         if(this.tabs){
35668             this.tabs.autoSizeTabs();
35669         }
35670     },
35671
35672     updateBody : function(w, h)
35673     {
35674         if(w !== null){
35675             this.el.setWidth(w);
35676             w -= this.el.getBorderWidth("rl");
35677             if(this.config.adjustments){
35678                 w += this.config.adjustments[0];
35679             }
35680         }
35681         if(h !== null && h > 0){
35682             this.el.setHeight(h);
35683             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35684             h -= this.el.getBorderWidth("tb");
35685             if(this.config.adjustments){
35686                 h += this.config.adjustments[1];
35687             }
35688             this.bodyEl.setHeight(h);
35689             if(this.tabs){
35690                 h = this.tabs.syncHeight(h);
35691             }
35692         }
35693         if(this.panelSize){
35694             w = w !== null ? w : this.panelSize.width;
35695             h = h !== null ? h : this.panelSize.height;
35696         }
35697         if(this.activePanel){
35698             var el = this.activePanel.getEl();
35699             w = w !== null ? w : el.getWidth();
35700             h = h !== null ? h : el.getHeight();
35701             this.panelSize = {width: w, height: h};
35702             this.activePanel.setSize(w, h);
35703         }
35704         if(Roo.isIE && this.tabs){
35705             this.tabs.el.repaint();
35706         }
35707     },
35708
35709     /**
35710      * Returns the container element for this region.
35711      * @return {Roo.Element}
35712      */
35713     getEl : function(){
35714         return this.el;
35715     },
35716
35717     /**
35718      * Hides this region.
35719      */
35720     hide : function(){
35721         //if(!this.collapsed){
35722             this.el.dom.style.left = "-2000px";
35723             this.el.hide();
35724         //}else{
35725          //   this.collapsedEl.dom.style.left = "-2000px";
35726          //   this.collapsedEl.hide();
35727        // }
35728         this.visible = false;
35729         this.fireEvent("visibilitychange", this, false);
35730     },
35731
35732     /**
35733      * Shows this region if it was previously hidden.
35734      */
35735     show : function(){
35736         //if(!this.collapsed){
35737             this.el.show();
35738         //}else{
35739         //    this.collapsedEl.show();
35740        // }
35741         this.visible = true;
35742         this.fireEvent("visibilitychange", this, true);
35743     },
35744 /*
35745     closeClicked : function(){
35746         if(this.activePanel){
35747             this.remove(this.activePanel);
35748         }
35749     },
35750
35751     collapseClick : function(e){
35752         if(this.isSlid){
35753            e.stopPropagation();
35754            this.slideIn();
35755         }else{
35756            e.stopPropagation();
35757            this.slideOut();
35758         }
35759     },
35760 */
35761     /**
35762      * Collapses this region.
35763      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35764      */
35765     /*
35766     collapse : function(skipAnim, skipCheck = false){
35767         if(this.collapsed) {
35768             return;
35769         }
35770         
35771         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35772             
35773             this.collapsed = true;
35774             if(this.split){
35775                 this.split.el.hide();
35776             }
35777             if(this.config.animate && skipAnim !== true){
35778                 this.fireEvent("invalidated", this);
35779                 this.animateCollapse();
35780             }else{
35781                 this.el.setLocation(-20000,-20000);
35782                 this.el.hide();
35783                 this.collapsedEl.show();
35784                 this.fireEvent("collapsed", this);
35785                 this.fireEvent("invalidated", this);
35786             }
35787         }
35788         
35789     },
35790 */
35791     animateCollapse : function(){
35792         // overridden
35793     },
35794
35795     /**
35796      * Expands this region if it was previously collapsed.
35797      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35798      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35799      */
35800     /*
35801     expand : function(e, skipAnim){
35802         if(e) {
35803             e.stopPropagation();
35804         }
35805         if(!this.collapsed || this.el.hasActiveFx()) {
35806             return;
35807         }
35808         if(this.isSlid){
35809             this.afterSlideIn();
35810             skipAnim = true;
35811         }
35812         this.collapsed = false;
35813         if(this.config.animate && skipAnim !== true){
35814             this.animateExpand();
35815         }else{
35816             this.el.show();
35817             if(this.split){
35818                 this.split.el.show();
35819             }
35820             this.collapsedEl.setLocation(-2000,-2000);
35821             this.collapsedEl.hide();
35822             this.fireEvent("invalidated", this);
35823             this.fireEvent("expanded", this);
35824         }
35825     },
35826 */
35827     animateExpand : function(){
35828         // overridden
35829     },
35830
35831     initTabs : function()
35832     {
35833         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35834         
35835         var ts = new Roo.bootstrap.panel.Tabs({
35836                 el: this.bodyEl.dom,
35837                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35838                 disableTooltips: this.config.disableTabTips,
35839                 toolbar : this.config.toolbar
35840             });
35841         
35842         if(this.config.hideTabs){
35843             ts.stripWrap.setDisplayed(false);
35844         }
35845         this.tabs = ts;
35846         ts.resizeTabs = this.config.resizeTabs === true;
35847         ts.minTabWidth = this.config.minTabWidth || 40;
35848         ts.maxTabWidth = this.config.maxTabWidth || 250;
35849         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35850         ts.monitorResize = false;
35851         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35852         ts.bodyEl.addClass('roo-layout-tabs-body');
35853         this.panels.each(this.initPanelAsTab, this);
35854     },
35855
35856     initPanelAsTab : function(panel){
35857         var ti = this.tabs.addTab(
35858             panel.getEl().id,
35859             panel.getTitle(),
35860             null,
35861             this.config.closeOnTab && panel.isClosable(),
35862             panel.tpl
35863         );
35864         if(panel.tabTip !== undefined){
35865             ti.setTooltip(panel.tabTip);
35866         }
35867         ti.on("activate", function(){
35868               this.setActivePanel(panel);
35869         }, this);
35870         
35871         if(this.config.closeOnTab){
35872             ti.on("beforeclose", function(t, e){
35873                 e.cancel = true;
35874                 this.remove(panel);
35875             }, this);
35876         }
35877         
35878         panel.tabItem = ti;
35879         
35880         return ti;
35881     },
35882
35883     updatePanelTitle : function(panel, title)
35884     {
35885         if(this.activePanel == panel){
35886             this.updateTitle(title);
35887         }
35888         if(this.tabs){
35889             var ti = this.tabs.getTab(panel.getEl().id);
35890             ti.setText(title);
35891             if(panel.tabTip !== undefined){
35892                 ti.setTooltip(panel.tabTip);
35893             }
35894         }
35895     },
35896
35897     updateTitle : function(title){
35898         if(this.titleTextEl && !this.config.title){
35899             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35900         }
35901     },
35902
35903     setActivePanel : function(panel)
35904     {
35905         panel = this.getPanel(panel);
35906         if(this.activePanel && this.activePanel != panel){
35907             if(this.activePanel.setActiveState(false) === false){
35908                 return;
35909             }
35910         }
35911         this.activePanel = panel;
35912         panel.setActiveState(true);
35913         if(this.panelSize){
35914             panel.setSize(this.panelSize.width, this.panelSize.height);
35915         }
35916         if(this.closeBtn){
35917             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35918         }
35919         this.updateTitle(panel.getTitle());
35920         if(this.tabs){
35921             this.fireEvent("invalidated", this);
35922         }
35923         this.fireEvent("panelactivated", this, panel);
35924     },
35925
35926     /**
35927      * Shows the specified panel.
35928      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35929      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35930      */
35931     showPanel : function(panel)
35932     {
35933         panel = this.getPanel(panel);
35934         if(panel){
35935             if(this.tabs){
35936                 var tab = this.tabs.getTab(panel.getEl().id);
35937                 if(tab.isHidden()){
35938                     this.tabs.unhideTab(tab.id);
35939                 }
35940                 tab.activate();
35941             }else{
35942                 this.setActivePanel(panel);
35943             }
35944         }
35945         return panel;
35946     },
35947
35948     /**
35949      * Get the active panel for this region.
35950      * @return {Roo.ContentPanel} The active panel or null
35951      */
35952     getActivePanel : function(){
35953         return this.activePanel;
35954     },
35955
35956     validateVisibility : function(){
35957         if(this.panels.getCount() < 1){
35958             this.updateTitle("&#160;");
35959             this.closeBtn.hide();
35960             this.hide();
35961         }else{
35962             if(!this.isVisible()){
35963                 this.show();
35964             }
35965         }
35966     },
35967
35968     /**
35969      * Adds the passed ContentPanel(s) to this region.
35970      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35971      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35972      */
35973     add : function(panel)
35974     {
35975         if(arguments.length > 1){
35976             for(var i = 0, len = arguments.length; i < len; i++) {
35977                 this.add(arguments[i]);
35978             }
35979             return null;
35980         }
35981         
35982         // if we have not been rendered yet, then we can not really do much of this..
35983         if (!this.bodyEl) {
35984             this.unrendered_panels.push(panel);
35985             return panel;
35986         }
35987         
35988         
35989         
35990         
35991         if(this.hasPanel(panel)){
35992             this.showPanel(panel);
35993             return panel;
35994         }
35995         panel.setRegion(this);
35996         this.panels.add(panel);
35997        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35998             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35999             // and hide them... ???
36000             this.bodyEl.dom.appendChild(panel.getEl().dom);
36001             if(panel.background !== true){
36002                 this.setActivePanel(panel);
36003             }
36004             this.fireEvent("paneladded", this, panel);
36005             return panel;
36006         }
36007         */
36008         if(!this.tabs){
36009             this.initTabs();
36010         }else{
36011             this.initPanelAsTab(panel);
36012         }
36013         
36014         
36015         if(panel.background !== true){
36016             this.tabs.activate(panel.getEl().id);
36017         }
36018         this.fireEvent("paneladded", this, panel);
36019         return panel;
36020     },
36021
36022     /**
36023      * Hides the tab for the specified panel.
36024      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36025      */
36026     hidePanel : function(panel){
36027         if(this.tabs && (panel = this.getPanel(panel))){
36028             this.tabs.hideTab(panel.getEl().id);
36029         }
36030     },
36031
36032     /**
36033      * Unhides the tab for a previously hidden panel.
36034      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36035      */
36036     unhidePanel : function(panel){
36037         if(this.tabs && (panel = this.getPanel(panel))){
36038             this.tabs.unhideTab(panel.getEl().id);
36039         }
36040     },
36041
36042     clearPanels : function(){
36043         while(this.panels.getCount() > 0){
36044              this.remove(this.panels.first());
36045         }
36046     },
36047
36048     /**
36049      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36050      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36051      * @param {Boolean} preservePanel Overrides the config preservePanel option
36052      * @return {Roo.ContentPanel} The panel that was removed
36053      */
36054     remove : function(panel, preservePanel)
36055     {
36056         panel = this.getPanel(panel);
36057         if(!panel){
36058             return null;
36059         }
36060         var e = {};
36061         this.fireEvent("beforeremove", this, panel, e);
36062         if(e.cancel === true){
36063             return null;
36064         }
36065         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36066         var panelId = panel.getId();
36067         this.panels.removeKey(panelId);
36068         if(preservePanel){
36069             document.body.appendChild(panel.getEl().dom);
36070         }
36071         if(this.tabs){
36072             this.tabs.removeTab(panel.getEl().id);
36073         }else if (!preservePanel){
36074             this.bodyEl.dom.removeChild(panel.getEl().dom);
36075         }
36076         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36077             var p = this.panels.first();
36078             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36079             tempEl.appendChild(p.getEl().dom);
36080             this.bodyEl.update("");
36081             this.bodyEl.dom.appendChild(p.getEl().dom);
36082             tempEl = null;
36083             this.updateTitle(p.getTitle());
36084             this.tabs = null;
36085             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36086             this.setActivePanel(p);
36087         }
36088         panel.setRegion(null);
36089         if(this.activePanel == panel){
36090             this.activePanel = null;
36091         }
36092         if(this.config.autoDestroy !== false && preservePanel !== true){
36093             try{panel.destroy();}catch(e){}
36094         }
36095         this.fireEvent("panelremoved", this, panel);
36096         return panel;
36097     },
36098
36099     /**
36100      * Returns the TabPanel component used by this region
36101      * @return {Roo.TabPanel}
36102      */
36103     getTabs : function(){
36104         return this.tabs;
36105     },
36106
36107     createTool : function(parentEl, className){
36108         var btn = Roo.DomHelper.append(parentEl, {
36109             tag: "div",
36110             cls: "x-layout-tools-button",
36111             children: [ {
36112                 tag: "div",
36113                 cls: "roo-layout-tools-button-inner " + className,
36114                 html: "&#160;"
36115             }]
36116         }, true);
36117         btn.addClassOnOver("roo-layout-tools-button-over");
36118         return btn;
36119     }
36120 });/*
36121  * Based on:
36122  * Ext JS Library 1.1.1
36123  * Copyright(c) 2006-2007, Ext JS, LLC.
36124  *
36125  * Originally Released Under LGPL - original licence link has changed is not relivant.
36126  *
36127  * Fork - LGPL
36128  * <script type="text/javascript">
36129  */
36130  
36131
36132
36133 /**
36134  * @class Roo.SplitLayoutRegion
36135  * @extends Roo.LayoutRegion
36136  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36137  */
36138 Roo.bootstrap.layout.Split = function(config){
36139     this.cursor = config.cursor;
36140     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36141 };
36142
36143 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36144 {
36145     splitTip : "Drag to resize.",
36146     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36147     useSplitTips : false,
36148
36149     applyConfig : function(config){
36150         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36151     },
36152     
36153     onRender : function(ctr,pos) {
36154         
36155         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36156         if(!this.config.split){
36157             return;
36158         }
36159         if(!this.split){
36160             
36161             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36162                             tag: "div",
36163                             id: this.el.id + "-split",
36164                             cls: "roo-layout-split roo-layout-split-"+this.position,
36165                             html: "&#160;"
36166             });
36167             /** The SplitBar for this region 
36168             * @type Roo.SplitBar */
36169             // does not exist yet...
36170             Roo.log([this.position, this.orientation]);
36171             
36172             this.split = new Roo.bootstrap.SplitBar({
36173                 dragElement : splitEl,
36174                 resizingElement: this.el,
36175                 orientation : this.orientation
36176             });
36177             
36178             this.split.on("moved", this.onSplitMove, this);
36179             this.split.useShim = this.config.useShim === true;
36180             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36181             if(this.useSplitTips){
36182                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36183             }
36184             //if(config.collapsible){
36185             //    this.split.el.on("dblclick", this.collapse,  this);
36186             //}
36187         }
36188         if(typeof this.config.minSize != "undefined"){
36189             this.split.minSize = this.config.minSize;
36190         }
36191         if(typeof this.config.maxSize != "undefined"){
36192             this.split.maxSize = this.config.maxSize;
36193         }
36194         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36195             this.hideSplitter();
36196         }
36197         
36198     },
36199
36200     getHMaxSize : function(){
36201          var cmax = this.config.maxSize || 10000;
36202          var center = this.mgr.getRegion("center");
36203          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36204     },
36205
36206     getVMaxSize : function(){
36207          var cmax = this.config.maxSize || 10000;
36208          var center = this.mgr.getRegion("center");
36209          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36210     },
36211
36212     onSplitMove : function(split, newSize){
36213         this.fireEvent("resized", this, newSize);
36214     },
36215     
36216     /** 
36217      * Returns the {@link Roo.SplitBar} for this region.
36218      * @return {Roo.SplitBar}
36219      */
36220     getSplitBar : function(){
36221         return this.split;
36222     },
36223     
36224     hide : function(){
36225         this.hideSplitter();
36226         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36227     },
36228
36229     hideSplitter : function(){
36230         if(this.split){
36231             this.split.el.setLocation(-2000,-2000);
36232             this.split.el.hide();
36233         }
36234     },
36235
36236     show : function(){
36237         if(this.split){
36238             this.split.el.show();
36239         }
36240         Roo.bootstrap.layout.Split.superclass.show.call(this);
36241     },
36242     
36243     beforeSlide: function(){
36244         if(Roo.isGecko){// firefox overflow auto bug workaround
36245             this.bodyEl.clip();
36246             if(this.tabs) {
36247                 this.tabs.bodyEl.clip();
36248             }
36249             if(this.activePanel){
36250                 this.activePanel.getEl().clip();
36251                 
36252                 if(this.activePanel.beforeSlide){
36253                     this.activePanel.beforeSlide();
36254                 }
36255             }
36256         }
36257     },
36258     
36259     afterSlide : function(){
36260         if(Roo.isGecko){// firefox overflow auto bug workaround
36261             this.bodyEl.unclip();
36262             if(this.tabs) {
36263                 this.tabs.bodyEl.unclip();
36264             }
36265             if(this.activePanel){
36266                 this.activePanel.getEl().unclip();
36267                 if(this.activePanel.afterSlide){
36268                     this.activePanel.afterSlide();
36269                 }
36270             }
36271         }
36272     },
36273
36274     initAutoHide : function(){
36275         if(this.autoHide !== false){
36276             if(!this.autoHideHd){
36277                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36278                 this.autoHideHd = {
36279                     "mouseout": function(e){
36280                         if(!e.within(this.el, true)){
36281                             st.delay(500);
36282                         }
36283                     },
36284                     "mouseover" : function(e){
36285                         st.cancel();
36286                     },
36287                     scope : this
36288                 };
36289             }
36290             this.el.on(this.autoHideHd);
36291         }
36292     },
36293
36294     clearAutoHide : function(){
36295         if(this.autoHide !== false){
36296             this.el.un("mouseout", this.autoHideHd.mouseout);
36297             this.el.un("mouseover", this.autoHideHd.mouseover);
36298         }
36299     },
36300
36301     clearMonitor : function(){
36302         Roo.get(document).un("click", this.slideInIf, this);
36303     },
36304
36305     // these names are backwards but not changed for compat
36306     slideOut : function(){
36307         if(this.isSlid || this.el.hasActiveFx()){
36308             return;
36309         }
36310         this.isSlid = true;
36311         if(this.collapseBtn){
36312             this.collapseBtn.hide();
36313         }
36314         this.closeBtnState = this.closeBtn.getStyle('display');
36315         this.closeBtn.hide();
36316         if(this.stickBtn){
36317             this.stickBtn.show();
36318         }
36319         this.el.show();
36320         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36321         this.beforeSlide();
36322         this.el.setStyle("z-index", 10001);
36323         this.el.slideIn(this.getSlideAnchor(), {
36324             callback: function(){
36325                 this.afterSlide();
36326                 this.initAutoHide();
36327                 Roo.get(document).on("click", this.slideInIf, this);
36328                 this.fireEvent("slideshow", this);
36329             },
36330             scope: this,
36331             block: true
36332         });
36333     },
36334
36335     afterSlideIn : function(){
36336         this.clearAutoHide();
36337         this.isSlid = false;
36338         this.clearMonitor();
36339         this.el.setStyle("z-index", "");
36340         if(this.collapseBtn){
36341             this.collapseBtn.show();
36342         }
36343         this.closeBtn.setStyle('display', this.closeBtnState);
36344         if(this.stickBtn){
36345             this.stickBtn.hide();
36346         }
36347         this.fireEvent("slidehide", this);
36348     },
36349
36350     slideIn : function(cb){
36351         if(!this.isSlid || this.el.hasActiveFx()){
36352             Roo.callback(cb);
36353             return;
36354         }
36355         this.isSlid = false;
36356         this.beforeSlide();
36357         this.el.slideOut(this.getSlideAnchor(), {
36358             callback: function(){
36359                 this.el.setLeftTop(-10000, -10000);
36360                 this.afterSlide();
36361                 this.afterSlideIn();
36362                 Roo.callback(cb);
36363             },
36364             scope: this,
36365             block: true
36366         });
36367     },
36368     
36369     slideInIf : function(e){
36370         if(!e.within(this.el)){
36371             this.slideIn();
36372         }
36373     },
36374
36375     animateCollapse : function(){
36376         this.beforeSlide();
36377         this.el.setStyle("z-index", 20000);
36378         var anchor = this.getSlideAnchor();
36379         this.el.slideOut(anchor, {
36380             callback : function(){
36381                 this.el.setStyle("z-index", "");
36382                 this.collapsedEl.slideIn(anchor, {duration:.3});
36383                 this.afterSlide();
36384                 this.el.setLocation(-10000,-10000);
36385                 this.el.hide();
36386                 this.fireEvent("collapsed", this);
36387             },
36388             scope: this,
36389             block: true
36390         });
36391     },
36392
36393     animateExpand : function(){
36394         this.beforeSlide();
36395         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36396         this.el.setStyle("z-index", 20000);
36397         this.collapsedEl.hide({
36398             duration:.1
36399         });
36400         this.el.slideIn(this.getSlideAnchor(), {
36401             callback : function(){
36402                 this.el.setStyle("z-index", "");
36403                 this.afterSlide();
36404                 if(this.split){
36405                     this.split.el.show();
36406                 }
36407                 this.fireEvent("invalidated", this);
36408                 this.fireEvent("expanded", this);
36409             },
36410             scope: this,
36411             block: true
36412         });
36413     },
36414
36415     anchors : {
36416         "west" : "left",
36417         "east" : "right",
36418         "north" : "top",
36419         "south" : "bottom"
36420     },
36421
36422     sanchors : {
36423         "west" : "l",
36424         "east" : "r",
36425         "north" : "t",
36426         "south" : "b"
36427     },
36428
36429     canchors : {
36430         "west" : "tl-tr",
36431         "east" : "tr-tl",
36432         "north" : "tl-bl",
36433         "south" : "bl-tl"
36434     },
36435
36436     getAnchor : function(){
36437         return this.anchors[this.position];
36438     },
36439
36440     getCollapseAnchor : function(){
36441         return this.canchors[this.position];
36442     },
36443
36444     getSlideAnchor : function(){
36445         return this.sanchors[this.position];
36446     },
36447
36448     getAlignAdj : function(){
36449         var cm = this.cmargins;
36450         switch(this.position){
36451             case "west":
36452                 return [0, 0];
36453             break;
36454             case "east":
36455                 return [0, 0];
36456             break;
36457             case "north":
36458                 return [0, 0];
36459             break;
36460             case "south":
36461                 return [0, 0];
36462             break;
36463         }
36464     },
36465
36466     getExpandAdj : function(){
36467         var c = this.collapsedEl, cm = this.cmargins;
36468         switch(this.position){
36469             case "west":
36470                 return [-(cm.right+c.getWidth()+cm.left), 0];
36471             break;
36472             case "east":
36473                 return [cm.right+c.getWidth()+cm.left, 0];
36474             break;
36475             case "north":
36476                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36477             break;
36478             case "south":
36479                 return [0, cm.top+cm.bottom+c.getHeight()];
36480             break;
36481         }
36482     }
36483 });/*
36484  * Based on:
36485  * Ext JS Library 1.1.1
36486  * Copyright(c) 2006-2007, Ext JS, LLC.
36487  *
36488  * Originally Released Under LGPL - original licence link has changed is not relivant.
36489  *
36490  * Fork - LGPL
36491  * <script type="text/javascript">
36492  */
36493 /*
36494  * These classes are private internal classes
36495  */
36496 Roo.bootstrap.layout.Center = function(config){
36497     config.region = "center";
36498     Roo.bootstrap.layout.Region.call(this, config);
36499     this.visible = true;
36500     this.minWidth = config.minWidth || 20;
36501     this.minHeight = config.minHeight || 20;
36502 };
36503
36504 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36505     hide : function(){
36506         // center panel can't be hidden
36507     },
36508     
36509     show : function(){
36510         // center panel can't be hidden
36511     },
36512     
36513     getMinWidth: function(){
36514         return this.minWidth;
36515     },
36516     
36517     getMinHeight: function(){
36518         return this.minHeight;
36519     }
36520 });
36521
36522
36523
36524
36525  
36526
36527
36528
36529
36530
36531 Roo.bootstrap.layout.North = function(config)
36532 {
36533     config.region = 'north';
36534     config.cursor = 'n-resize';
36535     
36536     Roo.bootstrap.layout.Split.call(this, config);
36537     
36538     
36539     if(this.split){
36540         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36541         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36542         this.split.el.addClass("roo-layout-split-v");
36543     }
36544     var size = config.initialSize || config.height;
36545     if(typeof size != "undefined"){
36546         this.el.setHeight(size);
36547     }
36548 };
36549 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36550 {
36551     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36552     
36553     
36554     
36555     getBox : function(){
36556         if(this.collapsed){
36557             return this.collapsedEl.getBox();
36558         }
36559         var box = this.el.getBox();
36560         if(this.split){
36561             box.height += this.split.el.getHeight();
36562         }
36563         return box;
36564     },
36565     
36566     updateBox : function(box){
36567         if(this.split && !this.collapsed){
36568             box.height -= this.split.el.getHeight();
36569             this.split.el.setLeft(box.x);
36570             this.split.el.setTop(box.y+box.height);
36571             this.split.el.setWidth(box.width);
36572         }
36573         if(this.collapsed){
36574             this.updateBody(box.width, null);
36575         }
36576         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36577     }
36578 });
36579
36580
36581
36582
36583
36584 Roo.bootstrap.layout.South = function(config){
36585     config.region = 'south';
36586     config.cursor = 's-resize';
36587     Roo.bootstrap.layout.Split.call(this, config);
36588     if(this.split){
36589         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36590         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36591         this.split.el.addClass("roo-layout-split-v");
36592     }
36593     var size = config.initialSize || config.height;
36594     if(typeof size != "undefined"){
36595         this.el.setHeight(size);
36596     }
36597 };
36598
36599 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36600     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36601     getBox : function(){
36602         if(this.collapsed){
36603             return this.collapsedEl.getBox();
36604         }
36605         var box = this.el.getBox();
36606         if(this.split){
36607             var sh = this.split.el.getHeight();
36608             box.height += sh;
36609             box.y -= sh;
36610         }
36611         return box;
36612     },
36613     
36614     updateBox : function(box){
36615         if(this.split && !this.collapsed){
36616             var sh = this.split.el.getHeight();
36617             box.height -= sh;
36618             box.y += sh;
36619             this.split.el.setLeft(box.x);
36620             this.split.el.setTop(box.y-sh);
36621             this.split.el.setWidth(box.width);
36622         }
36623         if(this.collapsed){
36624             this.updateBody(box.width, null);
36625         }
36626         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36627     }
36628 });
36629
36630 Roo.bootstrap.layout.East = function(config){
36631     config.region = "east";
36632     config.cursor = "e-resize";
36633     Roo.bootstrap.layout.Split.call(this, config);
36634     if(this.split){
36635         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36636         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36637         this.split.el.addClass("roo-layout-split-h");
36638     }
36639     var size = config.initialSize || config.width;
36640     if(typeof size != "undefined"){
36641         this.el.setWidth(size);
36642     }
36643 };
36644 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36645     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36646     getBox : function(){
36647         if(this.collapsed){
36648             return this.collapsedEl.getBox();
36649         }
36650         var box = this.el.getBox();
36651         if(this.split){
36652             var sw = this.split.el.getWidth();
36653             box.width += sw;
36654             box.x -= sw;
36655         }
36656         return box;
36657     },
36658
36659     updateBox : function(box){
36660         if(this.split && !this.collapsed){
36661             var sw = this.split.el.getWidth();
36662             box.width -= sw;
36663             this.split.el.setLeft(box.x);
36664             this.split.el.setTop(box.y);
36665             this.split.el.setHeight(box.height);
36666             box.x += sw;
36667         }
36668         if(this.collapsed){
36669             this.updateBody(null, box.height);
36670         }
36671         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36672     }
36673 });
36674
36675 Roo.bootstrap.layout.West = function(config){
36676     config.region = "west";
36677     config.cursor = "w-resize";
36678     
36679     Roo.bootstrap.layout.Split.call(this, config);
36680     if(this.split){
36681         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36682         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36683         this.split.el.addClass("roo-layout-split-h");
36684     }
36685     
36686 };
36687 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36688     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36689     
36690     onRender: function(ctr, pos)
36691     {
36692         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36693         var size = this.config.initialSize || this.config.width;
36694         if(typeof size != "undefined"){
36695             this.el.setWidth(size);
36696         }
36697     },
36698     
36699     getBox : function(){
36700         if(this.collapsed){
36701             return this.collapsedEl.getBox();
36702         }
36703         var box = this.el.getBox();
36704         if(this.split){
36705             box.width += this.split.el.getWidth();
36706         }
36707         return box;
36708     },
36709     
36710     updateBox : function(box){
36711         if(this.split && !this.collapsed){
36712             var sw = this.split.el.getWidth();
36713             box.width -= sw;
36714             this.split.el.setLeft(box.x+box.width);
36715             this.split.el.setTop(box.y);
36716             this.split.el.setHeight(box.height);
36717         }
36718         if(this.collapsed){
36719             this.updateBody(null, box.height);
36720         }
36721         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36722     }
36723 });
36724 Roo.namespace("Roo.bootstrap.panel");/*
36725  * Based on:
36726  * Ext JS Library 1.1.1
36727  * Copyright(c) 2006-2007, Ext JS, LLC.
36728  *
36729  * Originally Released Under LGPL - original licence link has changed is not relivant.
36730  *
36731  * Fork - LGPL
36732  * <script type="text/javascript">
36733  */
36734 /**
36735  * @class Roo.ContentPanel
36736  * @extends Roo.util.Observable
36737  * A basic ContentPanel element.
36738  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36739  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36740  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
36741  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36742  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36743  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36744  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36745  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36746  * @cfg {String} title          The title for this panel
36747  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36748  * @cfg {String} url            Calls {@link #setUrl} with this value
36749  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36750  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36751  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36752  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36753  * @cfg {Boolean} badges render the badges
36754
36755  * @constructor
36756  * Create a new ContentPanel.
36757  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36758  * @param {String/Object} config A string to set only the title or a config object
36759  * @param {String} content (optional) Set the HTML content for this panel
36760  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36761  */
36762 Roo.bootstrap.panel.Content = function( config){
36763     
36764     this.tpl = config.tpl || false;
36765     
36766     var el = config.el;
36767     var content = config.content;
36768
36769     if(config.autoCreate){ // xtype is available if this is called from factory
36770         el = Roo.id();
36771     }
36772     this.el = Roo.get(el);
36773     if(!this.el && config && config.autoCreate){
36774         if(typeof config.autoCreate == "object"){
36775             if(!config.autoCreate.id){
36776                 config.autoCreate.id = config.id||el;
36777             }
36778             this.el = Roo.DomHelper.append(document.body,
36779                         config.autoCreate, true);
36780         }else{
36781             var elcfg =  {   tag: "div",
36782                             cls: "roo-layout-inactive-content",
36783                             id: config.id||el
36784                             };
36785             if (config.html) {
36786                 elcfg.html = config.html;
36787                 
36788             }
36789                         
36790             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36791         }
36792     } 
36793     this.closable = false;
36794     this.loaded = false;
36795     this.active = false;
36796    
36797       
36798     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36799         
36800         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36801         
36802         this.wrapEl = this.el; //this.el.wrap();
36803         var ti = [];
36804         if (config.toolbar.items) {
36805             ti = config.toolbar.items ;
36806             delete config.toolbar.items ;
36807         }
36808         
36809         var nitems = [];
36810         this.toolbar.render(this.wrapEl, 'before');
36811         for(var i =0;i < ti.length;i++) {
36812           //  Roo.log(['add child', items[i]]);
36813             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36814         }
36815         this.toolbar.items = nitems;
36816         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36817         delete config.toolbar;
36818         
36819     }
36820     /*
36821     // xtype created footer. - not sure if will work as we normally have to render first..
36822     if (this.footer && !this.footer.el && this.footer.xtype) {
36823         if (!this.wrapEl) {
36824             this.wrapEl = this.el.wrap();
36825         }
36826     
36827         this.footer.container = this.wrapEl.createChild();
36828          
36829         this.footer = Roo.factory(this.footer, Roo);
36830         
36831     }
36832     */
36833     
36834      if(typeof config == "string"){
36835         this.title = config;
36836     }else{
36837         Roo.apply(this, config);
36838     }
36839     
36840     if(this.resizeEl){
36841         this.resizeEl = Roo.get(this.resizeEl, true);
36842     }else{
36843         this.resizeEl = this.el;
36844     }
36845     // handle view.xtype
36846     
36847  
36848     
36849     
36850     this.addEvents({
36851         /**
36852          * @event activate
36853          * Fires when this panel is activated. 
36854          * @param {Roo.ContentPanel} this
36855          */
36856         "activate" : true,
36857         /**
36858          * @event deactivate
36859          * Fires when this panel is activated. 
36860          * @param {Roo.ContentPanel} this
36861          */
36862         "deactivate" : true,
36863
36864         /**
36865          * @event resize
36866          * Fires when this panel is resized if fitToFrame is true.
36867          * @param {Roo.ContentPanel} this
36868          * @param {Number} width The width after any component adjustments
36869          * @param {Number} height The height after any component adjustments
36870          */
36871         "resize" : true,
36872         
36873          /**
36874          * @event render
36875          * Fires when this tab is created
36876          * @param {Roo.ContentPanel} this
36877          */
36878         "render" : true
36879         
36880         
36881         
36882     });
36883     
36884
36885     
36886     
36887     if(this.autoScroll){
36888         this.resizeEl.setStyle("overflow", "auto");
36889     } else {
36890         // fix randome scrolling
36891         //this.el.on('scroll', function() {
36892         //    Roo.log('fix random scolling');
36893         //    this.scrollTo('top',0); 
36894         //});
36895     }
36896     content = content || this.content;
36897     if(content){
36898         this.setContent(content);
36899     }
36900     if(config && config.url){
36901         this.setUrl(this.url, this.params, this.loadOnce);
36902     }
36903     
36904     
36905     
36906     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36907     
36908     if (this.view && typeof(this.view.xtype) != 'undefined') {
36909         this.view.el = this.el.appendChild(document.createElement("div"));
36910         this.view = Roo.factory(this.view); 
36911         this.view.render  &&  this.view.render(false, '');  
36912     }
36913     
36914     
36915     this.fireEvent('render', this);
36916 };
36917
36918 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36919     
36920     tabTip : '',
36921     
36922     setRegion : function(region){
36923         this.region = region;
36924         this.setActiveClass(region && !this.background);
36925     },
36926     
36927     
36928     setActiveClass: function(state)
36929     {
36930         if(state){
36931            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36932            this.el.setStyle('position','relative');
36933         }else{
36934            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36935            this.el.setStyle('position', 'absolute');
36936         } 
36937     },
36938     
36939     /**
36940      * Returns the toolbar for this Panel if one was configured. 
36941      * @return {Roo.Toolbar} 
36942      */
36943     getToolbar : function(){
36944         return this.toolbar;
36945     },
36946     
36947     setActiveState : function(active)
36948     {
36949         this.active = active;
36950         this.setActiveClass(active);
36951         if(!active){
36952             if(this.fireEvent("deactivate", this) === false){
36953                 return false;
36954             }
36955             return true;
36956         }
36957         this.fireEvent("activate", this);
36958         return true;
36959     },
36960     /**
36961      * Updates this panel's element
36962      * @param {String} content The new content
36963      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36964     */
36965     setContent : function(content, loadScripts){
36966         this.el.update(content, loadScripts);
36967     },
36968
36969     ignoreResize : function(w, h){
36970         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36971             return true;
36972         }else{
36973             this.lastSize = {width: w, height: h};
36974             return false;
36975         }
36976     },
36977     /**
36978      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36979      * @return {Roo.UpdateManager} The UpdateManager
36980      */
36981     getUpdateManager : function(){
36982         return this.el.getUpdateManager();
36983     },
36984      /**
36985      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36986      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
36987 <pre><code>
36988 panel.load({
36989     url: "your-url.php",
36990     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36991     callback: yourFunction,
36992     scope: yourObject, //(optional scope)
36993     discardUrl: false,
36994     nocache: false,
36995     text: "Loading...",
36996     timeout: 30,
36997     scripts: false
36998 });
36999 </code></pre>
37000      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37001      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
37002      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
37003      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37004      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
37005      * @return {Roo.ContentPanel} this
37006      */
37007     load : function(){
37008         var um = this.el.getUpdateManager();
37009         um.update.apply(um, arguments);
37010         return this;
37011     },
37012
37013
37014     /**
37015      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
37016      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37017      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
37018      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
37019      * @return {Roo.UpdateManager} The UpdateManager
37020      */
37021     setUrl : function(url, params, loadOnce){
37022         if(this.refreshDelegate){
37023             this.removeListener("activate", this.refreshDelegate);
37024         }
37025         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37026         this.on("activate", this.refreshDelegate);
37027         return this.el.getUpdateManager();
37028     },
37029     
37030     _handleRefresh : function(url, params, loadOnce){
37031         if(!loadOnce || !this.loaded){
37032             var updater = this.el.getUpdateManager();
37033             updater.update(url, params, this._setLoaded.createDelegate(this));
37034         }
37035     },
37036     
37037     _setLoaded : function(){
37038         this.loaded = true;
37039     }, 
37040     
37041     /**
37042      * Returns this panel's id
37043      * @return {String} 
37044      */
37045     getId : function(){
37046         return this.el.id;
37047     },
37048     
37049     /** 
37050      * Returns this panel's element - used by regiosn to add.
37051      * @return {Roo.Element} 
37052      */
37053     getEl : function(){
37054         return this.wrapEl || this.el;
37055     },
37056     
37057    
37058     
37059     adjustForComponents : function(width, height)
37060     {
37061         //Roo.log('adjustForComponents ');
37062         if(this.resizeEl != this.el){
37063             width -= this.el.getFrameWidth('lr');
37064             height -= this.el.getFrameWidth('tb');
37065         }
37066         if(this.toolbar){
37067             var te = this.toolbar.getEl();
37068             te.setWidth(width);
37069             height -= te.getHeight();
37070         }
37071         if(this.footer){
37072             var te = this.footer.getEl();
37073             te.setWidth(width);
37074             height -= te.getHeight();
37075         }
37076         
37077         
37078         if(this.adjustments){
37079             width += this.adjustments[0];
37080             height += this.adjustments[1];
37081         }
37082         return {"width": width, "height": height};
37083     },
37084     
37085     setSize : function(width, height){
37086         if(this.fitToFrame && !this.ignoreResize(width, height)){
37087             if(this.fitContainer && this.resizeEl != this.el){
37088                 this.el.setSize(width, height);
37089             }
37090             var size = this.adjustForComponents(width, height);
37091             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37092             this.fireEvent('resize', this, size.width, size.height);
37093         }
37094     },
37095     
37096     /**
37097      * Returns this panel's title
37098      * @return {String} 
37099      */
37100     getTitle : function(){
37101         
37102         if (typeof(this.title) != 'object') {
37103             return this.title;
37104         }
37105         
37106         var t = '';
37107         for (var k in this.title) {
37108             if (!this.title.hasOwnProperty(k)) {
37109                 continue;
37110             }
37111             
37112             if (k.indexOf('-') >= 0) {
37113                 var s = k.split('-');
37114                 for (var i = 0; i<s.length; i++) {
37115                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37116                 }
37117             } else {
37118                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37119             }
37120         }
37121         return t;
37122     },
37123     
37124     /**
37125      * Set this panel's title
37126      * @param {String} title
37127      */
37128     setTitle : function(title){
37129         this.title = title;
37130         if(this.region){
37131             this.region.updatePanelTitle(this, title);
37132         }
37133     },
37134     
37135     /**
37136      * Returns true is this panel was configured to be closable
37137      * @return {Boolean} 
37138      */
37139     isClosable : function(){
37140         return this.closable;
37141     },
37142     
37143     beforeSlide : function(){
37144         this.el.clip();
37145         this.resizeEl.clip();
37146     },
37147     
37148     afterSlide : function(){
37149         this.el.unclip();
37150         this.resizeEl.unclip();
37151     },
37152     
37153     /**
37154      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37155      *   Will fail silently if the {@link #setUrl} method has not been called.
37156      *   This does not activate the panel, just updates its content.
37157      */
37158     refresh : function(){
37159         if(this.refreshDelegate){
37160            this.loaded = false;
37161            this.refreshDelegate();
37162         }
37163     },
37164     
37165     /**
37166      * Destroys this panel
37167      */
37168     destroy : function(){
37169         this.el.removeAllListeners();
37170         var tempEl = document.createElement("span");
37171         tempEl.appendChild(this.el.dom);
37172         tempEl.innerHTML = "";
37173         this.el.remove();
37174         this.el = null;
37175     },
37176     
37177     /**
37178      * form - if the content panel contains a form - this is a reference to it.
37179      * @type {Roo.form.Form}
37180      */
37181     form : false,
37182     /**
37183      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37184      *    This contains a reference to it.
37185      * @type {Roo.View}
37186      */
37187     view : false,
37188     
37189       /**
37190      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37191      * <pre><code>
37192
37193 layout.addxtype({
37194        xtype : 'Form',
37195        items: [ .... ]
37196    }
37197 );
37198
37199 </code></pre>
37200      * @param {Object} cfg Xtype definition of item to add.
37201      */
37202     
37203     
37204     getChildContainer: function () {
37205         return this.getEl();
37206     }
37207     
37208     
37209     /*
37210         var  ret = new Roo.factory(cfg);
37211         return ret;
37212         
37213         
37214         // add form..
37215         if (cfg.xtype.match(/^Form$/)) {
37216             
37217             var el;
37218             //if (this.footer) {
37219             //    el = this.footer.container.insertSibling(false, 'before');
37220             //} else {
37221                 el = this.el.createChild();
37222             //}
37223
37224             this.form = new  Roo.form.Form(cfg);
37225             
37226             
37227             if ( this.form.allItems.length) {
37228                 this.form.render(el.dom);
37229             }
37230             return this.form;
37231         }
37232         // should only have one of theses..
37233         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37234             // views.. should not be just added - used named prop 'view''
37235             
37236             cfg.el = this.el.appendChild(document.createElement("div"));
37237             // factory?
37238             
37239             var ret = new Roo.factory(cfg);
37240              
37241              ret.render && ret.render(false, ''); // render blank..
37242             this.view = ret;
37243             return ret;
37244         }
37245         return false;
37246     }
37247     \*/
37248 });
37249  
37250 /**
37251  * @class Roo.bootstrap.panel.Grid
37252  * @extends Roo.bootstrap.panel.Content
37253  * @constructor
37254  * Create a new GridPanel.
37255  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37256  * @param {Object} config A the config object
37257   
37258  */
37259
37260
37261
37262 Roo.bootstrap.panel.Grid = function(config)
37263 {
37264     
37265       
37266     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37267         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37268
37269     config.el = this.wrapper;
37270     //this.el = this.wrapper;
37271     
37272       if (config.container) {
37273         // ctor'ed from a Border/panel.grid
37274         
37275         
37276         this.wrapper.setStyle("overflow", "hidden");
37277         this.wrapper.addClass('roo-grid-container');
37278
37279     }
37280     
37281     
37282     if(config.toolbar){
37283         var tool_el = this.wrapper.createChild();    
37284         this.toolbar = Roo.factory(config.toolbar);
37285         var ti = [];
37286         if (config.toolbar.items) {
37287             ti = config.toolbar.items ;
37288             delete config.toolbar.items ;
37289         }
37290         
37291         var nitems = [];
37292         this.toolbar.render(tool_el);
37293         for(var i =0;i < ti.length;i++) {
37294           //  Roo.log(['add child', items[i]]);
37295             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37296         }
37297         this.toolbar.items = nitems;
37298         
37299         delete config.toolbar;
37300     }
37301     
37302     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37303     config.grid.scrollBody = true;;
37304     config.grid.monitorWindowResize = false; // turn off autosizing
37305     config.grid.autoHeight = false;
37306     config.grid.autoWidth = false;
37307     
37308     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37309     
37310     if (config.background) {
37311         // render grid on panel activation (if panel background)
37312         this.on('activate', function(gp) {
37313             if (!gp.grid.rendered) {
37314                 gp.grid.render(this.wrapper);
37315                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37316             }
37317         });
37318             
37319     } else {
37320         this.grid.render(this.wrapper);
37321         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37322
37323     }
37324     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37325     // ??? needed ??? config.el = this.wrapper;
37326     
37327     
37328     
37329   
37330     // xtype created footer. - not sure if will work as we normally have to render first..
37331     if (this.footer && !this.footer.el && this.footer.xtype) {
37332         
37333         var ctr = this.grid.getView().getFooterPanel(true);
37334         this.footer.dataSource = this.grid.dataSource;
37335         this.footer = Roo.factory(this.footer, Roo);
37336         this.footer.render(ctr);
37337         
37338     }
37339     
37340     
37341     
37342     
37343      
37344 };
37345
37346 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37347     getId : function(){
37348         return this.grid.id;
37349     },
37350     
37351     /**
37352      * Returns the grid for this panel
37353      * @return {Roo.bootstrap.Table} 
37354      */
37355     getGrid : function(){
37356         return this.grid;    
37357     },
37358     
37359     setSize : function(width, height){
37360         if(!this.ignoreResize(width, height)){
37361             var grid = this.grid;
37362             var size = this.adjustForComponents(width, height);
37363             var gridel = grid.getGridEl();
37364             gridel.setSize(size.width, size.height);
37365             /*
37366             var thd = grid.getGridEl().select('thead',true).first();
37367             var tbd = grid.getGridEl().select('tbody', true).first();
37368             if (tbd) {
37369                 tbd.setSize(width, height - thd.getHeight());
37370             }
37371             */
37372             grid.autoSize();
37373         }
37374     },
37375      
37376     
37377     
37378     beforeSlide : function(){
37379         this.grid.getView().scroller.clip();
37380     },
37381     
37382     afterSlide : function(){
37383         this.grid.getView().scroller.unclip();
37384     },
37385     
37386     destroy : function(){
37387         this.grid.destroy();
37388         delete this.grid;
37389         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37390     }
37391 });
37392
37393 /**
37394  * @class Roo.bootstrap.panel.Nest
37395  * @extends Roo.bootstrap.panel.Content
37396  * @constructor
37397  * Create a new Panel, that can contain a layout.Border.
37398  * 
37399  * 
37400  * @param {Roo.BorderLayout} layout The layout for this panel
37401  * @param {String/Object} config A string to set only the title or a config object
37402  */
37403 Roo.bootstrap.panel.Nest = function(config)
37404 {
37405     // construct with only one argument..
37406     /* FIXME - implement nicer consturctors
37407     if (layout.layout) {
37408         config = layout;
37409         layout = config.layout;
37410         delete config.layout;
37411     }
37412     if (layout.xtype && !layout.getEl) {
37413         // then layout needs constructing..
37414         layout = Roo.factory(layout, Roo);
37415     }
37416     */
37417     
37418     config.el =  config.layout.getEl();
37419     
37420     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37421     
37422     config.layout.monitorWindowResize = false; // turn off autosizing
37423     this.layout = config.layout;
37424     this.layout.getEl().addClass("roo-layout-nested-layout");
37425     
37426     
37427     
37428     
37429 };
37430
37431 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37432
37433     setSize : function(width, height){
37434         if(!this.ignoreResize(width, height)){
37435             var size = this.adjustForComponents(width, height);
37436             var el = this.layout.getEl();
37437             if (size.height < 1) {
37438                 el.setWidth(size.width);   
37439             } else {
37440                 el.setSize(size.width, size.height);
37441             }
37442             var touch = el.dom.offsetWidth;
37443             this.layout.layout();
37444             // ie requires a double layout on the first pass
37445             if(Roo.isIE && !this.initialized){
37446                 this.initialized = true;
37447                 this.layout.layout();
37448             }
37449         }
37450     },
37451     
37452     // activate all subpanels if not currently active..
37453     
37454     setActiveState : function(active){
37455         this.active = active;
37456         this.setActiveClass(active);
37457         
37458         if(!active){
37459             this.fireEvent("deactivate", this);
37460             return;
37461         }
37462         
37463         this.fireEvent("activate", this);
37464         // not sure if this should happen before or after..
37465         if (!this.layout) {
37466             return; // should not happen..
37467         }
37468         var reg = false;
37469         for (var r in this.layout.regions) {
37470             reg = this.layout.getRegion(r);
37471             if (reg.getActivePanel()) {
37472                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37473                 reg.setActivePanel(reg.getActivePanel());
37474                 continue;
37475             }
37476             if (!reg.panels.length) {
37477                 continue;
37478             }
37479             reg.showPanel(reg.getPanel(0));
37480         }
37481         
37482         
37483         
37484         
37485     },
37486     
37487     /**
37488      * Returns the nested BorderLayout for this panel
37489      * @return {Roo.BorderLayout} 
37490      */
37491     getLayout : function(){
37492         return this.layout;
37493     },
37494     
37495      /**
37496      * Adds a xtype elements to the layout of the nested panel
37497      * <pre><code>
37498
37499 panel.addxtype({
37500        xtype : 'ContentPanel',
37501        region: 'west',
37502        items: [ .... ]
37503    }
37504 );
37505
37506 panel.addxtype({
37507         xtype : 'NestedLayoutPanel',
37508         region: 'west',
37509         layout: {
37510            center: { },
37511            west: { }   
37512         },
37513         items : [ ... list of content panels or nested layout panels.. ]
37514    }
37515 );
37516 </code></pre>
37517      * @param {Object} cfg Xtype definition of item to add.
37518      */
37519     addxtype : function(cfg) {
37520         return this.layout.addxtype(cfg);
37521     
37522     }
37523 });        /*
37524  * Based on:
37525  * Ext JS Library 1.1.1
37526  * Copyright(c) 2006-2007, Ext JS, LLC.
37527  *
37528  * Originally Released Under LGPL - original licence link has changed is not relivant.
37529  *
37530  * Fork - LGPL
37531  * <script type="text/javascript">
37532  */
37533 /**
37534  * @class Roo.TabPanel
37535  * @extends Roo.util.Observable
37536  * A lightweight tab container.
37537  * <br><br>
37538  * Usage:
37539  * <pre><code>
37540 // basic tabs 1, built from existing content
37541 var tabs = new Roo.TabPanel("tabs1");
37542 tabs.addTab("script", "View Script");
37543 tabs.addTab("markup", "View Markup");
37544 tabs.activate("script");
37545
37546 // more advanced tabs, built from javascript
37547 var jtabs = new Roo.TabPanel("jtabs");
37548 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37549
37550 // set up the UpdateManager
37551 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37552 var updater = tab2.getUpdateManager();
37553 updater.setDefaultUrl("ajax1.htm");
37554 tab2.on('activate', updater.refresh, updater, true);
37555
37556 // Use setUrl for Ajax loading
37557 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37558 tab3.setUrl("ajax2.htm", null, true);
37559
37560 // Disabled tab
37561 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37562 tab4.disable();
37563
37564 jtabs.activate("jtabs-1");
37565  * </code></pre>
37566  * @constructor
37567  * Create a new TabPanel.
37568  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37569  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37570  */
37571 Roo.bootstrap.panel.Tabs = function(config){
37572     /**
37573     * The container element for this TabPanel.
37574     * @type Roo.Element
37575     */
37576     this.el = Roo.get(config.el);
37577     delete config.el;
37578     if(config){
37579         if(typeof config == "boolean"){
37580             this.tabPosition = config ? "bottom" : "top";
37581         }else{
37582             Roo.apply(this, config);
37583         }
37584     }
37585     
37586     if(this.tabPosition == "bottom"){
37587         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37588         this.el.addClass("roo-tabs-bottom");
37589     }
37590     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37591     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37592     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37593     if(Roo.isIE){
37594         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37595     }
37596     if(this.tabPosition != "bottom"){
37597         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37598          * @type Roo.Element
37599          */
37600         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37601         this.el.addClass("roo-tabs-top");
37602     }
37603     this.items = [];
37604
37605     this.bodyEl.setStyle("position", "relative");
37606
37607     this.active = null;
37608     this.activateDelegate = this.activate.createDelegate(this);
37609
37610     this.addEvents({
37611         /**
37612          * @event tabchange
37613          * Fires when the active tab changes
37614          * @param {Roo.TabPanel} this
37615          * @param {Roo.TabPanelItem} activePanel The new active tab
37616          */
37617         "tabchange": true,
37618         /**
37619          * @event beforetabchange
37620          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37621          * @param {Roo.TabPanel} this
37622          * @param {Object} e Set cancel to true on this object to cancel the tab change
37623          * @param {Roo.TabPanelItem} tab The tab being changed to
37624          */
37625         "beforetabchange" : true
37626     });
37627
37628     Roo.EventManager.onWindowResize(this.onResize, this);
37629     this.cpad = this.el.getPadding("lr");
37630     this.hiddenCount = 0;
37631
37632
37633     // toolbar on the tabbar support...
37634     if (this.toolbar) {
37635         alert("no toolbar support yet");
37636         this.toolbar  = false;
37637         /*
37638         var tcfg = this.toolbar;
37639         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37640         this.toolbar = new Roo.Toolbar(tcfg);
37641         if (Roo.isSafari) {
37642             var tbl = tcfg.container.child('table', true);
37643             tbl.setAttribute('width', '100%');
37644         }
37645         */
37646         
37647     }
37648    
37649
37650
37651     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37652 };
37653
37654 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37655     /*
37656      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37657      */
37658     tabPosition : "top",
37659     /*
37660      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37661      */
37662     currentTabWidth : 0,
37663     /*
37664      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37665      */
37666     minTabWidth : 40,
37667     /*
37668      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37669      */
37670     maxTabWidth : 250,
37671     /*
37672      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37673      */
37674     preferredTabWidth : 175,
37675     /*
37676      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37677      */
37678     resizeTabs : false,
37679     /*
37680      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37681      */
37682     monitorResize : true,
37683     /*
37684      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37685      */
37686     toolbar : false,
37687
37688     /**
37689      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37690      * @param {String} id The id of the div to use <b>or create</b>
37691      * @param {String} text The text for the tab
37692      * @param {String} content (optional) Content to put in the TabPanelItem body
37693      * @param {Boolean} closable (optional) True to create a close icon on the tab
37694      * @return {Roo.TabPanelItem} The created TabPanelItem
37695      */
37696     addTab : function(id, text, content, closable, tpl)
37697     {
37698         var item = new Roo.bootstrap.panel.TabItem({
37699             panel: this,
37700             id : id,
37701             text : text,
37702             closable : closable,
37703             tpl : tpl
37704         });
37705         this.addTabItem(item);
37706         if(content){
37707             item.setContent(content);
37708         }
37709         return item;
37710     },
37711
37712     /**
37713      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37714      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37715      * @return {Roo.TabPanelItem}
37716      */
37717     getTab : function(id){
37718         return this.items[id];
37719     },
37720
37721     /**
37722      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37723      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37724      */
37725     hideTab : function(id){
37726         var t = this.items[id];
37727         if(!t.isHidden()){
37728            t.setHidden(true);
37729            this.hiddenCount++;
37730            this.autoSizeTabs();
37731         }
37732     },
37733
37734     /**
37735      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37736      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37737      */
37738     unhideTab : function(id){
37739         var t = this.items[id];
37740         if(t.isHidden()){
37741            t.setHidden(false);
37742            this.hiddenCount--;
37743            this.autoSizeTabs();
37744         }
37745     },
37746
37747     /**
37748      * Adds an existing {@link Roo.TabPanelItem}.
37749      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37750      */
37751     addTabItem : function(item){
37752         this.items[item.id] = item;
37753         this.items.push(item);
37754       //  if(this.resizeTabs){
37755     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37756   //         this.autoSizeTabs();
37757 //        }else{
37758 //            item.autoSize();
37759        // }
37760     },
37761
37762     /**
37763      * Removes a {@link Roo.TabPanelItem}.
37764      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37765      */
37766     removeTab : function(id){
37767         var items = this.items;
37768         var tab = items[id];
37769         if(!tab) { return; }
37770         var index = items.indexOf(tab);
37771         if(this.active == tab && items.length > 1){
37772             var newTab = this.getNextAvailable(index);
37773             if(newTab) {
37774                 newTab.activate();
37775             }
37776         }
37777         this.stripEl.dom.removeChild(tab.pnode.dom);
37778         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37779             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37780         }
37781         items.splice(index, 1);
37782         delete this.items[tab.id];
37783         tab.fireEvent("close", tab);
37784         tab.purgeListeners();
37785         this.autoSizeTabs();
37786     },
37787
37788     getNextAvailable : function(start){
37789         var items = this.items;
37790         var index = start;
37791         // look for a next tab that will slide over to
37792         // replace the one being removed
37793         while(index < items.length){
37794             var item = items[++index];
37795             if(item && !item.isHidden()){
37796                 return item;
37797             }
37798         }
37799         // if one isn't found select the previous tab (on the left)
37800         index = start;
37801         while(index >= 0){
37802             var item = items[--index];
37803             if(item && !item.isHidden()){
37804                 return item;
37805             }
37806         }
37807         return null;
37808     },
37809
37810     /**
37811      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37812      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37813      */
37814     disableTab : function(id){
37815         var tab = this.items[id];
37816         if(tab && this.active != tab){
37817             tab.disable();
37818         }
37819     },
37820
37821     /**
37822      * Enables a {@link Roo.TabPanelItem} that is disabled.
37823      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37824      */
37825     enableTab : function(id){
37826         var tab = this.items[id];
37827         tab.enable();
37828     },
37829
37830     /**
37831      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37832      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37833      * @return {Roo.TabPanelItem} The TabPanelItem.
37834      */
37835     activate : function(id){
37836         var tab = this.items[id];
37837         if(!tab){
37838             return null;
37839         }
37840         if(tab == this.active || tab.disabled){
37841             return tab;
37842         }
37843         var e = {};
37844         this.fireEvent("beforetabchange", this, e, tab);
37845         if(e.cancel !== true && !tab.disabled){
37846             if(this.active){
37847                 this.active.hide();
37848             }
37849             this.active = this.items[id];
37850             this.active.show();
37851             this.fireEvent("tabchange", this, this.active);
37852         }
37853         return tab;
37854     },
37855
37856     /**
37857      * Gets the active {@link Roo.TabPanelItem}.
37858      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37859      */
37860     getActiveTab : function(){
37861         return this.active;
37862     },
37863
37864     /**
37865      * Updates the tab body element to fit the height of the container element
37866      * for overflow scrolling
37867      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37868      */
37869     syncHeight : function(targetHeight){
37870         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37871         var bm = this.bodyEl.getMargins();
37872         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37873         this.bodyEl.setHeight(newHeight);
37874         return newHeight;
37875     },
37876
37877     onResize : function(){
37878         if(this.monitorResize){
37879             this.autoSizeTabs();
37880         }
37881     },
37882
37883     /**
37884      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37885      */
37886     beginUpdate : function(){
37887         this.updating = true;
37888     },
37889
37890     /**
37891      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37892      */
37893     endUpdate : function(){
37894         this.updating = false;
37895         this.autoSizeTabs();
37896     },
37897
37898     /**
37899      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37900      */
37901     autoSizeTabs : function(){
37902         var count = this.items.length;
37903         var vcount = count - this.hiddenCount;
37904         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37905             return;
37906         }
37907         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37908         var availWidth = Math.floor(w / vcount);
37909         var b = this.stripBody;
37910         if(b.getWidth() > w){
37911             var tabs = this.items;
37912             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37913             if(availWidth < this.minTabWidth){
37914                 /*if(!this.sleft){    // incomplete scrolling code
37915                     this.createScrollButtons();
37916                 }
37917                 this.showScroll();
37918                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37919             }
37920         }else{
37921             if(this.currentTabWidth < this.preferredTabWidth){
37922                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37923             }
37924         }
37925     },
37926
37927     /**
37928      * Returns the number of tabs in this TabPanel.
37929      * @return {Number}
37930      */
37931      getCount : function(){
37932          return this.items.length;
37933      },
37934
37935     /**
37936      * Resizes all the tabs to the passed width
37937      * @param {Number} The new width
37938      */
37939     setTabWidth : function(width){
37940         this.currentTabWidth = width;
37941         for(var i = 0, len = this.items.length; i < len; i++) {
37942                 if(!this.items[i].isHidden()) {
37943                 this.items[i].setWidth(width);
37944             }
37945         }
37946     },
37947
37948     /**
37949      * Destroys this TabPanel
37950      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37951      */
37952     destroy : function(removeEl){
37953         Roo.EventManager.removeResizeListener(this.onResize, this);
37954         for(var i = 0, len = this.items.length; i < len; i++){
37955             this.items[i].purgeListeners();
37956         }
37957         if(removeEl === true){
37958             this.el.update("");
37959             this.el.remove();
37960         }
37961     },
37962     
37963     createStrip : function(container)
37964     {
37965         var strip = document.createElement("nav");
37966         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37967         container.appendChild(strip);
37968         return strip;
37969     },
37970     
37971     createStripList : function(strip)
37972     {
37973         // div wrapper for retard IE
37974         // returns the "tr" element.
37975         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37976         //'<div class="x-tabs-strip-wrap">'+
37977           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37978           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37979         return strip.firstChild; //.firstChild.firstChild.firstChild;
37980     },
37981     createBody : function(container)
37982     {
37983         var body = document.createElement("div");
37984         Roo.id(body, "tab-body");
37985         //Roo.fly(body).addClass("x-tabs-body");
37986         Roo.fly(body).addClass("tab-content");
37987         container.appendChild(body);
37988         return body;
37989     },
37990     createItemBody :function(bodyEl, id){
37991         var body = Roo.getDom(id);
37992         if(!body){
37993             body = document.createElement("div");
37994             body.id = id;
37995         }
37996         //Roo.fly(body).addClass("x-tabs-item-body");
37997         Roo.fly(body).addClass("tab-pane");
37998          bodyEl.insertBefore(body, bodyEl.firstChild);
37999         return body;
38000     },
38001     /** @private */
38002     createStripElements :  function(stripEl, text, closable, tpl)
38003     {
38004         var td = document.createElement("li"); // was td..
38005         
38006         
38007         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38008         
38009         
38010         stripEl.appendChild(td);
38011         /*if(closable){
38012             td.className = "x-tabs-closable";
38013             if(!this.closeTpl){
38014                 this.closeTpl = new Roo.Template(
38015                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38016                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38017                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38018                 );
38019             }
38020             var el = this.closeTpl.overwrite(td, {"text": text});
38021             var close = el.getElementsByTagName("div")[0];
38022             var inner = el.getElementsByTagName("em")[0];
38023             return {"el": el, "close": close, "inner": inner};
38024         } else {
38025         */
38026         // not sure what this is..
38027 //            if(!this.tabTpl){
38028                 //this.tabTpl = new Roo.Template(
38029                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38030                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38031                 //);
38032 //                this.tabTpl = new Roo.Template(
38033 //                   '<a href="#">' +
38034 //                   '<span unselectable="on"' +
38035 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38036 //                            ' >{text}</span></a>'
38037 //                );
38038 //                
38039 //            }
38040
38041
38042             var template = tpl || this.tabTpl || false;
38043             
38044             if(!template){
38045                 
38046                 template = new Roo.Template(
38047                    '<a href="#">' +
38048                    '<span unselectable="on"' +
38049                             (this.disableTooltips ? '' : ' title="{text}"') +
38050                             ' >{text}</span></a>'
38051                 );
38052             }
38053             
38054             switch (typeof(template)) {
38055                 case 'object' :
38056                     break;
38057                 case 'string' :
38058                     template = new Roo.Template(template);
38059                     break;
38060                 default :
38061                     break;
38062             }
38063             
38064             var el = template.overwrite(td, {"text": text});
38065             
38066             var inner = el.getElementsByTagName("span")[0];
38067             
38068             return {"el": el, "inner": inner};
38069             
38070     }
38071         
38072     
38073 });
38074
38075 /**
38076  * @class Roo.TabPanelItem
38077  * @extends Roo.util.Observable
38078  * Represents an individual item (tab plus body) in a TabPanel.
38079  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38080  * @param {String} id The id of this TabPanelItem
38081  * @param {String} text The text for the tab of this TabPanelItem
38082  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38083  */
38084 Roo.bootstrap.panel.TabItem = function(config){
38085     /**
38086      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38087      * @type Roo.TabPanel
38088      */
38089     this.tabPanel = config.panel;
38090     /**
38091      * The id for this TabPanelItem
38092      * @type String
38093      */
38094     this.id = config.id;
38095     /** @private */
38096     this.disabled = false;
38097     /** @private */
38098     this.text = config.text;
38099     /** @private */
38100     this.loaded = false;
38101     this.closable = config.closable;
38102
38103     /**
38104      * The body element for this TabPanelItem.
38105      * @type Roo.Element
38106      */
38107     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38108     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38109     this.bodyEl.setStyle("display", "block");
38110     this.bodyEl.setStyle("zoom", "1");
38111     //this.hideAction();
38112
38113     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38114     /** @private */
38115     this.el = Roo.get(els.el);
38116     this.inner = Roo.get(els.inner, true);
38117     this.textEl = Roo.get(this.el.dom.firstChild, true);
38118     this.pnode = Roo.get(els.el.parentNode, true);
38119 //    this.el.on("mousedown", this.onTabMouseDown, this);
38120     this.el.on("click", this.onTabClick, this);
38121     /** @private */
38122     if(config.closable){
38123         var c = Roo.get(els.close, true);
38124         c.dom.title = this.closeText;
38125         c.addClassOnOver("close-over");
38126         c.on("click", this.closeClick, this);
38127      }
38128
38129     this.addEvents({
38130          /**
38131          * @event activate
38132          * Fires when this tab becomes the active tab.
38133          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38134          * @param {Roo.TabPanelItem} this
38135          */
38136         "activate": true,
38137         /**
38138          * @event beforeclose
38139          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38140          * @param {Roo.TabPanelItem} this
38141          * @param {Object} e Set cancel to true on this object to cancel the close.
38142          */
38143         "beforeclose": true,
38144         /**
38145          * @event close
38146          * Fires when this tab is closed.
38147          * @param {Roo.TabPanelItem} this
38148          */
38149          "close": true,
38150         /**
38151          * @event deactivate
38152          * Fires when this tab is no longer the active tab.
38153          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38154          * @param {Roo.TabPanelItem} this
38155          */
38156          "deactivate" : true
38157     });
38158     this.hidden = false;
38159
38160     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38161 };
38162
38163 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38164            {
38165     purgeListeners : function(){
38166        Roo.util.Observable.prototype.purgeListeners.call(this);
38167        this.el.removeAllListeners();
38168     },
38169     /**
38170      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38171      */
38172     show : function(){
38173         this.pnode.addClass("active");
38174         this.showAction();
38175         if(Roo.isOpera){
38176             this.tabPanel.stripWrap.repaint();
38177         }
38178         this.fireEvent("activate", this.tabPanel, this);
38179     },
38180
38181     /**
38182      * Returns true if this tab is the active tab.
38183      * @return {Boolean}
38184      */
38185     isActive : function(){
38186         return this.tabPanel.getActiveTab() == this;
38187     },
38188
38189     /**
38190      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38191      */
38192     hide : function(){
38193         this.pnode.removeClass("active");
38194         this.hideAction();
38195         this.fireEvent("deactivate", this.tabPanel, this);
38196     },
38197
38198     hideAction : function(){
38199         this.bodyEl.hide();
38200         this.bodyEl.setStyle("position", "absolute");
38201         this.bodyEl.setLeft("-20000px");
38202         this.bodyEl.setTop("-20000px");
38203     },
38204
38205     showAction : function(){
38206         this.bodyEl.setStyle("position", "relative");
38207         this.bodyEl.setTop("");
38208         this.bodyEl.setLeft("");
38209         this.bodyEl.show();
38210     },
38211
38212     /**
38213      * Set the tooltip for the tab.
38214      * @param {String} tooltip The tab's tooltip
38215      */
38216     setTooltip : function(text){
38217         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38218             this.textEl.dom.qtip = text;
38219             this.textEl.dom.removeAttribute('title');
38220         }else{
38221             this.textEl.dom.title = text;
38222         }
38223     },
38224
38225     onTabClick : function(e){
38226         e.preventDefault();
38227         this.tabPanel.activate(this.id);
38228     },
38229
38230     onTabMouseDown : function(e){
38231         e.preventDefault();
38232         this.tabPanel.activate(this.id);
38233     },
38234 /*
38235     getWidth : function(){
38236         return this.inner.getWidth();
38237     },
38238
38239     setWidth : function(width){
38240         var iwidth = width - this.pnode.getPadding("lr");
38241         this.inner.setWidth(iwidth);
38242         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38243         this.pnode.setWidth(width);
38244     },
38245 */
38246     /**
38247      * Show or hide the tab
38248      * @param {Boolean} hidden True to hide or false to show.
38249      */
38250     setHidden : function(hidden){
38251         this.hidden = hidden;
38252         this.pnode.setStyle("display", hidden ? "none" : "");
38253     },
38254
38255     /**
38256      * Returns true if this tab is "hidden"
38257      * @return {Boolean}
38258      */
38259     isHidden : function(){
38260         return this.hidden;
38261     },
38262
38263     /**
38264      * Returns the text for this tab
38265      * @return {String}
38266      */
38267     getText : function(){
38268         return this.text;
38269     },
38270     /*
38271     autoSize : function(){
38272         //this.el.beginMeasure();
38273         this.textEl.setWidth(1);
38274         /*
38275          *  #2804 [new] Tabs in Roojs
38276          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38277          */
38278         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38279         //this.el.endMeasure();
38280     //},
38281
38282     /**
38283      * Sets the text for the tab (Note: this also sets the tooltip text)
38284      * @param {String} text The tab's text and tooltip
38285      */
38286     setText : function(text){
38287         this.text = text;
38288         this.textEl.update(text);
38289         this.setTooltip(text);
38290         //if(!this.tabPanel.resizeTabs){
38291         //    this.autoSize();
38292         //}
38293     },
38294     /**
38295      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38296      */
38297     activate : function(){
38298         this.tabPanel.activate(this.id);
38299     },
38300
38301     /**
38302      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38303      */
38304     disable : function(){
38305         if(this.tabPanel.active != this){
38306             this.disabled = true;
38307             this.pnode.addClass("disabled");
38308         }
38309     },
38310
38311     /**
38312      * Enables this TabPanelItem if it was previously disabled.
38313      */
38314     enable : function(){
38315         this.disabled = false;
38316         this.pnode.removeClass("disabled");
38317     },
38318
38319     /**
38320      * Sets the content for this TabPanelItem.
38321      * @param {String} content The content
38322      * @param {Boolean} loadScripts true to look for and load scripts
38323      */
38324     setContent : function(content, loadScripts){
38325         this.bodyEl.update(content, loadScripts);
38326     },
38327
38328     /**
38329      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38330      * @return {Roo.UpdateManager} The UpdateManager
38331      */
38332     getUpdateManager : function(){
38333         return this.bodyEl.getUpdateManager();
38334     },
38335
38336     /**
38337      * Set a URL to be used to load the content for this TabPanelItem.
38338      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38339      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
38340      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
38341      * @return {Roo.UpdateManager} The UpdateManager
38342      */
38343     setUrl : function(url, params, loadOnce){
38344         if(this.refreshDelegate){
38345             this.un('activate', this.refreshDelegate);
38346         }
38347         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38348         this.on("activate", this.refreshDelegate);
38349         return this.bodyEl.getUpdateManager();
38350     },
38351
38352     /** @private */
38353     _handleRefresh : function(url, params, loadOnce){
38354         if(!loadOnce || !this.loaded){
38355             var updater = this.bodyEl.getUpdateManager();
38356             updater.update(url, params, this._setLoaded.createDelegate(this));
38357         }
38358     },
38359
38360     /**
38361      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38362      *   Will fail silently if the setUrl method has not been called.
38363      *   This does not activate the panel, just updates its content.
38364      */
38365     refresh : function(){
38366         if(this.refreshDelegate){
38367            this.loaded = false;
38368            this.refreshDelegate();
38369         }
38370     },
38371
38372     /** @private */
38373     _setLoaded : function(){
38374         this.loaded = true;
38375     },
38376
38377     /** @private */
38378     closeClick : function(e){
38379         var o = {};
38380         e.stopEvent();
38381         this.fireEvent("beforeclose", this, o);
38382         if(o.cancel !== true){
38383             this.tabPanel.removeTab(this.id);
38384         }
38385     },
38386     /**
38387      * The text displayed in the tooltip for the close icon.
38388      * @type String
38389      */
38390     closeText : "Close this tab"
38391 });
38392 /**
38393 *    This script refer to:
38394 *    Title: International Telephone Input
38395 *    Author: Jack O'Connor
38396 *    Code version:  v12.1.12
38397 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38398 **/
38399
38400 Roo.bootstrap.PhoneInputData = function() {
38401     var d = [
38402       [
38403         "Afghanistan (‫افغانستان‬‎)",
38404         "af",
38405         "93"
38406       ],
38407       [
38408         "Albania (Shqipëri)",
38409         "al",
38410         "355"
38411       ],
38412       [
38413         "Algeria (‫الجزائر‬‎)",
38414         "dz",
38415         "213"
38416       ],
38417       [
38418         "American Samoa",
38419         "as",
38420         "1684"
38421       ],
38422       [
38423         "Andorra",
38424         "ad",
38425         "376"
38426       ],
38427       [
38428         "Angola",
38429         "ao",
38430         "244"
38431       ],
38432       [
38433         "Anguilla",
38434         "ai",
38435         "1264"
38436       ],
38437       [
38438         "Antigua and Barbuda",
38439         "ag",
38440         "1268"
38441       ],
38442       [
38443         "Argentina",
38444         "ar",
38445         "54"
38446       ],
38447       [
38448         "Armenia (Հայաստան)",
38449         "am",
38450         "374"
38451       ],
38452       [
38453         "Aruba",
38454         "aw",
38455         "297"
38456       ],
38457       [
38458         "Australia",
38459         "au",
38460         "61",
38461         0
38462       ],
38463       [
38464         "Austria (Österreich)",
38465         "at",
38466         "43"
38467       ],
38468       [
38469         "Azerbaijan (Azərbaycan)",
38470         "az",
38471         "994"
38472       ],
38473       [
38474         "Bahamas",
38475         "bs",
38476         "1242"
38477       ],
38478       [
38479         "Bahrain (‫البحرين‬‎)",
38480         "bh",
38481         "973"
38482       ],
38483       [
38484         "Bangladesh (বাংলাদেশ)",
38485         "bd",
38486         "880"
38487       ],
38488       [
38489         "Barbados",
38490         "bb",
38491         "1246"
38492       ],
38493       [
38494         "Belarus (Беларусь)",
38495         "by",
38496         "375"
38497       ],
38498       [
38499         "Belgium (België)",
38500         "be",
38501         "32"
38502       ],
38503       [
38504         "Belize",
38505         "bz",
38506         "501"
38507       ],
38508       [
38509         "Benin (Bénin)",
38510         "bj",
38511         "229"
38512       ],
38513       [
38514         "Bermuda",
38515         "bm",
38516         "1441"
38517       ],
38518       [
38519         "Bhutan (འབྲུག)",
38520         "bt",
38521         "975"
38522       ],
38523       [
38524         "Bolivia",
38525         "bo",
38526         "591"
38527       ],
38528       [
38529         "Bosnia and Herzegovina (Босна и Херцеговина)",
38530         "ba",
38531         "387"
38532       ],
38533       [
38534         "Botswana",
38535         "bw",
38536         "267"
38537       ],
38538       [
38539         "Brazil (Brasil)",
38540         "br",
38541         "55"
38542       ],
38543       [
38544         "British Indian Ocean Territory",
38545         "io",
38546         "246"
38547       ],
38548       [
38549         "British Virgin Islands",
38550         "vg",
38551         "1284"
38552       ],
38553       [
38554         "Brunei",
38555         "bn",
38556         "673"
38557       ],
38558       [
38559         "Bulgaria (България)",
38560         "bg",
38561         "359"
38562       ],
38563       [
38564         "Burkina Faso",
38565         "bf",
38566         "226"
38567       ],
38568       [
38569         "Burundi (Uburundi)",
38570         "bi",
38571         "257"
38572       ],
38573       [
38574         "Cambodia (កម្ពុជា)",
38575         "kh",
38576         "855"
38577       ],
38578       [
38579         "Cameroon (Cameroun)",
38580         "cm",
38581         "237"
38582       ],
38583       [
38584         "Canada",
38585         "ca",
38586         "1",
38587         1,
38588         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38589       ],
38590       [
38591         "Cape Verde (Kabu Verdi)",
38592         "cv",
38593         "238"
38594       ],
38595       [
38596         "Caribbean Netherlands",
38597         "bq",
38598         "599",
38599         1
38600       ],
38601       [
38602         "Cayman Islands",
38603         "ky",
38604         "1345"
38605       ],
38606       [
38607         "Central African Republic (République centrafricaine)",
38608         "cf",
38609         "236"
38610       ],
38611       [
38612         "Chad (Tchad)",
38613         "td",
38614         "235"
38615       ],
38616       [
38617         "Chile",
38618         "cl",
38619         "56"
38620       ],
38621       [
38622         "China (中国)",
38623         "cn",
38624         "86"
38625       ],
38626       [
38627         "Christmas Island",
38628         "cx",
38629         "61",
38630         2
38631       ],
38632       [
38633         "Cocos (Keeling) Islands",
38634         "cc",
38635         "61",
38636         1
38637       ],
38638       [
38639         "Colombia",
38640         "co",
38641         "57"
38642       ],
38643       [
38644         "Comoros (‫جزر القمر‬‎)",
38645         "km",
38646         "269"
38647       ],
38648       [
38649         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38650         "cd",
38651         "243"
38652       ],
38653       [
38654         "Congo (Republic) (Congo-Brazzaville)",
38655         "cg",
38656         "242"
38657       ],
38658       [
38659         "Cook Islands",
38660         "ck",
38661         "682"
38662       ],
38663       [
38664         "Costa Rica",
38665         "cr",
38666         "506"
38667       ],
38668       [
38669         "Côte d’Ivoire",
38670         "ci",
38671         "225"
38672       ],
38673       [
38674         "Croatia (Hrvatska)",
38675         "hr",
38676         "385"
38677       ],
38678       [
38679         "Cuba",
38680         "cu",
38681         "53"
38682       ],
38683       [
38684         "Curaçao",
38685         "cw",
38686         "599",
38687         0
38688       ],
38689       [
38690         "Cyprus (Κύπρος)",
38691         "cy",
38692         "357"
38693       ],
38694       [
38695         "Czech Republic (Česká republika)",
38696         "cz",
38697         "420"
38698       ],
38699       [
38700         "Denmark (Danmark)",
38701         "dk",
38702         "45"
38703       ],
38704       [
38705         "Djibouti",
38706         "dj",
38707         "253"
38708       ],
38709       [
38710         "Dominica",
38711         "dm",
38712         "1767"
38713       ],
38714       [
38715         "Dominican Republic (República Dominicana)",
38716         "do",
38717         "1",
38718         2,
38719         ["809", "829", "849"]
38720       ],
38721       [
38722         "Ecuador",
38723         "ec",
38724         "593"
38725       ],
38726       [
38727         "Egypt (‫مصر‬‎)",
38728         "eg",
38729         "20"
38730       ],
38731       [
38732         "El Salvador",
38733         "sv",
38734         "503"
38735       ],
38736       [
38737         "Equatorial Guinea (Guinea Ecuatorial)",
38738         "gq",
38739         "240"
38740       ],
38741       [
38742         "Eritrea",
38743         "er",
38744         "291"
38745       ],
38746       [
38747         "Estonia (Eesti)",
38748         "ee",
38749         "372"
38750       ],
38751       [
38752         "Ethiopia",
38753         "et",
38754         "251"
38755       ],
38756       [
38757         "Falkland Islands (Islas Malvinas)",
38758         "fk",
38759         "500"
38760       ],
38761       [
38762         "Faroe Islands (Føroyar)",
38763         "fo",
38764         "298"
38765       ],
38766       [
38767         "Fiji",
38768         "fj",
38769         "679"
38770       ],
38771       [
38772         "Finland (Suomi)",
38773         "fi",
38774         "358",
38775         0
38776       ],
38777       [
38778         "France",
38779         "fr",
38780         "33"
38781       ],
38782       [
38783         "French Guiana (Guyane française)",
38784         "gf",
38785         "594"
38786       ],
38787       [
38788         "French Polynesia (Polynésie française)",
38789         "pf",
38790         "689"
38791       ],
38792       [
38793         "Gabon",
38794         "ga",
38795         "241"
38796       ],
38797       [
38798         "Gambia",
38799         "gm",
38800         "220"
38801       ],
38802       [
38803         "Georgia (საქართველო)",
38804         "ge",
38805         "995"
38806       ],
38807       [
38808         "Germany (Deutschland)",
38809         "de",
38810         "49"
38811       ],
38812       [
38813         "Ghana (Gaana)",
38814         "gh",
38815         "233"
38816       ],
38817       [
38818         "Gibraltar",
38819         "gi",
38820         "350"
38821       ],
38822       [
38823         "Greece (Ελλάδα)",
38824         "gr",
38825         "30"
38826       ],
38827       [
38828         "Greenland (Kalaallit Nunaat)",
38829         "gl",
38830         "299"
38831       ],
38832       [
38833         "Grenada",
38834         "gd",
38835         "1473"
38836       ],
38837       [
38838         "Guadeloupe",
38839         "gp",
38840         "590",
38841         0
38842       ],
38843       [
38844         "Guam",
38845         "gu",
38846         "1671"
38847       ],
38848       [
38849         "Guatemala",
38850         "gt",
38851         "502"
38852       ],
38853       [
38854         "Guernsey",
38855         "gg",
38856         "44",
38857         1
38858       ],
38859       [
38860         "Guinea (Guinée)",
38861         "gn",
38862         "224"
38863       ],
38864       [
38865         "Guinea-Bissau (Guiné Bissau)",
38866         "gw",
38867         "245"
38868       ],
38869       [
38870         "Guyana",
38871         "gy",
38872         "592"
38873       ],
38874       [
38875         "Haiti",
38876         "ht",
38877         "509"
38878       ],
38879       [
38880         "Honduras",
38881         "hn",
38882         "504"
38883       ],
38884       [
38885         "Hong Kong (香港)",
38886         "hk",
38887         "852"
38888       ],
38889       [
38890         "Hungary (Magyarország)",
38891         "hu",
38892         "36"
38893       ],
38894       [
38895         "Iceland (Ísland)",
38896         "is",
38897         "354"
38898       ],
38899       [
38900         "India (भारत)",
38901         "in",
38902         "91"
38903       ],
38904       [
38905         "Indonesia",
38906         "id",
38907         "62"
38908       ],
38909       [
38910         "Iran (‫ایران‬‎)",
38911         "ir",
38912         "98"
38913       ],
38914       [
38915         "Iraq (‫العراق‬‎)",
38916         "iq",
38917         "964"
38918       ],
38919       [
38920         "Ireland",
38921         "ie",
38922         "353"
38923       ],
38924       [
38925         "Isle of Man",
38926         "im",
38927         "44",
38928         2
38929       ],
38930       [
38931         "Israel (‫ישראל‬‎)",
38932         "il",
38933         "972"
38934       ],
38935       [
38936         "Italy (Italia)",
38937         "it",
38938         "39",
38939         0
38940       ],
38941       [
38942         "Jamaica",
38943         "jm",
38944         "1876"
38945       ],
38946       [
38947         "Japan (日本)",
38948         "jp",
38949         "81"
38950       ],
38951       [
38952         "Jersey",
38953         "je",
38954         "44",
38955         3
38956       ],
38957       [
38958         "Jordan (‫الأردن‬‎)",
38959         "jo",
38960         "962"
38961       ],
38962       [
38963         "Kazakhstan (Казахстан)",
38964         "kz",
38965         "7",
38966         1
38967       ],
38968       [
38969         "Kenya",
38970         "ke",
38971         "254"
38972       ],
38973       [
38974         "Kiribati",
38975         "ki",
38976         "686"
38977       ],
38978       [
38979         "Kosovo",
38980         "xk",
38981         "383"
38982       ],
38983       [
38984         "Kuwait (‫الكويت‬‎)",
38985         "kw",
38986         "965"
38987       ],
38988       [
38989         "Kyrgyzstan (Кыргызстан)",
38990         "kg",
38991         "996"
38992       ],
38993       [
38994         "Laos (ລາວ)",
38995         "la",
38996         "856"
38997       ],
38998       [
38999         "Latvia (Latvija)",
39000         "lv",
39001         "371"
39002       ],
39003       [
39004         "Lebanon (‫لبنان‬‎)",
39005         "lb",
39006         "961"
39007       ],
39008       [
39009         "Lesotho",
39010         "ls",
39011         "266"
39012       ],
39013       [
39014         "Liberia",
39015         "lr",
39016         "231"
39017       ],
39018       [
39019         "Libya (‫ليبيا‬‎)",
39020         "ly",
39021         "218"
39022       ],
39023       [
39024         "Liechtenstein",
39025         "li",
39026         "423"
39027       ],
39028       [
39029         "Lithuania (Lietuva)",
39030         "lt",
39031         "370"
39032       ],
39033       [
39034         "Luxembourg",
39035         "lu",
39036         "352"
39037       ],
39038       [
39039         "Macau (澳門)",
39040         "mo",
39041         "853"
39042       ],
39043       [
39044         "Macedonia (FYROM) (Македонија)",
39045         "mk",
39046         "389"
39047       ],
39048       [
39049         "Madagascar (Madagasikara)",
39050         "mg",
39051         "261"
39052       ],
39053       [
39054         "Malawi",
39055         "mw",
39056         "265"
39057       ],
39058       [
39059         "Malaysia",
39060         "my",
39061         "60"
39062       ],
39063       [
39064         "Maldives",
39065         "mv",
39066         "960"
39067       ],
39068       [
39069         "Mali",
39070         "ml",
39071         "223"
39072       ],
39073       [
39074         "Malta",
39075         "mt",
39076         "356"
39077       ],
39078       [
39079         "Marshall Islands",
39080         "mh",
39081         "692"
39082       ],
39083       [
39084         "Martinique",
39085         "mq",
39086         "596"
39087       ],
39088       [
39089         "Mauritania (‫موريتانيا‬‎)",
39090         "mr",
39091         "222"
39092       ],
39093       [
39094         "Mauritius (Moris)",
39095         "mu",
39096         "230"
39097       ],
39098       [
39099         "Mayotte",
39100         "yt",
39101         "262",
39102         1
39103       ],
39104       [
39105         "Mexico (México)",
39106         "mx",
39107         "52"
39108       ],
39109       [
39110         "Micronesia",
39111         "fm",
39112         "691"
39113       ],
39114       [
39115         "Moldova (Republica Moldova)",
39116         "md",
39117         "373"
39118       ],
39119       [
39120         "Monaco",
39121         "mc",
39122         "377"
39123       ],
39124       [
39125         "Mongolia (Монгол)",
39126         "mn",
39127         "976"
39128       ],
39129       [
39130         "Montenegro (Crna Gora)",
39131         "me",
39132         "382"
39133       ],
39134       [
39135         "Montserrat",
39136         "ms",
39137         "1664"
39138       ],
39139       [
39140         "Morocco (‫المغرب‬‎)",
39141         "ma",
39142         "212",
39143         0
39144       ],
39145       [
39146         "Mozambique (Moçambique)",
39147         "mz",
39148         "258"
39149       ],
39150       [
39151         "Myanmar (Burma) (မြန်မာ)",
39152         "mm",
39153         "95"
39154       ],
39155       [
39156         "Namibia (Namibië)",
39157         "na",
39158         "264"
39159       ],
39160       [
39161         "Nauru",
39162         "nr",
39163         "674"
39164       ],
39165       [
39166         "Nepal (नेपाल)",
39167         "np",
39168         "977"
39169       ],
39170       [
39171         "Netherlands (Nederland)",
39172         "nl",
39173         "31"
39174       ],
39175       [
39176         "New Caledonia (Nouvelle-Calédonie)",
39177         "nc",
39178         "687"
39179       ],
39180       [
39181         "New Zealand",
39182         "nz",
39183         "64"
39184       ],
39185       [
39186         "Nicaragua",
39187         "ni",
39188         "505"
39189       ],
39190       [
39191         "Niger (Nijar)",
39192         "ne",
39193         "227"
39194       ],
39195       [
39196         "Nigeria",
39197         "ng",
39198         "234"
39199       ],
39200       [
39201         "Niue",
39202         "nu",
39203         "683"
39204       ],
39205       [
39206         "Norfolk Island",
39207         "nf",
39208         "672"
39209       ],
39210       [
39211         "North Korea (조선 민주주의 인민 공화국)",
39212         "kp",
39213         "850"
39214       ],
39215       [
39216         "Northern Mariana Islands",
39217         "mp",
39218         "1670"
39219       ],
39220       [
39221         "Norway (Norge)",
39222         "no",
39223         "47",
39224         0
39225       ],
39226       [
39227         "Oman (‫عُمان‬‎)",
39228         "om",
39229         "968"
39230       ],
39231       [
39232         "Pakistan (‫پاکستان‬‎)",
39233         "pk",
39234         "92"
39235       ],
39236       [
39237         "Palau",
39238         "pw",
39239         "680"
39240       ],
39241       [
39242         "Palestine (‫فلسطين‬‎)",
39243         "ps",
39244         "970"
39245       ],
39246       [
39247         "Panama (Panamá)",
39248         "pa",
39249         "507"
39250       ],
39251       [
39252         "Papua New Guinea",
39253         "pg",
39254         "675"
39255       ],
39256       [
39257         "Paraguay",
39258         "py",
39259         "595"
39260       ],
39261       [
39262         "Peru (Perú)",
39263         "pe",
39264         "51"
39265       ],
39266       [
39267         "Philippines",
39268         "ph",
39269         "63"
39270       ],
39271       [
39272         "Poland (Polska)",
39273         "pl",
39274         "48"
39275       ],
39276       [
39277         "Portugal",
39278         "pt",
39279         "351"
39280       ],
39281       [
39282         "Puerto Rico",
39283         "pr",
39284         "1",
39285         3,
39286         ["787", "939"]
39287       ],
39288       [
39289         "Qatar (‫قطر‬‎)",
39290         "qa",
39291         "974"
39292       ],
39293       [
39294         "Réunion (La Réunion)",
39295         "re",
39296         "262",
39297         0
39298       ],
39299       [
39300         "Romania (România)",
39301         "ro",
39302         "40"
39303       ],
39304       [
39305         "Russia (Россия)",
39306         "ru",
39307         "7",
39308         0
39309       ],
39310       [
39311         "Rwanda",
39312         "rw",
39313         "250"
39314       ],
39315       [
39316         "Saint Barthélemy",
39317         "bl",
39318         "590",
39319         1
39320       ],
39321       [
39322         "Saint Helena",
39323         "sh",
39324         "290"
39325       ],
39326       [
39327         "Saint Kitts and Nevis",
39328         "kn",
39329         "1869"
39330       ],
39331       [
39332         "Saint Lucia",
39333         "lc",
39334         "1758"
39335       ],
39336       [
39337         "Saint Martin (Saint-Martin (partie française))",
39338         "mf",
39339         "590",
39340         2
39341       ],
39342       [
39343         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39344         "pm",
39345         "508"
39346       ],
39347       [
39348         "Saint Vincent and the Grenadines",
39349         "vc",
39350         "1784"
39351       ],
39352       [
39353         "Samoa",
39354         "ws",
39355         "685"
39356       ],
39357       [
39358         "San Marino",
39359         "sm",
39360         "378"
39361       ],
39362       [
39363         "São Tomé and Príncipe (São Tomé e Príncipe)",
39364         "st",
39365         "239"
39366       ],
39367       [
39368         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39369         "sa",
39370         "966"
39371       ],
39372       [
39373         "Senegal (Sénégal)",
39374         "sn",
39375         "221"
39376       ],
39377       [
39378         "Serbia (Србија)",
39379         "rs",
39380         "381"
39381       ],
39382       [
39383         "Seychelles",
39384         "sc",
39385         "248"
39386       ],
39387       [
39388         "Sierra Leone",
39389         "sl",
39390         "232"
39391       ],
39392       [
39393         "Singapore",
39394         "sg",
39395         "65"
39396       ],
39397       [
39398         "Sint Maarten",
39399         "sx",
39400         "1721"
39401       ],
39402       [
39403         "Slovakia (Slovensko)",
39404         "sk",
39405         "421"
39406       ],
39407       [
39408         "Slovenia (Slovenija)",
39409         "si",
39410         "386"
39411       ],
39412       [
39413         "Solomon Islands",
39414         "sb",
39415         "677"
39416       ],
39417       [
39418         "Somalia (Soomaaliya)",
39419         "so",
39420         "252"
39421       ],
39422       [
39423         "South Africa",
39424         "za",
39425         "27"
39426       ],
39427       [
39428         "South Korea (대한민국)",
39429         "kr",
39430         "82"
39431       ],
39432       [
39433         "South Sudan (‫جنوب السودان‬‎)",
39434         "ss",
39435         "211"
39436       ],
39437       [
39438         "Spain (España)",
39439         "es",
39440         "34"
39441       ],
39442       [
39443         "Sri Lanka (ශ්‍රී ලංකාව)",
39444         "lk",
39445         "94"
39446       ],
39447       [
39448         "Sudan (‫السودان‬‎)",
39449         "sd",
39450         "249"
39451       ],
39452       [
39453         "Suriname",
39454         "sr",
39455         "597"
39456       ],
39457       [
39458         "Svalbard and Jan Mayen",
39459         "sj",
39460         "47",
39461         1
39462       ],
39463       [
39464         "Swaziland",
39465         "sz",
39466         "268"
39467       ],
39468       [
39469         "Sweden (Sverige)",
39470         "se",
39471         "46"
39472       ],
39473       [
39474         "Switzerland (Schweiz)",
39475         "ch",
39476         "41"
39477       ],
39478       [
39479         "Syria (‫سوريا‬‎)",
39480         "sy",
39481         "963"
39482       ],
39483       [
39484         "Taiwan (台灣)",
39485         "tw",
39486         "886"
39487       ],
39488       [
39489         "Tajikistan",
39490         "tj",
39491         "992"
39492       ],
39493       [
39494         "Tanzania",
39495         "tz",
39496         "255"
39497       ],
39498       [
39499         "Thailand (ไทย)",
39500         "th",
39501         "66"
39502       ],
39503       [
39504         "Timor-Leste",
39505         "tl",
39506         "670"
39507       ],
39508       [
39509         "Togo",
39510         "tg",
39511         "228"
39512       ],
39513       [
39514         "Tokelau",
39515         "tk",
39516         "690"
39517       ],
39518       [
39519         "Tonga",
39520         "to",
39521         "676"
39522       ],
39523       [
39524         "Trinidad and Tobago",
39525         "tt",
39526         "1868"
39527       ],
39528       [
39529         "Tunisia (‫تونس‬‎)",
39530         "tn",
39531         "216"
39532       ],
39533       [
39534         "Turkey (Türkiye)",
39535         "tr",
39536         "90"
39537       ],
39538       [
39539         "Turkmenistan",
39540         "tm",
39541         "993"
39542       ],
39543       [
39544         "Turks and Caicos Islands",
39545         "tc",
39546         "1649"
39547       ],
39548       [
39549         "Tuvalu",
39550         "tv",
39551         "688"
39552       ],
39553       [
39554         "U.S. Virgin Islands",
39555         "vi",
39556         "1340"
39557       ],
39558       [
39559         "Uganda",
39560         "ug",
39561         "256"
39562       ],
39563       [
39564         "Ukraine (Україна)",
39565         "ua",
39566         "380"
39567       ],
39568       [
39569         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39570         "ae",
39571         "971"
39572       ],
39573       [
39574         "United Kingdom",
39575         "gb",
39576         "44",
39577         0
39578       ],
39579       [
39580         "United States",
39581         "us",
39582         "1",
39583         0
39584       ],
39585       [
39586         "Uruguay",
39587         "uy",
39588         "598"
39589       ],
39590       [
39591         "Uzbekistan (Oʻzbekiston)",
39592         "uz",
39593         "998"
39594       ],
39595       [
39596         "Vanuatu",
39597         "vu",
39598         "678"
39599       ],
39600       [
39601         "Vatican City (Città del Vaticano)",
39602         "va",
39603         "39",
39604         1
39605       ],
39606       [
39607         "Venezuela",
39608         "ve",
39609         "58"
39610       ],
39611       [
39612         "Vietnam (Việt Nam)",
39613         "vn",
39614         "84"
39615       ],
39616       [
39617         "Wallis and Futuna (Wallis-et-Futuna)",
39618         "wf",
39619         "681"
39620       ],
39621       [
39622         "Western Sahara (‫الصحراء الغربية‬‎)",
39623         "eh",
39624         "212",
39625         1
39626       ],
39627       [
39628         "Yemen (‫اليمن‬‎)",
39629         "ye",
39630         "967"
39631       ],
39632       [
39633         "Zambia",
39634         "zm",
39635         "260"
39636       ],
39637       [
39638         "Zimbabwe",
39639         "zw",
39640         "263"
39641       ],
39642       [
39643         "Åland Islands",
39644         "ax",
39645         "358",
39646         1
39647       ]
39648   ];
39649   
39650   return d;
39651 }/**
39652 *    This script refer to:
39653 *    Title: International Telephone Input
39654 *    Author: Jack O'Connor
39655 *    Code version:  v12.1.12
39656 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39657 **/
39658
39659 /**
39660  * @class Roo.bootstrap.PhoneInput
39661  * @extends Roo.bootstrap.TriggerField
39662  * An input with International dial-code selection
39663  
39664  * @cfg {String} defaultDialCode default '+852'
39665  * @cfg {Array} preferedCountries default []
39666   
39667  * @constructor
39668  * Create a new PhoneInput.
39669  * @param {Object} config Configuration options
39670  */
39671
39672 Roo.bootstrap.PhoneInput = function(config) {
39673     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39674 };
39675
39676 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39677         
39678         listWidth: undefined,
39679         
39680         selectedClass: 'active',
39681         
39682         invalidClass : "has-warning",
39683         
39684         validClass: 'has-success',
39685         
39686         allowed: '0123456789',
39687         
39688         /**
39689          * @cfg {String} defaultDialCode The default dial code when initializing the input
39690          */
39691         defaultDialCode: '+852',
39692         
39693         /**
39694          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39695          */
39696         preferedCountries: false,
39697         
39698         getAutoCreate : function()
39699         {
39700             var data = Roo.bootstrap.PhoneInputData();
39701             var align = this.labelAlign || this.parentLabelAlign();
39702             var id = Roo.id();
39703             
39704             this.allCountries = [];
39705             this.dialCodeMapping = [];
39706             
39707             for (var i = 0; i < data.length; i++) {
39708               var c = data[i];
39709               this.allCountries[i] = {
39710                 name: c[0],
39711                 iso2: c[1],
39712                 dialCode: c[2],
39713                 priority: c[3] || 0,
39714                 areaCodes: c[4] || null
39715               };
39716               this.dialCodeMapping[c[2]] = {
39717                   name: c[0],
39718                   iso2: c[1],
39719                   priority: c[3] || 0,
39720                   areaCodes: c[4] || null
39721               };
39722             }
39723             
39724             var cfg = {
39725                 cls: 'form-group',
39726                 cn: []
39727             };
39728             
39729             var input =  {
39730                 tag: 'input',
39731                 id : id,
39732                 cls : 'form-control tel-input',
39733                 autocomplete: 'new-password'
39734             };
39735             
39736             var hiddenInput = {
39737                 tag: 'input',
39738                 type: 'hidden',
39739                 cls: 'hidden-tel-input'
39740             };
39741             
39742             if (this.name) {
39743                 hiddenInput.name = this.name;
39744             }
39745             
39746             if (this.disabled) {
39747                 input.disabled = true;
39748             }
39749             
39750             var flag_container = {
39751                 tag: 'div',
39752                 cls: 'flag-box',
39753                 cn: [
39754                     {
39755                         tag: 'div',
39756                         cls: 'flag'
39757                     },
39758                     {
39759                         tag: 'div',
39760                         cls: 'caret'
39761                     }
39762                 ]
39763             };
39764             
39765             var box = {
39766                 tag: 'div',
39767                 cls: this.hasFeedback ? 'has-feedback' : '',
39768                 cn: [
39769                     hiddenInput,
39770                     input,
39771                     {
39772                         tag: 'input',
39773                         cls: 'dial-code-holder',
39774                         disabled: true
39775                     }
39776                 ]
39777             };
39778             
39779             var container = {
39780                 cls: 'roo-select2-container input-group',
39781                 cn: [
39782                     flag_container,
39783                     box
39784                 ]
39785             };
39786             
39787             if (this.fieldLabel.length) {
39788                 var indicator = {
39789                     tag: 'i',
39790                     tooltip: 'This field is required'
39791                 };
39792                 
39793                 var label = {
39794                     tag: 'label',
39795                     'for':  id,
39796                     cls: 'control-label',
39797                     cn: []
39798                 };
39799                 
39800                 var label_text = {
39801                     tag: 'span',
39802                     html: this.fieldLabel
39803                 };
39804                 
39805                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39806                 label.cn = [
39807                     indicator,
39808                     label_text
39809                 ];
39810                 
39811                 if(this.indicatorpos == 'right') {
39812                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39813                     label.cn = [
39814                         label_text,
39815                         indicator
39816                     ];
39817                 }
39818                 
39819                 if(align == 'left') {
39820                     container = {
39821                         tag: 'div',
39822                         cn: [
39823                             container
39824                         ]
39825                     };
39826                     
39827                     if(this.labelWidth > 12){
39828                         label.style = "width: " + this.labelWidth + 'px';
39829                     }
39830                     if(this.labelWidth < 13 && this.labelmd == 0){
39831                         this.labelmd = this.labelWidth;
39832                     }
39833                     if(this.labellg > 0){
39834                         label.cls += ' col-lg-' + this.labellg;
39835                         input.cls += ' col-lg-' + (12 - this.labellg);
39836                     }
39837                     if(this.labelmd > 0){
39838                         label.cls += ' col-md-' + this.labelmd;
39839                         container.cls += ' col-md-' + (12 - this.labelmd);
39840                     }
39841                     if(this.labelsm > 0){
39842                         label.cls += ' col-sm-' + this.labelsm;
39843                         container.cls += ' col-sm-' + (12 - this.labelsm);
39844                     }
39845                     if(this.labelxs > 0){
39846                         label.cls += ' col-xs-' + this.labelxs;
39847                         container.cls += ' col-xs-' + (12 - this.labelxs);
39848                     }
39849                 }
39850             }
39851             
39852             cfg.cn = [
39853                 label,
39854                 container
39855             ];
39856             
39857             var settings = this;
39858             
39859             ['xs','sm','md','lg'].map(function(size){
39860                 if (settings[size]) {
39861                     cfg.cls += ' col-' + size + '-' + settings[size];
39862                 }
39863             });
39864             
39865             this.store = new Roo.data.Store({
39866                 proxy : new Roo.data.MemoryProxy({}),
39867                 reader : new Roo.data.JsonReader({
39868                     fields : [
39869                         {
39870                             'name' : 'name',
39871                             'type' : 'string'
39872                         },
39873                         {
39874                             'name' : 'iso2',
39875                             'type' : 'string'
39876                         },
39877                         {
39878                             'name' : 'dialCode',
39879                             'type' : 'string'
39880                         },
39881                         {
39882                             'name' : 'priority',
39883                             'type' : 'string'
39884                         },
39885                         {
39886                             'name' : 'areaCodes',
39887                             'type' : 'string'
39888                         }
39889                     ]
39890                 })
39891             });
39892             
39893             if(!this.preferedCountries) {
39894                 this.preferedCountries = [
39895                     'hk',
39896                     'gb',
39897                     'us'
39898                 ];
39899             }
39900             
39901             var p = this.preferedCountries.reverse();
39902             
39903             if(p) {
39904                 for (var i = 0; i < p.length; i++) {
39905                     for (var j = 0; j < this.allCountries.length; j++) {
39906                         if(this.allCountries[j].iso2 == p[i]) {
39907                             var t = this.allCountries[j];
39908                             this.allCountries.splice(j,1);
39909                             this.allCountries.unshift(t);
39910                         }
39911                     } 
39912                 }
39913             }
39914             
39915             this.store.proxy.data = {
39916                 success: true,
39917                 data: this.allCountries
39918             };
39919             
39920             return cfg;
39921         },
39922         
39923         initEvents : function()
39924         {
39925             this.createList();
39926             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39927             
39928             this.indicator = this.indicatorEl();
39929             this.flag = this.flagEl();
39930             this.dialCodeHolder = this.dialCodeHolderEl();
39931             
39932             this.trigger = this.el.select('div.flag-box',true).first();
39933             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39934             
39935             var _this = this;
39936             
39937             (function(){
39938                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39939                 _this.list.setWidth(lw);
39940             }).defer(100);
39941             
39942             this.list.on('mouseover', this.onViewOver, this);
39943             this.list.on('mousemove', this.onViewMove, this);
39944             this.inputEl().on("keyup", this.onKeyUp, this);
39945             
39946             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39947
39948             this.view = new Roo.View(this.list, this.tpl, {
39949                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39950             });
39951             
39952             this.view.on('click', this.onViewClick, this);
39953             this.setValue(this.defaultDialCode);
39954         },
39955         
39956         onTriggerClick : function(e)
39957         {
39958             Roo.log('trigger click');
39959             if(this.disabled){
39960                 return;
39961             }
39962             
39963             if(this.isExpanded()){
39964                 this.collapse();
39965                 this.hasFocus = false;
39966             }else {
39967                 this.store.load({});
39968                 this.hasFocus = true;
39969                 this.expand();
39970             }
39971         },
39972         
39973         isExpanded : function()
39974         {
39975             return this.list.isVisible();
39976         },
39977         
39978         collapse : function()
39979         {
39980             if(!this.isExpanded()){
39981                 return;
39982             }
39983             this.list.hide();
39984             Roo.get(document).un('mousedown', this.collapseIf, this);
39985             Roo.get(document).un('mousewheel', this.collapseIf, this);
39986             this.fireEvent('collapse', this);
39987             this.validate();
39988         },
39989         
39990         expand : function()
39991         {
39992             Roo.log('expand');
39993
39994             if(this.isExpanded() || !this.hasFocus){
39995                 return;
39996             }
39997             
39998             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39999             this.list.setWidth(lw);
40000             
40001             this.list.show();
40002             this.restrictHeight();
40003             
40004             Roo.get(document).on('mousedown', this.collapseIf, this);
40005             Roo.get(document).on('mousewheel', this.collapseIf, this);
40006             
40007             this.fireEvent('expand', this);
40008         },
40009         
40010         restrictHeight : function()
40011         {
40012             this.list.alignTo(this.inputEl(), this.listAlign);
40013             this.list.alignTo(this.inputEl(), this.listAlign);
40014         },
40015         
40016         onViewOver : function(e, t)
40017         {
40018             if(this.inKeyMode){
40019                 return;
40020             }
40021             var item = this.view.findItemFromChild(t);
40022             
40023             if(item){
40024                 var index = this.view.indexOf(item);
40025                 this.select(index, false);
40026             }
40027         },
40028
40029         // private
40030         onViewClick : function(view, doFocus, el, e)
40031         {
40032             var index = this.view.getSelectedIndexes()[0];
40033             
40034             var r = this.store.getAt(index);
40035             
40036             if(r){
40037                 this.onSelect(r, index);
40038             }
40039             if(doFocus !== false && !this.blockFocus){
40040                 this.inputEl().focus();
40041             }
40042         },
40043         
40044         onViewMove : function(e, t)
40045         {
40046             this.inKeyMode = false;
40047         },
40048         
40049         select : function(index, scrollIntoView)
40050         {
40051             this.selectedIndex = index;
40052             this.view.select(index);
40053             if(scrollIntoView !== false){
40054                 var el = this.view.getNode(index);
40055                 if(el){
40056                     this.list.scrollChildIntoView(el, false);
40057                 }
40058             }
40059         },
40060         
40061         createList : function()
40062         {
40063             this.list = Roo.get(document.body).createChild({
40064                 tag: 'ul',
40065                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40066                 style: 'display:none'
40067             });
40068             
40069             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40070         },
40071         
40072         collapseIf : function(e)
40073         {
40074             var in_combo  = e.within(this.el);
40075             var in_list =  e.within(this.list);
40076             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40077             
40078             if (in_combo || in_list || is_list) {
40079                 return;
40080             }
40081             this.collapse();
40082         },
40083         
40084         onSelect : function(record, index)
40085         {
40086             if(this.fireEvent('beforeselect', this, record, index) !== false){
40087                 
40088                 this.setFlagClass(record.data.iso2);
40089                 this.setDialCode(record.data.dialCode);
40090                 this.hasFocus = false;
40091                 this.collapse();
40092                 this.fireEvent('select', this, record, index);
40093             }
40094         },
40095         
40096         flagEl : function()
40097         {
40098             var flag = this.el.select('div.flag',true).first();
40099             if(!flag){
40100                 return false;
40101             }
40102             return flag;
40103         },
40104         
40105         dialCodeHolderEl : function()
40106         {
40107             var d = this.el.select('input.dial-code-holder',true).first();
40108             if(!d){
40109                 return false;
40110             }
40111             return d;
40112         },
40113         
40114         setDialCode : function(v)
40115         {
40116             this.dialCodeHolder.dom.value = '+'+v;
40117         },
40118         
40119         setFlagClass : function(n)
40120         {
40121             this.flag.dom.className = 'flag '+n;
40122         },
40123         
40124         getValue : function()
40125         {
40126             var v = this.inputEl().getValue();
40127             if(this.dialCodeHolder) {
40128                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40129             }
40130             return v;
40131         },
40132         
40133         setValue : function(v)
40134         {
40135             var d = this.getDialCode(v);
40136             
40137             //invalid dial code
40138             if(v.length == 0 || !d || d.length == 0) {
40139                 if(this.rendered){
40140                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40141                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40142                 }
40143                 return;
40144             }
40145             
40146             //valid dial code
40147             this.setFlagClass(this.dialCodeMapping[d].iso2);
40148             this.setDialCode(d);
40149             this.inputEl().dom.value = v.replace('+'+d,'');
40150             this.hiddenEl().dom.value = this.getValue();
40151             
40152             this.validate();
40153         },
40154         
40155         getDialCode : function(v)
40156         {
40157             v = v ||  '';
40158             
40159             if (v.length == 0) {
40160                 return this.dialCodeHolder.dom.value;
40161             }
40162             
40163             var dialCode = "";
40164             if (v.charAt(0) != "+") {
40165                 return false;
40166             }
40167             var numericChars = "";
40168             for (var i = 1; i < v.length; i++) {
40169               var c = v.charAt(i);
40170               if (!isNaN(c)) {
40171                 numericChars += c;
40172                 if (this.dialCodeMapping[numericChars]) {
40173                   dialCode = v.substr(1, i);
40174                 }
40175                 if (numericChars.length == 4) {
40176                   break;
40177                 }
40178               }
40179             }
40180             return dialCode;
40181         },
40182         
40183         reset : function()
40184         {
40185             this.setValue(this.defaultDialCode);
40186             this.validate();
40187         },
40188         
40189         hiddenEl : function()
40190         {
40191             return this.el.select('input.hidden-tel-input',true).first();
40192         },
40193         
40194         onKeyUp : function(e){
40195             
40196             var k = e.getKey();
40197             var c = e.getCharCode();
40198             
40199             if(
40200                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40201                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40202             ){
40203                 e.stopEvent();
40204             }
40205             
40206             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40207             //     return;
40208             // }
40209             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40210                 e.stopEvent();
40211             }
40212             
40213             this.setValue(this.getValue());
40214         }
40215         
40216 });
40217 /**
40218  * @class Roo.bootstrap.MoneyField
40219  * @extends Roo.bootstrap.ComboBox
40220  * Bootstrap MoneyField class
40221  * 
40222  * @constructor
40223  * Create a new MoneyField.
40224  * @param {Object} config Configuration options
40225  */
40226
40227 Roo.bootstrap.MoneyField = function(config) {
40228     
40229     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40230     
40231 };
40232
40233 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40234     
40235     /**
40236      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40237      */
40238     allowDecimals : true,
40239     /**
40240      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40241      */
40242     decimalSeparator : ".",
40243     /**
40244      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40245      */
40246     decimalPrecision : 0,
40247     /**
40248      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40249      */
40250     allowNegative : true,
40251     /**
40252      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40253      */
40254     allowZero: true,
40255     /**
40256      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40257      */
40258     minValue : Number.NEGATIVE_INFINITY,
40259     /**
40260      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40261      */
40262     maxValue : Number.MAX_VALUE,
40263     /**
40264      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40265      */
40266     minText : "The minimum value for this field is {0}",
40267     /**
40268      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40269      */
40270     maxText : "The maximum value for this field is {0}",
40271     /**
40272      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40273      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40274      */
40275     nanText : "{0} is not a valid number",
40276     /**
40277      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40278      */
40279     castInt : true,
40280     /**
40281      * @cfg {String} defaults currency of the MoneyField
40282      * value should be in lkey
40283      */
40284     defaultCurrency : false,
40285     /**
40286      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40287      */
40288     thousandsDelimiter : false,
40289     
40290     
40291     inputlg : 9,
40292     inputmd : 9,
40293     inputsm : 9,
40294     inputxs : 6,
40295     
40296     store : false,
40297     
40298     getAutoCreate : function()
40299     {
40300         var align = this.labelAlign || this.parentLabelAlign();
40301         
40302         var id = Roo.id();
40303
40304         var cfg = {
40305             cls: 'form-group',
40306             cn: []
40307         };
40308
40309         var input =  {
40310             tag: 'input',
40311             id : id,
40312             cls : 'form-control roo-money-amount-input',
40313             autocomplete: 'new-password'
40314         };
40315         
40316         var hiddenInput = {
40317             tag: 'input',
40318             type: 'hidden',
40319             id: Roo.id(),
40320             cls: 'hidden-number-input'
40321         };
40322         
40323         if (this.name) {
40324             hiddenInput.name = this.name;
40325         }
40326
40327         if (this.disabled) {
40328             input.disabled = true;
40329         }
40330
40331         var clg = 12 - this.inputlg;
40332         var cmd = 12 - this.inputmd;
40333         var csm = 12 - this.inputsm;
40334         var cxs = 12 - this.inputxs;
40335         
40336         var container = {
40337             tag : 'div',
40338             cls : 'row roo-money-field',
40339             cn : [
40340                 {
40341                     tag : 'div',
40342                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40343                     cn : [
40344                         {
40345                             tag : 'div',
40346                             cls: 'roo-select2-container input-group',
40347                             cn: [
40348                                 {
40349                                     tag : 'input',
40350                                     cls : 'form-control roo-money-currency-input',
40351                                     autocomplete: 'new-password',
40352                                     readOnly : 1,
40353                                     name : this.currencyName
40354                                 },
40355                                 {
40356                                     tag :'span',
40357                                     cls : 'input-group-addon',
40358                                     cn : [
40359                                         {
40360                                             tag: 'span',
40361                                             cls: 'caret'
40362                                         }
40363                                     ]
40364                                 }
40365                             ]
40366                         }
40367                     ]
40368                 },
40369                 {
40370                     tag : 'div',
40371                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40372                     cn : [
40373                         {
40374                             tag: 'div',
40375                             cls: this.hasFeedback ? 'has-feedback' : '',
40376                             cn: [
40377                                 input
40378                             ]
40379                         }
40380                     ]
40381                 }
40382             ]
40383             
40384         };
40385         
40386         if (this.fieldLabel.length) {
40387             var indicator = {
40388                 tag: 'i',
40389                 tooltip: 'This field is required'
40390             };
40391
40392             var label = {
40393                 tag: 'label',
40394                 'for':  id,
40395                 cls: 'control-label',
40396                 cn: []
40397             };
40398
40399             var label_text = {
40400                 tag: 'span',
40401                 html: this.fieldLabel
40402             };
40403
40404             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40405             label.cn = [
40406                 indicator,
40407                 label_text
40408             ];
40409
40410             if(this.indicatorpos == 'right') {
40411                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40412                 label.cn = [
40413                     label_text,
40414                     indicator
40415                 ];
40416             }
40417
40418             if(align == 'left') {
40419                 container = {
40420                     tag: 'div',
40421                     cn: [
40422                         container
40423                     ]
40424                 };
40425
40426                 if(this.labelWidth > 12){
40427                     label.style = "width: " + this.labelWidth + 'px';
40428                 }
40429                 if(this.labelWidth < 13 && this.labelmd == 0){
40430                     this.labelmd = this.labelWidth;
40431                 }
40432                 if(this.labellg > 0){
40433                     label.cls += ' col-lg-' + this.labellg;
40434                     input.cls += ' col-lg-' + (12 - this.labellg);
40435                 }
40436                 if(this.labelmd > 0){
40437                     label.cls += ' col-md-' + this.labelmd;
40438                     container.cls += ' col-md-' + (12 - this.labelmd);
40439                 }
40440                 if(this.labelsm > 0){
40441                     label.cls += ' col-sm-' + this.labelsm;
40442                     container.cls += ' col-sm-' + (12 - this.labelsm);
40443                 }
40444                 if(this.labelxs > 0){
40445                     label.cls += ' col-xs-' + this.labelxs;
40446                     container.cls += ' col-xs-' + (12 - this.labelxs);
40447                 }
40448             }
40449         }
40450
40451         cfg.cn = [
40452             label,
40453             container,
40454             hiddenInput
40455         ];
40456         
40457         var settings = this;
40458
40459         ['xs','sm','md','lg'].map(function(size){
40460             if (settings[size]) {
40461                 cfg.cls += ' col-' + size + '-' + settings[size];
40462             }
40463         });
40464         
40465         return cfg;
40466     },
40467     
40468     initEvents : function()
40469     {
40470         this.indicator = this.indicatorEl();
40471         
40472         this.initCurrencyEvent();
40473         
40474         this.initNumberEvent();
40475     },
40476     
40477     initCurrencyEvent : function()
40478     {
40479         if (!this.store) {
40480             throw "can not find store for combo";
40481         }
40482         
40483         this.store = Roo.factory(this.store, Roo.data);
40484         this.store.parent = this;
40485         
40486         this.createList();
40487         
40488         this.triggerEl = this.el.select('.input-group-addon', true).first();
40489         
40490         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40491         
40492         var _this = this;
40493         
40494         (function(){
40495             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40496             _this.list.setWidth(lw);
40497         }).defer(100);
40498         
40499         this.list.on('mouseover', this.onViewOver, this);
40500         this.list.on('mousemove', this.onViewMove, this);
40501         this.list.on('scroll', this.onViewScroll, this);
40502         
40503         if(!this.tpl){
40504             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40505         }
40506         
40507         this.view = new Roo.View(this.list, this.tpl, {
40508             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40509         });
40510         
40511         this.view.on('click', this.onViewClick, this);
40512         
40513         this.store.on('beforeload', this.onBeforeLoad, this);
40514         this.store.on('load', this.onLoad, this);
40515         this.store.on('loadexception', this.onLoadException, this);
40516         
40517         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40518             "up" : function(e){
40519                 this.inKeyMode = true;
40520                 this.selectPrev();
40521             },
40522
40523             "down" : function(e){
40524                 if(!this.isExpanded()){
40525                     this.onTriggerClick();
40526                 }else{
40527                     this.inKeyMode = true;
40528                     this.selectNext();
40529                 }
40530             },
40531
40532             "enter" : function(e){
40533                 this.collapse();
40534                 
40535                 if(this.fireEvent("specialkey", this, e)){
40536                     this.onViewClick(false);
40537                 }
40538                 
40539                 return true;
40540             },
40541
40542             "esc" : function(e){
40543                 this.collapse();
40544             },
40545
40546             "tab" : function(e){
40547                 this.collapse();
40548                 
40549                 if(this.fireEvent("specialkey", this, e)){
40550                     this.onViewClick(false);
40551                 }
40552                 
40553                 return true;
40554             },
40555
40556             scope : this,
40557
40558             doRelay : function(foo, bar, hname){
40559                 if(hname == 'down' || this.scope.isExpanded()){
40560                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40561                 }
40562                 return true;
40563             },
40564
40565             forceKeyDown: true
40566         });
40567         
40568         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40569         
40570     },
40571     
40572     initNumberEvent : function(e)
40573     {
40574         this.inputEl().on("keydown" , this.fireKey,  this);
40575         this.inputEl().on("focus", this.onFocus,  this);
40576         this.inputEl().on("blur", this.onBlur,  this);
40577         
40578         this.inputEl().relayEvent('keyup', this);
40579         
40580         if(this.indicator){
40581             this.indicator.addClass('invisible');
40582         }
40583  
40584         this.originalValue = this.getValue();
40585         
40586         if(this.validationEvent == 'keyup'){
40587             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40588             this.inputEl().on('keyup', this.filterValidation, this);
40589         }
40590         else if(this.validationEvent !== false){
40591             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40592         }
40593         
40594         if(this.selectOnFocus){
40595             this.on("focus", this.preFocus, this);
40596             
40597         }
40598         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40599             this.inputEl().on("keypress", this.filterKeys, this);
40600         } else {
40601             this.inputEl().relayEvent('keypress', this);
40602         }
40603         
40604         var allowed = "0123456789";
40605         
40606         if(this.allowDecimals){
40607             allowed += this.decimalSeparator;
40608         }
40609         
40610         if(this.allowNegative){
40611             allowed += "-";
40612         }
40613         
40614         if(this.thousandsDelimiter) {
40615             allowed += ",";
40616         }
40617         
40618         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40619         
40620         var keyPress = function(e){
40621             
40622             var k = e.getKey();
40623             
40624             var c = e.getCharCode();
40625             
40626             if(
40627                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40628                     allowed.indexOf(String.fromCharCode(c)) === -1
40629             ){
40630                 e.stopEvent();
40631                 return;
40632             }
40633             
40634             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40635                 return;
40636             }
40637             
40638             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40639                 e.stopEvent();
40640             }
40641         };
40642         
40643         this.inputEl().on("keypress", keyPress, this);
40644         
40645     },
40646     
40647     onTriggerClick : function(e)
40648     {   
40649         if(this.disabled){
40650             return;
40651         }
40652         
40653         this.page = 0;
40654         this.loadNext = false;
40655         
40656         if(this.isExpanded()){
40657             this.collapse();
40658             return;
40659         }
40660         
40661         this.hasFocus = true;
40662         
40663         if(this.triggerAction == 'all') {
40664             this.doQuery(this.allQuery, true);
40665             return;
40666         }
40667         
40668         this.doQuery(this.getRawValue());
40669     },
40670     
40671     getCurrency : function()
40672     {   
40673         var v = this.currencyEl().getValue();
40674         
40675         return v;
40676     },
40677     
40678     restrictHeight : function()
40679     {
40680         this.list.alignTo(this.currencyEl(), this.listAlign);
40681         this.list.alignTo(this.currencyEl(), this.listAlign);
40682     },
40683     
40684     onViewClick : function(view, doFocus, el, e)
40685     {
40686         var index = this.view.getSelectedIndexes()[0];
40687         
40688         var r = this.store.getAt(index);
40689         
40690         if(r){
40691             this.onSelect(r, index);
40692         }
40693     },
40694     
40695     onSelect : function(record, index){
40696         
40697         if(this.fireEvent('beforeselect', this, record, index) !== false){
40698         
40699             this.setFromCurrencyData(index > -1 ? record.data : false);
40700             
40701             this.collapse();
40702             
40703             this.fireEvent('select', this, record, index);
40704         }
40705     },
40706     
40707     setFromCurrencyData : function(o)
40708     {
40709         var currency = '';
40710         
40711         this.lastCurrency = o;
40712         
40713         if (this.currencyField) {
40714             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40715         } else {
40716             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40717         }
40718         
40719         this.lastSelectionText = currency;
40720         
40721         //setting default currency
40722         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40723             this.setCurrency(this.defaultCurrency);
40724             return;
40725         }
40726         
40727         this.setCurrency(currency);
40728     },
40729     
40730     setFromData : function(o)
40731     {
40732         var c = {};
40733         
40734         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40735         
40736         this.setFromCurrencyData(c);
40737         
40738         var value = '';
40739         
40740         if (this.name) {
40741             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40742         } else {
40743             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40744         }
40745         
40746         this.setValue(value);
40747         
40748     },
40749     
40750     setCurrency : function(v)
40751     {   
40752         this.currencyValue = v;
40753         
40754         if(this.rendered){
40755             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40756             this.validate();
40757         }
40758     },
40759     
40760     setValue : function(v)
40761     {
40762         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40763         
40764         this.value = v;
40765         
40766         if(this.rendered){
40767             
40768             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40769             
40770             this.inputEl().dom.value = (v == '') ? '' :
40771                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40772             
40773             if(!this.allowZero && v === '0') {
40774                 this.hiddenEl().dom.value = '';
40775                 this.inputEl().dom.value = '';
40776             }
40777             
40778             this.validate();
40779         }
40780     },
40781     
40782     getRawValue : function()
40783     {
40784         var v = this.inputEl().getValue();
40785         
40786         return v;
40787     },
40788     
40789     getValue : function()
40790     {
40791         return this.fixPrecision(this.parseValue(this.getRawValue()));
40792     },
40793     
40794     parseValue : function(value)
40795     {
40796         if(this.thousandsDelimiter) {
40797             value += "";
40798             r = new RegExp(",", "g");
40799             value = value.replace(r, "");
40800         }
40801         
40802         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40803         return isNaN(value) ? '' : value;
40804         
40805     },
40806     
40807     fixPrecision : function(value)
40808     {
40809         if(this.thousandsDelimiter) {
40810             value += "";
40811             r = new RegExp(",", "g");
40812             value = value.replace(r, "");
40813         }
40814         
40815         var nan = isNaN(value);
40816         
40817         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40818             return nan ? '' : value;
40819         }
40820         return parseFloat(value).toFixed(this.decimalPrecision);
40821     },
40822     
40823     decimalPrecisionFcn : function(v)
40824     {
40825         return Math.floor(v);
40826     },
40827     
40828     validateValue : function(value)
40829     {
40830         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40831             return false;
40832         }
40833         
40834         var num = this.parseValue(value);
40835         
40836         if(isNaN(num)){
40837             this.markInvalid(String.format(this.nanText, value));
40838             return false;
40839         }
40840         
40841         if(num < this.minValue){
40842             this.markInvalid(String.format(this.minText, this.minValue));
40843             return false;
40844         }
40845         
40846         if(num > this.maxValue){
40847             this.markInvalid(String.format(this.maxText, this.maxValue));
40848             return false;
40849         }
40850         
40851         return true;
40852     },
40853     
40854     validate : function()
40855     {
40856         if(this.disabled || this.allowBlank){
40857             this.markValid();
40858             return true;
40859         }
40860         
40861         var currency = this.getCurrency();
40862         
40863         if(this.validateValue(this.getRawValue()) && currency.length){
40864             this.markValid();
40865             return true;
40866         }
40867         
40868         this.markInvalid();
40869         return false;
40870     },
40871     
40872     getName: function()
40873     {
40874         return this.name;
40875     },
40876     
40877     beforeBlur : function()
40878     {
40879         if(!this.castInt){
40880             return;
40881         }
40882         
40883         var v = this.parseValue(this.getRawValue());
40884         
40885         if(v || v == 0){
40886             this.setValue(v);
40887         }
40888     },
40889     
40890     onBlur : function()
40891     {
40892         this.beforeBlur();
40893         
40894         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40895             //this.el.removeClass(this.focusClass);
40896         }
40897         
40898         this.hasFocus = false;
40899         
40900         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40901             this.validate();
40902         }
40903         
40904         var v = this.getValue();
40905         
40906         if(String(v) !== String(this.startValue)){
40907             this.fireEvent('change', this, v, this.startValue);
40908         }
40909         
40910         this.fireEvent("blur", this);
40911     },
40912     
40913     inputEl : function()
40914     {
40915         return this.el.select('.roo-money-amount-input', true).first();
40916     },
40917     
40918     currencyEl : function()
40919     {
40920         return this.el.select('.roo-money-currency-input', true).first();
40921     },
40922     
40923     hiddenEl : function()
40924     {
40925         return this.el.select('input.hidden-number-input',true).first();
40926     }
40927     
40928 });