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         Roo.log('EL----------------');
2229         Roo.log(el);
2230         Roo.log(pos);
2231         Roo.log(this.defaultAlign);
2232         
2233         
2234         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2235     },
2236      /**
2237      * Displays this menu at a specific xy position
2238      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2239      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2240      */
2241     showAt : function(xy, parentMenu, /* private: */_e){
2242         this.parentMenu = parentMenu;
2243         if(!this.el){
2244             this.render();
2245         }
2246         if(_e !== false){
2247             this.fireEvent("beforeshow", this);
2248             //xy = this.el.adjustForConstraints(xy);
2249         }
2250         
2251         //this.el.show();
2252         this.hideMenuItems();
2253         this.hidden = false;
2254         this.triggerEl.addClass('open');
2255         
2256         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2257             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258         }
2259         
2260         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2261             this.el.setXY(xy);
2262         }
2263         
2264         this.focus();
2265         this.fireEvent("show", this);
2266     },
2267     
2268     focus : function(){
2269         return;
2270         if(!this.hidden){
2271             this.doFocus.defer(50, this);
2272         }
2273     },
2274
2275     doFocus : function(){
2276         if(!this.hidden){
2277             this.focusEl.focus();
2278         }
2279     },
2280
2281     /**
2282      * Hides this menu and optionally all parent menus
2283      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2284      */
2285     hide : function(deep)
2286     {
2287         
2288         this.hideMenuItems();
2289         if(this.el && this.isVisible()){
2290             this.fireEvent("beforehide", this);
2291             if(this.activeItem){
2292                 this.activeItem.deactivate();
2293                 this.activeItem = null;
2294             }
2295             this.triggerEl.removeClass('open');;
2296             this.hidden = true;
2297             this.fireEvent("hide", this);
2298         }
2299         if(deep === true && this.parentMenu){
2300             this.parentMenu.hide(true);
2301         }
2302     },
2303     
2304     onTriggerClick : function(e)
2305     {
2306         Roo.log('trigger click');
2307         
2308         var target = e.getTarget();
2309         
2310         Roo.log(target.nodeName.toLowerCase());
2311         
2312         if(target.nodeName.toLowerCase() === 'i'){
2313             e.preventDefault();
2314         }
2315         
2316     },
2317     
2318     onTriggerPress  : function(e)
2319     {
2320         Roo.log('trigger press');
2321         //Roo.log(e.getTarget());
2322        // Roo.log(this.triggerEl.dom);
2323        
2324         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2325         var pel = Roo.get(e.getTarget());
2326         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2327             Roo.log('is treeview or dropdown?');
2328             return;
2329         }
2330         
2331         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2332             return;
2333         }
2334         
2335         if (this.isVisible()) {
2336             Roo.log('hide');
2337             this.hide();
2338         } else {
2339             Roo.log('show');
2340             this.show(this.triggerEl, false, false);
2341         }
2342         
2343         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2344             e.stopEvent();
2345         }
2346         
2347     },
2348        
2349     
2350     hideMenuItems : function()
2351     {
2352         Roo.log("hide Menu Items");
2353         if (!this.el) { 
2354             return;
2355         }
2356         //$(backdrop).remove()
2357         this.el.select('.open',true).each(function(aa) {
2358             
2359             aa.removeClass('open');
2360           //var parent = getParent($(this))
2361           //var relatedTarget = { relatedTarget: this }
2362           
2363            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2364           //if (e.isDefaultPrevented()) return
2365            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2366         });
2367     },
2368     addxtypeChild : function (tree, cntr) {
2369         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2370           
2371         this.menuitems.add(comp);
2372         return comp;
2373
2374     },
2375     getEl : function()
2376     {
2377         Roo.log(this.el);
2378         return this.el;
2379     },
2380     
2381     clear : function()
2382     {
2383         this.getEl().dom.innerHTML = '';
2384         this.menuitems.clear();
2385     }
2386 });
2387
2388  
2389  /*
2390  * - LGPL
2391  *
2392  * menu item
2393  * 
2394  */
2395
2396
2397 /**
2398  * @class Roo.bootstrap.MenuItem
2399  * @extends Roo.bootstrap.Component
2400  * Bootstrap MenuItem class
2401  * @cfg {String} html the menu label
2402  * @cfg {String} href the link
2403  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2404  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2405  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2406  * @cfg {String} fa favicon to show on left of menu item.
2407  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2408  * 
2409  * 
2410  * @constructor
2411  * Create a new MenuItem
2412  * @param {Object} config The config object
2413  */
2414
2415
2416 Roo.bootstrap.MenuItem = function(config){
2417     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2418     this.addEvents({
2419         // raw events
2420         /**
2421          * @event click
2422          * The raw click event for the entire grid.
2423          * @param {Roo.bootstrap.MenuItem} this
2424          * @param {Roo.EventObject} e
2425          */
2426         "click" : true
2427     });
2428 };
2429
2430 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2431     
2432     href : false,
2433     html : false,
2434     preventDefault: false,
2435     isContainer : false,
2436     active : false,
2437     fa: false,
2438     
2439     getAutoCreate : function(){
2440         
2441         if(this.isContainer){
2442             return {
2443                 tag: 'li',
2444                 cls: 'dropdown-menu-item'
2445             };
2446         }
2447         var ctag = {
2448             tag: 'span',
2449             html: 'Link'
2450         };
2451         
2452         var anc = {
2453             tag : 'a',
2454             href : '#',
2455             cn : [  ]
2456         };
2457         
2458         if (this.fa !== false) {
2459             anc.cn.push({
2460                 tag : 'i',
2461                 cls : 'fa fa-' + this.fa
2462             });
2463         }
2464         
2465         anc.cn.push(ctag);
2466         
2467         
2468         var cfg= {
2469             tag: 'li',
2470             cls: 'dropdown-menu-item',
2471             cn: [ anc ]
2472         };
2473         if (this.parent().type == 'treeview') {
2474             cfg.cls = 'treeview-menu';
2475         }
2476         if (this.active) {
2477             cfg.cls += ' active';
2478         }
2479         
2480         
2481         
2482         anc.href = this.href || cfg.cn[0].href ;
2483         ctag.html = this.html || cfg.cn[0].html ;
2484         return cfg;
2485     },
2486     
2487     initEvents: function()
2488     {
2489         if (this.parent().type == 'treeview') {
2490             this.el.select('a').on('click', this.onClick, this);
2491         }
2492         
2493         if (this.menu) {
2494             this.menu.parentType = this.xtype;
2495             this.menu.triggerEl = this.el;
2496             this.menu = this.addxtype(Roo.apply({}, this.menu));
2497         }
2498         
2499     },
2500     onClick : function(e)
2501     {
2502         Roo.log('item on click ');
2503         
2504         if(this.preventDefault){
2505             e.preventDefault();
2506         }
2507         //this.parent().hideMenuItems();
2508         
2509         this.fireEvent('click', this, e);
2510     },
2511     getEl : function()
2512     {
2513         return this.el;
2514     } 
2515 });
2516
2517  
2518
2519  /*
2520  * - LGPL
2521  *
2522  * menu separator
2523  * 
2524  */
2525
2526
2527 /**
2528  * @class Roo.bootstrap.MenuSeparator
2529  * @extends Roo.bootstrap.Component
2530  * Bootstrap MenuSeparator class
2531  * 
2532  * @constructor
2533  * Create a new MenuItem
2534  * @param {Object} config The config object
2535  */
2536
2537
2538 Roo.bootstrap.MenuSeparator = function(config){
2539     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2540 };
2541
2542 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2543     
2544     getAutoCreate : function(){
2545         var cfg = {
2546             cls: 'divider',
2547             tag : 'li'
2548         };
2549         
2550         return cfg;
2551     }
2552    
2553 });
2554
2555  
2556
2557  
2558 /*
2559 * Licence: LGPL
2560 */
2561
2562 /**
2563  * @class Roo.bootstrap.Modal
2564  * @extends Roo.bootstrap.Component
2565  * Bootstrap Modal class
2566  * @cfg {String} title Title of dialog
2567  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2568  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2569  * @cfg {Boolean} specificTitle default false
2570  * @cfg {Array} buttons Array of buttons or standard button set..
2571  * @cfg {String} buttonPosition (left|right|center) default right
2572  * @cfg {Boolean} animate default true
2573  * @cfg {Boolean} allow_close default true
2574  * @cfg {Boolean} fitwindow default false
2575  * @cfg {String} size (sm|lg) default empty
2576  *
2577  *
2578  * @constructor
2579  * Create a new Modal Dialog
2580  * @param {Object} config The config object
2581  */
2582
2583 Roo.bootstrap.Modal = function(config){
2584     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2585     this.addEvents({
2586         // raw events
2587         /**
2588          * @event btnclick
2589          * The raw btnclick event for the button
2590          * @param {Roo.EventObject} e
2591          */
2592         "btnclick" : true,
2593         /**
2594          * @event resize
2595          * Fire when dialog resize
2596          * @param {Roo.bootstrap.Modal} this
2597          * @param {Roo.EventObject} e
2598          */
2599         "resize" : true
2600     });
2601     this.buttons = this.buttons || [];
2602
2603     if (this.tmpl) {
2604         this.tmpl = Roo.factory(this.tmpl);
2605     }
2606
2607 };
2608
2609 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2610
2611     title : 'test dialog',
2612
2613     buttons : false,
2614
2615     // set on load...
2616
2617     html: false,
2618
2619     tmp: false,
2620
2621     specificTitle: false,
2622
2623     buttonPosition: 'right',
2624
2625     allow_close : true,
2626
2627     animate : true,
2628
2629     fitwindow: false,
2630
2631
2632      // private
2633     dialogEl: false,
2634     bodyEl:  false,
2635     footerEl:  false,
2636     titleEl:  false,
2637     closeEl:  false,
2638
2639     size: '',
2640
2641
2642     onRender : function(ct, position)
2643     {
2644         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2645
2646         if(!this.el){
2647             var cfg = Roo.apply({},  this.getAutoCreate());
2648             cfg.id = Roo.id();
2649             //if(!cfg.name){
2650             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2651             //}
2652             //if (!cfg.name.length) {
2653             //    delete cfg.name;
2654            // }
2655             if (this.cls) {
2656                 cfg.cls += ' ' + this.cls;
2657             }
2658             if (this.style) {
2659                 cfg.style = this.style;
2660             }
2661             this.el = Roo.get(document.body).createChild(cfg, position);
2662         }
2663         //var type = this.el.dom.type;
2664
2665
2666         if(this.tabIndex !== undefined){
2667             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2668         }
2669
2670         this.dialogEl = this.el.select('.modal-dialog',true).first();
2671         this.bodyEl = this.el.select('.modal-body',true).first();
2672         this.closeEl = this.el.select('.modal-header .close', true).first();
2673         this.headerEl = this.el.select('.modal-header',true).first();
2674         this.titleEl = this.el.select('.modal-title',true).first();
2675         this.footerEl = this.el.select('.modal-footer',true).first();
2676
2677         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2678         
2679         //this.el.addClass("x-dlg-modal");
2680
2681         if (this.buttons.length) {
2682             Roo.each(this.buttons, function(bb) {
2683                 var b = Roo.apply({}, bb);
2684                 b.xns = b.xns || Roo.bootstrap;
2685                 b.xtype = b.xtype || 'Button';
2686                 if (typeof(b.listeners) == 'undefined') {
2687                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2688                 }
2689
2690                 var btn = Roo.factory(b);
2691
2692                 btn.render(this.el.select('.modal-footer div').first());
2693
2694             },this);
2695         }
2696         // render the children.
2697         var nitems = [];
2698
2699         if(typeof(this.items) != 'undefined'){
2700             var items = this.items;
2701             delete this.items;
2702
2703             for(var i =0;i < items.length;i++) {
2704                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2705             }
2706         }
2707
2708         this.items = nitems;
2709
2710         // where are these used - they used to be body/close/footer
2711
2712
2713         this.initEvents();
2714         //this.el.addClass([this.fieldClass, this.cls]);
2715
2716     },
2717
2718     getAutoCreate : function(){
2719
2720
2721         var bdy = {
2722                 cls : 'modal-body',
2723                 html : this.html || ''
2724         };
2725
2726         var title = {
2727             tag: 'h4',
2728             cls : 'modal-title',
2729             html : this.title
2730         };
2731
2732         if(this.specificTitle){
2733             title = this.title;
2734
2735         };
2736
2737         var header = [];
2738         if (this.allow_close) {
2739             header.push({
2740                 tag: 'button',
2741                 cls : 'close',
2742                 html : '&times'
2743             });
2744         }
2745
2746         header.push(title);
2747
2748         var size = '';
2749
2750         if(this.size.length){
2751             size = 'modal-' + this.size;
2752         }
2753
2754         var modal = {
2755             cls: "modal",
2756              cn : [
2757                 {
2758                     cls: "modal-dialog " + size,
2759                     cn : [
2760                         {
2761                             cls : "modal-content",
2762                             cn : [
2763                                 {
2764                                     cls : 'modal-header',
2765                                     cn : header
2766                                 },
2767                                 bdy,
2768                                 {
2769                                     cls : 'modal-footer',
2770                                     cn : [
2771                                         {
2772                                             tag: 'div',
2773                                             cls: 'btn-' + this.buttonPosition
2774                                         }
2775                                     ]
2776
2777                                 }
2778
2779
2780                             ]
2781
2782                         }
2783                     ]
2784
2785                 }
2786             ]
2787         };
2788
2789         if(this.animate){
2790             modal.cls += ' fade';
2791         }
2792
2793         return modal;
2794
2795     },
2796     getChildContainer : function() {
2797
2798          return this.bodyEl;
2799
2800     },
2801     getButtonContainer : function() {
2802          return this.el.select('.modal-footer div',true).first();
2803
2804     },
2805     initEvents : function()
2806     {
2807         if (this.allow_close) {
2808             this.closeEl.on('click', this.hide, this);
2809         }
2810         Roo.EventManager.onWindowResize(this.resize, this, true);
2811
2812
2813     },
2814
2815     resize : function()
2816     {
2817         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2818         if (this.fitwindow) {
2819             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2820             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2821             this.setSize(w,h);
2822         }
2823     },
2824
2825     setSize : function(w,h)
2826     {
2827         if (!w && !h) {
2828             return;
2829         }
2830         this.resizeTo(w,h);
2831     },
2832
2833     show : function() {
2834
2835         if (!this.rendered) {
2836             this.render();
2837         }
2838
2839         //this.el.setStyle('display', 'block');
2840         this.el.removeClass('hideing');        
2841         this.el.addClass('show');
2842  
2843         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2844             var _this = this;
2845             (function(){
2846                 this.el.addClass('in');
2847             }).defer(50, this);
2848         }else{
2849             this.el.addClass('in');
2850
2851         }
2852
2853         // not sure how we can show data in here..
2854         //if (this.tmpl) {
2855         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2856         //}
2857
2858         Roo.get(document.body).addClass("x-body-masked");
2859         
2860         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2861         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2862         this.maskEl.addClass('show');
2863         
2864         this.resize();
2865         
2866         this.fireEvent('show', this);
2867
2868         // set zindex here - otherwise it appears to be ignored...
2869         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2870
2871         (function () {
2872             this.items.forEach( function(e) {
2873                 e.layout ? e.layout() : false;
2874
2875             });
2876         }).defer(100,this);
2877
2878     },
2879     hide : function()
2880     {
2881         if(this.fireEvent("beforehide", this) !== false){
2882             this.maskEl.removeClass('show');
2883             Roo.get(document.body).removeClass("x-body-masked");
2884             this.el.removeClass('in');
2885             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2886
2887             if(this.animate){ // why
2888                 this.el.addClass('hideing');
2889                 (function(){
2890                     if (!this.el.hasClass('hideing')) {
2891                         return; // it's been shown again...
2892                     }
2893                     this.el.removeClass('show');
2894                     this.el.removeClass('hideing');
2895                 }).defer(150,this);
2896                 
2897             }else{
2898                  this.el.removeClass('show');
2899             }
2900             this.fireEvent('hide', this);
2901         }
2902     },
2903     isVisible : function()
2904     {
2905         
2906         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2907         
2908     },
2909
2910     addButton : function(str, cb)
2911     {
2912
2913
2914         var b = Roo.apply({}, { html : str } );
2915         b.xns = b.xns || Roo.bootstrap;
2916         b.xtype = b.xtype || 'Button';
2917         if (typeof(b.listeners) == 'undefined') {
2918             b.listeners = { click : cb.createDelegate(this)  };
2919         }
2920
2921         var btn = Roo.factory(b);
2922
2923         btn.render(this.el.select('.modal-footer div').first());
2924
2925         return btn;
2926
2927     },
2928
2929     setDefaultButton : function(btn)
2930     {
2931         //this.el.select('.modal-footer').()
2932     },
2933     diff : false,
2934
2935     resizeTo: function(w,h)
2936     {
2937         // skip.. ?? why??
2938
2939         this.dialogEl.setWidth(w);
2940         if (this.diff === false) {
2941             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2942         }
2943
2944         this.bodyEl.setHeight(h-this.diff);
2945
2946         this.fireEvent('resize', this);
2947
2948     },
2949     setContentSize  : function(w, h)
2950     {
2951
2952     },
2953     onButtonClick: function(btn,e)
2954     {
2955         //Roo.log([a,b,c]);
2956         this.fireEvent('btnclick', btn.name, e);
2957     },
2958      /**
2959      * Set the title of the Dialog
2960      * @param {String} str new Title
2961      */
2962     setTitle: function(str) {
2963         this.titleEl.dom.innerHTML = str;
2964     },
2965     /**
2966      * Set the body of the Dialog
2967      * @param {String} str new Title
2968      */
2969     setBody: function(str) {
2970         this.bodyEl.dom.innerHTML = str;
2971     },
2972     /**
2973      * Set the body of the Dialog using the template
2974      * @param {Obj} data - apply this data to the template and replace the body contents.
2975      */
2976     applyBody: function(obj)
2977     {
2978         if (!this.tmpl) {
2979             Roo.log("Error - using apply Body without a template");
2980             //code
2981         }
2982         this.tmpl.overwrite(this.bodyEl, obj);
2983     }
2984
2985 });
2986
2987
2988 Roo.apply(Roo.bootstrap.Modal,  {
2989     /**
2990          * Button config that displays a single OK button
2991          * @type Object
2992          */
2993         OK :  [{
2994             name : 'ok',
2995             weight : 'primary',
2996             html : 'OK'
2997         }],
2998         /**
2999          * Button config that displays Yes and No buttons
3000          * @type Object
3001          */
3002         YESNO : [
3003             {
3004                 name  : 'no',
3005                 html : 'No'
3006             },
3007             {
3008                 name  :'yes',
3009                 weight : 'primary',
3010                 html : 'Yes'
3011             }
3012         ],
3013
3014         /**
3015          * Button config that displays OK and Cancel buttons
3016          * @type Object
3017          */
3018         OKCANCEL : [
3019             {
3020                name : 'cancel',
3021                 html : 'Cancel'
3022             },
3023             {
3024                 name : 'ok',
3025                 weight : 'primary',
3026                 html : 'OK'
3027             }
3028         ],
3029         /**
3030          * Button config that displays Yes, No and Cancel buttons
3031          * @type Object
3032          */
3033         YESNOCANCEL : [
3034             {
3035                 name : 'yes',
3036                 weight : 'primary',
3037                 html : 'Yes'
3038             },
3039             {
3040                 name : 'no',
3041                 html : 'No'
3042             },
3043             {
3044                 name : 'cancel',
3045                 html : 'Cancel'
3046             }
3047         ],
3048         
3049         zIndex : 10001
3050 });
3051 /*
3052  * - LGPL
3053  *
3054  * messagebox - can be used as a replace
3055  * 
3056  */
3057 /**
3058  * @class Roo.MessageBox
3059  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3060  * Example usage:
3061  *<pre><code>
3062 // Basic alert:
3063 Roo.Msg.alert('Status', 'Changes saved successfully.');
3064
3065 // Prompt for user data:
3066 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3067     if (btn == 'ok'){
3068         // process text value...
3069     }
3070 });
3071
3072 // Show a dialog using config options:
3073 Roo.Msg.show({
3074    title:'Save Changes?',
3075    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3076    buttons: Roo.Msg.YESNOCANCEL,
3077    fn: processResult,
3078    animEl: 'elId'
3079 });
3080 </code></pre>
3081  * @singleton
3082  */
3083 Roo.bootstrap.MessageBox = function(){
3084     var dlg, opt, mask, waitTimer;
3085     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3086     var buttons, activeTextEl, bwidth;
3087
3088     
3089     // private
3090     var handleButton = function(button){
3091         dlg.hide();
3092         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3093     };
3094
3095     // private
3096     var handleHide = function(){
3097         if(opt && opt.cls){
3098             dlg.el.removeClass(opt.cls);
3099         }
3100         //if(waitTimer){
3101         //    Roo.TaskMgr.stop(waitTimer);
3102         //    waitTimer = null;
3103         //}
3104     };
3105
3106     // private
3107     var updateButtons = function(b){
3108         var width = 0;
3109         if(!b){
3110             buttons["ok"].hide();
3111             buttons["cancel"].hide();
3112             buttons["yes"].hide();
3113             buttons["no"].hide();
3114             //dlg.footer.dom.style.display = 'none';
3115             return width;
3116         }
3117         dlg.footerEl.dom.style.display = '';
3118         for(var k in buttons){
3119             if(typeof buttons[k] != "function"){
3120                 if(b[k]){
3121                     buttons[k].show();
3122                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3123                     width += buttons[k].el.getWidth()+15;
3124                 }else{
3125                     buttons[k].hide();
3126                 }
3127             }
3128         }
3129         return width;
3130     };
3131
3132     // private
3133     var handleEsc = function(d, k, e){
3134         if(opt && opt.closable !== false){
3135             dlg.hide();
3136         }
3137         if(e){
3138             e.stopEvent();
3139         }
3140     };
3141
3142     return {
3143         /**
3144          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3145          * @return {Roo.BasicDialog} The BasicDialog element
3146          */
3147         getDialog : function(){
3148            if(!dlg){
3149                 dlg = new Roo.bootstrap.Modal( {
3150                     //draggable: true,
3151                     //resizable:false,
3152                     //constraintoviewport:false,
3153                     //fixedcenter:true,
3154                     //collapsible : false,
3155                     //shim:true,
3156                     //modal: true,
3157                 //    width: 'auto',
3158                   //  height:100,
3159                     //buttonAlign:"center",
3160                     closeClick : function(){
3161                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3162                             handleButton("no");
3163                         }else{
3164                             handleButton("cancel");
3165                         }
3166                     }
3167                 });
3168                 dlg.render();
3169                 dlg.on("hide", handleHide);
3170                 mask = dlg.mask;
3171                 //dlg.addKeyListener(27, handleEsc);
3172                 buttons = {};
3173                 this.buttons = buttons;
3174                 var bt = this.buttonText;
3175                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3176                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3177                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3178                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3179                 //Roo.log(buttons);
3180                 bodyEl = dlg.bodyEl.createChild({
3181
3182                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3183                         '<textarea class="roo-mb-textarea"></textarea>' +
3184                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3185                 });
3186                 msgEl = bodyEl.dom.firstChild;
3187                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3188                 textboxEl.enableDisplayMode();
3189                 textboxEl.addKeyListener([10,13], function(){
3190                     if(dlg.isVisible() && opt && opt.buttons){
3191                         if(opt.buttons.ok){
3192                             handleButton("ok");
3193                         }else if(opt.buttons.yes){
3194                             handleButton("yes");
3195                         }
3196                     }
3197                 });
3198                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3199                 textareaEl.enableDisplayMode();
3200                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3201                 progressEl.enableDisplayMode();
3202                 
3203                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3204                 var pf = progressEl.dom.firstChild;
3205                 if (pf) {
3206                     pp = Roo.get(pf.firstChild);
3207                     pp.setHeight(pf.offsetHeight);
3208                 }
3209                 
3210             }
3211             return dlg;
3212         },
3213
3214         /**
3215          * Updates the message box body text
3216          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3217          * the XHTML-compliant non-breaking space character '&amp;#160;')
3218          * @return {Roo.MessageBox} This message box
3219          */
3220         updateText : function(text)
3221         {
3222             if(!dlg.isVisible() && !opt.width){
3223                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3224                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3225             }
3226             msgEl.innerHTML = text || '&#160;';
3227       
3228             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3229             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3230             var w = Math.max(
3231                     Math.min(opt.width || cw , this.maxWidth), 
3232                     Math.max(opt.minWidth || this.minWidth, bwidth)
3233             );
3234             if(opt.prompt){
3235                 activeTextEl.setWidth(w);
3236             }
3237             if(dlg.isVisible()){
3238                 dlg.fixedcenter = false;
3239             }
3240             // to big, make it scroll. = But as usual stupid IE does not support
3241             // !important..
3242             
3243             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3244                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3245                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3246             } else {
3247                 bodyEl.dom.style.height = '';
3248                 bodyEl.dom.style.overflowY = '';
3249             }
3250             if (cw > w) {
3251                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3252             } else {
3253                 bodyEl.dom.style.overflowX = '';
3254             }
3255             
3256             dlg.setContentSize(w, bodyEl.getHeight());
3257             if(dlg.isVisible()){
3258                 dlg.fixedcenter = true;
3259             }
3260             return this;
3261         },
3262
3263         /**
3264          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3265          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3266          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3267          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3268          * @return {Roo.MessageBox} This message box
3269          */
3270         updateProgress : function(value, text){
3271             if(text){
3272                 this.updateText(text);
3273             }
3274             
3275             if (pp) { // weird bug on my firefox - for some reason this is not defined
3276                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3277                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3278             }
3279             return this;
3280         },        
3281
3282         /**
3283          * Returns true if the message box is currently displayed
3284          * @return {Boolean} True if the message box is visible, else false
3285          */
3286         isVisible : function(){
3287             return dlg && dlg.isVisible();  
3288         },
3289
3290         /**
3291          * Hides the message box if it is displayed
3292          */
3293         hide : function(){
3294             if(this.isVisible()){
3295                 dlg.hide();
3296             }  
3297         },
3298
3299         /**
3300          * Displays a new message box, or reinitializes an existing message box, based on the config options
3301          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3302          * The following config object properties are supported:
3303          * <pre>
3304 Property    Type             Description
3305 ----------  ---------------  ------------------------------------------------------------------------------------
3306 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3307                                    closes (defaults to undefined)
3308 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3309                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3310 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3311                                    progress and wait dialogs will ignore this property and always hide the
3312                                    close button as they can only be closed programmatically.
3313 cls               String           A custom CSS class to apply to the message box element
3314 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3315                                    displayed (defaults to 75)
3316 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3317                                    function will be btn (the name of the button that was clicked, if applicable,
3318                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3319                                    Progress and wait dialogs will ignore this option since they do not respond to
3320                                    user actions and can only be closed programmatically, so any required function
3321                                    should be called by the same code after it closes the dialog.
3322 icon              String           A CSS class that provides a background image to be used as an icon for
3323                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3324 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3325 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3326 modal             Boolean          False to allow user interaction with the page while the message box is
3327                                    displayed (defaults to true)
3328 msg               String           A string that will replace the existing message box body text (defaults
3329                                    to the XHTML-compliant non-breaking space character '&#160;')
3330 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3331 progress          Boolean          True to display a progress bar (defaults to false)
3332 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3333 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3334 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3335 title             String           The title text
3336 value             String           The string value to set into the active textbox element if displayed
3337 wait              Boolean          True to display a progress bar (defaults to false)
3338 width             Number           The width of the dialog in pixels
3339 </pre>
3340          *
3341          * Example usage:
3342          * <pre><code>
3343 Roo.Msg.show({
3344    title: 'Address',
3345    msg: 'Please enter your address:',
3346    width: 300,
3347    buttons: Roo.MessageBox.OKCANCEL,
3348    multiline: true,
3349    fn: saveAddress,
3350    animEl: 'addAddressBtn'
3351 });
3352 </code></pre>
3353          * @param {Object} config Configuration options
3354          * @return {Roo.MessageBox} This message box
3355          */
3356         show : function(options)
3357         {
3358             
3359             // this causes nightmares if you show one dialog after another
3360             // especially on callbacks..
3361              
3362             if(this.isVisible()){
3363                 
3364                 this.hide();
3365                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3366                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3367                 Roo.log("New Dialog Message:" +  options.msg )
3368                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3369                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3370                 
3371             }
3372             var d = this.getDialog();
3373             opt = options;
3374             d.setTitle(opt.title || "&#160;");
3375             d.closeEl.setDisplayed(opt.closable !== false);
3376             activeTextEl = textboxEl;
3377             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3378             if(opt.prompt){
3379                 if(opt.multiline){
3380                     textboxEl.hide();
3381                     textareaEl.show();
3382                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3383                         opt.multiline : this.defaultTextHeight);
3384                     activeTextEl = textareaEl;
3385                 }else{
3386                     textboxEl.show();
3387                     textareaEl.hide();
3388                 }
3389             }else{
3390                 textboxEl.hide();
3391                 textareaEl.hide();
3392             }
3393             progressEl.setDisplayed(opt.progress === true);
3394             this.updateProgress(0);
3395             activeTextEl.dom.value = opt.value || "";
3396             if(opt.prompt){
3397                 dlg.setDefaultButton(activeTextEl);
3398             }else{
3399                 var bs = opt.buttons;
3400                 var db = null;
3401                 if(bs && bs.ok){
3402                     db = buttons["ok"];
3403                 }else if(bs && bs.yes){
3404                     db = buttons["yes"];
3405                 }
3406                 dlg.setDefaultButton(db);
3407             }
3408             bwidth = updateButtons(opt.buttons);
3409             this.updateText(opt.msg);
3410             if(opt.cls){
3411                 d.el.addClass(opt.cls);
3412             }
3413             d.proxyDrag = opt.proxyDrag === true;
3414             d.modal = opt.modal !== false;
3415             d.mask = opt.modal !== false ? mask : false;
3416             if(!d.isVisible()){
3417                 // force it to the end of the z-index stack so it gets a cursor in FF
3418                 document.body.appendChild(dlg.el.dom);
3419                 d.animateTarget = null;
3420                 d.show(options.animEl);
3421             }
3422             return this;
3423         },
3424
3425         /**
3426          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3427          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3428          * and closing the message box when the process is complete.
3429          * @param {String} title The title bar text
3430          * @param {String} msg The message box body text
3431          * @return {Roo.MessageBox} This message box
3432          */
3433         progress : function(title, msg){
3434             this.show({
3435                 title : title,
3436                 msg : msg,
3437                 buttons: false,
3438                 progress:true,
3439                 closable:false,
3440                 minWidth: this.minProgressWidth,
3441                 modal : true
3442             });
3443             return this;
3444         },
3445
3446         /**
3447          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3448          * If a callback function is passed it will be called after the user clicks the button, and the
3449          * id of the button that was clicked will be passed as the only parameter to the callback
3450          * (could also be the top-right close button).
3451          * @param {String} title The title bar text
3452          * @param {String} msg The message box body text
3453          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3454          * @param {Object} scope (optional) The scope of the callback function
3455          * @return {Roo.MessageBox} This message box
3456          */
3457         alert : function(title, msg, fn, scope)
3458         {
3459             this.show({
3460                 title : title,
3461                 msg : msg,
3462                 buttons: this.OK,
3463                 fn: fn,
3464                 closable : false,
3465                 scope : scope,
3466                 modal : true
3467             });
3468             return this;
3469         },
3470
3471         /**
3472          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3473          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3474          * You are responsible for closing the message box when the process is complete.
3475          * @param {String} msg The message box body text
3476          * @param {String} title (optional) The title bar text
3477          * @return {Roo.MessageBox} This message box
3478          */
3479         wait : function(msg, title){
3480             this.show({
3481                 title : title,
3482                 msg : msg,
3483                 buttons: false,
3484                 closable:false,
3485                 progress:true,
3486                 modal:true,
3487                 width:300,
3488                 wait:true
3489             });
3490             waitTimer = Roo.TaskMgr.start({
3491                 run: function(i){
3492                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3493                 },
3494                 interval: 1000
3495             });
3496             return this;
3497         },
3498
3499         /**
3500          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3501          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3502          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3503          * @param {String} title The title bar text
3504          * @param {String} msg The message box body text
3505          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3506          * @param {Object} scope (optional) The scope of the callback function
3507          * @return {Roo.MessageBox} This message box
3508          */
3509         confirm : function(title, msg, fn, scope){
3510             this.show({
3511                 title : title,
3512                 msg : msg,
3513                 buttons: this.YESNO,
3514                 fn: fn,
3515                 scope : scope,
3516                 modal : true
3517             });
3518             return this;
3519         },
3520
3521         /**
3522          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3523          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3524          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3525          * (could also be the top-right close button) and the text that was entered will be passed as the two
3526          * parameters to the callback.
3527          * @param {String} title The title bar text
3528          * @param {String} msg The message box body text
3529          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3530          * @param {Object} scope (optional) The scope of the callback function
3531          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3532          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3533          * @return {Roo.MessageBox} This message box
3534          */
3535         prompt : function(title, msg, fn, scope, multiline){
3536             this.show({
3537                 title : title,
3538                 msg : msg,
3539                 buttons: this.OKCANCEL,
3540                 fn: fn,
3541                 minWidth:250,
3542                 scope : scope,
3543                 prompt:true,
3544                 multiline: multiline,
3545                 modal : true
3546             });
3547             return this;
3548         },
3549
3550         /**
3551          * Button config that displays a single OK button
3552          * @type Object
3553          */
3554         OK : {ok:true},
3555         /**
3556          * Button config that displays Yes and No buttons
3557          * @type Object
3558          */
3559         YESNO : {yes:true, no:true},
3560         /**
3561          * Button config that displays OK and Cancel buttons
3562          * @type Object
3563          */
3564         OKCANCEL : {ok:true, cancel:true},
3565         /**
3566          * Button config that displays Yes, No and Cancel buttons
3567          * @type Object
3568          */
3569         YESNOCANCEL : {yes:true, no:true, cancel:true},
3570
3571         /**
3572          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3573          * @type Number
3574          */
3575         defaultTextHeight : 75,
3576         /**
3577          * The maximum width in pixels of the message box (defaults to 600)
3578          * @type Number
3579          */
3580         maxWidth : 600,
3581         /**
3582          * The minimum width in pixels of the message box (defaults to 100)
3583          * @type Number
3584          */
3585         minWidth : 100,
3586         /**
3587          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3588          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3589          * @type Number
3590          */
3591         minProgressWidth : 250,
3592         /**
3593          * An object containing the default button text strings that can be overriden for localized language support.
3594          * Supported properties are: ok, cancel, yes and no.
3595          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3596          * @type Object
3597          */
3598         buttonText : {
3599             ok : "OK",
3600             cancel : "Cancel",
3601             yes : "Yes",
3602             no : "No"
3603         }
3604     };
3605 }();
3606
3607 /**
3608  * Shorthand for {@link Roo.MessageBox}
3609  */
3610 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3611 Roo.Msg = Roo.Msg || Roo.MessageBox;
3612 /*
3613  * - LGPL
3614  *
3615  * navbar
3616  * 
3617  */
3618
3619 /**
3620  * @class Roo.bootstrap.Navbar
3621  * @extends Roo.bootstrap.Component
3622  * Bootstrap Navbar class
3623
3624  * @constructor
3625  * Create a new Navbar
3626  * @param {Object} config The config object
3627  */
3628
3629
3630 Roo.bootstrap.Navbar = function(config){
3631     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3632     this.addEvents({
3633         // raw events
3634         /**
3635          * @event beforetoggle
3636          * Fire before toggle the menu
3637          * @param {Roo.EventObject} e
3638          */
3639         "beforetoggle" : true
3640     });
3641 };
3642
3643 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3644     
3645     
3646    
3647     // private
3648     navItems : false,
3649     loadMask : false,
3650     
3651     
3652     getAutoCreate : function(){
3653         
3654         
3655         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3656         
3657     },
3658     
3659     initEvents :function ()
3660     {
3661         //Roo.log(this.el.select('.navbar-toggle',true));
3662         this.el.select('.navbar-toggle',true).on('click', function() {
3663             if(this.fireEvent('beforetoggle', this) !== false){
3664                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3665             }
3666             
3667         }, this);
3668         
3669         var mark = {
3670             tag: "div",
3671             cls:"x-dlg-mask"
3672         };
3673         
3674         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3675         
3676         var size = this.el.getSize();
3677         this.maskEl.setSize(size.width, size.height);
3678         this.maskEl.enableDisplayMode("block");
3679         this.maskEl.hide();
3680         
3681         if(this.loadMask){
3682             this.maskEl.show();
3683         }
3684     },
3685     
3686     
3687     getChildContainer : function()
3688     {
3689         if (this.el.select('.collapse').getCount()) {
3690             return this.el.select('.collapse',true).first();
3691         }
3692         
3693         return this.el;
3694     },
3695     
3696     mask : function()
3697     {
3698         this.maskEl.show();
3699     },
3700     
3701     unmask : function()
3702     {
3703         this.maskEl.hide();
3704     } 
3705     
3706     
3707     
3708     
3709 });
3710
3711
3712
3713  
3714
3715  /*
3716  * - LGPL
3717  *
3718  * navbar
3719  * 
3720  */
3721
3722 /**
3723  * @class Roo.bootstrap.NavSimplebar
3724  * @extends Roo.bootstrap.Navbar
3725  * Bootstrap Sidebar class
3726  *
3727  * @cfg {Boolean} inverse is inverted color
3728  * 
3729  * @cfg {String} type (nav | pills | tabs)
3730  * @cfg {Boolean} arrangement stacked | justified
3731  * @cfg {String} align (left | right) alignment
3732  * 
3733  * @cfg {Boolean} main (true|false) main nav bar? default false
3734  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3735  * 
3736  * @cfg {String} tag (header|footer|nav|div) default is nav 
3737
3738  * 
3739  * 
3740  * 
3741  * @constructor
3742  * Create a new Sidebar
3743  * @param {Object} config The config object
3744  */
3745
3746
3747 Roo.bootstrap.NavSimplebar = function(config){
3748     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3749 };
3750
3751 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3752     
3753     inverse: false,
3754     
3755     type: false,
3756     arrangement: '',
3757     align : false,
3758     
3759     
3760     
3761     main : false,
3762     
3763     
3764     tag : false,
3765     
3766     
3767     getAutoCreate : function(){
3768         
3769         
3770         var cfg = {
3771             tag : this.tag || 'div',
3772             cls : 'navbar'
3773         };
3774           
3775         
3776         cfg.cn = [
3777             {
3778                 cls: 'nav',
3779                 tag : 'ul'
3780             }
3781         ];
3782         
3783          
3784         this.type = this.type || 'nav';
3785         if (['tabs','pills'].indexOf(this.type)!==-1) {
3786             cfg.cn[0].cls += ' nav-' + this.type
3787         
3788         
3789         } else {
3790             if (this.type!=='nav') {
3791                 Roo.log('nav type must be nav/tabs/pills')
3792             }
3793             cfg.cn[0].cls += ' navbar-nav'
3794         }
3795         
3796         
3797         
3798         
3799         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3800             cfg.cn[0].cls += ' nav-' + this.arrangement;
3801         }
3802         
3803         
3804         if (this.align === 'right') {
3805             cfg.cn[0].cls += ' navbar-right';
3806         }
3807         
3808         if (this.inverse) {
3809             cfg.cls += ' navbar-inverse';
3810             
3811         }
3812         
3813         
3814         return cfg;
3815     
3816         
3817     }
3818     
3819     
3820     
3821 });
3822
3823
3824
3825  
3826
3827  
3828        /*
3829  * - LGPL
3830  *
3831  * navbar
3832  * 
3833  */
3834
3835 /**
3836  * @class Roo.bootstrap.NavHeaderbar
3837  * @extends Roo.bootstrap.NavSimplebar
3838  * Bootstrap Sidebar class
3839  *
3840  * @cfg {String} brand what is brand
3841  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3842  * @cfg {String} brand_href href of the brand
3843  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3844  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3845  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3846  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3847  * 
3848  * @constructor
3849  * Create a new Sidebar
3850  * @param {Object} config The config object
3851  */
3852
3853
3854 Roo.bootstrap.NavHeaderbar = function(config){
3855     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3856       
3857 };
3858
3859 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3860     
3861     position: '',
3862     brand: '',
3863     brand_href: false,
3864     srButton : true,
3865     autohide : false,
3866     desktopCenter : false,
3867    
3868     
3869     getAutoCreate : function(){
3870         
3871         var   cfg = {
3872             tag: this.nav || 'nav',
3873             cls: 'navbar',
3874             role: 'navigation',
3875             cn: []
3876         };
3877         
3878         var cn = cfg.cn;
3879         if (this.desktopCenter) {
3880             cn.push({cls : 'container', cn : []});
3881             cn = cn[0].cn;
3882         }
3883         
3884         if(this.srButton){
3885             cn.push({
3886                 tag: 'div',
3887                 cls: 'navbar-header',
3888                 cn: [
3889                     {
3890                         tag: 'button',
3891                         type: 'button',
3892                         cls: 'navbar-toggle',
3893                         'data-toggle': 'collapse',
3894                         cn: [
3895                             {
3896                                 tag: 'span',
3897                                 cls: 'sr-only',
3898                                 html: 'Toggle navigation'
3899                             },
3900                             {
3901                                 tag: 'span',
3902                                 cls: 'icon-bar'
3903                             },
3904                             {
3905                                 tag: 'span',
3906                                 cls: 'icon-bar'
3907                             },
3908                             {
3909                                 tag: 'span',
3910                                 cls: 'icon-bar'
3911                             }
3912                         ]
3913                     }
3914                 ]
3915             });
3916         }
3917         
3918         cn.push({
3919             tag: 'div',
3920             cls: 'collapse navbar-collapse',
3921             cn : []
3922         });
3923         
3924         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3925         
3926         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3927             cfg.cls += ' navbar-' + this.position;
3928             
3929             // tag can override this..
3930             
3931             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3932         }
3933         
3934         if (this.brand !== '') {
3935             cn[0].cn.push({
3936                 tag: 'a',
3937                 href: this.brand_href ? this.brand_href : '#',
3938                 cls: 'navbar-brand',
3939                 cn: [
3940                 this.brand
3941                 ]
3942             });
3943         }
3944         
3945         if(this.main){
3946             cfg.cls += ' main-nav';
3947         }
3948         
3949         
3950         return cfg;
3951
3952         
3953     },
3954     getHeaderChildContainer : function()
3955     {
3956         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3957             return this.el.select('.navbar-header',true).first();
3958         }
3959         
3960         return this.getChildContainer();
3961     },
3962     
3963     
3964     initEvents : function()
3965     {
3966         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3967         
3968         if (this.autohide) {
3969             
3970             var prevScroll = 0;
3971             var ft = this.el;
3972             
3973             Roo.get(document).on('scroll',function(e) {
3974                 var ns = Roo.get(document).getScroll().top;
3975                 var os = prevScroll;
3976                 prevScroll = ns;
3977                 
3978                 if(ns > os){
3979                     ft.removeClass('slideDown');
3980                     ft.addClass('slideUp');
3981                     return;
3982                 }
3983                 ft.removeClass('slideUp');
3984                 ft.addClass('slideDown');
3985                  
3986               
3987           },this);
3988         }
3989     }    
3990     
3991 });
3992
3993
3994
3995  
3996
3997  /*
3998  * - LGPL
3999  *
4000  * navbar
4001  * 
4002  */
4003
4004 /**
4005  * @class Roo.bootstrap.NavSidebar
4006  * @extends Roo.bootstrap.Navbar
4007  * Bootstrap Sidebar class
4008  * 
4009  * @constructor
4010  * Create a new Sidebar
4011  * @param {Object} config The config object
4012  */
4013
4014
4015 Roo.bootstrap.NavSidebar = function(config){
4016     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4017 };
4018
4019 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4020     
4021     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4022     
4023     getAutoCreate : function(){
4024         
4025         
4026         return  {
4027             tag: 'div',
4028             cls: 'sidebar sidebar-nav'
4029         };
4030     
4031         
4032     }
4033     
4034     
4035     
4036 });
4037
4038
4039
4040  
4041
4042  /*
4043  * - LGPL
4044  *
4045  * nav group
4046  * 
4047  */
4048
4049 /**
4050  * @class Roo.bootstrap.NavGroup
4051  * @extends Roo.bootstrap.Component
4052  * Bootstrap NavGroup class
4053  * @cfg {String} align (left|right)
4054  * @cfg {Boolean} inverse
4055  * @cfg {String} type (nav|pills|tab) default nav
4056  * @cfg {String} navId - reference Id for navbar.
4057
4058  * 
4059  * @constructor
4060  * Create a new nav group
4061  * @param {Object} config The config object
4062  */
4063
4064 Roo.bootstrap.NavGroup = function(config){
4065     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4066     this.navItems = [];
4067    
4068     Roo.bootstrap.NavGroup.register(this);
4069      this.addEvents({
4070         /**
4071              * @event changed
4072              * Fires when the active item changes
4073              * @param {Roo.bootstrap.NavGroup} this
4074              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4075              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4076          */
4077         'changed': true
4078      });
4079     
4080 };
4081
4082 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4083     
4084     align: '',
4085     inverse: false,
4086     form: false,
4087     type: 'nav',
4088     navId : '',
4089     // private
4090     
4091     navItems : false, 
4092     
4093     getAutoCreate : function()
4094     {
4095         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4096         
4097         cfg = {
4098             tag : 'ul',
4099             cls: 'nav' 
4100         };
4101         
4102         if (['tabs','pills'].indexOf(this.type)!==-1) {
4103             cfg.cls += ' nav-' + this.type
4104         } else {
4105             if (this.type!=='nav') {
4106                 Roo.log('nav type must be nav/tabs/pills')
4107             }
4108             cfg.cls += ' navbar-nav'
4109         }
4110         
4111         if (this.parent() && this.parent().sidebar) {
4112             cfg = {
4113                 tag: 'ul',
4114                 cls: 'dashboard-menu sidebar-menu'
4115             };
4116             
4117             return cfg;
4118         }
4119         
4120         if (this.form === true) {
4121             cfg = {
4122                 tag: 'form',
4123                 cls: 'navbar-form'
4124             };
4125             
4126             if (this.align === 'right') {
4127                 cfg.cls += ' navbar-right';
4128             } else {
4129                 cfg.cls += ' navbar-left';
4130             }
4131         }
4132         
4133         if (this.align === 'right') {
4134             cfg.cls += ' navbar-right';
4135         }
4136         
4137         if (this.inverse) {
4138             cfg.cls += ' navbar-inverse';
4139             
4140         }
4141         
4142         
4143         return cfg;
4144     },
4145     /**
4146     * sets the active Navigation item
4147     * @param {Roo.bootstrap.NavItem} the new current navitem
4148     */
4149     setActiveItem : function(item)
4150     {
4151         var prev = false;
4152         Roo.each(this.navItems, function(v){
4153             if (v == item) {
4154                 return ;
4155             }
4156             if (v.isActive()) {
4157                 v.setActive(false, true);
4158                 prev = v;
4159                 
4160             }
4161             
4162         });
4163
4164         item.setActive(true, true);
4165         this.fireEvent('changed', this, item, prev);
4166         
4167         
4168     },
4169     /**
4170     * gets the active Navigation item
4171     * @return {Roo.bootstrap.NavItem} the current navitem
4172     */
4173     getActive : function()
4174     {
4175         
4176         var prev = false;
4177         Roo.each(this.navItems, function(v){
4178             
4179             if (v.isActive()) {
4180                 prev = v;
4181                 
4182             }
4183             
4184         });
4185         return prev;
4186     },
4187     
4188     indexOfNav : function()
4189     {
4190         
4191         var prev = false;
4192         Roo.each(this.navItems, function(v,i){
4193             
4194             if (v.isActive()) {
4195                 prev = i;
4196                 
4197             }
4198             
4199         });
4200         return prev;
4201     },
4202     /**
4203     * adds a Navigation item
4204     * @param {Roo.bootstrap.NavItem} the navitem to add
4205     */
4206     addItem : function(cfg)
4207     {
4208         var cn = new Roo.bootstrap.NavItem(cfg);
4209         this.register(cn);
4210         cn.parentId = this.id;
4211         cn.onRender(this.el, null);
4212         return cn;
4213     },
4214     /**
4215     * register a Navigation item
4216     * @param {Roo.bootstrap.NavItem} the navitem to add
4217     */
4218     register : function(item)
4219     {
4220         this.navItems.push( item);
4221         item.navId = this.navId;
4222     
4223     },
4224     
4225     /**
4226     * clear all the Navigation item
4227     */
4228    
4229     clearAll : function()
4230     {
4231         this.navItems = [];
4232         this.el.dom.innerHTML = '';
4233     },
4234     
4235     getNavItem: function(tabId)
4236     {
4237         var ret = false;
4238         Roo.each(this.navItems, function(e) {
4239             if (e.tabId == tabId) {
4240                ret =  e;
4241                return false;
4242             }
4243             return true;
4244             
4245         });
4246         return ret;
4247     },
4248     
4249     setActiveNext : function()
4250     {
4251         var i = this.indexOfNav(this.getActive());
4252         if (i > this.navItems.length) {
4253             return;
4254         }
4255         this.setActiveItem(this.navItems[i+1]);
4256     },
4257     setActivePrev : function()
4258     {
4259         var i = this.indexOfNav(this.getActive());
4260         if (i  < 1) {
4261             return;
4262         }
4263         this.setActiveItem(this.navItems[i-1]);
4264     },
4265     clearWasActive : function(except) {
4266         Roo.each(this.navItems, function(e) {
4267             if (e.tabId != except.tabId && e.was_active) {
4268                e.was_active = false;
4269                return false;
4270             }
4271             return true;
4272             
4273         });
4274     },
4275     getWasActive : function ()
4276     {
4277         var r = false;
4278         Roo.each(this.navItems, function(e) {
4279             if (e.was_active) {
4280                r = e;
4281                return false;
4282             }
4283             return true;
4284             
4285         });
4286         return r;
4287     }
4288     
4289     
4290 });
4291
4292  
4293 Roo.apply(Roo.bootstrap.NavGroup, {
4294     
4295     groups: {},
4296      /**
4297     * register a Navigation Group
4298     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4299     */
4300     register : function(navgrp)
4301     {
4302         this.groups[navgrp.navId] = navgrp;
4303         
4304     },
4305     /**
4306     * fetch a Navigation Group based on the navigation ID
4307     * @param {string} the navgroup to add
4308     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4309     */
4310     get: function(navId) {
4311         if (typeof(this.groups[navId]) == 'undefined') {
4312             return false;
4313             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4314         }
4315         return this.groups[navId] ;
4316     }
4317     
4318     
4319     
4320 });
4321
4322  /*
4323  * - LGPL
4324  *
4325  * row
4326  * 
4327  */
4328
4329 /**
4330  * @class Roo.bootstrap.NavItem
4331  * @extends Roo.bootstrap.Component
4332  * Bootstrap Navbar.NavItem class
4333  * @cfg {String} href  link to
4334  * @cfg {String} html content of button
4335  * @cfg {String} badge text inside badge
4336  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4337  * @cfg {String} glyphicon name of glyphicon
4338  * @cfg {String} icon name of font awesome icon
4339  * @cfg {Boolean} active Is item active
4340  * @cfg {Boolean} disabled Is item disabled
4341  
4342  * @cfg {Boolean} preventDefault (true | false) default false
4343  * @cfg {String} tabId the tab that this item activates.
4344  * @cfg {String} tagtype (a|span) render as a href or span?
4345  * @cfg {Boolean} animateRef (true|false) link to element default false  
4346   
4347  * @constructor
4348  * Create a new Navbar Item
4349  * @param {Object} config The config object
4350  */
4351 Roo.bootstrap.NavItem = function(config){
4352     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4353     this.addEvents({
4354         // raw events
4355         /**
4356          * @event click
4357          * The raw click event for the entire grid.
4358          * @param {Roo.EventObject} e
4359          */
4360         "click" : true,
4361          /**
4362             * @event changed
4363             * Fires when the active item active state changes
4364             * @param {Roo.bootstrap.NavItem} this
4365             * @param {boolean} state the new state
4366              
4367          */
4368         'changed': true,
4369         /**
4370             * @event scrollto
4371             * Fires when scroll to element
4372             * @param {Roo.bootstrap.NavItem} this
4373             * @param {Object} options
4374             * @param {Roo.EventObject} e
4375              
4376          */
4377         'scrollto': true
4378     });
4379    
4380 };
4381
4382 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4383     
4384     href: false,
4385     html: '',
4386     badge: '',
4387     icon: false,
4388     glyphicon: false,
4389     active: false,
4390     preventDefault : false,
4391     tabId : false,
4392     tagtype : 'a',
4393     disabled : false,
4394     animateRef : false,
4395     was_active : false,
4396     
4397     getAutoCreate : function(){
4398          
4399         var cfg = {
4400             tag: 'li',
4401             cls: 'nav-item'
4402             
4403         };
4404         
4405         if (this.active) {
4406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4407         }
4408         if (this.disabled) {
4409             cfg.cls += ' disabled';
4410         }
4411         
4412         if (this.href || this.html || this.glyphicon || this.icon) {
4413             cfg.cn = [
4414                 {
4415                     tag: this.tagtype,
4416                     href : this.href || "#",
4417                     html: this.html || ''
4418                 }
4419             ];
4420             
4421             if (this.icon) {
4422                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4423             }
4424
4425             if(this.glyphicon) {
4426                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4427             }
4428             
4429             if (this.menu) {
4430                 
4431                 cfg.cn[0].html += " <span class='caret'></span>";
4432              
4433             }
4434             
4435             if (this.badge !== '') {
4436                  
4437                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4438             }
4439         }
4440         
4441         
4442         
4443         return cfg;
4444     },
4445     initEvents: function() 
4446     {
4447         if (typeof (this.menu) != 'undefined') {
4448             this.menu.parentType = this.xtype;
4449             this.menu.triggerEl = this.el;
4450             this.menu = this.addxtype(Roo.apply({}, this.menu));
4451         }
4452         
4453         this.el.select('a',true).on('click', this.onClick, this);
4454         
4455         if(this.tagtype == 'span'){
4456             this.el.select('span',true).on('click', this.onClick, this);
4457         }
4458        
4459         // at this point parent should be available..
4460         this.parent().register(this);
4461     },
4462     
4463     onClick : function(e)
4464     {
4465         if (e.getTarget('.dropdown-menu-item')) {
4466             // did you click on a menu itemm.... - then don't trigger onclick..
4467             return;
4468         }
4469         
4470         if(
4471                 this.preventDefault || 
4472                 this.href == '#' 
4473         ){
4474             Roo.log("NavItem - prevent Default?");
4475             e.preventDefault();
4476         }
4477         
4478         if (this.disabled) {
4479             return;
4480         }
4481         
4482         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4483         if (tg && tg.transition) {
4484             Roo.log("waiting for the transitionend");
4485             return;
4486         }
4487         
4488         
4489         
4490         //Roo.log("fire event clicked");
4491         if(this.fireEvent('click', this, e) === false){
4492             return;
4493         };
4494         
4495         if(this.tagtype == 'span'){
4496             return;
4497         }
4498         
4499         //Roo.log(this.href);
4500         var ael = this.el.select('a',true).first();
4501         //Roo.log(ael);
4502         
4503         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4504             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4505             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4506                 return; // ignore... - it's a 'hash' to another page.
4507             }
4508             Roo.log("NavItem - prevent Default?");
4509             e.preventDefault();
4510             this.scrollToElement(e);
4511         }
4512         
4513         
4514         var p =  this.parent();
4515    
4516         if (['tabs','pills'].indexOf(p.type)!==-1) {
4517             if (typeof(p.setActiveItem) !== 'undefined') {
4518                 p.setActiveItem(this);
4519             }
4520         }
4521         
4522         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4523         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4524             // remove the collapsed menu expand...
4525             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4526         }
4527     },
4528     
4529     isActive: function () {
4530         return this.active
4531     },
4532     setActive : function(state, fire, is_was_active)
4533     {
4534         if (this.active && !state && this.navId) {
4535             this.was_active = true;
4536             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4537             if (nv) {
4538                 nv.clearWasActive(this);
4539             }
4540             
4541         }
4542         this.active = state;
4543         
4544         if (!state ) {
4545             this.el.removeClass('active');
4546         } else if (!this.el.hasClass('active')) {
4547             this.el.addClass('active');
4548         }
4549         if (fire) {
4550             this.fireEvent('changed', this, state);
4551         }
4552         
4553         // show a panel if it's registered and related..
4554         
4555         if (!this.navId || !this.tabId || !state || is_was_active) {
4556             return;
4557         }
4558         
4559         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4560         if (!tg) {
4561             return;
4562         }
4563         var pan = tg.getPanelByName(this.tabId);
4564         if (!pan) {
4565             return;
4566         }
4567         // if we can not flip to new panel - go back to old nav highlight..
4568         if (false == tg.showPanel(pan)) {
4569             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4570             if (nv) {
4571                 var onav = nv.getWasActive();
4572                 if (onav) {
4573                     onav.setActive(true, false, true);
4574                 }
4575             }
4576             
4577         }
4578         
4579         
4580         
4581     },
4582      // this should not be here...
4583     setDisabled : function(state)
4584     {
4585         this.disabled = state;
4586         if (!state ) {
4587             this.el.removeClass('disabled');
4588         } else if (!this.el.hasClass('disabled')) {
4589             this.el.addClass('disabled');
4590         }
4591         
4592     },
4593     
4594     /**
4595      * Fetch the element to display the tooltip on.
4596      * @return {Roo.Element} defaults to this.el
4597      */
4598     tooltipEl : function()
4599     {
4600         return this.el.select('' + this.tagtype + '', true).first();
4601     },
4602     
4603     scrollToElement : function(e)
4604     {
4605         var c = document.body;
4606         
4607         /*
4608          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4609          */
4610         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4611             c = document.documentElement;
4612         }
4613         
4614         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4615         
4616         if(!target){
4617             return;
4618         }
4619
4620         var o = target.calcOffsetsTo(c);
4621         
4622         var options = {
4623             target : target,
4624             value : o[1]
4625         };
4626         
4627         this.fireEvent('scrollto', this, options, e);
4628         
4629         Roo.get(c).scrollTo('top', options.value, true);
4630         
4631         return;
4632     }
4633 });
4634  
4635
4636  /*
4637  * - LGPL
4638  *
4639  * sidebar item
4640  *
4641  *  li
4642  *    <span> icon </span>
4643  *    <span> text </span>
4644  *    <span>badge </span>
4645  */
4646
4647 /**
4648  * @class Roo.bootstrap.NavSidebarItem
4649  * @extends Roo.bootstrap.NavItem
4650  * Bootstrap Navbar.NavSidebarItem class
4651  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4652  * {Boolean} open is the menu open
4653  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4654  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4655  * {String} buttonSize (sm|md|lg)the extra classes for the button
4656  * {Boolean} showArrow show arrow next to the text (default true)
4657  * @constructor
4658  * Create a new Navbar Button
4659  * @param {Object} config The config object
4660  */
4661 Roo.bootstrap.NavSidebarItem = function(config){
4662     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4663     this.addEvents({
4664         // raw events
4665         /**
4666          * @event click
4667          * The raw click event for the entire grid.
4668          * @param {Roo.EventObject} e
4669          */
4670         "click" : true,
4671          /**
4672             * @event changed
4673             * Fires when the active item active state changes
4674             * @param {Roo.bootstrap.NavSidebarItem} this
4675             * @param {boolean} state the new state
4676              
4677          */
4678         'changed': true
4679     });
4680    
4681 };
4682
4683 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4684     
4685     badgeWeight : 'default',
4686     
4687     open: false,
4688     
4689     buttonView : false,
4690     
4691     buttonWeight : 'default',
4692     
4693     buttonSize : 'md',
4694     
4695     showArrow : true,
4696     
4697     getAutoCreate : function(){
4698         
4699         
4700         var a = {
4701                 tag: 'a',
4702                 href : this.href || '#',
4703                 cls: '',
4704                 html : '',
4705                 cn : []
4706         };
4707         
4708         if(this.buttonView){
4709             a = {
4710                 tag: 'button',
4711                 href : this.href || '#',
4712                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4713                 html : this.html,
4714                 cn : []
4715             };
4716         }
4717         
4718         var cfg = {
4719             tag: 'li',
4720             cls: '',
4721             cn: [ a ]
4722         };
4723         
4724         if (this.active) {
4725             cfg.cls += ' active';
4726         }
4727         
4728         if (this.disabled) {
4729             cfg.cls += ' disabled';
4730         }
4731         if (this.open) {
4732             cfg.cls += ' open x-open';
4733         }
4734         // left icon..
4735         if (this.glyphicon || this.icon) {
4736             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4737             a.cn.push({ tag : 'i', cls : c }) ;
4738         }
4739         
4740         if(!this.buttonView){
4741             var span = {
4742                 tag: 'span',
4743                 html : this.html || ''
4744             };
4745
4746             a.cn.push(span);
4747             
4748         }
4749         
4750         if (this.badge !== '') {
4751             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4752         }
4753         
4754         if (this.menu) {
4755             
4756             if(this.showArrow){
4757                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4758             }
4759             
4760             a.cls += ' dropdown-toggle treeview' ;
4761         }
4762         
4763         return cfg;
4764     },
4765     
4766     initEvents : function()
4767     { 
4768         if (typeof (this.menu) != 'undefined') {
4769             this.menu.parentType = this.xtype;
4770             this.menu.triggerEl = this.el;
4771             this.menu = this.addxtype(Roo.apply({}, this.menu));
4772         }
4773         
4774         this.el.on('click', this.onClick, this);
4775         
4776         if(this.badge !== ''){
4777             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4778         }
4779         
4780     },
4781     
4782     onClick : function(e)
4783     {
4784         if(this.disabled){
4785             e.preventDefault();
4786             return;
4787         }
4788         
4789         if(this.preventDefault){
4790             e.preventDefault();
4791         }
4792         
4793         this.fireEvent('click', this);
4794     },
4795     
4796     disable : function()
4797     {
4798         this.setDisabled(true);
4799     },
4800     
4801     enable : function()
4802     {
4803         this.setDisabled(false);
4804     },
4805     
4806     setDisabled : function(state)
4807     {
4808         if(this.disabled == state){
4809             return;
4810         }
4811         
4812         this.disabled = state;
4813         
4814         if (state) {
4815             this.el.addClass('disabled');
4816             return;
4817         }
4818         
4819         this.el.removeClass('disabled');
4820         
4821         return;
4822     },
4823     
4824     setActive : function(state)
4825     {
4826         if(this.active == state){
4827             return;
4828         }
4829         
4830         this.active = state;
4831         
4832         if (state) {
4833             this.el.addClass('active');
4834             return;
4835         }
4836         
4837         this.el.removeClass('active');
4838         
4839         return;
4840     },
4841     
4842     isActive: function () 
4843     {
4844         return this.active;
4845     },
4846     
4847     setBadge : function(str)
4848     {
4849         if(!this.badgeEl){
4850             return;
4851         }
4852         
4853         this.badgeEl.dom.innerHTML = str;
4854     }
4855     
4856    
4857      
4858  
4859 });
4860  
4861
4862  /*
4863  * - LGPL
4864  *
4865  * row
4866  * 
4867  */
4868
4869 /**
4870  * @class Roo.bootstrap.Row
4871  * @extends Roo.bootstrap.Component
4872  * Bootstrap Row class (contains columns...)
4873  * 
4874  * @constructor
4875  * Create a new Row
4876  * @param {Object} config The config object
4877  */
4878
4879 Roo.bootstrap.Row = function(config){
4880     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4881 };
4882
4883 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4884     
4885     getAutoCreate : function(){
4886        return {
4887             cls: 'row clearfix'
4888        };
4889     }
4890     
4891     
4892 });
4893
4894  
4895
4896  /*
4897  * - LGPL
4898  *
4899  * element
4900  * 
4901  */
4902
4903 /**
4904  * @class Roo.bootstrap.Element
4905  * @extends Roo.bootstrap.Component
4906  * Bootstrap Element class
4907  * @cfg {String} html contents of the element
4908  * @cfg {String} tag tag of the element
4909  * @cfg {String} cls class of the element
4910  * @cfg {Boolean} preventDefault (true|false) default false
4911  * @cfg {Boolean} clickable (true|false) default false
4912  * 
4913  * @constructor
4914  * Create a new Element
4915  * @param {Object} config The config object
4916  */
4917
4918 Roo.bootstrap.Element = function(config){
4919     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4920     
4921     this.addEvents({
4922         // raw events
4923         /**
4924          * @event click
4925          * When a element is chick
4926          * @param {Roo.bootstrap.Element} this
4927          * @param {Roo.EventObject} e
4928          */
4929         "click" : true
4930     });
4931 };
4932
4933 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4934     
4935     tag: 'div',
4936     cls: '',
4937     html: '',
4938     preventDefault: false, 
4939     clickable: false,
4940     
4941     getAutoCreate : function(){
4942         
4943         var cfg = {
4944             tag: this.tag,
4945             // cls: this.cls, double assign in parent class Component.js :: onRender
4946             html: this.html
4947         };
4948         
4949         return cfg;
4950     },
4951     
4952     initEvents: function() 
4953     {
4954         Roo.bootstrap.Element.superclass.initEvents.call(this);
4955         
4956         if(this.clickable){
4957             this.el.on('click', this.onClick, this);
4958         }
4959         
4960     },
4961     
4962     onClick : function(e)
4963     {
4964         if(this.preventDefault){
4965             e.preventDefault();
4966         }
4967         
4968         this.fireEvent('click', this, e);
4969     },
4970     
4971     getValue : function()
4972     {
4973         return this.el.dom.innerHTML;
4974     },
4975     
4976     setValue : function(value)
4977     {
4978         this.el.dom.innerHTML = value;
4979     }
4980    
4981 });
4982
4983  
4984
4985  /*
4986  * - LGPL
4987  *
4988  * pagination
4989  * 
4990  */
4991
4992 /**
4993  * @class Roo.bootstrap.Pagination
4994  * @extends Roo.bootstrap.Component
4995  * Bootstrap Pagination class
4996  * @cfg {String} size xs | sm | md | lg
4997  * @cfg {Boolean} inverse false | true
4998  * 
4999  * @constructor
5000  * Create a new Pagination
5001  * @param {Object} config The config object
5002  */
5003
5004 Roo.bootstrap.Pagination = function(config){
5005     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5006 };
5007
5008 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5009     
5010     cls: false,
5011     size: false,
5012     inverse: false,
5013     
5014     getAutoCreate : function(){
5015         var cfg = {
5016             tag: 'ul',
5017                 cls: 'pagination'
5018         };
5019         if (this.inverse) {
5020             cfg.cls += ' inverse';
5021         }
5022         if (this.html) {
5023             cfg.html=this.html;
5024         }
5025         if (this.cls) {
5026             cfg.cls += " " + this.cls;
5027         }
5028         return cfg;
5029     }
5030    
5031 });
5032
5033  
5034
5035  /*
5036  * - LGPL
5037  *
5038  * Pagination item
5039  * 
5040  */
5041
5042
5043 /**
5044  * @class Roo.bootstrap.PaginationItem
5045  * @extends Roo.bootstrap.Component
5046  * Bootstrap PaginationItem class
5047  * @cfg {String} html text
5048  * @cfg {String} href the link
5049  * @cfg {Boolean} preventDefault (true | false) default true
5050  * @cfg {Boolean} active (true | false) default false
5051  * @cfg {Boolean} disabled default false
5052  * 
5053  * 
5054  * @constructor
5055  * Create a new PaginationItem
5056  * @param {Object} config The config object
5057  */
5058
5059
5060 Roo.bootstrap.PaginationItem = function(config){
5061     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5062     this.addEvents({
5063         // raw events
5064         /**
5065          * @event click
5066          * The raw click event for the entire grid.
5067          * @param {Roo.EventObject} e
5068          */
5069         "click" : true
5070     });
5071 };
5072
5073 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5074     
5075     href : false,
5076     html : false,
5077     preventDefault: true,
5078     active : false,
5079     cls : false,
5080     disabled: false,
5081     
5082     getAutoCreate : function(){
5083         var cfg= {
5084             tag: 'li',
5085             cn: [
5086                 {
5087                     tag : 'a',
5088                     href : this.href ? this.href : '#',
5089                     html : this.html ? this.html : ''
5090                 }
5091             ]
5092         };
5093         
5094         if(this.cls){
5095             cfg.cls = this.cls;
5096         }
5097         
5098         if(this.disabled){
5099             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5100         }
5101         
5102         if(this.active){
5103             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5104         }
5105         
5106         return cfg;
5107     },
5108     
5109     initEvents: function() {
5110         
5111         this.el.on('click', this.onClick, this);
5112         
5113     },
5114     onClick : function(e)
5115     {
5116         Roo.log('PaginationItem on click ');
5117         if(this.preventDefault){
5118             e.preventDefault();
5119         }
5120         
5121         if(this.disabled){
5122             return;
5123         }
5124         
5125         this.fireEvent('click', this, e);
5126     }
5127    
5128 });
5129
5130  
5131
5132  /*
5133  * - LGPL
5134  *
5135  * slider
5136  * 
5137  */
5138
5139
5140 /**
5141  * @class Roo.bootstrap.Slider
5142  * @extends Roo.bootstrap.Component
5143  * Bootstrap Slider class
5144  *    
5145  * @constructor
5146  * Create a new Slider
5147  * @param {Object} config The config object
5148  */
5149
5150 Roo.bootstrap.Slider = function(config){
5151     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5152 };
5153
5154 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5155     
5156     getAutoCreate : function(){
5157         
5158         var cfg = {
5159             tag: 'div',
5160             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5161             cn: [
5162                 {
5163                     tag: 'a',
5164                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5165                 }
5166             ]
5167         };
5168         
5169         return cfg;
5170     }
5171    
5172 });
5173
5174  /*
5175  * Based on:
5176  * Ext JS Library 1.1.1
5177  * Copyright(c) 2006-2007, Ext JS, LLC.
5178  *
5179  * Originally Released Under LGPL - original licence link has changed is not relivant.
5180  *
5181  * Fork - LGPL
5182  * <script type="text/javascript">
5183  */
5184  
5185
5186 /**
5187  * @class Roo.grid.ColumnModel
5188  * @extends Roo.util.Observable
5189  * This is the default implementation of a ColumnModel used by the Grid. It defines
5190  * the columns in the grid.
5191  * <br>Usage:<br>
5192  <pre><code>
5193  var colModel = new Roo.grid.ColumnModel([
5194         {header: "Ticker", width: 60, sortable: true, locked: true},
5195         {header: "Company Name", width: 150, sortable: true},
5196         {header: "Market Cap.", width: 100, sortable: true},
5197         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5198         {header: "Employees", width: 100, sortable: true, resizable: false}
5199  ]);
5200  </code></pre>
5201  * <p>
5202  
5203  * The config options listed for this class are options which may appear in each
5204  * individual column definition.
5205  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5206  * @constructor
5207  * @param {Object} config An Array of column config objects. See this class's
5208  * config objects for details.
5209 */
5210 Roo.grid.ColumnModel = function(config){
5211         /**
5212      * The config passed into the constructor
5213      */
5214     this.config = config;
5215     this.lookup = {};
5216
5217     // if no id, create one
5218     // if the column does not have a dataIndex mapping,
5219     // map it to the order it is in the config
5220     for(var i = 0, len = config.length; i < len; i++){
5221         var c = config[i];
5222         if(typeof c.dataIndex == "undefined"){
5223             c.dataIndex = i;
5224         }
5225         if(typeof c.renderer == "string"){
5226             c.renderer = Roo.util.Format[c.renderer];
5227         }
5228         if(typeof c.id == "undefined"){
5229             c.id = Roo.id();
5230         }
5231         if(c.editor && c.editor.xtype){
5232             c.editor  = Roo.factory(c.editor, Roo.grid);
5233         }
5234         if(c.editor && c.editor.isFormField){
5235             c.editor = new Roo.grid.GridEditor(c.editor);
5236         }
5237         this.lookup[c.id] = c;
5238     }
5239
5240     /**
5241      * The width of columns which have no width specified (defaults to 100)
5242      * @type Number
5243      */
5244     this.defaultWidth = 100;
5245
5246     /**
5247      * Default sortable of columns which have no sortable specified (defaults to false)
5248      * @type Boolean
5249      */
5250     this.defaultSortable = false;
5251
5252     this.addEvents({
5253         /**
5254              * @event widthchange
5255              * Fires when the width of a column changes.
5256              * @param {ColumnModel} this
5257              * @param {Number} columnIndex The column index
5258              * @param {Number} newWidth The new width
5259              */
5260             "widthchange": true,
5261         /**
5262              * @event headerchange
5263              * Fires when the text of a header changes.
5264              * @param {ColumnModel} this
5265              * @param {Number} columnIndex The column index
5266              * @param {Number} newText The new header text
5267              */
5268             "headerchange": true,
5269         /**
5270              * @event hiddenchange
5271              * Fires when a column is hidden or "unhidden".
5272              * @param {ColumnModel} this
5273              * @param {Number} columnIndex The column index
5274              * @param {Boolean} hidden true if hidden, false otherwise
5275              */
5276             "hiddenchange": true,
5277             /**
5278          * @event columnmoved
5279          * Fires when a column is moved.
5280          * @param {ColumnModel} this
5281          * @param {Number} oldIndex
5282          * @param {Number} newIndex
5283          */
5284         "columnmoved" : true,
5285         /**
5286          * @event columlockchange
5287          * Fires when a column's locked state is changed
5288          * @param {ColumnModel} this
5289          * @param {Number} colIndex
5290          * @param {Boolean} locked true if locked
5291          */
5292         "columnlockchange" : true
5293     });
5294     Roo.grid.ColumnModel.superclass.constructor.call(this);
5295 };
5296 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5297     /**
5298      * @cfg {String} header The header text to display in the Grid view.
5299      */
5300     /**
5301      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5302      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5303      * specified, the column's index is used as an index into the Record's data Array.
5304      */
5305     /**
5306      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5307      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5308      */
5309     /**
5310      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5311      * Defaults to the value of the {@link #defaultSortable} property.
5312      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5313      */
5314     /**
5315      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5316      */
5317     /**
5318      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5319      */
5320     /**
5321      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5322      */
5323     /**
5324      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5325      */
5326     /**
5327      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5328      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5329      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5330      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5331      */
5332        /**
5333      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5334      */
5335     /**
5336      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5337      */
5338     /**
5339      * @cfg {String} cursor (Optional)
5340      */
5341     /**
5342      * @cfg {String} tooltip (Optional)
5343      */
5344     /**
5345      * @cfg {Number} xs (Optional)
5346      */
5347     /**
5348      * @cfg {Number} sm (Optional)
5349      */
5350     /**
5351      * @cfg {Number} md (Optional)
5352      */
5353     /**
5354      * @cfg {Number} lg (Optional)
5355      */
5356     /**
5357      * Returns the id of the column at the specified index.
5358      * @param {Number} index The column index
5359      * @return {String} the id
5360      */
5361     getColumnId : function(index){
5362         return this.config[index].id;
5363     },
5364
5365     /**
5366      * Returns the column for a specified id.
5367      * @param {String} id The column id
5368      * @return {Object} the column
5369      */
5370     getColumnById : function(id){
5371         return this.lookup[id];
5372     },
5373
5374     
5375     /**
5376      * Returns the column for a specified dataIndex.
5377      * @param {String} dataIndex The column dataIndex
5378      * @return {Object|Boolean} the column or false if not found
5379      */
5380     getColumnByDataIndex: function(dataIndex){
5381         var index = this.findColumnIndex(dataIndex);
5382         return index > -1 ? this.config[index] : false;
5383     },
5384     
5385     /**
5386      * Returns the index for a specified column id.
5387      * @param {String} id The column id
5388      * @return {Number} the index, or -1 if not found
5389      */
5390     getIndexById : function(id){
5391         for(var i = 0, len = this.config.length; i < len; i++){
5392             if(this.config[i].id == id){
5393                 return i;
5394             }
5395         }
5396         return -1;
5397     },
5398     
5399     /**
5400      * Returns the index for a specified column dataIndex.
5401      * @param {String} dataIndex The column dataIndex
5402      * @return {Number} the index, or -1 if not found
5403      */
5404     
5405     findColumnIndex : function(dataIndex){
5406         for(var i = 0, len = this.config.length; i < len; i++){
5407             if(this.config[i].dataIndex == dataIndex){
5408                 return i;
5409             }
5410         }
5411         return -1;
5412     },
5413     
5414     
5415     moveColumn : function(oldIndex, newIndex){
5416         var c = this.config[oldIndex];
5417         this.config.splice(oldIndex, 1);
5418         this.config.splice(newIndex, 0, c);
5419         this.dataMap = null;
5420         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5421     },
5422
5423     isLocked : function(colIndex){
5424         return this.config[colIndex].locked === true;
5425     },
5426
5427     setLocked : function(colIndex, value, suppressEvent){
5428         if(this.isLocked(colIndex) == value){
5429             return;
5430         }
5431         this.config[colIndex].locked = value;
5432         if(!suppressEvent){
5433             this.fireEvent("columnlockchange", this, colIndex, value);
5434         }
5435     },
5436
5437     getTotalLockedWidth : function(){
5438         var totalWidth = 0;
5439         for(var i = 0; i < this.config.length; i++){
5440             if(this.isLocked(i) && !this.isHidden(i)){
5441                 this.totalWidth += this.getColumnWidth(i);
5442             }
5443         }
5444         return totalWidth;
5445     },
5446
5447     getLockedCount : function(){
5448         for(var i = 0, len = this.config.length; i < len; i++){
5449             if(!this.isLocked(i)){
5450                 return i;
5451             }
5452         }
5453         
5454         return this.config.length;
5455     },
5456
5457     /**
5458      * Returns the number of columns.
5459      * @return {Number}
5460      */
5461     getColumnCount : function(visibleOnly){
5462         if(visibleOnly === true){
5463             var c = 0;
5464             for(var i = 0, len = this.config.length; i < len; i++){
5465                 if(!this.isHidden(i)){
5466                     c++;
5467                 }
5468             }
5469             return c;
5470         }
5471         return this.config.length;
5472     },
5473
5474     /**
5475      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5476      * @param {Function} fn
5477      * @param {Object} scope (optional)
5478      * @return {Array} result
5479      */
5480     getColumnsBy : function(fn, scope){
5481         var r = [];
5482         for(var i = 0, len = this.config.length; i < len; i++){
5483             var c = this.config[i];
5484             if(fn.call(scope||this, c, i) === true){
5485                 r[r.length] = c;
5486             }
5487         }
5488         return r;
5489     },
5490
5491     /**
5492      * Returns true if the specified column is sortable.
5493      * @param {Number} col The column index
5494      * @return {Boolean}
5495      */
5496     isSortable : function(col){
5497         if(typeof this.config[col].sortable == "undefined"){
5498             return this.defaultSortable;
5499         }
5500         return this.config[col].sortable;
5501     },
5502
5503     /**
5504      * Returns the rendering (formatting) function defined for the column.
5505      * @param {Number} col The column index.
5506      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5507      */
5508     getRenderer : function(col){
5509         if(!this.config[col].renderer){
5510             return Roo.grid.ColumnModel.defaultRenderer;
5511         }
5512         return this.config[col].renderer;
5513     },
5514
5515     /**
5516      * Sets the rendering (formatting) function for a column.
5517      * @param {Number} col The column index
5518      * @param {Function} fn The function to use to process the cell's raw data
5519      * to return HTML markup for the grid view. The render function is called with
5520      * the following parameters:<ul>
5521      * <li>Data value.</li>
5522      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5523      * <li>css A CSS style string to apply to the table cell.</li>
5524      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5525      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5526      * <li>Row index</li>
5527      * <li>Column index</li>
5528      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5529      */
5530     setRenderer : function(col, fn){
5531         this.config[col].renderer = fn;
5532     },
5533
5534     /**
5535      * Returns the width for the specified column.
5536      * @param {Number} col The column index
5537      * @return {Number}
5538      */
5539     getColumnWidth : function(col){
5540         return this.config[col].width * 1 || this.defaultWidth;
5541     },
5542
5543     /**
5544      * Sets the width for a column.
5545      * @param {Number} col The column index
5546      * @param {Number} width The new width
5547      */
5548     setColumnWidth : function(col, width, suppressEvent){
5549         this.config[col].width = width;
5550         this.totalWidth = null;
5551         if(!suppressEvent){
5552              this.fireEvent("widthchange", this, col, width);
5553         }
5554     },
5555
5556     /**
5557      * Returns the total width of all columns.
5558      * @param {Boolean} includeHidden True to include hidden column widths
5559      * @return {Number}
5560      */
5561     getTotalWidth : function(includeHidden){
5562         if(!this.totalWidth){
5563             this.totalWidth = 0;
5564             for(var i = 0, len = this.config.length; i < len; i++){
5565                 if(includeHidden || !this.isHidden(i)){
5566                     this.totalWidth += this.getColumnWidth(i);
5567                 }
5568             }
5569         }
5570         return this.totalWidth;
5571     },
5572
5573     /**
5574      * Returns the header for the specified column.
5575      * @param {Number} col The column index
5576      * @return {String}
5577      */
5578     getColumnHeader : function(col){
5579         return this.config[col].header;
5580     },
5581
5582     /**
5583      * Sets the header for a column.
5584      * @param {Number} col The column index
5585      * @param {String} header The new header
5586      */
5587     setColumnHeader : function(col, header){
5588         this.config[col].header = header;
5589         this.fireEvent("headerchange", this, col, header);
5590     },
5591
5592     /**
5593      * Returns the tooltip for the specified column.
5594      * @param {Number} col The column index
5595      * @return {String}
5596      */
5597     getColumnTooltip : function(col){
5598             return this.config[col].tooltip;
5599     },
5600     /**
5601      * Sets the tooltip for a column.
5602      * @param {Number} col The column index
5603      * @param {String} tooltip The new tooltip
5604      */
5605     setColumnTooltip : function(col, tooltip){
5606             this.config[col].tooltip = tooltip;
5607     },
5608
5609     /**
5610      * Returns the dataIndex for the specified column.
5611      * @param {Number} col The column index
5612      * @return {Number}
5613      */
5614     getDataIndex : function(col){
5615         return this.config[col].dataIndex;
5616     },
5617
5618     /**
5619      * Sets the dataIndex for a column.
5620      * @param {Number} col The column index
5621      * @param {Number} dataIndex The new dataIndex
5622      */
5623     setDataIndex : function(col, dataIndex){
5624         this.config[col].dataIndex = dataIndex;
5625     },
5626
5627     
5628     
5629     /**
5630      * Returns true if the cell is editable.
5631      * @param {Number} colIndex The column index
5632      * @param {Number} rowIndex The row index - this is nto actually used..?
5633      * @return {Boolean}
5634      */
5635     isCellEditable : function(colIndex, rowIndex){
5636         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5637     },
5638
5639     /**
5640      * Returns the editor defined for the cell/column.
5641      * return false or null to disable editing.
5642      * @param {Number} colIndex The column index
5643      * @param {Number} rowIndex The row index
5644      * @return {Object}
5645      */
5646     getCellEditor : function(colIndex, rowIndex){
5647         return this.config[colIndex].editor;
5648     },
5649
5650     /**
5651      * Sets if a column is editable.
5652      * @param {Number} col The column index
5653      * @param {Boolean} editable True if the column is editable
5654      */
5655     setEditable : function(col, editable){
5656         this.config[col].editable = editable;
5657     },
5658
5659
5660     /**
5661      * Returns true if the column is hidden.
5662      * @param {Number} colIndex The column index
5663      * @return {Boolean}
5664      */
5665     isHidden : function(colIndex){
5666         return this.config[colIndex].hidden;
5667     },
5668
5669
5670     /**
5671      * Returns true if the column width cannot be changed
5672      */
5673     isFixed : function(colIndex){
5674         return this.config[colIndex].fixed;
5675     },
5676
5677     /**
5678      * Returns true if the column can be resized
5679      * @return {Boolean}
5680      */
5681     isResizable : function(colIndex){
5682         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5683     },
5684     /**
5685      * Sets if a column is hidden.
5686      * @param {Number} colIndex The column index
5687      * @param {Boolean} hidden True if the column is hidden
5688      */
5689     setHidden : function(colIndex, hidden){
5690         this.config[colIndex].hidden = hidden;
5691         this.totalWidth = null;
5692         this.fireEvent("hiddenchange", this, colIndex, hidden);
5693     },
5694
5695     /**
5696      * Sets the editor for a column.
5697      * @param {Number} col The column index
5698      * @param {Object} editor The editor object
5699      */
5700     setEditor : function(col, editor){
5701         this.config[col].editor = editor;
5702     }
5703 });
5704
5705 Roo.grid.ColumnModel.defaultRenderer = function(value)
5706 {
5707     if(typeof value == "object") {
5708         return value;
5709     }
5710         if(typeof value == "string" && value.length < 1){
5711             return "&#160;";
5712         }
5713     
5714         return String.format("{0}", value);
5715 };
5716
5717 // Alias for backwards compatibility
5718 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729  
5730 /**
5731  * @class Roo.LoadMask
5732  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5733  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5734  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5735  * element's UpdateManager load indicator and will be destroyed after the initial load.
5736  * @constructor
5737  * Create a new LoadMask
5738  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5739  * @param {Object} config The config object
5740  */
5741 Roo.LoadMask = function(el, config){
5742     this.el = Roo.get(el);
5743     Roo.apply(this, config);
5744     if(this.store){
5745         this.store.on('beforeload', this.onBeforeLoad, this);
5746         this.store.on('load', this.onLoad, this);
5747         this.store.on('loadexception', this.onLoadException, this);
5748         this.removeMask = false;
5749     }else{
5750         var um = this.el.getUpdateManager();
5751         um.showLoadIndicator = false; // disable the default indicator
5752         um.on('beforeupdate', this.onBeforeLoad, this);
5753         um.on('update', this.onLoad, this);
5754         um.on('failure', this.onLoad, this);
5755         this.removeMask = true;
5756     }
5757 };
5758
5759 Roo.LoadMask.prototype = {
5760     /**
5761      * @cfg {Boolean} removeMask
5762      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5763      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5764      */
5765     /**
5766      * @cfg {String} msg
5767      * The text to display in a centered loading message box (defaults to 'Loading...')
5768      */
5769     msg : 'Loading...',
5770     /**
5771      * @cfg {String} msgCls
5772      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5773      */
5774     msgCls : 'x-mask-loading',
5775
5776     /**
5777      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5778      * @type Boolean
5779      */
5780     disabled: false,
5781
5782     /**
5783      * Disables the mask to prevent it from being displayed
5784      */
5785     disable : function(){
5786        this.disabled = true;
5787     },
5788
5789     /**
5790      * Enables the mask so that it can be displayed
5791      */
5792     enable : function(){
5793         this.disabled = false;
5794     },
5795     
5796     onLoadException : function()
5797     {
5798         Roo.log(arguments);
5799         
5800         if (typeof(arguments[3]) != 'undefined') {
5801             Roo.MessageBox.alert("Error loading",arguments[3]);
5802         } 
5803         /*
5804         try {
5805             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5806                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5807             }   
5808         } catch(e) {
5809             
5810         }
5811         */
5812     
5813         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5814     },
5815     // private
5816     onLoad : function()
5817     {
5818         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5819     },
5820
5821     // private
5822     onBeforeLoad : function(){
5823         if(!this.disabled){
5824             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5825         }
5826     },
5827
5828     // private
5829     destroy : function(){
5830         if(this.store){
5831             this.store.un('beforeload', this.onBeforeLoad, this);
5832             this.store.un('load', this.onLoad, this);
5833             this.store.un('loadexception', this.onLoadException, this);
5834         }else{
5835             var um = this.el.getUpdateManager();
5836             um.un('beforeupdate', this.onBeforeLoad, this);
5837             um.un('update', this.onLoad, this);
5838             um.un('failure', this.onLoad, this);
5839         }
5840     }
5841 };/*
5842  * - LGPL
5843  *
5844  * table
5845  * 
5846  */
5847
5848 /**
5849  * @class Roo.bootstrap.Table
5850  * @extends Roo.bootstrap.Component
5851  * Bootstrap Table class
5852  * @cfg {String} cls table class
5853  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5854  * @cfg {String} bgcolor Specifies the background color for a table
5855  * @cfg {Number} border Specifies whether the table cells should have borders or not
5856  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5857  * @cfg {Number} cellspacing Specifies the space between cells
5858  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5859  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5860  * @cfg {String} sortable Specifies that the table should be sortable
5861  * @cfg {String} summary Specifies a summary of the content of a table
5862  * @cfg {Number} width Specifies the width of a table
5863  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5864  * 
5865  * @cfg {boolean} striped Should the rows be alternative striped
5866  * @cfg {boolean} bordered Add borders to the table
5867  * @cfg {boolean} hover Add hover highlighting
5868  * @cfg {boolean} condensed Format condensed
5869  * @cfg {boolean} responsive Format condensed
5870  * @cfg {Boolean} loadMask (true|false) default false
5871  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5872  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5873  * @cfg {Boolean} rowSelection (true|false) default false
5874  * @cfg {Boolean} cellSelection (true|false) default false
5875  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5876  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5877  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5878  
5879  * 
5880  * @constructor
5881  * Create a new Table
5882  * @param {Object} config The config object
5883  */
5884
5885 Roo.bootstrap.Table = function(config){
5886     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5887     
5888   
5889     
5890     // BC...
5891     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5892     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5893     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5894     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5895     
5896     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5897     if (this.sm) {
5898         this.sm.grid = this;
5899         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5900         this.sm = this.selModel;
5901         this.sm.xmodule = this.xmodule || false;
5902     }
5903     
5904     if (this.cm && typeof(this.cm.config) == 'undefined') {
5905         this.colModel = new Roo.grid.ColumnModel(this.cm);
5906         this.cm = this.colModel;
5907         this.cm.xmodule = this.xmodule || false;
5908     }
5909     if (this.store) {
5910         this.store= Roo.factory(this.store, Roo.data);
5911         this.ds = this.store;
5912         this.ds.xmodule = this.xmodule || false;
5913          
5914     }
5915     if (this.footer && this.store) {
5916         this.footer.dataSource = this.ds;
5917         this.footer = Roo.factory(this.footer);
5918     }
5919     
5920     /** @private */
5921     this.addEvents({
5922         /**
5923          * @event cellclick
5924          * Fires when a cell is clicked
5925          * @param {Roo.bootstrap.Table} this
5926          * @param {Roo.Element} el
5927          * @param {Number} rowIndex
5928          * @param {Number} columnIndex
5929          * @param {Roo.EventObject} e
5930          */
5931         "cellclick" : true,
5932         /**
5933          * @event celldblclick
5934          * Fires when a cell is double clicked
5935          * @param {Roo.bootstrap.Table} this
5936          * @param {Roo.Element} el
5937          * @param {Number} rowIndex
5938          * @param {Number} columnIndex
5939          * @param {Roo.EventObject} e
5940          */
5941         "celldblclick" : true,
5942         /**
5943          * @event rowclick
5944          * Fires when a row is clicked
5945          * @param {Roo.bootstrap.Table} this
5946          * @param {Roo.Element} el
5947          * @param {Number} rowIndex
5948          * @param {Roo.EventObject} e
5949          */
5950         "rowclick" : true,
5951         /**
5952          * @event rowdblclick
5953          * Fires when a row is double clicked
5954          * @param {Roo.bootstrap.Table} this
5955          * @param {Roo.Element} el
5956          * @param {Number} rowIndex
5957          * @param {Roo.EventObject} e
5958          */
5959         "rowdblclick" : true,
5960         /**
5961          * @event mouseover
5962          * Fires when a mouseover occur
5963          * @param {Roo.bootstrap.Table} this
5964          * @param {Roo.Element} el
5965          * @param {Number} rowIndex
5966          * @param {Number} columnIndex
5967          * @param {Roo.EventObject} e
5968          */
5969         "mouseover" : true,
5970         /**
5971          * @event mouseout
5972          * Fires when a mouseout occur
5973          * @param {Roo.bootstrap.Table} this
5974          * @param {Roo.Element} el
5975          * @param {Number} rowIndex
5976          * @param {Number} columnIndex
5977          * @param {Roo.EventObject} e
5978          */
5979         "mouseout" : true,
5980         /**
5981          * @event rowclass
5982          * Fires when a row is rendered, so you can change add a style to it.
5983          * @param {Roo.bootstrap.Table} this
5984          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5985          */
5986         'rowclass' : true,
5987           /**
5988          * @event rowsrendered
5989          * Fires when all the  rows have been rendered
5990          * @param {Roo.bootstrap.Table} this
5991          */
5992         'rowsrendered' : true,
5993         /**
5994          * @event contextmenu
5995          * The raw contextmenu event for the entire grid.
5996          * @param {Roo.EventObject} e
5997          */
5998         "contextmenu" : true,
5999         /**
6000          * @event rowcontextmenu
6001          * Fires when a row is right clicked
6002          * @param {Roo.bootstrap.Table} this
6003          * @param {Number} rowIndex
6004          * @param {Roo.EventObject} e
6005          */
6006         "rowcontextmenu" : true,
6007         /**
6008          * @event cellcontextmenu
6009          * Fires when a cell is right clicked
6010          * @param {Roo.bootstrap.Table} this
6011          * @param {Number} rowIndex
6012          * @param {Number} cellIndex
6013          * @param {Roo.EventObject} e
6014          */
6015          "cellcontextmenu" : true,
6016          /**
6017          * @event headercontextmenu
6018          * Fires when a header is right clicked
6019          * @param {Roo.bootstrap.Table} this
6020          * @param {Number} columnIndex
6021          * @param {Roo.EventObject} e
6022          */
6023         "headercontextmenu" : true
6024     });
6025 };
6026
6027 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6028     
6029     cls: false,
6030     align: false,
6031     bgcolor: false,
6032     border: false,
6033     cellpadding: false,
6034     cellspacing: false,
6035     frame: false,
6036     rules: false,
6037     sortable: false,
6038     summary: false,
6039     width: false,
6040     striped : false,
6041     scrollBody : false,
6042     bordered: false,
6043     hover:  false,
6044     condensed : false,
6045     responsive : false,
6046     sm : false,
6047     cm : false,
6048     store : false,
6049     loadMask : false,
6050     footerShow : true,
6051     headerShow : true,
6052   
6053     rowSelection : false,
6054     cellSelection : false,
6055     layout : false,
6056     
6057     // Roo.Element - the tbody
6058     mainBody: false,
6059     // Roo.Element - thead element
6060     mainHead: false,
6061     
6062     container: false, // used by gridpanel...
6063     
6064     lazyLoad : false,
6065     
6066     CSS : Roo.util.CSS,
6067     
6068     getAutoCreate : function()
6069     {
6070         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6071         
6072         cfg = {
6073             tag: 'table',
6074             cls : 'table',
6075             cn : []
6076         };
6077         if (this.scrollBody) {
6078             cfg.cls += ' table-body-fixed';
6079         }    
6080         if (this.striped) {
6081             cfg.cls += ' table-striped';
6082         }
6083         
6084         if (this.hover) {
6085             cfg.cls += ' table-hover';
6086         }
6087         if (this.bordered) {
6088             cfg.cls += ' table-bordered';
6089         }
6090         if (this.condensed) {
6091             cfg.cls += ' table-condensed';
6092         }
6093         if (this.responsive) {
6094             cfg.cls += ' table-responsive';
6095         }
6096         
6097         if (this.cls) {
6098             cfg.cls+=  ' ' +this.cls;
6099         }
6100         
6101         // this lot should be simplifed...
6102         
6103         if (this.align) {
6104             cfg.align=this.align;
6105         }
6106         if (this.bgcolor) {
6107             cfg.bgcolor=this.bgcolor;
6108         }
6109         if (this.border) {
6110             cfg.border=this.border;
6111         }
6112         if (this.cellpadding) {
6113             cfg.cellpadding=this.cellpadding;
6114         }
6115         if (this.cellspacing) {
6116             cfg.cellspacing=this.cellspacing;
6117         }
6118         if (this.frame) {
6119             cfg.frame=this.frame;
6120         }
6121         if (this.rules) {
6122             cfg.rules=this.rules;
6123         }
6124         if (this.sortable) {
6125             cfg.sortable=this.sortable;
6126         }
6127         if (this.summary) {
6128             cfg.summary=this.summary;
6129         }
6130         if (this.width) {
6131             cfg.width=this.width;
6132         }
6133         if (this.layout) {
6134             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6135         }
6136         
6137         if(this.store || this.cm){
6138             if(this.headerShow){
6139                 cfg.cn.push(this.renderHeader());
6140             }
6141             
6142             cfg.cn.push(this.renderBody());
6143             
6144             if(this.footerShow){
6145                 cfg.cn.push(this.renderFooter());
6146             }
6147             // where does this come from?
6148             //cfg.cls+=  ' TableGrid';
6149         }
6150         
6151         return { cn : [ cfg ] };
6152     },
6153     
6154     initEvents : function()
6155     {   
6156         if(!this.store || !this.cm){
6157             return;
6158         }
6159         if (this.selModel) {
6160             this.selModel.initEvents();
6161         }
6162         
6163         
6164         //Roo.log('initEvents with ds!!!!');
6165         
6166         this.mainBody = this.el.select('tbody', true).first();
6167         this.mainHead = this.el.select('thead', true).first();
6168         
6169         
6170         
6171         
6172         var _this = this;
6173         
6174         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6175             e.on('click', _this.sort, _this);
6176         });
6177         
6178         this.mainBody.on("click", this.onClick, this);
6179         this.mainBody.on("dblclick", this.onDblClick, this);
6180         
6181         // why is this done????? = it breaks dialogs??
6182         //this.parent().el.setStyle('position', 'relative');
6183         
6184         
6185         if (this.footer) {
6186             this.footer.parentId = this.id;
6187             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6188             
6189             if(this.lazyLoad){
6190                 this.el.select('tfoot tr td').first().addClass('hide');
6191             }
6192         } 
6193         
6194         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6195         
6196         this.store.on('load', this.onLoad, this);
6197         this.store.on('beforeload', this.onBeforeLoad, this);
6198         this.store.on('update', this.onUpdate, this);
6199         this.store.on('add', this.onAdd, this);
6200         this.store.on("clear", this.clear, this);
6201         
6202         this.el.on("contextmenu", this.onContextMenu, this);
6203         
6204         this.mainBody.on('scroll', this.onBodyScroll, this);
6205         
6206         this.cm.on("headerchange", this.onHeaderChange, this);
6207         
6208         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6209         
6210     },
6211     
6212     onContextMenu : function(e, t)
6213     {
6214         this.processEvent("contextmenu", e);
6215     },
6216     
6217     processEvent : function(name, e)
6218     {
6219         if (name != 'touchstart' ) {
6220             this.fireEvent(name, e);    
6221         }
6222         
6223         var t = e.getTarget();
6224         
6225         var cell = Roo.get(t);
6226         
6227         if(!cell){
6228             return;
6229         }
6230         
6231         if(cell.findParent('tfoot', false, true)){
6232             return;
6233         }
6234         
6235         if(cell.findParent('thead', false, true)){
6236             
6237             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6238                 cell = Roo.get(t).findParent('th', false, true);
6239                 if (!cell) {
6240                     Roo.log("failed to find th in thead?");
6241                     Roo.log(e.getTarget());
6242                     return;
6243                 }
6244             }
6245             
6246             var cellIndex = cell.dom.cellIndex;
6247             
6248             var ename = name == 'touchstart' ? 'click' : name;
6249             this.fireEvent("header" + ename, this, cellIndex, e);
6250             
6251             return;
6252         }
6253         
6254         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6255             cell = Roo.get(t).findParent('td', false, true);
6256             if (!cell) {
6257                 Roo.log("failed to find th in tbody?");
6258                 Roo.log(e.getTarget());
6259                 return;
6260             }
6261         }
6262         
6263         var row = cell.findParent('tr', false, true);
6264         var cellIndex = cell.dom.cellIndex;
6265         var rowIndex = row.dom.rowIndex - 1;
6266         
6267         if(row !== false){
6268             
6269             this.fireEvent("row" + name, this, rowIndex, e);
6270             
6271             if(cell !== false){
6272             
6273                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6274             }
6275         }
6276         
6277     },
6278     
6279     onMouseover : function(e, el)
6280     {
6281         var cell = Roo.get(el);
6282         
6283         if(!cell){
6284             return;
6285         }
6286         
6287         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6288             cell = cell.findParent('td', false, true);
6289         }
6290         
6291         var row = cell.findParent('tr', false, true);
6292         var cellIndex = cell.dom.cellIndex;
6293         var rowIndex = row.dom.rowIndex - 1; // start from 0
6294         
6295         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6296         
6297     },
6298     
6299     onMouseout : function(e, el)
6300     {
6301         var cell = Roo.get(el);
6302         
6303         if(!cell){
6304             return;
6305         }
6306         
6307         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6308             cell = cell.findParent('td', false, true);
6309         }
6310         
6311         var row = cell.findParent('tr', false, true);
6312         var cellIndex = cell.dom.cellIndex;
6313         var rowIndex = row.dom.rowIndex - 1; // start from 0
6314         
6315         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6316         
6317     },
6318     
6319     onClick : function(e, el)
6320     {
6321         var cell = Roo.get(el);
6322         
6323         if(!cell || (!this.cellSelection && !this.rowSelection)){
6324             return;
6325         }
6326         
6327         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6328             cell = cell.findParent('td', false, true);
6329         }
6330         
6331         if(!cell || typeof(cell) == 'undefined'){
6332             return;
6333         }
6334         
6335         var row = cell.findParent('tr', false, true);
6336         
6337         if(!row || typeof(row) == 'undefined'){
6338             return;
6339         }
6340         
6341         var cellIndex = cell.dom.cellIndex;
6342         var rowIndex = this.getRowIndex(row);
6343         
6344         // why??? - should these not be based on SelectionModel?
6345         if(this.cellSelection){
6346             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6347         }
6348         
6349         if(this.rowSelection){
6350             this.fireEvent('rowclick', this, row, rowIndex, e);
6351         }
6352         
6353         
6354     },
6355         
6356     onDblClick : function(e,el)
6357     {
6358         var cell = Roo.get(el);
6359         
6360         if(!cell || (!this.cellSelection && !this.rowSelection)){
6361             return;
6362         }
6363         
6364         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6365             cell = cell.findParent('td', false, true);
6366         }
6367         
6368         if(!cell || typeof(cell) == 'undefined'){
6369             return;
6370         }
6371         
6372         var row = cell.findParent('tr', false, true);
6373         
6374         if(!row || typeof(row) == 'undefined'){
6375             return;
6376         }
6377         
6378         var cellIndex = cell.dom.cellIndex;
6379         var rowIndex = this.getRowIndex(row);
6380         
6381         if(this.cellSelection){
6382             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6383         }
6384         
6385         if(this.rowSelection){
6386             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6387         }
6388     },
6389     
6390     sort : function(e,el)
6391     {
6392         var col = Roo.get(el);
6393         
6394         if(!col.hasClass('sortable')){
6395             return;
6396         }
6397         
6398         var sort = col.attr('sort');
6399         var dir = 'ASC';
6400         
6401         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6402             dir = 'DESC';
6403         }
6404         
6405         this.store.sortInfo = {field : sort, direction : dir};
6406         
6407         if (this.footer) {
6408             Roo.log("calling footer first");
6409             this.footer.onClick('first');
6410         } else {
6411         
6412             this.store.load({ params : { start : 0 } });
6413         }
6414     },
6415     
6416     renderHeader : function()
6417     {
6418         var header = {
6419             tag: 'thead',
6420             cn : []
6421         };
6422         
6423         var cm = this.cm;
6424         this.totalWidth = 0;
6425         
6426         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6427             
6428             var config = cm.config[i];
6429             
6430             var c = {
6431                 tag: 'th',
6432                 cls : 'x-hcol-' + i,
6433                 style : '',
6434                 html: cm.getColumnHeader(i)
6435             };
6436             
6437             var hh = '';
6438             
6439             if(typeof(config.sortable) != 'undefined' && config.sortable){
6440                 c.cls = 'sortable';
6441                 c.html = '<i class="glyphicon"></i>' + c.html;
6442             }
6443             
6444             if(typeof(config.lgHeader) != 'undefined'){
6445                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6446             }
6447             
6448             if(typeof(config.mdHeader) != 'undefined'){
6449                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6450             }
6451             
6452             if(typeof(config.smHeader) != 'undefined'){
6453                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6454             }
6455             
6456             if(typeof(config.xsHeader) != 'undefined'){
6457                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6458             }
6459             
6460             if(hh.length){
6461                 c.html = hh;
6462             }
6463             
6464             if(typeof(config.tooltip) != 'undefined'){
6465                 c.tooltip = config.tooltip;
6466             }
6467             
6468             if(typeof(config.colspan) != 'undefined'){
6469                 c.colspan = config.colspan;
6470             }
6471             
6472             if(typeof(config.hidden) != 'undefined' && config.hidden){
6473                 c.style += ' display:none;';
6474             }
6475             
6476             if(typeof(config.dataIndex) != 'undefined'){
6477                 c.sort = config.dataIndex;
6478             }
6479             
6480            
6481             
6482             if(typeof(config.align) != 'undefined' && config.align.length){
6483                 c.style += ' text-align:' + config.align + ';';
6484             }
6485             
6486             if(typeof(config.width) != 'undefined'){
6487                 c.style += ' width:' + config.width + 'px;';
6488                 this.totalWidth += config.width;
6489             } else {
6490                 this.totalWidth += 100; // assume minimum of 100 per column?
6491             }
6492             
6493             if(typeof(config.cls) != 'undefined'){
6494                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6495             }
6496             
6497             ['xs','sm','md','lg'].map(function(size){
6498                 
6499                 if(typeof(config[size]) == 'undefined'){
6500                     return;
6501                 }
6502                 
6503                 if (!config[size]) { // 0 = hidden
6504                     c.cls += ' hidden-' + size;
6505                     return;
6506                 }
6507                 
6508                 c.cls += ' col-' + size + '-' + config[size];
6509
6510             });
6511             
6512             header.cn.push(c)
6513         }
6514         
6515         return header;
6516     },
6517     
6518     renderBody : function()
6519     {
6520         var body = {
6521             tag: 'tbody',
6522             cn : [
6523                 {
6524                     tag: 'tr',
6525                     cn : [
6526                         {
6527                             tag : 'td',
6528                             colspan :  this.cm.getColumnCount()
6529                         }
6530                     ]
6531                 }
6532             ]
6533         };
6534         
6535         return body;
6536     },
6537     
6538     renderFooter : function()
6539     {
6540         var footer = {
6541             tag: 'tfoot',
6542             cn : [
6543                 {
6544                     tag: 'tr',
6545                     cn : [
6546                         {
6547                             tag : 'td',
6548                             colspan :  this.cm.getColumnCount()
6549                         }
6550                     ]
6551                 }
6552             ]
6553         };
6554         
6555         return footer;
6556     },
6557     
6558     
6559     
6560     onLoad : function()
6561     {
6562 //        Roo.log('ds onload');
6563         this.clear();
6564         
6565         var _this = this;
6566         var cm = this.cm;
6567         var ds = this.store;
6568         
6569         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6570             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6571             if (_this.store.sortInfo) {
6572                     
6573                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6574                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6575                 }
6576                 
6577                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6578                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6579                 }
6580             }
6581         });
6582         
6583         var tbody =  this.mainBody;
6584               
6585         if(ds.getCount() > 0){
6586             ds.data.each(function(d,rowIndex){
6587                 var row =  this.renderRow(cm, ds, rowIndex);
6588                 
6589                 tbody.createChild(row);
6590                 
6591                 var _this = this;
6592                 
6593                 if(row.cellObjects.length){
6594                     Roo.each(row.cellObjects, function(r){
6595                         _this.renderCellObject(r);
6596                     })
6597                 }
6598                 
6599             }, this);
6600         }
6601         
6602         Roo.each(this.el.select('tbody td', true).elements, function(e){
6603             e.on('mouseover', _this.onMouseover, _this);
6604         });
6605         
6606         Roo.each(this.el.select('tbody td', true).elements, function(e){
6607             e.on('mouseout', _this.onMouseout, _this);
6608         });
6609         this.fireEvent('rowsrendered', this);
6610         //if(this.loadMask){
6611         //    this.maskEl.hide();
6612         //}
6613         
6614         this.autoSize();
6615     },
6616     
6617     
6618     onUpdate : function(ds,record)
6619     {
6620         this.refreshRow(record);
6621         this.autoSize();
6622     },
6623     
6624     onRemove : function(ds, record, index, isUpdate){
6625         if(isUpdate !== true){
6626             this.fireEvent("beforerowremoved", this, index, record);
6627         }
6628         var bt = this.mainBody.dom;
6629         
6630         var rows = this.el.select('tbody > tr', true).elements;
6631         
6632         if(typeof(rows[index]) != 'undefined'){
6633             bt.removeChild(rows[index].dom);
6634         }
6635         
6636 //        if(bt.rows[index]){
6637 //            bt.removeChild(bt.rows[index]);
6638 //        }
6639         
6640         if(isUpdate !== true){
6641             //this.stripeRows(index);
6642             //this.syncRowHeights(index, index);
6643             //this.layout();
6644             this.fireEvent("rowremoved", this, index, record);
6645         }
6646     },
6647     
6648     onAdd : function(ds, records, rowIndex)
6649     {
6650         //Roo.log('on Add called');
6651         // - note this does not handle multiple adding very well..
6652         var bt = this.mainBody.dom;
6653         for (var i =0 ; i < records.length;i++) {
6654             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6655             //Roo.log(records[i]);
6656             //Roo.log(this.store.getAt(rowIndex+i));
6657             this.insertRow(this.store, rowIndex + i, false);
6658             return;
6659         }
6660         
6661     },
6662     
6663     
6664     refreshRow : function(record){
6665         var ds = this.store, index;
6666         if(typeof record == 'number'){
6667             index = record;
6668             record = ds.getAt(index);
6669         }else{
6670             index = ds.indexOf(record);
6671         }
6672         this.insertRow(ds, index, true);
6673         this.autoSize();
6674         this.onRemove(ds, record, index+1, true);
6675         this.autoSize();
6676         //this.syncRowHeights(index, index);
6677         //this.layout();
6678         this.fireEvent("rowupdated", this, index, record);
6679     },
6680     
6681     insertRow : function(dm, rowIndex, isUpdate){
6682         
6683         if(!isUpdate){
6684             this.fireEvent("beforerowsinserted", this, rowIndex);
6685         }
6686             //var s = this.getScrollState();
6687         var row = this.renderRow(this.cm, this.store, rowIndex);
6688         // insert before rowIndex..
6689         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6690         
6691         var _this = this;
6692                 
6693         if(row.cellObjects.length){
6694             Roo.each(row.cellObjects, function(r){
6695                 _this.renderCellObject(r);
6696             })
6697         }
6698             
6699         if(!isUpdate){
6700             this.fireEvent("rowsinserted", this, rowIndex);
6701             //this.syncRowHeights(firstRow, lastRow);
6702             //this.stripeRows(firstRow);
6703             //this.layout();
6704         }
6705         
6706     },
6707     
6708     
6709     getRowDom : function(rowIndex)
6710     {
6711         var rows = this.el.select('tbody > tr', true).elements;
6712         
6713         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6714         
6715     },
6716     // returns the object tree for a tr..
6717   
6718     
6719     renderRow : function(cm, ds, rowIndex) 
6720     {
6721         var d = ds.getAt(rowIndex);
6722         
6723         var row = {
6724             tag : 'tr',
6725             cls : 'x-row-' + rowIndex,
6726             cn : []
6727         };
6728             
6729         var cellObjects = [];
6730         
6731         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6732             var config = cm.config[i];
6733             
6734             var renderer = cm.getRenderer(i);
6735             var value = '';
6736             var id = false;
6737             
6738             if(typeof(renderer) !== 'undefined'){
6739                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6740             }
6741             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6742             // and are rendered into the cells after the row is rendered - using the id for the element.
6743             
6744             if(typeof(value) === 'object'){
6745                 id = Roo.id();
6746                 cellObjects.push({
6747                     container : id,
6748                     cfg : value 
6749                 })
6750             }
6751             
6752             var rowcfg = {
6753                 record: d,
6754                 rowIndex : rowIndex,
6755                 colIndex : i,
6756                 rowClass : ''
6757             };
6758
6759             this.fireEvent('rowclass', this, rowcfg);
6760             
6761             var td = {
6762                 tag: 'td',
6763                 cls : rowcfg.rowClass + ' x-col-' + i,
6764                 style: '',
6765                 html: (typeof(value) === 'object') ? '' : value
6766             };
6767             
6768             if (id) {
6769                 td.id = id;
6770             }
6771             
6772             if(typeof(config.colspan) != 'undefined'){
6773                 td.colspan = config.colspan;
6774             }
6775             
6776             if(typeof(config.hidden) != 'undefined' && config.hidden){
6777                 td.style += ' display:none;';
6778             }
6779             
6780             if(typeof(config.align) != 'undefined' && config.align.length){
6781                 td.style += ' text-align:' + config.align + ';';
6782             }
6783             
6784             if(typeof(config.width) != 'undefined'){
6785                 td.style += ' width:' +  config.width + 'px;';
6786             }
6787             
6788             if(typeof(config.cursor) != 'undefined'){
6789                 td.style += ' cursor:' +  config.cursor + ';';
6790             }
6791             
6792             if(typeof(config.cls) != 'undefined'){
6793                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6794             }
6795             
6796             ['xs','sm','md','lg'].map(function(size){
6797                 
6798                 if(typeof(config[size]) == 'undefined'){
6799                     return;
6800                 }
6801                 
6802                 if (!config[size]) { // 0 = hidden
6803                     td.cls += ' hidden-' + size;
6804                     return;
6805                 }
6806                 
6807                 td.cls += ' col-' + size + '-' + config[size];
6808
6809             });
6810             
6811             row.cn.push(td);
6812            
6813         }
6814         
6815         row.cellObjects = cellObjects;
6816         
6817         return row;
6818           
6819     },
6820     
6821     
6822     
6823     onBeforeLoad : function()
6824     {
6825         //Roo.log('ds onBeforeLoad');
6826         
6827         //this.clear();
6828         
6829         //if(this.loadMask){
6830         //    this.maskEl.show();
6831         //}
6832     },
6833      /**
6834      * Remove all rows
6835      */
6836     clear : function()
6837     {
6838         this.el.select('tbody', true).first().dom.innerHTML = '';
6839     },
6840     /**
6841      * Show or hide a row.
6842      * @param {Number} rowIndex to show or hide
6843      * @param {Boolean} state hide
6844      */
6845     setRowVisibility : function(rowIndex, state)
6846     {
6847         var bt = this.mainBody.dom;
6848         
6849         var rows = this.el.select('tbody > tr', true).elements;
6850         
6851         if(typeof(rows[rowIndex]) == 'undefined'){
6852             return;
6853         }
6854         rows[rowIndex].dom.style.display = state ? '' : 'none';
6855     },
6856     
6857     
6858     getSelectionModel : function(){
6859         if(!this.selModel){
6860             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6861         }
6862         return this.selModel;
6863     },
6864     /*
6865      * Render the Roo.bootstrap object from renderder
6866      */
6867     renderCellObject : function(r)
6868     {
6869         var _this = this;
6870         
6871         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6872         
6873         var t = r.cfg.render(r.container);
6874         
6875         if(r.cfg.cn){
6876             Roo.each(r.cfg.cn, function(c){
6877                 var child = {
6878                     container: t.getChildContainer(),
6879                     cfg: c
6880                 };
6881                 _this.renderCellObject(child);
6882             })
6883         }
6884     },
6885     
6886     getRowIndex : function(row)
6887     {
6888         var rowIndex = -1;
6889         
6890         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6891             if(el != row){
6892                 return;
6893             }
6894             
6895             rowIndex = index;
6896         });
6897         
6898         return rowIndex;
6899     },
6900      /**
6901      * Returns the grid's underlying element = used by panel.Grid
6902      * @return {Element} The element
6903      */
6904     getGridEl : function(){
6905         return this.el;
6906     },
6907      /**
6908      * Forces a resize - used by panel.Grid
6909      * @return {Element} The element
6910      */
6911     autoSize : function()
6912     {
6913         //var ctr = Roo.get(this.container.dom.parentElement);
6914         var ctr = Roo.get(this.el.dom);
6915         
6916         var thd = this.getGridEl().select('thead',true).first();
6917         var tbd = this.getGridEl().select('tbody', true).first();
6918         var tfd = this.getGridEl().select('tfoot', true).first();
6919         
6920         var cw = ctr.getWidth();
6921         
6922         if (tbd) {
6923             
6924             tbd.setSize(ctr.getWidth(),
6925                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6926             );
6927             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6928             cw -= barsize;
6929         }
6930         cw = Math.max(cw, this.totalWidth);
6931         this.getGridEl().select('tr',true).setWidth(cw);
6932         // resize 'expandable coloumn?
6933         
6934         return; // we doe not have a view in this design..
6935         
6936     },
6937     onBodyScroll: function()
6938     {
6939         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6940         if(this.mainHead){
6941             this.mainHead.setStyle({
6942                 'position' : 'relative',
6943                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6944             });
6945         }
6946         
6947         if(this.lazyLoad){
6948             
6949             var scrollHeight = this.mainBody.dom.scrollHeight;
6950             
6951             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6952             
6953             var height = this.mainBody.getHeight();
6954             
6955             if(scrollHeight - height == scrollTop) {
6956                 
6957                 var total = this.ds.getTotalCount();
6958                 
6959                 if(this.footer.cursor + this.footer.pageSize < total){
6960                     
6961                     this.footer.ds.load({
6962                         params : {
6963                             start : this.footer.cursor + this.footer.pageSize,
6964                             limit : this.footer.pageSize
6965                         },
6966                         add : true
6967                     });
6968                 }
6969             }
6970             
6971         }
6972     },
6973     
6974     onHeaderChange : function()
6975     {
6976         var header = this.renderHeader();
6977         var table = this.el.select('table', true).first();
6978         
6979         this.mainHead.remove();
6980         this.mainHead = table.createChild(header, this.mainBody, false);
6981     },
6982     
6983     onHiddenChange : function(colModel, colIndex, hidden)
6984     {
6985         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6986         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6987         
6988         this.CSS.updateRule(thSelector, "display", "");
6989         this.CSS.updateRule(tdSelector, "display", "");
6990         
6991         if(hidden){
6992             this.CSS.updateRule(thSelector, "display", "none");
6993             this.CSS.updateRule(tdSelector, "display", "none");
6994         }
6995         
6996         this.onHeaderChange();
6997         this.onLoad();
6998         
6999     }
7000     
7001 });
7002
7003  
7004
7005  /*
7006  * - LGPL
7007  *
7008  * table cell
7009  * 
7010  */
7011
7012 /**
7013  * @class Roo.bootstrap.TableCell
7014  * @extends Roo.bootstrap.Component
7015  * Bootstrap TableCell class
7016  * @cfg {String} html cell contain text
7017  * @cfg {String} cls cell class
7018  * @cfg {String} tag cell tag (td|th) default td
7019  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7020  * @cfg {String} align Aligns the content in a cell
7021  * @cfg {String} axis Categorizes cells
7022  * @cfg {String} bgcolor Specifies the background color of a cell
7023  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7024  * @cfg {Number} colspan Specifies the number of columns a cell should span
7025  * @cfg {String} headers Specifies one or more header cells a cell is related to
7026  * @cfg {Number} height Sets the height of a cell
7027  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7028  * @cfg {Number} rowspan Sets the number of rows a cell should span
7029  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7030  * @cfg {String} valign Vertical aligns the content in a cell
7031  * @cfg {Number} width Specifies the width of a cell
7032  * 
7033  * @constructor
7034  * Create a new TableCell
7035  * @param {Object} config The config object
7036  */
7037
7038 Roo.bootstrap.TableCell = function(config){
7039     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7040 };
7041
7042 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7043     
7044     html: false,
7045     cls: false,
7046     tag: false,
7047     abbr: false,
7048     align: false,
7049     axis: false,
7050     bgcolor: false,
7051     charoff: false,
7052     colspan: false,
7053     headers: false,
7054     height: false,
7055     nowrap: false,
7056     rowspan: false,
7057     scope: false,
7058     valign: false,
7059     width: false,
7060     
7061     
7062     getAutoCreate : function(){
7063         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7064         
7065         cfg = {
7066             tag: 'td'
7067         };
7068         
7069         if(this.tag){
7070             cfg.tag = this.tag;
7071         }
7072         
7073         if (this.html) {
7074             cfg.html=this.html
7075         }
7076         if (this.cls) {
7077             cfg.cls=this.cls
7078         }
7079         if (this.abbr) {
7080             cfg.abbr=this.abbr
7081         }
7082         if (this.align) {
7083             cfg.align=this.align
7084         }
7085         if (this.axis) {
7086             cfg.axis=this.axis
7087         }
7088         if (this.bgcolor) {
7089             cfg.bgcolor=this.bgcolor
7090         }
7091         if (this.charoff) {
7092             cfg.charoff=this.charoff
7093         }
7094         if (this.colspan) {
7095             cfg.colspan=this.colspan
7096         }
7097         if (this.headers) {
7098             cfg.headers=this.headers
7099         }
7100         if (this.height) {
7101             cfg.height=this.height
7102         }
7103         if (this.nowrap) {
7104             cfg.nowrap=this.nowrap
7105         }
7106         if (this.rowspan) {
7107             cfg.rowspan=this.rowspan
7108         }
7109         if (this.scope) {
7110             cfg.scope=this.scope
7111         }
7112         if (this.valign) {
7113             cfg.valign=this.valign
7114         }
7115         if (this.width) {
7116             cfg.width=this.width
7117         }
7118         
7119         
7120         return cfg;
7121     }
7122    
7123 });
7124
7125  
7126
7127  /*
7128  * - LGPL
7129  *
7130  * table row
7131  * 
7132  */
7133
7134 /**
7135  * @class Roo.bootstrap.TableRow
7136  * @extends Roo.bootstrap.Component
7137  * Bootstrap TableRow class
7138  * @cfg {String} cls row class
7139  * @cfg {String} align Aligns the content in a table row
7140  * @cfg {String} bgcolor Specifies a background color for a table row
7141  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7142  * @cfg {String} valign Vertical aligns the content in a table row
7143  * 
7144  * @constructor
7145  * Create a new TableRow
7146  * @param {Object} config The config object
7147  */
7148
7149 Roo.bootstrap.TableRow = function(config){
7150     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7151 };
7152
7153 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7154     
7155     cls: false,
7156     align: false,
7157     bgcolor: false,
7158     charoff: false,
7159     valign: false,
7160     
7161     getAutoCreate : function(){
7162         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7163         
7164         cfg = {
7165             tag: 'tr'
7166         };
7167             
7168         if(this.cls){
7169             cfg.cls = this.cls;
7170         }
7171         if(this.align){
7172             cfg.align = this.align;
7173         }
7174         if(this.bgcolor){
7175             cfg.bgcolor = this.bgcolor;
7176         }
7177         if(this.charoff){
7178             cfg.charoff = this.charoff;
7179         }
7180         if(this.valign){
7181             cfg.valign = this.valign;
7182         }
7183         
7184         return cfg;
7185     }
7186    
7187 });
7188
7189  
7190
7191  /*
7192  * - LGPL
7193  *
7194  * table body
7195  * 
7196  */
7197
7198 /**
7199  * @class Roo.bootstrap.TableBody
7200  * @extends Roo.bootstrap.Component
7201  * Bootstrap TableBody class
7202  * @cfg {String} cls element class
7203  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7204  * @cfg {String} align Aligns the content inside the element
7205  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7206  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7207  * 
7208  * @constructor
7209  * Create a new TableBody
7210  * @param {Object} config The config object
7211  */
7212
7213 Roo.bootstrap.TableBody = function(config){
7214     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7215 };
7216
7217 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7218     
7219     cls: false,
7220     tag: false,
7221     align: false,
7222     charoff: false,
7223     valign: false,
7224     
7225     getAutoCreate : function(){
7226         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7227         
7228         cfg = {
7229             tag: 'tbody'
7230         };
7231             
7232         if (this.cls) {
7233             cfg.cls=this.cls
7234         }
7235         if(this.tag){
7236             cfg.tag = this.tag;
7237         }
7238         
7239         if(this.align){
7240             cfg.align = this.align;
7241         }
7242         if(this.charoff){
7243             cfg.charoff = this.charoff;
7244         }
7245         if(this.valign){
7246             cfg.valign = this.valign;
7247         }
7248         
7249         return cfg;
7250     }
7251     
7252     
7253 //    initEvents : function()
7254 //    {
7255 //        
7256 //        if(!this.store){
7257 //            return;
7258 //        }
7259 //        
7260 //        this.store = Roo.factory(this.store, Roo.data);
7261 //        this.store.on('load', this.onLoad, this);
7262 //        
7263 //        this.store.load();
7264 //        
7265 //    },
7266 //    
7267 //    onLoad: function () 
7268 //    {   
7269 //        this.fireEvent('load', this);
7270 //    }
7271 //    
7272 //   
7273 });
7274
7275  
7276
7277  /*
7278  * Based on:
7279  * Ext JS Library 1.1.1
7280  * Copyright(c) 2006-2007, Ext JS, LLC.
7281  *
7282  * Originally Released Under LGPL - original licence link has changed is not relivant.
7283  *
7284  * Fork - LGPL
7285  * <script type="text/javascript">
7286  */
7287
7288 // as we use this in bootstrap.
7289 Roo.namespace('Roo.form');
7290  /**
7291  * @class Roo.form.Action
7292  * Internal Class used to handle form actions
7293  * @constructor
7294  * @param {Roo.form.BasicForm} el The form element or its id
7295  * @param {Object} config Configuration options
7296  */
7297
7298  
7299  
7300 // define the action interface
7301 Roo.form.Action = function(form, options){
7302     this.form = form;
7303     this.options = options || {};
7304 };
7305 /**
7306  * Client Validation Failed
7307  * @const 
7308  */
7309 Roo.form.Action.CLIENT_INVALID = 'client';
7310 /**
7311  * Server Validation Failed
7312  * @const 
7313  */
7314 Roo.form.Action.SERVER_INVALID = 'server';
7315  /**
7316  * Connect to Server Failed
7317  * @const 
7318  */
7319 Roo.form.Action.CONNECT_FAILURE = 'connect';
7320 /**
7321  * Reading Data from Server Failed
7322  * @const 
7323  */
7324 Roo.form.Action.LOAD_FAILURE = 'load';
7325
7326 Roo.form.Action.prototype = {
7327     type : 'default',
7328     failureType : undefined,
7329     response : undefined,
7330     result : undefined,
7331
7332     // interface method
7333     run : function(options){
7334
7335     },
7336
7337     // interface method
7338     success : function(response){
7339
7340     },
7341
7342     // interface method
7343     handleResponse : function(response){
7344
7345     },
7346
7347     // default connection failure
7348     failure : function(response){
7349         
7350         this.response = response;
7351         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7352         this.form.afterAction(this, false);
7353     },
7354
7355     processResponse : function(response){
7356         this.response = response;
7357         if(!response.responseText){
7358             return true;
7359         }
7360         this.result = this.handleResponse(response);
7361         return this.result;
7362     },
7363
7364     // utility functions used internally
7365     getUrl : function(appendParams){
7366         var url = this.options.url || this.form.url || this.form.el.dom.action;
7367         if(appendParams){
7368             var p = this.getParams();
7369             if(p){
7370                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7371             }
7372         }
7373         return url;
7374     },
7375
7376     getMethod : function(){
7377         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7378     },
7379
7380     getParams : function(){
7381         var bp = this.form.baseParams;
7382         var p = this.options.params;
7383         if(p){
7384             if(typeof p == "object"){
7385                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7386             }else if(typeof p == 'string' && bp){
7387                 p += '&' + Roo.urlEncode(bp);
7388             }
7389         }else if(bp){
7390             p = Roo.urlEncode(bp);
7391         }
7392         return p;
7393     },
7394
7395     createCallback : function(){
7396         return {
7397             success: this.success,
7398             failure: this.failure,
7399             scope: this,
7400             timeout: (this.form.timeout*1000),
7401             upload: this.form.fileUpload ? this.success : undefined
7402         };
7403     }
7404 };
7405
7406 Roo.form.Action.Submit = function(form, options){
7407     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7408 };
7409
7410 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7411     type : 'submit',
7412
7413     haveProgress : false,
7414     uploadComplete : false,
7415     
7416     // uploadProgress indicator.
7417     uploadProgress : function()
7418     {
7419         if (!this.form.progressUrl) {
7420             return;
7421         }
7422         
7423         if (!this.haveProgress) {
7424             Roo.MessageBox.progress("Uploading", "Uploading");
7425         }
7426         if (this.uploadComplete) {
7427            Roo.MessageBox.hide();
7428            return;
7429         }
7430         
7431         this.haveProgress = true;
7432    
7433         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7434         
7435         var c = new Roo.data.Connection();
7436         c.request({
7437             url : this.form.progressUrl,
7438             params: {
7439                 id : uid
7440             },
7441             method: 'GET',
7442             success : function(req){
7443                //console.log(data);
7444                 var rdata = false;
7445                 var edata;
7446                 try  {
7447                    rdata = Roo.decode(req.responseText)
7448                 } catch (e) {
7449                     Roo.log("Invalid data from server..");
7450                     Roo.log(edata);
7451                     return;
7452                 }
7453                 if (!rdata || !rdata.success) {
7454                     Roo.log(rdata);
7455                     Roo.MessageBox.alert(Roo.encode(rdata));
7456                     return;
7457                 }
7458                 var data = rdata.data;
7459                 
7460                 if (this.uploadComplete) {
7461                    Roo.MessageBox.hide();
7462                    return;
7463                 }
7464                    
7465                 if (data){
7466                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7467                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7468                     );
7469                 }
7470                 this.uploadProgress.defer(2000,this);
7471             },
7472        
7473             failure: function(data) {
7474                 Roo.log('progress url failed ');
7475                 Roo.log(data);
7476             },
7477             scope : this
7478         });
7479            
7480     },
7481     
7482     
7483     run : function()
7484     {
7485         // run get Values on the form, so it syncs any secondary forms.
7486         this.form.getValues();
7487         
7488         var o = this.options;
7489         var method = this.getMethod();
7490         var isPost = method == 'POST';
7491         if(o.clientValidation === false || this.form.isValid()){
7492             
7493             if (this.form.progressUrl) {
7494                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7495                     (new Date() * 1) + '' + Math.random());
7496                     
7497             } 
7498             
7499             
7500             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7501                 form:this.form.el.dom,
7502                 url:this.getUrl(!isPost),
7503                 method: method,
7504                 params:isPost ? this.getParams() : null,
7505                 isUpload: this.form.fileUpload
7506             }));
7507             
7508             this.uploadProgress();
7509
7510         }else if (o.clientValidation !== false){ // client validation failed
7511             this.failureType = Roo.form.Action.CLIENT_INVALID;
7512             this.form.afterAction(this, false);
7513         }
7514     },
7515
7516     success : function(response)
7517     {
7518         this.uploadComplete= true;
7519         if (this.haveProgress) {
7520             Roo.MessageBox.hide();
7521         }
7522         
7523         
7524         var result = this.processResponse(response);
7525         if(result === true || result.success){
7526             this.form.afterAction(this, true);
7527             return;
7528         }
7529         if(result.errors){
7530             this.form.markInvalid(result.errors);
7531             this.failureType = Roo.form.Action.SERVER_INVALID;
7532         }
7533         this.form.afterAction(this, false);
7534     },
7535     failure : function(response)
7536     {
7537         this.uploadComplete= true;
7538         if (this.haveProgress) {
7539             Roo.MessageBox.hide();
7540         }
7541         
7542         this.response = response;
7543         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7544         this.form.afterAction(this, false);
7545     },
7546     
7547     handleResponse : function(response){
7548         if(this.form.errorReader){
7549             var rs = this.form.errorReader.read(response);
7550             var errors = [];
7551             if(rs.records){
7552                 for(var i = 0, len = rs.records.length; i < len; i++) {
7553                     var r = rs.records[i];
7554                     errors[i] = r.data;
7555                 }
7556             }
7557             if(errors.length < 1){
7558                 errors = null;
7559             }
7560             return {
7561                 success : rs.success,
7562                 errors : errors
7563             };
7564         }
7565         var ret = false;
7566         try {
7567             ret = Roo.decode(response.responseText);
7568         } catch (e) {
7569             ret = {
7570                 success: false,
7571                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7572                 errors : []
7573             };
7574         }
7575         return ret;
7576         
7577     }
7578 });
7579
7580
7581 Roo.form.Action.Load = function(form, options){
7582     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7583     this.reader = this.form.reader;
7584 };
7585
7586 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7587     type : 'load',
7588
7589     run : function(){
7590         
7591         Roo.Ajax.request(Roo.apply(
7592                 this.createCallback(), {
7593                     method:this.getMethod(),
7594                     url:this.getUrl(false),
7595                     params:this.getParams()
7596         }));
7597     },
7598
7599     success : function(response){
7600         
7601         var result = this.processResponse(response);
7602         if(result === true || !result.success || !result.data){
7603             this.failureType = Roo.form.Action.LOAD_FAILURE;
7604             this.form.afterAction(this, false);
7605             return;
7606         }
7607         this.form.clearInvalid();
7608         this.form.setValues(result.data);
7609         this.form.afterAction(this, true);
7610     },
7611
7612     handleResponse : function(response){
7613         if(this.form.reader){
7614             var rs = this.form.reader.read(response);
7615             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7616             return {
7617                 success : rs.success,
7618                 data : data
7619             };
7620         }
7621         return Roo.decode(response.responseText);
7622     }
7623 });
7624
7625 Roo.form.Action.ACTION_TYPES = {
7626     'load' : Roo.form.Action.Load,
7627     'submit' : Roo.form.Action.Submit
7628 };/*
7629  * - LGPL
7630  *
7631  * form
7632  *
7633  */
7634
7635 /**
7636  * @class Roo.bootstrap.Form
7637  * @extends Roo.bootstrap.Component
7638  * Bootstrap Form class
7639  * @cfg {String} method  GET | POST (default POST)
7640  * @cfg {String} labelAlign top | left (default top)
7641  * @cfg {String} align left  | right - for navbars
7642  * @cfg {Boolean} loadMask load mask when submit (default true)
7643
7644  *
7645  * @constructor
7646  * Create a new Form
7647  * @param {Object} config The config object
7648  */
7649
7650
7651 Roo.bootstrap.Form = function(config){
7652     
7653     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7654     
7655     Roo.bootstrap.Form.popover.apply();
7656     
7657     this.addEvents({
7658         /**
7659          * @event clientvalidation
7660          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7661          * @param {Form} this
7662          * @param {Boolean} valid true if the form has passed client-side validation
7663          */
7664         clientvalidation: true,
7665         /**
7666          * @event beforeaction
7667          * Fires before any action is performed. Return false to cancel the action.
7668          * @param {Form} this
7669          * @param {Action} action The action to be performed
7670          */
7671         beforeaction: true,
7672         /**
7673          * @event actionfailed
7674          * Fires when an action fails.
7675          * @param {Form} this
7676          * @param {Action} action The action that failed
7677          */
7678         actionfailed : true,
7679         /**
7680          * @event actioncomplete
7681          * Fires when an action is completed.
7682          * @param {Form} this
7683          * @param {Action} action The action that completed
7684          */
7685         actioncomplete : true
7686     });
7687 };
7688
7689 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7690
7691      /**
7692      * @cfg {String} method
7693      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7694      */
7695     method : 'POST',
7696     /**
7697      * @cfg {String} url
7698      * The URL to use for form actions if one isn't supplied in the action options.
7699      */
7700     /**
7701      * @cfg {Boolean} fileUpload
7702      * Set to true if this form is a file upload.
7703      */
7704
7705     /**
7706      * @cfg {Object} baseParams
7707      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7708      */
7709
7710     /**
7711      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7712      */
7713     timeout: 30,
7714     /**
7715      * @cfg {Sting} align (left|right) for navbar forms
7716      */
7717     align : 'left',
7718
7719     // private
7720     activeAction : null,
7721
7722     /**
7723      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7724      * element by passing it or its id or mask the form itself by passing in true.
7725      * @type Mixed
7726      */
7727     waitMsgTarget : false,
7728
7729     loadMask : true,
7730     
7731     /**
7732      * @cfg {Boolean} errorMask (true|false) default false
7733      */
7734     errorMask : false,
7735     
7736     /**
7737      * @cfg {Number} maskOffset Default 100
7738      */
7739     maskOffset : 100,
7740     
7741     /**
7742      * @cfg {Boolean} maskBody
7743      */
7744     maskBody : false,
7745
7746     getAutoCreate : function(){
7747
7748         var cfg = {
7749             tag: 'form',
7750             method : this.method || 'POST',
7751             id : this.id || Roo.id(),
7752             cls : ''
7753         };
7754         if (this.parent().xtype.match(/^Nav/)) {
7755             cfg.cls = 'navbar-form navbar-' + this.align;
7756
7757         }
7758
7759         if (this.labelAlign == 'left' ) {
7760             cfg.cls += ' form-horizontal';
7761         }
7762
7763
7764         return cfg;
7765     },
7766     initEvents : function()
7767     {
7768         this.el.on('submit', this.onSubmit, this);
7769         // this was added as random key presses on the form where triggering form submit.
7770         this.el.on('keypress', function(e) {
7771             if (e.getCharCode() != 13) {
7772                 return true;
7773             }
7774             // we might need to allow it for textareas.. and some other items.
7775             // check e.getTarget().
7776
7777             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7778                 return true;
7779             }
7780
7781             Roo.log("keypress blocked");
7782
7783             e.preventDefault();
7784             return false;
7785         });
7786         
7787     },
7788     // private
7789     onSubmit : function(e){
7790         e.stopEvent();
7791     },
7792
7793      /**
7794      * Returns true if client-side validation on the form is successful.
7795      * @return Boolean
7796      */
7797     isValid : function(){
7798         var items = this.getItems();
7799         var valid = true;
7800         var target = false;
7801         
7802         items.each(function(f){
7803             
7804             if(f.validate()){
7805                 return;
7806             }
7807             valid = false;
7808
7809             if(!target && f.el.isVisible(true)){
7810                 target = f;
7811             }
7812            
7813         });
7814         
7815         if(this.errorMask && !valid){
7816             Roo.bootstrap.Form.popover.mask(this, target);
7817         }
7818         
7819         return valid;
7820     },
7821     
7822     /**
7823      * Returns true if any fields in this form have changed since their original load.
7824      * @return Boolean
7825      */
7826     isDirty : function(){
7827         var dirty = false;
7828         var items = this.getItems();
7829         items.each(function(f){
7830            if(f.isDirty()){
7831                dirty = true;
7832                return false;
7833            }
7834            return true;
7835         });
7836         return dirty;
7837     },
7838      /**
7839      * Performs a predefined action (submit or load) or custom actions you define on this form.
7840      * @param {String} actionName The name of the action type
7841      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7842      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7843      * accept other config options):
7844      * <pre>
7845 Property          Type             Description
7846 ----------------  ---------------  ----------------------------------------------------------------------------------
7847 url               String           The url for the action (defaults to the form's url)
7848 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7849 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7850 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7851                                    validate the form on the client (defaults to false)
7852      * </pre>
7853      * @return {BasicForm} this
7854      */
7855     doAction : function(action, options){
7856         if(typeof action == 'string'){
7857             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7858         }
7859         if(this.fireEvent('beforeaction', this, action) !== false){
7860             this.beforeAction(action);
7861             action.run.defer(100, action);
7862         }
7863         return this;
7864     },
7865
7866     // private
7867     beforeAction : function(action){
7868         var o = action.options;
7869         
7870         if(this.loadMask){
7871             
7872             if(this.maskBody){
7873                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7874             } else {
7875                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7876             }
7877         }
7878         // not really supported yet.. ??
7879
7880         //if(this.waitMsgTarget === true){
7881         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7882         //}else if(this.waitMsgTarget){
7883         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7884         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7885         //}else {
7886         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7887        // }
7888
7889     },
7890
7891     // private
7892     afterAction : function(action, success){
7893         this.activeAction = null;
7894         var o = action.options;
7895
7896         if(this.loadMask){
7897             
7898             if(this.maskBody){
7899                 Roo.get(document.body).unmask();
7900             } else {
7901                 this.el.unmask();
7902             }
7903         }
7904         
7905         //if(this.waitMsgTarget === true){
7906 //            this.el.unmask();
7907         //}else if(this.waitMsgTarget){
7908         //    this.waitMsgTarget.unmask();
7909         //}else{
7910         //    Roo.MessageBox.updateProgress(1);
7911         //    Roo.MessageBox.hide();
7912        // }
7913         //
7914         if(success){
7915             if(o.reset){
7916                 this.reset();
7917             }
7918             Roo.callback(o.success, o.scope, [this, action]);
7919             this.fireEvent('actioncomplete', this, action);
7920
7921         }else{
7922
7923             // failure condition..
7924             // we have a scenario where updates need confirming.
7925             // eg. if a locking scenario exists..
7926             // we look for { errors : { needs_confirm : true }} in the response.
7927             if (
7928                 (typeof(action.result) != 'undefined')  &&
7929                 (typeof(action.result.errors) != 'undefined')  &&
7930                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7931            ){
7932                 var _t = this;
7933                 Roo.log("not supported yet");
7934                  /*
7935
7936                 Roo.MessageBox.confirm(
7937                     "Change requires confirmation",
7938                     action.result.errorMsg,
7939                     function(r) {
7940                         if (r != 'yes') {
7941                             return;
7942                         }
7943                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7944                     }
7945
7946                 );
7947                 */
7948
7949
7950                 return;
7951             }
7952
7953             Roo.callback(o.failure, o.scope, [this, action]);
7954             // show an error message if no failed handler is set..
7955             if (!this.hasListener('actionfailed')) {
7956                 Roo.log("need to add dialog support");
7957                 /*
7958                 Roo.MessageBox.alert("Error",
7959                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7960                         action.result.errorMsg :
7961                         "Saving Failed, please check your entries or try again"
7962                 );
7963                 */
7964             }
7965
7966             this.fireEvent('actionfailed', this, action);
7967         }
7968
7969     },
7970     /**
7971      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7972      * @param {String} id The value to search for
7973      * @return Field
7974      */
7975     findField : function(id){
7976         var items = this.getItems();
7977         var field = items.get(id);
7978         if(!field){
7979              items.each(function(f){
7980                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7981                     field = f;
7982                     return false;
7983                 }
7984                 return true;
7985             });
7986         }
7987         return field || null;
7988     },
7989      /**
7990      * Mark fields in this form invalid in bulk.
7991      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7992      * @return {BasicForm} this
7993      */
7994     markInvalid : function(errors){
7995         if(errors instanceof Array){
7996             for(var i = 0, len = errors.length; i < len; i++){
7997                 var fieldError = errors[i];
7998                 var f = this.findField(fieldError.id);
7999                 if(f){
8000                     f.markInvalid(fieldError.msg);
8001                 }
8002             }
8003         }else{
8004             var field, id;
8005             for(id in errors){
8006                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8007                     field.markInvalid(errors[id]);
8008                 }
8009             }
8010         }
8011         //Roo.each(this.childForms || [], function (f) {
8012         //    f.markInvalid(errors);
8013         //});
8014
8015         return this;
8016     },
8017
8018     /**
8019      * Set values for fields in this form in bulk.
8020      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8021      * @return {BasicForm} this
8022      */
8023     setValues : function(values){
8024         if(values instanceof Array){ // array of objects
8025             for(var i = 0, len = values.length; i < len; i++){
8026                 var v = values[i];
8027                 var f = this.findField(v.id);
8028                 if(f){
8029                     f.setValue(v.value);
8030                     if(this.trackResetOnLoad){
8031                         f.originalValue = f.getValue();
8032                     }
8033                 }
8034             }
8035         }else{ // object hash
8036             var field, id;
8037             for(id in values){
8038                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8039
8040                     if (field.setFromData &&
8041                         field.valueField &&
8042                         field.displayField &&
8043                         // combos' with local stores can
8044                         // be queried via setValue()
8045                         // to set their value..
8046                         (field.store && !field.store.isLocal)
8047                         ) {
8048                         // it's a combo
8049                         var sd = { };
8050                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8051                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8052                         field.setFromData(sd);
8053
8054                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8055                         
8056                         field.setFromData(values);
8057                         
8058                     } else {
8059                         field.setValue(values[id]);
8060                     }
8061
8062
8063                     if(this.trackResetOnLoad){
8064                         field.originalValue = field.getValue();
8065                     }
8066                 }
8067             }
8068         }
8069
8070         //Roo.each(this.childForms || [], function (f) {
8071         //    f.setValues(values);
8072         //});
8073
8074         return this;
8075     },
8076
8077     /**
8078      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8079      * they are returned as an array.
8080      * @param {Boolean} asString
8081      * @return {Object}
8082      */
8083     getValues : function(asString){
8084         //if (this.childForms) {
8085             // copy values from the child forms
8086         //    Roo.each(this.childForms, function (f) {
8087         //        this.setValues(f.getValues());
8088         //    }, this);
8089         //}
8090
8091
8092
8093         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8094         if(asString === true){
8095             return fs;
8096         }
8097         return Roo.urlDecode(fs);
8098     },
8099
8100     /**
8101      * Returns the fields in this form as an object with key/value pairs.
8102      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8103      * @return {Object}
8104      */
8105     getFieldValues : function(with_hidden)
8106     {
8107         var items = this.getItems();
8108         var ret = {};
8109         items.each(function(f){
8110             
8111             if (!f.getName()) {
8112                 return;
8113             }
8114             
8115             var v = f.getValue();
8116             
8117             if (f.inputType =='radio') {
8118                 if (typeof(ret[f.getName()]) == 'undefined') {
8119                     ret[f.getName()] = ''; // empty..
8120                 }
8121
8122                 if (!f.el.dom.checked) {
8123                     return;
8124
8125                 }
8126                 v = f.el.dom.value;
8127
8128             }
8129             
8130             if(f.xtype == 'MoneyField'){
8131                 ret[f.currencyName] = f.getCurrency();
8132             }
8133
8134             // not sure if this supported any more..
8135             if ((typeof(v) == 'object') && f.getRawValue) {
8136                 v = f.getRawValue() ; // dates..
8137             }
8138             // combo boxes where name != hiddenName...
8139             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8140                 ret[f.name] = f.getRawValue();
8141             }
8142             ret[f.getName()] = v;
8143         });
8144
8145         return ret;
8146     },
8147
8148     /**
8149      * Clears all invalid messages in this form.
8150      * @return {BasicForm} this
8151      */
8152     clearInvalid : function(){
8153         var items = this.getItems();
8154
8155         items.each(function(f){
8156            f.clearInvalid();
8157         });
8158
8159         return this;
8160     },
8161
8162     /**
8163      * Resets this form.
8164      * @return {BasicForm} this
8165      */
8166     reset : function(){
8167         var items = this.getItems();
8168         items.each(function(f){
8169             f.reset();
8170         });
8171
8172         Roo.each(this.childForms || [], function (f) {
8173             f.reset();
8174         });
8175
8176
8177         return this;
8178     },
8179     
8180     getItems : function()
8181     {
8182         var r=new Roo.util.MixedCollection(false, function(o){
8183             return o.id || (o.id = Roo.id());
8184         });
8185         var iter = function(el) {
8186             if (el.inputEl) {
8187                 r.add(el);
8188             }
8189             if (!el.items) {
8190                 return;
8191             }
8192             Roo.each(el.items,function(e) {
8193                 iter(e);
8194             });
8195         };
8196
8197         iter(this);
8198         return r;
8199     },
8200     
8201     hideFields : function(items)
8202     {
8203         Roo.each(items, function(i){
8204             
8205             var f = this.findField(i);
8206             
8207             if(!f){
8208                 return;
8209             }
8210             
8211             if(f.xtype == 'DateField'){
8212                 f.setVisible(false);
8213                 return;
8214             }
8215             
8216             f.hide();
8217             
8218         }, this);
8219     },
8220     
8221     showFields : function(items)
8222     {
8223         Roo.each(items, function(i){
8224             
8225             var f = this.findField(i);
8226             
8227             if(!f){
8228                 return;
8229             }
8230             
8231             if(f.xtype == 'DateField'){
8232                 f.setVisible(true);
8233                 return;
8234             }
8235             
8236             f.show();
8237             
8238         }, this);
8239     }
8240
8241 });
8242
8243 Roo.apply(Roo.bootstrap.Form, {
8244     
8245     popover : {
8246         
8247         padding : 5,
8248         
8249         isApplied : false,
8250         
8251         isMasked : false,
8252         
8253         form : false,
8254         
8255         target : false,
8256         
8257         toolTip : false,
8258         
8259         intervalID : false,
8260         
8261         maskEl : false,
8262         
8263         apply : function()
8264         {
8265             if(this.isApplied){
8266                 return;
8267             }
8268             
8269             this.maskEl = {
8270                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8271                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8272                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8273                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8274             };
8275             
8276             this.maskEl.top.enableDisplayMode("block");
8277             this.maskEl.left.enableDisplayMode("block");
8278             this.maskEl.bottom.enableDisplayMode("block");
8279             this.maskEl.right.enableDisplayMode("block");
8280             
8281             this.toolTip = new Roo.bootstrap.Tooltip({
8282                 cls : 'roo-form-error-popover',
8283                 alignment : {
8284                     'left' : ['r-l', [-2,0], 'right'],
8285                     'right' : ['l-r', [2,0], 'left'],
8286                     'bottom' : ['tl-bl', [0,2], 'top'],
8287                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8288                 }
8289             });
8290             
8291             this.toolTip.render(Roo.get(document.body));
8292
8293             this.toolTip.el.enableDisplayMode("block");
8294             
8295             Roo.get(document.body).on('click', function(){
8296                 this.unmask();
8297             }, this);
8298             
8299             Roo.get(document.body).on('touchstart', function(){
8300                 this.unmask();
8301             }, this);
8302             
8303             this.isApplied = true
8304         },
8305         
8306         mask : function(form, target)
8307         {
8308             this.form = form;
8309             
8310             this.target = target;
8311             
8312             if(!this.form.errorMask || !target.el){
8313                 return;
8314             }
8315             
8316             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8317             
8318             Roo.log(scrollable);
8319             
8320             var ot = this.target.el.calcOffsetsTo(scrollable);
8321             
8322             var scrollTo = ot[1] - this.form.maskOffset;
8323             
8324             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8325             
8326             scrollable.scrollTo('top', scrollTo);
8327             
8328             var box = this.target.el.getBox();
8329             Roo.log(box);
8330             var zIndex = Roo.bootstrap.Modal.zIndex++;
8331
8332             
8333             this.maskEl.top.setStyle('position', 'absolute');
8334             this.maskEl.top.setStyle('z-index', zIndex);
8335             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8336             this.maskEl.top.setLeft(0);
8337             this.maskEl.top.setTop(0);
8338             this.maskEl.top.show();
8339             
8340             this.maskEl.left.setStyle('position', 'absolute');
8341             this.maskEl.left.setStyle('z-index', zIndex);
8342             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8343             this.maskEl.left.setLeft(0);
8344             this.maskEl.left.setTop(box.y - this.padding);
8345             this.maskEl.left.show();
8346
8347             this.maskEl.bottom.setStyle('position', 'absolute');
8348             this.maskEl.bottom.setStyle('z-index', zIndex);
8349             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8350             this.maskEl.bottom.setLeft(0);
8351             this.maskEl.bottom.setTop(box.bottom + this.padding);
8352             this.maskEl.bottom.show();
8353
8354             this.maskEl.right.setStyle('position', 'absolute');
8355             this.maskEl.right.setStyle('z-index', zIndex);
8356             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8357             this.maskEl.right.setLeft(box.right + this.padding);
8358             this.maskEl.right.setTop(box.y - this.padding);
8359             this.maskEl.right.show();
8360
8361             this.toolTip.bindEl = this.target.el;
8362
8363             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8364
8365             var tip = this.target.blankText;
8366
8367             if(this.target.getValue() !== '' ) {
8368                 
8369                 if (this.target.invalidText.length) {
8370                     tip = this.target.invalidText;
8371                 } else if (this.target.regexText.length){
8372                     tip = this.target.regexText;
8373                 }
8374             }
8375
8376             this.toolTip.show(tip);
8377
8378             this.intervalID = window.setInterval(function() {
8379                 Roo.bootstrap.Form.popover.unmask();
8380             }, 10000);
8381
8382             window.onwheel = function(){ return false;};
8383             
8384             (function(){ this.isMasked = true; }).defer(500, this);
8385             
8386         },
8387         
8388         unmask : function()
8389         {
8390             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8391                 return;
8392             }
8393             
8394             this.maskEl.top.setStyle('position', 'absolute');
8395             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8396             this.maskEl.top.hide();
8397
8398             this.maskEl.left.setStyle('position', 'absolute');
8399             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8400             this.maskEl.left.hide();
8401
8402             this.maskEl.bottom.setStyle('position', 'absolute');
8403             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8404             this.maskEl.bottom.hide();
8405
8406             this.maskEl.right.setStyle('position', 'absolute');
8407             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8408             this.maskEl.right.hide();
8409             
8410             this.toolTip.hide();
8411             
8412             this.toolTip.el.hide();
8413             
8414             window.onwheel = function(){ return true;};
8415             
8416             if(this.intervalID){
8417                 window.clearInterval(this.intervalID);
8418                 this.intervalID = false;
8419             }
8420             
8421             this.isMasked = false;
8422             
8423         }
8424         
8425     }
8426     
8427 });
8428
8429 /*
8430  * Based on:
8431  * Ext JS Library 1.1.1
8432  * Copyright(c) 2006-2007, Ext JS, LLC.
8433  *
8434  * Originally Released Under LGPL - original licence link has changed is not relivant.
8435  *
8436  * Fork - LGPL
8437  * <script type="text/javascript">
8438  */
8439 /**
8440  * @class Roo.form.VTypes
8441  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8442  * @singleton
8443  */
8444 Roo.form.VTypes = function(){
8445     // closure these in so they are only created once.
8446     var alpha = /^[a-zA-Z_]+$/;
8447     var alphanum = /^[a-zA-Z0-9_]+$/;
8448     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8449     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8450
8451     // All these messages and functions are configurable
8452     return {
8453         /**
8454          * The function used to validate email addresses
8455          * @param {String} value The email address
8456          */
8457         'email' : function(v){
8458             return email.test(v);
8459         },
8460         /**
8461          * The error text to display when the email validation function returns false
8462          * @type String
8463          */
8464         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8465         /**
8466          * The keystroke filter mask to be applied on email input
8467          * @type RegExp
8468          */
8469         'emailMask' : /[a-z0-9_\.\-@]/i,
8470
8471         /**
8472          * The function used to validate URLs
8473          * @param {String} value The URL
8474          */
8475         'url' : function(v){
8476             return url.test(v);
8477         },
8478         /**
8479          * The error text to display when the url validation function returns false
8480          * @type String
8481          */
8482         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8483         
8484         /**
8485          * The function used to validate alpha values
8486          * @param {String} value The value
8487          */
8488         'alpha' : function(v){
8489             return alpha.test(v);
8490         },
8491         /**
8492          * The error text to display when the alpha validation function returns false
8493          * @type String
8494          */
8495         'alphaText' : 'This field should only contain letters and _',
8496         /**
8497          * The keystroke filter mask to be applied on alpha input
8498          * @type RegExp
8499          */
8500         'alphaMask' : /[a-z_]/i,
8501
8502         /**
8503          * The function used to validate alphanumeric values
8504          * @param {String} value The value
8505          */
8506         'alphanum' : function(v){
8507             return alphanum.test(v);
8508         },
8509         /**
8510          * The error text to display when the alphanumeric validation function returns false
8511          * @type String
8512          */
8513         'alphanumText' : 'This field should only contain letters, numbers and _',
8514         /**
8515          * The keystroke filter mask to be applied on alphanumeric input
8516          * @type RegExp
8517          */
8518         'alphanumMask' : /[a-z0-9_]/i
8519     };
8520 }();/*
8521  * - LGPL
8522  *
8523  * Input
8524  * 
8525  */
8526
8527 /**
8528  * @class Roo.bootstrap.Input
8529  * @extends Roo.bootstrap.Component
8530  * Bootstrap Input class
8531  * @cfg {Boolean} disabled is it disabled
8532  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8533  * @cfg {String} name name of the input
8534  * @cfg {string} fieldLabel - the label associated
8535  * @cfg {string} placeholder - placeholder to put in text.
8536  * @cfg {string}  before - input group add on before
8537  * @cfg {string} after - input group add on after
8538  * @cfg {string} size - (lg|sm) or leave empty..
8539  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8540  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8541  * @cfg {Number} md colspan out of 12 for computer-sized screens
8542  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8543  * @cfg {string} value default value of the input
8544  * @cfg {Number} labelWidth set the width of label 
8545  * @cfg {Number} labellg set the width of label (1-12)
8546  * @cfg {Number} labelmd set the width of label (1-12)
8547  * @cfg {Number} labelsm set the width of label (1-12)
8548  * @cfg {Number} labelxs set the width of label (1-12)
8549  * @cfg {String} labelAlign (top|left)
8550  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8551  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8552  * @cfg {String} indicatorpos (left|right) default left
8553  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8554  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8555
8556  * @cfg {String} align (left|center|right) Default left
8557  * @cfg {Boolean} forceFeedback (true|false) Default false
8558  * 
8559  * @constructor
8560  * Create a new Input
8561  * @param {Object} config The config object
8562  */
8563
8564 Roo.bootstrap.Input = function(config){
8565     
8566     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8567     
8568     this.addEvents({
8569         /**
8570          * @event focus
8571          * Fires when this field receives input focus.
8572          * @param {Roo.form.Field} this
8573          */
8574         focus : true,
8575         /**
8576          * @event blur
8577          * Fires when this field loses input focus.
8578          * @param {Roo.form.Field} this
8579          */
8580         blur : true,
8581         /**
8582          * @event specialkey
8583          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8584          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8585          * @param {Roo.form.Field} this
8586          * @param {Roo.EventObject} e The event object
8587          */
8588         specialkey : true,
8589         /**
8590          * @event change
8591          * Fires just before the field blurs if the field value has changed.
8592          * @param {Roo.form.Field} this
8593          * @param {Mixed} newValue The new value
8594          * @param {Mixed} oldValue The original value
8595          */
8596         change : true,
8597         /**
8598          * @event invalid
8599          * Fires after the field has been marked as invalid.
8600          * @param {Roo.form.Field} this
8601          * @param {String} msg The validation message
8602          */
8603         invalid : true,
8604         /**
8605          * @event valid
8606          * Fires after the field has been validated with no errors.
8607          * @param {Roo.form.Field} this
8608          */
8609         valid : true,
8610          /**
8611          * @event keyup
8612          * Fires after the key up
8613          * @param {Roo.form.Field} this
8614          * @param {Roo.EventObject}  e The event Object
8615          */
8616         keyup : true
8617     });
8618 };
8619
8620 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8621      /**
8622      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8623       automatic validation (defaults to "keyup").
8624      */
8625     validationEvent : "keyup",
8626      /**
8627      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8628      */
8629     validateOnBlur : true,
8630     /**
8631      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8632      */
8633     validationDelay : 250,
8634      /**
8635      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8636      */
8637     focusClass : "x-form-focus",  // not needed???
8638     
8639        
8640     /**
8641      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8642      */
8643     invalidClass : "has-warning",
8644     
8645     /**
8646      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8647      */
8648     validClass : "has-success",
8649     
8650     /**
8651      * @cfg {Boolean} hasFeedback (true|false) default true
8652      */
8653     hasFeedback : true,
8654     
8655     /**
8656      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8657      */
8658     invalidFeedbackClass : "glyphicon-warning-sign",
8659     
8660     /**
8661      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8662      */
8663     validFeedbackClass : "glyphicon-ok",
8664     
8665     /**
8666      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8667      */
8668     selectOnFocus : false,
8669     
8670      /**
8671      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8672      */
8673     maskRe : null,
8674        /**
8675      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8676      */
8677     vtype : null,
8678     
8679       /**
8680      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8681      */
8682     disableKeyFilter : false,
8683     
8684        /**
8685      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8686      */
8687     disabled : false,
8688      /**
8689      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8690      */
8691     allowBlank : true,
8692     /**
8693      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8694      */
8695     blankText : "Please complete this mandatory field",
8696     
8697      /**
8698      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8699      */
8700     minLength : 0,
8701     /**
8702      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8703      */
8704     maxLength : Number.MAX_VALUE,
8705     /**
8706      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8707      */
8708     minLengthText : "The minimum length for this field is {0}",
8709     /**
8710      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8711      */
8712     maxLengthText : "The maximum length for this field is {0}",
8713   
8714     
8715     /**
8716      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8717      * If available, this function will be called only after the basic validators all return true, and will be passed the
8718      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8719      */
8720     validator : null,
8721     /**
8722      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8723      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8724      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8725      */
8726     regex : null,
8727     /**
8728      * @cfg {String} regexText -- Depricated - use Invalid Text
8729      */
8730     regexText : "",
8731     
8732     /**
8733      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8734      */
8735     invalidText : "",
8736     
8737     
8738     
8739     autocomplete: false,
8740     
8741     
8742     fieldLabel : '',
8743     inputType : 'text',
8744     
8745     name : false,
8746     placeholder: false,
8747     before : false,
8748     after : false,
8749     size : false,
8750     hasFocus : false,
8751     preventMark: false,
8752     isFormField : true,
8753     value : '',
8754     labelWidth : 2,
8755     labelAlign : false,
8756     readOnly : false,
8757     align : false,
8758     formatedValue : false,
8759     forceFeedback : false,
8760     
8761     indicatorpos : 'left',
8762     
8763     labellg : 0,
8764     labelmd : 0,
8765     labelsm : 0,
8766     labelxs : 0,
8767     
8768     capture : '',
8769     accept : '',
8770     
8771     parentLabelAlign : function()
8772     {
8773         var parent = this;
8774         while (parent.parent()) {
8775             parent = parent.parent();
8776             if (typeof(parent.labelAlign) !='undefined') {
8777                 return parent.labelAlign;
8778             }
8779         }
8780         return 'left';
8781         
8782     },
8783     
8784     getAutoCreate : function()
8785     {
8786         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8787         
8788         var id = Roo.id();
8789         
8790         var cfg = {};
8791         
8792         if(this.inputType != 'hidden'){
8793             cfg.cls = 'form-group' //input-group
8794         }
8795         
8796         var input =  {
8797             tag: 'input',
8798             id : id,
8799             type : this.inputType,
8800             value : this.value,
8801             cls : 'form-control',
8802             placeholder : this.placeholder || '',
8803             autocomplete : this.autocomplete || 'new-password'
8804         };
8805         
8806         if(this.capture.length){
8807             input.capture = this.capture;
8808         }
8809         
8810         if(this.accept.length){
8811             input.accept = this.accept + "/*";
8812         }
8813         
8814         if(this.align){
8815             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8816         }
8817         
8818         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8819             input.maxLength = this.maxLength;
8820         }
8821         
8822         if (this.disabled) {
8823             input.disabled=true;
8824         }
8825         
8826         if (this.readOnly) {
8827             input.readonly=true;
8828         }
8829         
8830         if (this.name) {
8831             input.name = this.name;
8832         }
8833         
8834         if (this.size) {
8835             input.cls += ' input-' + this.size;
8836         }
8837         
8838         var settings=this;
8839         ['xs','sm','md','lg'].map(function(size){
8840             if (settings[size]) {
8841                 cfg.cls += ' col-' + size + '-' + settings[size];
8842             }
8843         });
8844         
8845         var inputblock = input;
8846         
8847         var feedback = {
8848             tag: 'span',
8849             cls: 'glyphicon form-control-feedback'
8850         };
8851             
8852         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8853             
8854             inputblock = {
8855                 cls : 'has-feedback',
8856                 cn :  [
8857                     input,
8858                     feedback
8859                 ] 
8860             };  
8861         }
8862         
8863         if (this.before || this.after) {
8864             
8865             inputblock = {
8866                 cls : 'input-group',
8867                 cn :  [] 
8868             };
8869             
8870             if (this.before && typeof(this.before) == 'string') {
8871                 
8872                 inputblock.cn.push({
8873                     tag :'span',
8874                     cls : 'roo-input-before input-group-addon',
8875                     html : this.before
8876                 });
8877             }
8878             if (this.before && typeof(this.before) == 'object') {
8879                 this.before = Roo.factory(this.before);
8880                 
8881                 inputblock.cn.push({
8882                     tag :'span',
8883                     cls : 'roo-input-before input-group-' +
8884                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8885                 });
8886             }
8887             
8888             inputblock.cn.push(input);
8889             
8890             if (this.after && typeof(this.after) == 'string') {
8891                 inputblock.cn.push({
8892                     tag :'span',
8893                     cls : 'roo-input-after input-group-addon',
8894                     html : this.after
8895                 });
8896             }
8897             if (this.after && typeof(this.after) == 'object') {
8898                 this.after = Roo.factory(this.after);
8899                 
8900                 inputblock.cn.push({
8901                     tag :'span',
8902                     cls : 'roo-input-after input-group-' +
8903                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8904                 });
8905             }
8906             
8907             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8908                 inputblock.cls += ' has-feedback';
8909                 inputblock.cn.push(feedback);
8910             }
8911         };
8912         
8913         if (align ==='left' && this.fieldLabel.length) {
8914             
8915             cfg.cls += ' roo-form-group-label-left';
8916             
8917             cfg.cn = [
8918                 {
8919                     tag : 'i',
8920                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8921                     tooltip : 'This field is required'
8922                 },
8923                 {
8924                     tag: 'label',
8925                     'for' :  id,
8926                     cls : 'control-label',
8927                     html : this.fieldLabel
8928
8929                 },
8930                 {
8931                     cls : "", 
8932                     cn: [
8933                         inputblock
8934                     ]
8935                 }
8936             ];
8937             
8938             var labelCfg = cfg.cn[1];
8939             var contentCfg = cfg.cn[2];
8940             
8941             if(this.indicatorpos == 'right'){
8942                 cfg.cn = [
8943                     {
8944                         tag: 'label',
8945                         'for' :  id,
8946                         cls : 'control-label',
8947                         cn : [
8948                             {
8949                                 tag : 'span',
8950                                 html : this.fieldLabel
8951                             },
8952                             {
8953                                 tag : 'i',
8954                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8955                                 tooltip : 'This field is required'
8956                             }
8957                         ]
8958                     },
8959                     {
8960                         cls : "",
8961                         cn: [
8962                             inputblock
8963                         ]
8964                     }
8965
8966                 ];
8967                 
8968                 labelCfg = cfg.cn[0];
8969                 contentCfg = cfg.cn[1];
8970             
8971             }
8972             
8973             if(this.labelWidth > 12){
8974                 labelCfg.style = "width: " + this.labelWidth + 'px';
8975             }
8976             
8977             if(this.labelWidth < 13 && this.labelmd == 0){
8978                 this.labelmd = this.labelWidth;
8979             }
8980             
8981             if(this.labellg > 0){
8982                 labelCfg.cls += ' col-lg-' + this.labellg;
8983                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8984             }
8985             
8986             if(this.labelmd > 0){
8987                 labelCfg.cls += ' col-md-' + this.labelmd;
8988                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8989             }
8990             
8991             if(this.labelsm > 0){
8992                 labelCfg.cls += ' col-sm-' + this.labelsm;
8993                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8994             }
8995             
8996             if(this.labelxs > 0){
8997                 labelCfg.cls += ' col-xs-' + this.labelxs;
8998                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8999             }
9000             
9001             
9002         } else if ( this.fieldLabel.length) {
9003                 
9004             cfg.cn = [
9005                 {
9006                     tag : 'i',
9007                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9008                     tooltip : 'This field is required'
9009                 },
9010                 {
9011                     tag: 'label',
9012                    //cls : 'input-group-addon',
9013                     html : this.fieldLabel
9014
9015                 },
9016
9017                inputblock
9018
9019            ];
9020            
9021            if(this.indicatorpos == 'right'){
9022                 
9023                 cfg.cn = [
9024                     {
9025                         tag: 'label',
9026                        //cls : 'input-group-addon',
9027                         html : this.fieldLabel
9028
9029                     },
9030                     {
9031                         tag : 'i',
9032                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9033                         tooltip : 'This field is required'
9034                     },
9035
9036                    inputblock
9037
9038                ];
9039
9040             }
9041
9042         } else {
9043             
9044             cfg.cn = [
9045
9046                     inputblock
9047
9048             ];
9049                 
9050                 
9051         };
9052         
9053         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9054            cfg.cls += ' navbar-form';
9055         }
9056         
9057         if (this.parentType === 'NavGroup') {
9058            cfg.cls += ' navbar-form';
9059            cfg.tag = 'li';
9060         }
9061         
9062         return cfg;
9063         
9064     },
9065     /**
9066      * return the real input element.
9067      */
9068     inputEl: function ()
9069     {
9070         return this.el.select('input.form-control',true).first();
9071     },
9072     
9073     tooltipEl : function()
9074     {
9075         return this.inputEl();
9076     },
9077     
9078     indicatorEl : function()
9079     {
9080         var indicator = this.el.select('i.roo-required-indicator',true).first();
9081         
9082         if(!indicator){
9083             return false;
9084         }
9085         
9086         return indicator;
9087         
9088     },
9089     
9090     setDisabled : function(v)
9091     {
9092         var i  = this.inputEl().dom;
9093         if (!v) {
9094             i.removeAttribute('disabled');
9095             return;
9096             
9097         }
9098         i.setAttribute('disabled','true');
9099     },
9100     initEvents : function()
9101     {
9102           
9103         this.inputEl().on("keydown" , this.fireKey,  this);
9104         this.inputEl().on("focus", this.onFocus,  this);
9105         this.inputEl().on("blur", this.onBlur,  this);
9106         
9107         this.inputEl().relayEvent('keyup', this);
9108         
9109         this.indicator = this.indicatorEl();
9110         
9111         if(this.indicator){
9112             this.indicator.addClass('invisible');
9113         }
9114  
9115         // reference to original value for reset
9116         this.originalValue = this.getValue();
9117         //Roo.form.TextField.superclass.initEvents.call(this);
9118         if(this.validationEvent == 'keyup'){
9119             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9120             this.inputEl().on('keyup', this.filterValidation, this);
9121         }
9122         else if(this.validationEvent !== false){
9123             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9124         }
9125         
9126         if(this.selectOnFocus){
9127             this.on("focus", this.preFocus, this);
9128             
9129         }
9130         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9131             this.inputEl().on("keypress", this.filterKeys, this);
9132         } else {
9133             this.inputEl().relayEvent('keypress', this);
9134         }
9135        /* if(this.grow){
9136             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9137             this.el.on("click", this.autoSize,  this);
9138         }
9139         */
9140         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9141             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9142         }
9143         
9144         if (typeof(this.before) == 'object') {
9145             this.before.render(this.el.select('.roo-input-before',true).first());
9146         }
9147         if (typeof(this.after) == 'object') {
9148             this.after.render(this.el.select('.roo-input-after',true).first());
9149         }
9150         
9151         this.inputEl().on('change', this.onChange, this);
9152         
9153     },
9154     filterValidation : function(e){
9155         if(!e.isNavKeyPress()){
9156             this.validationTask.delay(this.validationDelay);
9157         }
9158     },
9159      /**
9160      * Validates the field value
9161      * @return {Boolean} True if the value is valid, else false
9162      */
9163     validate : function(){
9164         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9165         if(this.disabled || this.validateValue(this.getRawValue())){
9166             this.markValid();
9167             return true;
9168         }
9169         
9170         this.markInvalid();
9171         return false;
9172     },
9173     
9174     
9175     /**
9176      * Validates a value according to the field's validation rules and marks the field as invalid
9177      * if the validation fails
9178      * @param {Mixed} value The value to validate
9179      * @return {Boolean} True if the value is valid, else false
9180      */
9181     validateValue : function(value)
9182     {
9183         if(this.getVisibilityEl().hasClass('hidden')){
9184             return true;
9185         }
9186         
9187         if(value.length < 1)  { // if it's blank
9188             if(this.allowBlank){
9189                 return true;
9190             }
9191             return false;
9192         }
9193         
9194         if(value.length < this.minLength){
9195             return false;
9196         }
9197         if(value.length > this.maxLength){
9198             return false;
9199         }
9200         if(this.vtype){
9201             var vt = Roo.form.VTypes;
9202             if(!vt[this.vtype](value, this)){
9203                 return false;
9204             }
9205         }
9206         if(typeof this.validator == "function"){
9207             var msg = this.validator(value);
9208             if(msg !== true){
9209                 return false;
9210             }
9211             if (typeof(msg) == 'string') {
9212                 this.invalidText = msg;
9213             }
9214         }
9215         
9216         if(this.regex && !this.regex.test(value)){
9217             return false;
9218         }
9219         
9220         return true;
9221     },
9222     
9223      // private
9224     fireKey : function(e){
9225         //Roo.log('field ' + e.getKey());
9226         if(e.isNavKeyPress()){
9227             this.fireEvent("specialkey", this, e);
9228         }
9229     },
9230     focus : function (selectText){
9231         if(this.rendered){
9232             this.inputEl().focus();
9233             if(selectText === true){
9234                 this.inputEl().dom.select();
9235             }
9236         }
9237         return this;
9238     } ,
9239     
9240     onFocus : function(){
9241         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9242            // this.el.addClass(this.focusClass);
9243         }
9244         if(!this.hasFocus){
9245             this.hasFocus = true;
9246             this.startValue = this.getValue();
9247             this.fireEvent("focus", this);
9248         }
9249     },
9250     
9251     beforeBlur : Roo.emptyFn,
9252
9253     
9254     // private
9255     onBlur : function(){
9256         this.beforeBlur();
9257         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9258             //this.el.removeClass(this.focusClass);
9259         }
9260         this.hasFocus = false;
9261         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9262             this.validate();
9263         }
9264         var v = this.getValue();
9265         if(String(v) !== String(this.startValue)){
9266             this.fireEvent('change', this, v, this.startValue);
9267         }
9268         this.fireEvent("blur", this);
9269     },
9270     
9271     onChange : function(e)
9272     {
9273         var v = this.getValue();
9274         if(String(v) !== String(this.startValue)){
9275             this.fireEvent('change', this, v, this.startValue);
9276         }
9277         
9278     },
9279     
9280     /**
9281      * Resets the current field value to the originally loaded value and clears any validation messages
9282      */
9283     reset : function(){
9284         this.setValue(this.originalValue);
9285         this.validate();
9286     },
9287      /**
9288      * Returns the name of the field
9289      * @return {Mixed} name The name field
9290      */
9291     getName: function(){
9292         return this.name;
9293     },
9294      /**
9295      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9296      * @return {Mixed} value The field value
9297      */
9298     getValue : function(){
9299         
9300         var v = this.inputEl().getValue();
9301         
9302         return v;
9303     },
9304     /**
9305      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9306      * @return {Mixed} value The field value
9307      */
9308     getRawValue : function(){
9309         var v = this.inputEl().getValue();
9310         
9311         return v;
9312     },
9313     
9314     /**
9315      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9316      * @param {Mixed} value The value to set
9317      */
9318     setRawValue : function(v){
9319         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9320     },
9321     
9322     selectText : function(start, end){
9323         var v = this.getRawValue();
9324         if(v.length > 0){
9325             start = start === undefined ? 0 : start;
9326             end = end === undefined ? v.length : end;
9327             var d = this.inputEl().dom;
9328             if(d.setSelectionRange){
9329                 d.setSelectionRange(start, end);
9330             }else if(d.createTextRange){
9331                 var range = d.createTextRange();
9332                 range.moveStart("character", start);
9333                 range.moveEnd("character", v.length-end);
9334                 range.select();
9335             }
9336         }
9337     },
9338     
9339     /**
9340      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9341      * @param {Mixed} value The value to set
9342      */
9343     setValue : function(v){
9344         this.value = v;
9345         if(this.rendered){
9346             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9347             this.validate();
9348         }
9349     },
9350     
9351     /*
9352     processValue : function(value){
9353         if(this.stripCharsRe){
9354             var newValue = value.replace(this.stripCharsRe, '');
9355             if(newValue !== value){
9356                 this.setRawValue(newValue);
9357                 return newValue;
9358             }
9359         }
9360         return value;
9361     },
9362   */
9363     preFocus : function(){
9364         
9365         if(this.selectOnFocus){
9366             this.inputEl().dom.select();
9367         }
9368     },
9369     filterKeys : function(e){
9370         var k = e.getKey();
9371         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9372             return;
9373         }
9374         var c = e.getCharCode(), cc = String.fromCharCode(c);
9375         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9376             return;
9377         }
9378         if(!this.maskRe.test(cc)){
9379             e.stopEvent();
9380         }
9381     },
9382      /**
9383      * Clear any invalid styles/messages for this field
9384      */
9385     clearInvalid : function(){
9386         
9387         if(!this.el || this.preventMark){ // not rendered
9388             return;
9389         }
9390         
9391      
9392         this.el.removeClass(this.invalidClass);
9393         
9394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9395             
9396             var feedback = this.el.select('.form-control-feedback', true).first();
9397             
9398             if(feedback){
9399                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9400             }
9401             
9402         }
9403         
9404         this.fireEvent('valid', this);
9405     },
9406     
9407      /**
9408      * Mark this field as valid
9409      */
9410     markValid : function()
9411     {
9412         if(!this.el  || this.preventMark){ // not rendered...
9413             return;
9414         }
9415         
9416         this.el.removeClass([this.invalidClass, this.validClass]);
9417         
9418         var feedback = this.el.select('.form-control-feedback', true).first();
9419             
9420         if(feedback){
9421             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9422         }
9423         
9424         if(this.indicator){
9425             this.indicator.removeClass('visible');
9426             this.indicator.addClass('invisible');
9427         }
9428         
9429         if(this.disabled){
9430             return;
9431         }
9432         
9433         if(this.allowBlank && !this.getRawValue().length){
9434             return;
9435         }
9436         
9437         this.el.addClass(this.validClass);
9438         
9439         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9440             
9441             var feedback = this.el.select('.form-control-feedback', true).first();
9442             
9443             if(feedback){
9444                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9445                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9446             }
9447             
9448         }
9449         
9450         this.fireEvent('valid', this);
9451     },
9452     
9453      /**
9454      * Mark this field as invalid
9455      * @param {String} msg The validation message
9456      */
9457     markInvalid : function(msg)
9458     {
9459         if(!this.el  || this.preventMark){ // not rendered
9460             return;
9461         }
9462         
9463         this.el.removeClass([this.invalidClass, this.validClass]);
9464         
9465         var feedback = this.el.select('.form-control-feedback', true).first();
9466             
9467         if(feedback){
9468             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9469         }
9470
9471         if(this.disabled){
9472             return;
9473         }
9474         
9475         if(this.allowBlank && !this.getRawValue().length){
9476             return;
9477         }
9478         
9479         if(this.indicator){
9480             this.indicator.removeClass('invisible');
9481             this.indicator.addClass('visible');
9482         }
9483         
9484         this.el.addClass(this.invalidClass);
9485         
9486         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9487             
9488             var feedback = this.el.select('.form-control-feedback', true).first();
9489             
9490             if(feedback){
9491                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9492                 
9493                 if(this.getValue().length || this.forceFeedback){
9494                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9495                 }
9496                 
9497             }
9498             
9499         }
9500         
9501         this.fireEvent('invalid', this, msg);
9502     },
9503     // private
9504     SafariOnKeyDown : function(event)
9505     {
9506         // this is a workaround for a password hang bug on chrome/ webkit.
9507         if (this.inputEl().dom.type != 'password') {
9508             return;
9509         }
9510         
9511         var isSelectAll = false;
9512         
9513         if(this.inputEl().dom.selectionEnd > 0){
9514             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9515         }
9516         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9517             event.preventDefault();
9518             this.setValue('');
9519             return;
9520         }
9521         
9522         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9523             
9524             event.preventDefault();
9525             // this is very hacky as keydown always get's upper case.
9526             //
9527             var cc = String.fromCharCode(event.getCharCode());
9528             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9529             
9530         }
9531     },
9532     adjustWidth : function(tag, w){
9533         tag = tag.toLowerCase();
9534         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9535             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9536                 if(tag == 'input'){
9537                     return w + 2;
9538                 }
9539                 if(tag == 'textarea'){
9540                     return w-2;
9541                 }
9542             }else if(Roo.isOpera){
9543                 if(tag == 'input'){
9544                     return w + 2;
9545                 }
9546                 if(tag == 'textarea'){
9547                     return w-2;
9548                 }
9549             }
9550         }
9551         return w;
9552     },
9553     
9554     setFieldLabel : function(v)
9555     {
9556         if(!this.rendered){
9557             return;
9558         }
9559         
9560         if(this.indicator){
9561             var ar = this.el.select('label > span',true);
9562             
9563             if (ar.elements.length) {
9564                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9565                 this.fieldLabel = v;
9566                 return;
9567             }
9568             
9569             var br = this.el.select('label',true);
9570             
9571             if(br.elements.length) {
9572                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9573                 this.fieldLabel = v;
9574                 return;
9575             }
9576             
9577             Roo.log('Cannot Found any of label > span || label in input');
9578             return;
9579         }
9580         
9581         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9582         this.fieldLabel = v;
9583         
9584         
9585     }
9586 });
9587
9588  
9589 /*
9590  * - LGPL
9591  *
9592  * Input
9593  * 
9594  */
9595
9596 /**
9597  * @class Roo.bootstrap.TextArea
9598  * @extends Roo.bootstrap.Input
9599  * Bootstrap TextArea class
9600  * @cfg {Number} cols Specifies the visible width of a text area
9601  * @cfg {Number} rows Specifies the visible number of lines in a text area
9602  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9603  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9604  * @cfg {string} html text
9605  * 
9606  * @constructor
9607  * Create a new TextArea
9608  * @param {Object} config The config object
9609  */
9610
9611 Roo.bootstrap.TextArea = function(config){
9612     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9613    
9614 };
9615
9616 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9617      
9618     cols : false,
9619     rows : 5,
9620     readOnly : false,
9621     warp : 'soft',
9622     resize : false,
9623     value: false,
9624     html: false,
9625     
9626     getAutoCreate : function(){
9627         
9628         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9629         
9630         var id = Roo.id();
9631         
9632         var cfg = {};
9633         
9634         if(this.inputType != 'hidden'){
9635             cfg.cls = 'form-group' //input-group
9636         }
9637         
9638         var input =  {
9639             tag: 'textarea',
9640             id : id,
9641             warp : this.warp,
9642             rows : this.rows,
9643             value : this.value || '',
9644             html: this.html || '',
9645             cls : 'form-control',
9646             placeholder : this.placeholder || '' 
9647             
9648         };
9649         
9650         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9651             input.maxLength = this.maxLength;
9652         }
9653         
9654         if(this.resize){
9655             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9656         }
9657         
9658         if(this.cols){
9659             input.cols = this.cols;
9660         }
9661         
9662         if (this.readOnly) {
9663             input.readonly = true;
9664         }
9665         
9666         if (this.name) {
9667             input.name = this.name;
9668         }
9669         
9670         if (this.size) {
9671             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9672         }
9673         
9674         var settings=this;
9675         ['xs','sm','md','lg'].map(function(size){
9676             if (settings[size]) {
9677                 cfg.cls += ' col-' + size + '-' + settings[size];
9678             }
9679         });
9680         
9681         var inputblock = input;
9682         
9683         if(this.hasFeedback && !this.allowBlank){
9684             
9685             var feedback = {
9686                 tag: 'span',
9687                 cls: 'glyphicon form-control-feedback'
9688             };
9689
9690             inputblock = {
9691                 cls : 'has-feedback',
9692                 cn :  [
9693                     input,
9694                     feedback
9695                 ] 
9696             };  
9697         }
9698         
9699         
9700         if (this.before || this.after) {
9701             
9702             inputblock = {
9703                 cls : 'input-group',
9704                 cn :  [] 
9705             };
9706             if (this.before) {
9707                 inputblock.cn.push({
9708                     tag :'span',
9709                     cls : 'input-group-addon',
9710                     html : this.before
9711                 });
9712             }
9713             
9714             inputblock.cn.push(input);
9715             
9716             if(this.hasFeedback && !this.allowBlank){
9717                 inputblock.cls += ' has-feedback';
9718                 inputblock.cn.push(feedback);
9719             }
9720             
9721             if (this.after) {
9722                 inputblock.cn.push({
9723                     tag :'span',
9724                     cls : 'input-group-addon',
9725                     html : this.after
9726                 });
9727             }
9728             
9729         }
9730         
9731         if (align ==='left' && this.fieldLabel.length) {
9732             cfg.cn = [
9733                 {
9734                     tag: 'label',
9735                     'for' :  id,
9736                     cls : 'control-label',
9737                     html : this.fieldLabel
9738                 },
9739                 {
9740                     cls : "",
9741                     cn: [
9742                         inputblock
9743                     ]
9744                 }
9745
9746             ];
9747             
9748             if(this.labelWidth > 12){
9749                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9750             }
9751
9752             if(this.labelWidth < 13 && this.labelmd == 0){
9753                 this.labelmd = this.labelWidth;
9754             }
9755
9756             if(this.labellg > 0){
9757                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9758                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9759             }
9760
9761             if(this.labelmd > 0){
9762                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9763                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9764             }
9765
9766             if(this.labelsm > 0){
9767                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9768                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9769             }
9770
9771             if(this.labelxs > 0){
9772                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9773                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9774             }
9775             
9776         } else if ( this.fieldLabel.length) {
9777             cfg.cn = [
9778
9779                {
9780                    tag: 'label',
9781                    //cls : 'input-group-addon',
9782                    html : this.fieldLabel
9783
9784                },
9785
9786                inputblock
9787
9788            ];
9789
9790         } else {
9791
9792             cfg.cn = [
9793
9794                 inputblock
9795
9796             ];
9797                 
9798         }
9799         
9800         if (this.disabled) {
9801             input.disabled=true;
9802         }
9803         
9804         return cfg;
9805         
9806     },
9807     /**
9808      * return the real textarea element.
9809      */
9810     inputEl: function ()
9811     {
9812         return this.el.select('textarea.form-control',true).first();
9813     },
9814     
9815     /**
9816      * Clear any invalid styles/messages for this field
9817      */
9818     clearInvalid : function()
9819     {
9820         
9821         if(!this.el || this.preventMark){ // not rendered
9822             return;
9823         }
9824         
9825         var label = this.el.select('label', true).first();
9826         var icon = this.el.select('i.fa-star', true).first();
9827         
9828         if(label && icon){
9829             icon.remove();
9830         }
9831         
9832         this.el.removeClass(this.invalidClass);
9833         
9834         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9835             
9836             var feedback = this.el.select('.form-control-feedback', true).first();
9837             
9838             if(feedback){
9839                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9840             }
9841             
9842         }
9843         
9844         this.fireEvent('valid', this);
9845     },
9846     
9847      /**
9848      * Mark this field as valid
9849      */
9850     markValid : function()
9851     {
9852         if(!this.el  || this.preventMark){ // not rendered
9853             return;
9854         }
9855         
9856         this.el.removeClass([this.invalidClass, this.validClass]);
9857         
9858         var feedback = this.el.select('.form-control-feedback', true).first();
9859             
9860         if(feedback){
9861             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9862         }
9863
9864         if(this.disabled || this.allowBlank){
9865             return;
9866         }
9867         
9868         var label = this.el.select('label', true).first();
9869         var icon = this.el.select('i.fa-star', true).first();
9870         
9871         if(label && icon){
9872             icon.remove();
9873         }
9874         
9875         this.el.addClass(this.validClass);
9876         
9877         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9878             
9879             var feedback = this.el.select('.form-control-feedback', true).first();
9880             
9881             if(feedback){
9882                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9883                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9884             }
9885             
9886         }
9887         
9888         this.fireEvent('valid', this);
9889     },
9890     
9891      /**
9892      * Mark this field as invalid
9893      * @param {String} msg The validation message
9894      */
9895     markInvalid : function(msg)
9896     {
9897         if(!this.el  || this.preventMark){ // not rendered
9898             return;
9899         }
9900         
9901         this.el.removeClass([this.invalidClass, this.validClass]);
9902         
9903         var feedback = this.el.select('.form-control-feedback', true).first();
9904             
9905         if(feedback){
9906             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9907         }
9908
9909         if(this.disabled || this.allowBlank){
9910             return;
9911         }
9912         
9913         var label = this.el.select('label', true).first();
9914         var icon = this.el.select('i.fa-star', true).first();
9915         
9916         if(!this.getValue().length && label && !icon){
9917             this.el.createChild({
9918                 tag : 'i',
9919                 cls : 'text-danger fa fa-lg fa-star',
9920                 tooltip : 'This field is required',
9921                 style : 'margin-right:5px;'
9922             }, label, true);
9923         }
9924
9925         this.el.addClass(this.invalidClass);
9926         
9927         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9928             
9929             var feedback = this.el.select('.form-control-feedback', true).first();
9930             
9931             if(feedback){
9932                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9933                 
9934                 if(this.getValue().length || this.forceFeedback){
9935                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9936                 }
9937                 
9938             }
9939             
9940         }
9941         
9942         this.fireEvent('invalid', this, msg);
9943     }
9944 });
9945
9946  
9947 /*
9948  * - LGPL
9949  *
9950  * trigger field - base class for combo..
9951  * 
9952  */
9953  
9954 /**
9955  * @class Roo.bootstrap.TriggerField
9956  * @extends Roo.bootstrap.Input
9957  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9958  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9959  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9960  * for which you can provide a custom implementation.  For example:
9961  * <pre><code>
9962 var trigger = new Roo.bootstrap.TriggerField();
9963 trigger.onTriggerClick = myTriggerFn;
9964 trigger.applyTo('my-field');
9965 </code></pre>
9966  *
9967  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9968  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9969  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9970  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9971  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9972
9973  * @constructor
9974  * Create a new TriggerField.
9975  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9976  * to the base TextField)
9977  */
9978 Roo.bootstrap.TriggerField = function(config){
9979     this.mimicing = false;
9980     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9981 };
9982
9983 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9984     /**
9985      * @cfg {String} triggerClass A CSS class to apply to the trigger
9986      */
9987      /**
9988      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9989      */
9990     hideTrigger:false,
9991
9992     /**
9993      * @cfg {Boolean} removable (true|false) special filter default false
9994      */
9995     removable : false,
9996     
9997     /** @cfg {Boolean} grow @hide */
9998     /** @cfg {Number} growMin @hide */
9999     /** @cfg {Number} growMax @hide */
10000
10001     /**
10002      * @hide 
10003      * @method
10004      */
10005     autoSize: Roo.emptyFn,
10006     // private
10007     monitorTab : true,
10008     // private
10009     deferHeight : true,
10010
10011     
10012     actionMode : 'wrap',
10013     
10014     caret : false,
10015     
10016     
10017     getAutoCreate : function(){
10018        
10019         var align = this.labelAlign || this.parentLabelAlign();
10020         
10021         var id = Roo.id();
10022         
10023         var cfg = {
10024             cls: 'form-group' //input-group
10025         };
10026         
10027         
10028         var input =  {
10029             tag: 'input',
10030             id : id,
10031             type : this.inputType,
10032             cls : 'form-control',
10033             autocomplete: 'new-password',
10034             placeholder : this.placeholder || '' 
10035             
10036         };
10037         if (this.name) {
10038             input.name = this.name;
10039         }
10040         if (this.size) {
10041             input.cls += ' input-' + this.size;
10042         }
10043         
10044         if (this.disabled) {
10045             input.disabled=true;
10046         }
10047         
10048         var inputblock = input;
10049         
10050         if(this.hasFeedback && !this.allowBlank){
10051             
10052             var feedback = {
10053                 tag: 'span',
10054                 cls: 'glyphicon form-control-feedback'
10055             };
10056             
10057             if(this.removable && !this.editable && !this.tickable){
10058                 inputblock = {
10059                     cls : 'has-feedback',
10060                     cn :  [
10061                         inputblock,
10062                         {
10063                             tag: 'button',
10064                             html : 'x',
10065                             cls : 'roo-combo-removable-btn close'
10066                         },
10067                         feedback
10068                     ] 
10069                 };
10070             } else {
10071                 inputblock = {
10072                     cls : 'has-feedback',
10073                     cn :  [
10074                         inputblock,
10075                         feedback
10076                     ] 
10077                 };
10078             }
10079
10080         } else {
10081             if(this.removable && !this.editable && !this.tickable){
10082                 inputblock = {
10083                     cls : 'roo-removable',
10084                     cn :  [
10085                         inputblock,
10086                         {
10087                             tag: 'button',
10088                             html : 'x',
10089                             cls : 'roo-combo-removable-btn close'
10090                         }
10091                     ] 
10092                 };
10093             }
10094         }
10095         
10096         if (this.before || this.after) {
10097             
10098             inputblock = {
10099                 cls : 'input-group',
10100                 cn :  [] 
10101             };
10102             if (this.before) {
10103                 inputblock.cn.push({
10104                     tag :'span',
10105                     cls : 'input-group-addon',
10106                     html : this.before
10107                 });
10108             }
10109             
10110             inputblock.cn.push(input);
10111             
10112             if(this.hasFeedback && !this.allowBlank){
10113                 inputblock.cls += ' has-feedback';
10114                 inputblock.cn.push(feedback);
10115             }
10116             
10117             if (this.after) {
10118                 inputblock.cn.push({
10119                     tag :'span',
10120                     cls : 'input-group-addon',
10121                     html : this.after
10122                 });
10123             }
10124             
10125         };
10126         
10127         var box = {
10128             tag: 'div',
10129             cn: [
10130                 {
10131                     tag: 'input',
10132                     type : 'hidden',
10133                     cls: 'form-hidden-field'
10134                 },
10135                 inputblock
10136             ]
10137             
10138         };
10139         
10140         if(this.multiple){
10141             box = {
10142                 tag: 'div',
10143                 cn: [
10144                     {
10145                         tag: 'input',
10146                         type : 'hidden',
10147                         cls: 'form-hidden-field'
10148                     },
10149                     {
10150                         tag: 'ul',
10151                         cls: 'roo-select2-choices',
10152                         cn:[
10153                             {
10154                                 tag: 'li',
10155                                 cls: 'roo-select2-search-field',
10156                                 cn: [
10157
10158                                     inputblock
10159                                 ]
10160                             }
10161                         ]
10162                     }
10163                 ]
10164             }
10165         };
10166         
10167         var combobox = {
10168             cls: 'roo-select2-container input-group',
10169             cn: [
10170                 box
10171 //                {
10172 //                    tag: 'ul',
10173 //                    cls: 'typeahead typeahead-long dropdown-menu',
10174 //                    style: 'display:none'
10175 //                }
10176             ]
10177         };
10178         
10179         if(!this.multiple && this.showToggleBtn){
10180             
10181             var caret = {
10182                         tag: 'span',
10183                         cls: 'caret'
10184              };
10185             if (this.caret != false) {
10186                 caret = {
10187                      tag: 'i',
10188                      cls: 'fa fa-' + this.caret
10189                 };
10190                 
10191             }
10192             
10193             combobox.cn.push({
10194                 tag :'span',
10195                 cls : 'input-group-addon btn dropdown-toggle',
10196                 cn : [
10197                     caret,
10198                     {
10199                         tag: 'span',
10200                         cls: 'combobox-clear',
10201                         cn  : [
10202                             {
10203                                 tag : 'i',
10204                                 cls: 'icon-remove'
10205                             }
10206                         ]
10207                     }
10208                 ]
10209
10210             })
10211         }
10212         
10213         if(this.multiple){
10214             combobox.cls += ' roo-select2-container-multi';
10215         }
10216         
10217         if (align ==='left' && this.fieldLabel.length) {
10218             
10219             cfg.cls += ' roo-form-group-label-left';
10220
10221             cfg.cn = [
10222                 {
10223                     tag : 'i',
10224                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10225                     tooltip : 'This field is required'
10226                 },
10227                 {
10228                     tag: 'label',
10229                     'for' :  id,
10230                     cls : 'control-label',
10231                     html : this.fieldLabel
10232
10233                 },
10234                 {
10235                     cls : "", 
10236                     cn: [
10237                         combobox
10238                     ]
10239                 }
10240
10241             ];
10242             
10243             var labelCfg = cfg.cn[1];
10244             var contentCfg = cfg.cn[2];
10245             
10246             if(this.indicatorpos == 'right'){
10247                 cfg.cn = [
10248                     {
10249                         tag: 'label',
10250                         'for' :  id,
10251                         cls : 'control-label',
10252                         cn : [
10253                             {
10254                                 tag : 'span',
10255                                 html : this.fieldLabel
10256                             },
10257                             {
10258                                 tag : 'i',
10259                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10260                                 tooltip : 'This field is required'
10261                             }
10262                         ]
10263                     },
10264                     {
10265                         cls : "", 
10266                         cn: [
10267                             combobox
10268                         ]
10269                     }
10270
10271                 ];
10272                 
10273                 labelCfg = cfg.cn[0];
10274                 contentCfg = cfg.cn[1];
10275             }
10276             
10277             if(this.labelWidth > 12){
10278                 labelCfg.style = "width: " + this.labelWidth + 'px';
10279             }
10280             
10281             if(this.labelWidth < 13 && this.labelmd == 0){
10282                 this.labelmd = this.labelWidth;
10283             }
10284             
10285             if(this.labellg > 0){
10286                 labelCfg.cls += ' col-lg-' + this.labellg;
10287                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10288             }
10289             
10290             if(this.labelmd > 0){
10291                 labelCfg.cls += ' col-md-' + this.labelmd;
10292                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10293             }
10294             
10295             if(this.labelsm > 0){
10296                 labelCfg.cls += ' col-sm-' + this.labelsm;
10297                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10298             }
10299             
10300             if(this.labelxs > 0){
10301                 labelCfg.cls += ' col-xs-' + this.labelxs;
10302                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10303             }
10304             
10305         } else if ( this.fieldLabel.length) {
10306 //                Roo.log(" label");
10307             cfg.cn = [
10308                 {
10309                    tag : 'i',
10310                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10311                    tooltip : 'This field is required'
10312                },
10313                {
10314                    tag: 'label',
10315                    //cls : 'input-group-addon',
10316                    html : this.fieldLabel
10317
10318                },
10319
10320                combobox
10321
10322             ];
10323             
10324             if(this.indicatorpos == 'right'){
10325                 
10326                 cfg.cn = [
10327                     {
10328                        tag: 'label',
10329                        cn : [
10330                            {
10331                                tag : 'span',
10332                                html : this.fieldLabel
10333                            },
10334                            {
10335                               tag : 'i',
10336                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10337                               tooltip : 'This field is required'
10338                            }
10339                        ]
10340
10341                     },
10342                     combobox
10343
10344                 ];
10345
10346             }
10347
10348         } else {
10349             
10350 //                Roo.log(" no label && no align");
10351                 cfg = combobox
10352                      
10353                 
10354         }
10355         
10356         var settings=this;
10357         ['xs','sm','md','lg'].map(function(size){
10358             if (settings[size]) {
10359                 cfg.cls += ' col-' + size + '-' + settings[size];
10360             }
10361         });
10362         
10363         return cfg;
10364         
10365     },
10366     
10367     
10368     
10369     // private
10370     onResize : function(w, h){
10371 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10372 //        if(typeof w == 'number'){
10373 //            var x = w - this.trigger.getWidth();
10374 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10375 //            this.trigger.setStyle('left', x+'px');
10376 //        }
10377     },
10378
10379     // private
10380     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10381
10382     // private
10383     getResizeEl : function(){
10384         return this.inputEl();
10385     },
10386
10387     // private
10388     getPositionEl : function(){
10389         return this.inputEl();
10390     },
10391
10392     // private
10393     alignErrorIcon : function(){
10394         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10395     },
10396
10397     // private
10398     initEvents : function(){
10399         
10400         this.createList();
10401         
10402         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10403         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10404         if(!this.multiple && this.showToggleBtn){
10405             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10406             if(this.hideTrigger){
10407                 this.trigger.setDisplayed(false);
10408             }
10409             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10410         }
10411         
10412         if(this.multiple){
10413             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10414         }
10415         
10416         if(this.removable && !this.editable && !this.tickable){
10417             var close = this.closeTriggerEl();
10418             
10419             if(close){
10420                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10421                 close.on('click', this.removeBtnClick, this, close);
10422             }
10423         }
10424         
10425         //this.trigger.addClassOnOver('x-form-trigger-over');
10426         //this.trigger.addClassOnClick('x-form-trigger-click');
10427         
10428         //if(!this.width){
10429         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10430         //}
10431     },
10432     
10433     closeTriggerEl : function()
10434     {
10435         var close = this.el.select('.roo-combo-removable-btn', true).first();
10436         return close ? close : false;
10437     },
10438     
10439     removeBtnClick : function(e, h, el)
10440     {
10441         e.preventDefault();
10442         
10443         if(this.fireEvent("remove", this) !== false){
10444             this.reset();
10445             this.fireEvent("afterremove", this)
10446         }
10447     },
10448     
10449     createList : function()
10450     {
10451         this.list = Roo.get(document.body).createChild({
10452             tag: 'ul',
10453             cls: 'typeahead typeahead-long dropdown-menu',
10454             style: 'display:none'
10455         });
10456         
10457         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10458         
10459     },
10460
10461     // private
10462     initTrigger : function(){
10463        
10464     },
10465
10466     // private
10467     onDestroy : function(){
10468         if(this.trigger){
10469             this.trigger.removeAllListeners();
10470           //  this.trigger.remove();
10471         }
10472         //if(this.wrap){
10473         //    this.wrap.remove();
10474         //}
10475         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10476     },
10477
10478     // private
10479     onFocus : function(){
10480         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10481         /*
10482         if(!this.mimicing){
10483             this.wrap.addClass('x-trigger-wrap-focus');
10484             this.mimicing = true;
10485             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10486             if(this.monitorTab){
10487                 this.el.on("keydown", this.checkTab, this);
10488             }
10489         }
10490         */
10491     },
10492
10493     // private
10494     checkTab : function(e){
10495         if(e.getKey() == e.TAB){
10496             this.triggerBlur();
10497         }
10498     },
10499
10500     // private
10501     onBlur : function(){
10502         // do nothing
10503     },
10504
10505     // private
10506     mimicBlur : function(e, t){
10507         /*
10508         if(!this.wrap.contains(t) && this.validateBlur()){
10509             this.triggerBlur();
10510         }
10511         */
10512     },
10513
10514     // private
10515     triggerBlur : function(){
10516         this.mimicing = false;
10517         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10518         if(this.monitorTab){
10519             this.el.un("keydown", this.checkTab, this);
10520         }
10521         //this.wrap.removeClass('x-trigger-wrap-focus');
10522         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10523     },
10524
10525     // private
10526     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10527     validateBlur : function(e, t){
10528         return true;
10529     },
10530
10531     // private
10532     onDisable : function(){
10533         this.inputEl().dom.disabled = true;
10534         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10535         //if(this.wrap){
10536         //    this.wrap.addClass('x-item-disabled');
10537         //}
10538     },
10539
10540     // private
10541     onEnable : function(){
10542         this.inputEl().dom.disabled = false;
10543         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10544         //if(this.wrap){
10545         //    this.el.removeClass('x-item-disabled');
10546         //}
10547     },
10548
10549     // private
10550     onShow : function(){
10551         var ae = this.getActionEl();
10552         
10553         if(ae){
10554             ae.dom.style.display = '';
10555             ae.dom.style.visibility = 'visible';
10556         }
10557     },
10558
10559     // private
10560     
10561     onHide : function(){
10562         var ae = this.getActionEl();
10563         ae.dom.style.display = 'none';
10564     },
10565
10566     /**
10567      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10568      * by an implementing function.
10569      * @method
10570      * @param {EventObject} e
10571      */
10572     onTriggerClick : Roo.emptyFn
10573 });
10574  /*
10575  * Based on:
10576  * Ext JS Library 1.1.1
10577  * Copyright(c) 2006-2007, Ext JS, LLC.
10578  *
10579  * Originally Released Under LGPL - original licence link has changed is not relivant.
10580  *
10581  * Fork - LGPL
10582  * <script type="text/javascript">
10583  */
10584
10585
10586 /**
10587  * @class Roo.data.SortTypes
10588  * @singleton
10589  * Defines the default sorting (casting?) comparison functions used when sorting data.
10590  */
10591 Roo.data.SortTypes = {
10592     /**
10593      * Default sort that does nothing
10594      * @param {Mixed} s The value being converted
10595      * @return {Mixed} The comparison value
10596      */
10597     none : function(s){
10598         return s;
10599     },
10600     
10601     /**
10602      * The regular expression used to strip tags
10603      * @type {RegExp}
10604      * @property
10605      */
10606     stripTagsRE : /<\/?[^>]+>/gi,
10607     
10608     /**
10609      * Strips all HTML tags to sort on text only
10610      * @param {Mixed} s The value being converted
10611      * @return {String} The comparison value
10612      */
10613     asText : function(s){
10614         return String(s).replace(this.stripTagsRE, "");
10615     },
10616     
10617     /**
10618      * Strips all HTML tags to sort on text only - Case insensitive
10619      * @param {Mixed} s The value being converted
10620      * @return {String} The comparison value
10621      */
10622     asUCText : function(s){
10623         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10624     },
10625     
10626     /**
10627      * Case insensitive string
10628      * @param {Mixed} s The value being converted
10629      * @return {String} The comparison value
10630      */
10631     asUCString : function(s) {
10632         return String(s).toUpperCase();
10633     },
10634     
10635     /**
10636      * Date sorting
10637      * @param {Mixed} s The value being converted
10638      * @return {Number} The comparison value
10639      */
10640     asDate : function(s) {
10641         if(!s){
10642             return 0;
10643         }
10644         if(s instanceof Date){
10645             return s.getTime();
10646         }
10647         return Date.parse(String(s));
10648     },
10649     
10650     /**
10651      * Float sorting
10652      * @param {Mixed} s The value being converted
10653      * @return {Float} The comparison value
10654      */
10655     asFloat : function(s) {
10656         var val = parseFloat(String(s).replace(/,/g, ""));
10657         if(isNaN(val)) {
10658             val = 0;
10659         }
10660         return val;
10661     },
10662     
10663     /**
10664      * Integer sorting
10665      * @param {Mixed} s The value being converted
10666      * @return {Number} The comparison value
10667      */
10668     asInt : function(s) {
10669         var val = parseInt(String(s).replace(/,/g, ""));
10670         if(isNaN(val)) {
10671             val = 0;
10672         }
10673         return val;
10674     }
10675 };/*
10676  * Based on:
10677  * Ext JS Library 1.1.1
10678  * Copyright(c) 2006-2007, Ext JS, LLC.
10679  *
10680  * Originally Released Under LGPL - original licence link has changed is not relivant.
10681  *
10682  * Fork - LGPL
10683  * <script type="text/javascript">
10684  */
10685
10686 /**
10687 * @class Roo.data.Record
10688  * Instances of this class encapsulate both record <em>definition</em> information, and record
10689  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10690  * to access Records cached in an {@link Roo.data.Store} object.<br>
10691  * <p>
10692  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10693  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10694  * objects.<br>
10695  * <p>
10696  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10697  * @constructor
10698  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10699  * {@link #create}. The parameters are the same.
10700  * @param {Array} data An associative Array of data values keyed by the field name.
10701  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10702  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10703  * not specified an integer id is generated.
10704  */
10705 Roo.data.Record = function(data, id){
10706     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10707     this.data = data;
10708 };
10709
10710 /**
10711  * Generate a constructor for a specific record layout.
10712  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10713  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10714  * Each field definition object may contain the following properties: <ul>
10715  * <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,
10716  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10717  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10718  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10719  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10720  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10721  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10722  * this may be omitted.</p></li>
10723  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10724  * <ul><li>auto (Default, implies no conversion)</li>
10725  * <li>string</li>
10726  * <li>int</li>
10727  * <li>float</li>
10728  * <li>boolean</li>
10729  * <li>date</li></ul></p></li>
10730  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10731  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10732  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10733  * by the Reader into an object that will be stored in the Record. It is passed the
10734  * following parameters:<ul>
10735  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10736  * </ul></p></li>
10737  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10738  * </ul>
10739  * <br>usage:<br><pre><code>
10740 var TopicRecord = Roo.data.Record.create(
10741     {name: 'title', mapping: 'topic_title'},
10742     {name: 'author', mapping: 'username'},
10743     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10744     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10745     {name: 'lastPoster', mapping: 'user2'},
10746     {name: 'excerpt', mapping: 'post_text'}
10747 );
10748
10749 var myNewRecord = new TopicRecord({
10750     title: 'Do my job please',
10751     author: 'noobie',
10752     totalPosts: 1,
10753     lastPost: new Date(),
10754     lastPoster: 'Animal',
10755     excerpt: 'No way dude!'
10756 });
10757 myStore.add(myNewRecord);
10758 </code></pre>
10759  * @method create
10760  * @static
10761  */
10762 Roo.data.Record.create = function(o){
10763     var f = function(){
10764         f.superclass.constructor.apply(this, arguments);
10765     };
10766     Roo.extend(f, Roo.data.Record);
10767     var p = f.prototype;
10768     p.fields = new Roo.util.MixedCollection(false, function(field){
10769         return field.name;
10770     });
10771     for(var i = 0, len = o.length; i < len; i++){
10772         p.fields.add(new Roo.data.Field(o[i]));
10773     }
10774     f.getField = function(name){
10775         return p.fields.get(name);  
10776     };
10777     return f;
10778 };
10779
10780 Roo.data.Record.AUTO_ID = 1000;
10781 Roo.data.Record.EDIT = 'edit';
10782 Roo.data.Record.REJECT = 'reject';
10783 Roo.data.Record.COMMIT = 'commit';
10784
10785 Roo.data.Record.prototype = {
10786     /**
10787      * Readonly flag - true if this record has been modified.
10788      * @type Boolean
10789      */
10790     dirty : false,
10791     editing : false,
10792     error: null,
10793     modified: null,
10794
10795     // private
10796     join : function(store){
10797         this.store = store;
10798     },
10799
10800     /**
10801      * Set the named field to the specified value.
10802      * @param {String} name The name of the field to set.
10803      * @param {Object} value The value to set the field to.
10804      */
10805     set : function(name, value){
10806         if(this.data[name] == value){
10807             return;
10808         }
10809         this.dirty = true;
10810         if(!this.modified){
10811             this.modified = {};
10812         }
10813         if(typeof this.modified[name] == 'undefined'){
10814             this.modified[name] = this.data[name];
10815         }
10816         this.data[name] = value;
10817         if(!this.editing && this.store){
10818             this.store.afterEdit(this);
10819         }       
10820     },
10821
10822     /**
10823      * Get the value of the named field.
10824      * @param {String} name The name of the field to get the value of.
10825      * @return {Object} The value of the field.
10826      */
10827     get : function(name){
10828         return this.data[name]; 
10829     },
10830
10831     // private
10832     beginEdit : function(){
10833         this.editing = true;
10834         this.modified = {}; 
10835     },
10836
10837     // private
10838     cancelEdit : function(){
10839         this.editing = false;
10840         delete this.modified;
10841     },
10842
10843     // private
10844     endEdit : function(){
10845         this.editing = false;
10846         if(this.dirty && this.store){
10847             this.store.afterEdit(this);
10848         }
10849     },
10850
10851     /**
10852      * Usually called by the {@link Roo.data.Store} which owns the Record.
10853      * Rejects all changes made to the Record since either creation, or the last commit operation.
10854      * Modified fields are reverted to their original values.
10855      * <p>
10856      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10857      * of reject operations.
10858      */
10859     reject : function(){
10860         var m = this.modified;
10861         for(var n in m){
10862             if(typeof m[n] != "function"){
10863                 this.data[n] = m[n];
10864             }
10865         }
10866         this.dirty = false;
10867         delete this.modified;
10868         this.editing = false;
10869         if(this.store){
10870             this.store.afterReject(this);
10871         }
10872     },
10873
10874     /**
10875      * Usually called by the {@link Roo.data.Store} which owns the Record.
10876      * Commits all changes made to the Record since either creation, or the last commit operation.
10877      * <p>
10878      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10879      * of commit operations.
10880      */
10881     commit : function(){
10882         this.dirty = false;
10883         delete this.modified;
10884         this.editing = false;
10885         if(this.store){
10886             this.store.afterCommit(this);
10887         }
10888     },
10889
10890     // private
10891     hasError : function(){
10892         return this.error != null;
10893     },
10894
10895     // private
10896     clearError : function(){
10897         this.error = null;
10898     },
10899
10900     /**
10901      * Creates a copy of this record.
10902      * @param {String} id (optional) A new record id if you don't want to use this record's id
10903      * @return {Record}
10904      */
10905     copy : function(newId) {
10906         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10907     }
10908 };/*
10909  * Based on:
10910  * Ext JS Library 1.1.1
10911  * Copyright(c) 2006-2007, Ext JS, LLC.
10912  *
10913  * Originally Released Under LGPL - original licence link has changed is not relivant.
10914  *
10915  * Fork - LGPL
10916  * <script type="text/javascript">
10917  */
10918
10919
10920
10921 /**
10922  * @class Roo.data.Store
10923  * @extends Roo.util.Observable
10924  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10925  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10926  * <p>
10927  * 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
10928  * has no knowledge of the format of the data returned by the Proxy.<br>
10929  * <p>
10930  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10931  * instances from the data object. These records are cached and made available through accessor functions.
10932  * @constructor
10933  * Creates a new Store.
10934  * @param {Object} config A config object containing the objects needed for the Store to access data,
10935  * and read the data into Records.
10936  */
10937 Roo.data.Store = function(config){
10938     this.data = new Roo.util.MixedCollection(false);
10939     this.data.getKey = function(o){
10940         return o.id;
10941     };
10942     this.baseParams = {};
10943     // private
10944     this.paramNames = {
10945         "start" : "start",
10946         "limit" : "limit",
10947         "sort" : "sort",
10948         "dir" : "dir",
10949         "multisort" : "_multisort"
10950     };
10951
10952     if(config && config.data){
10953         this.inlineData = config.data;
10954         delete config.data;
10955     }
10956
10957     Roo.apply(this, config);
10958     
10959     if(this.reader){ // reader passed
10960         this.reader = Roo.factory(this.reader, Roo.data);
10961         this.reader.xmodule = this.xmodule || false;
10962         if(!this.recordType){
10963             this.recordType = this.reader.recordType;
10964         }
10965         if(this.reader.onMetaChange){
10966             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10967         }
10968     }
10969
10970     if(this.recordType){
10971         this.fields = this.recordType.prototype.fields;
10972     }
10973     this.modified = [];
10974
10975     this.addEvents({
10976         /**
10977          * @event datachanged
10978          * Fires when the data cache has changed, and a widget which is using this Store
10979          * as a Record cache should refresh its view.
10980          * @param {Store} this
10981          */
10982         datachanged : true,
10983         /**
10984          * @event metachange
10985          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10986          * @param {Store} this
10987          * @param {Object} meta The JSON metadata
10988          */
10989         metachange : true,
10990         /**
10991          * @event add
10992          * Fires when Records have been added to the Store
10993          * @param {Store} this
10994          * @param {Roo.data.Record[]} records The array of Records added
10995          * @param {Number} index The index at which the record(s) were added
10996          */
10997         add : true,
10998         /**
10999          * @event remove
11000          * Fires when a Record has been removed from the Store
11001          * @param {Store} this
11002          * @param {Roo.data.Record} record The Record that was removed
11003          * @param {Number} index The index at which the record was removed
11004          */
11005         remove : true,
11006         /**
11007          * @event update
11008          * Fires when a Record has been updated
11009          * @param {Store} this
11010          * @param {Roo.data.Record} record The Record that was updated
11011          * @param {String} operation The update operation being performed.  Value may be one of:
11012          * <pre><code>
11013  Roo.data.Record.EDIT
11014  Roo.data.Record.REJECT
11015  Roo.data.Record.COMMIT
11016          * </code></pre>
11017          */
11018         update : true,
11019         /**
11020          * @event clear
11021          * Fires when the data cache has been cleared.
11022          * @param {Store} this
11023          */
11024         clear : true,
11025         /**
11026          * @event beforeload
11027          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11028          * the load action will be canceled.
11029          * @param {Store} this
11030          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11031          */
11032         beforeload : true,
11033         /**
11034          * @event beforeloadadd
11035          * Fires after a new set of Records has been loaded.
11036          * @param {Store} this
11037          * @param {Roo.data.Record[]} records The Records that were loaded
11038          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11039          */
11040         beforeloadadd : true,
11041         /**
11042          * @event load
11043          * Fires after a new set of Records has been loaded, before they are added to the store.
11044          * @param {Store} this
11045          * @param {Roo.data.Record[]} records The Records that were loaded
11046          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11047          * @params {Object} return from reader
11048          */
11049         load : true,
11050         /**
11051          * @event loadexception
11052          * Fires if an exception occurs in the Proxy during loading.
11053          * Called with the signature of the Proxy's "loadexception" event.
11054          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11055          * 
11056          * @param {Proxy} 
11057          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11058          * @param {Object} load options 
11059          * @param {Object} jsonData from your request (normally this contains the Exception)
11060          */
11061         loadexception : true
11062     });
11063     
11064     if(this.proxy){
11065         this.proxy = Roo.factory(this.proxy, Roo.data);
11066         this.proxy.xmodule = this.xmodule || false;
11067         this.relayEvents(this.proxy,  ["loadexception"]);
11068     }
11069     this.sortToggle = {};
11070     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11071
11072     Roo.data.Store.superclass.constructor.call(this);
11073
11074     if(this.inlineData){
11075         this.loadData(this.inlineData);
11076         delete this.inlineData;
11077     }
11078 };
11079
11080 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11081      /**
11082     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11083     * without a remote query - used by combo/forms at present.
11084     */
11085     
11086     /**
11087     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11088     */
11089     /**
11090     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11091     */
11092     /**
11093     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11094     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11095     */
11096     /**
11097     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11098     * on any HTTP request
11099     */
11100     /**
11101     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11102     */
11103     /**
11104     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11105     */
11106     multiSort: false,
11107     /**
11108     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11109     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11110     */
11111     remoteSort : false,
11112
11113     /**
11114     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11115      * loaded or when a record is removed. (defaults to false).
11116     */
11117     pruneModifiedRecords : false,
11118
11119     // private
11120     lastOptions : null,
11121
11122     /**
11123      * Add Records to the Store and fires the add event.
11124      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11125      */
11126     add : function(records){
11127         records = [].concat(records);
11128         for(var i = 0, len = records.length; i < len; i++){
11129             records[i].join(this);
11130         }
11131         var index = this.data.length;
11132         this.data.addAll(records);
11133         this.fireEvent("add", this, records, index);
11134     },
11135
11136     /**
11137      * Remove a Record from the Store and fires the remove event.
11138      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11139      */
11140     remove : function(record){
11141         var index = this.data.indexOf(record);
11142         this.data.removeAt(index);
11143  
11144         if(this.pruneModifiedRecords){
11145             this.modified.remove(record);
11146         }
11147         this.fireEvent("remove", this, record, index);
11148     },
11149
11150     /**
11151      * Remove all Records from the Store and fires the clear event.
11152      */
11153     removeAll : function(){
11154         this.data.clear();
11155         if(this.pruneModifiedRecords){
11156             this.modified = [];
11157         }
11158         this.fireEvent("clear", this);
11159     },
11160
11161     /**
11162      * Inserts Records to the Store at the given index and fires the add event.
11163      * @param {Number} index The start index at which to insert the passed Records.
11164      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11165      */
11166     insert : function(index, records){
11167         records = [].concat(records);
11168         for(var i = 0, len = records.length; i < len; i++){
11169             this.data.insert(index, records[i]);
11170             records[i].join(this);
11171         }
11172         this.fireEvent("add", this, records, index);
11173     },
11174
11175     /**
11176      * Get the index within the cache of the passed Record.
11177      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11178      * @return {Number} The index of the passed Record. Returns -1 if not found.
11179      */
11180     indexOf : function(record){
11181         return this.data.indexOf(record);
11182     },
11183
11184     /**
11185      * Get the index within the cache of the Record with the passed id.
11186      * @param {String} id The id of the Record to find.
11187      * @return {Number} The index of the Record. Returns -1 if not found.
11188      */
11189     indexOfId : function(id){
11190         return this.data.indexOfKey(id);
11191     },
11192
11193     /**
11194      * Get the Record with the specified id.
11195      * @param {String} id The id of the Record to find.
11196      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11197      */
11198     getById : function(id){
11199         return this.data.key(id);
11200     },
11201
11202     /**
11203      * Get the Record at the specified index.
11204      * @param {Number} index The index of the Record to find.
11205      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11206      */
11207     getAt : function(index){
11208         return this.data.itemAt(index);
11209     },
11210
11211     /**
11212      * Returns a range of Records between specified indices.
11213      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11214      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11215      * @return {Roo.data.Record[]} An array of Records
11216      */
11217     getRange : function(start, end){
11218         return this.data.getRange(start, end);
11219     },
11220
11221     // private
11222     storeOptions : function(o){
11223         o = Roo.apply({}, o);
11224         delete o.callback;
11225         delete o.scope;
11226         this.lastOptions = o;
11227     },
11228
11229     /**
11230      * Loads the Record cache from the configured Proxy using the configured Reader.
11231      * <p>
11232      * If using remote paging, then the first load call must specify the <em>start</em>
11233      * and <em>limit</em> properties in the options.params property to establish the initial
11234      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11235      * <p>
11236      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11237      * and this call will return before the new data has been loaded. Perform any post-processing
11238      * in a callback function, or in a "load" event handler.</strong>
11239      * <p>
11240      * @param {Object} options An object containing properties which control loading options:<ul>
11241      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11242      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11243      * passed the following arguments:<ul>
11244      * <li>r : Roo.data.Record[]</li>
11245      * <li>options: Options object from the load call</li>
11246      * <li>success: Boolean success indicator</li></ul></li>
11247      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11248      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11249      * </ul>
11250      */
11251     load : function(options){
11252         options = options || {};
11253         if(this.fireEvent("beforeload", this, options) !== false){
11254             this.storeOptions(options);
11255             var p = Roo.apply(options.params || {}, this.baseParams);
11256             // if meta was not loaded from remote source.. try requesting it.
11257             if (!this.reader.metaFromRemote) {
11258                 p._requestMeta = 1;
11259             }
11260             if(this.sortInfo && this.remoteSort){
11261                 var pn = this.paramNames;
11262                 p[pn["sort"]] = this.sortInfo.field;
11263                 p[pn["dir"]] = this.sortInfo.direction;
11264             }
11265             if (this.multiSort) {
11266                 var pn = this.paramNames;
11267                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11268             }
11269             
11270             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11271         }
11272     },
11273
11274     /**
11275      * Reloads the Record cache from the configured Proxy using the configured Reader and
11276      * the options from the last load operation performed.
11277      * @param {Object} options (optional) An object containing properties which may override the options
11278      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11279      * the most recently used options are reused).
11280      */
11281     reload : function(options){
11282         this.load(Roo.applyIf(options||{}, this.lastOptions));
11283     },
11284
11285     // private
11286     // Called as a callback by the Reader during a load operation.
11287     loadRecords : function(o, options, success){
11288         if(!o || success === false){
11289             if(success !== false){
11290                 this.fireEvent("load", this, [], options, o);
11291             }
11292             if(options.callback){
11293                 options.callback.call(options.scope || this, [], options, false);
11294             }
11295             return;
11296         }
11297         // if data returned failure - throw an exception.
11298         if (o.success === false) {
11299             // show a message if no listener is registered.
11300             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11301                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11302             }
11303             // loadmask wil be hooked into this..
11304             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11305             return;
11306         }
11307         var r = o.records, t = o.totalRecords || r.length;
11308         
11309         this.fireEvent("beforeloadadd", this, r, options, o);
11310         
11311         if(!options || options.add !== true){
11312             if(this.pruneModifiedRecords){
11313                 this.modified = [];
11314             }
11315             for(var i = 0, len = r.length; i < len; i++){
11316                 r[i].join(this);
11317             }
11318             if(this.snapshot){
11319                 this.data = this.snapshot;
11320                 delete this.snapshot;
11321             }
11322             this.data.clear();
11323             this.data.addAll(r);
11324             this.totalLength = t;
11325             this.applySort();
11326             this.fireEvent("datachanged", this);
11327         }else{
11328             this.totalLength = Math.max(t, this.data.length+r.length);
11329             this.add(r);
11330         }
11331         
11332         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11333                 
11334             var e = new Roo.data.Record({});
11335
11336             e.set(this.parent.displayField, this.parent.emptyTitle);
11337             e.set(this.parent.valueField, '');
11338
11339             this.insert(0, e);
11340         }
11341             
11342         this.fireEvent("load", this, r, options, o);
11343         if(options.callback){
11344             options.callback.call(options.scope || this, r, options, true);
11345         }
11346     },
11347
11348
11349     /**
11350      * Loads data from a passed data block. A Reader which understands the format of the data
11351      * must have been configured in the constructor.
11352      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11353      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11354      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11355      */
11356     loadData : function(o, append){
11357         var r = this.reader.readRecords(o);
11358         this.loadRecords(r, {add: append}, true);
11359     },
11360
11361     /**
11362      * Gets the number of cached records.
11363      * <p>
11364      * <em>If using paging, this may not be the total size of the dataset. If the data object
11365      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11366      * the data set size</em>
11367      */
11368     getCount : function(){
11369         return this.data.length || 0;
11370     },
11371
11372     /**
11373      * Gets the total number of records in the dataset as returned by the server.
11374      * <p>
11375      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11376      * the dataset size</em>
11377      */
11378     getTotalCount : function(){
11379         return this.totalLength || 0;
11380     },
11381
11382     /**
11383      * Returns the sort state of the Store as an object with two properties:
11384      * <pre><code>
11385  field {String} The name of the field by which the Records are sorted
11386  direction {String} The sort order, "ASC" or "DESC"
11387      * </code></pre>
11388      */
11389     getSortState : function(){
11390         return this.sortInfo;
11391     },
11392
11393     // private
11394     applySort : function(){
11395         if(this.sortInfo && !this.remoteSort){
11396             var s = this.sortInfo, f = s.field;
11397             var st = this.fields.get(f).sortType;
11398             var fn = function(r1, r2){
11399                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11400                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11401             };
11402             this.data.sort(s.direction, fn);
11403             if(this.snapshot && this.snapshot != this.data){
11404                 this.snapshot.sort(s.direction, fn);
11405             }
11406         }
11407     },
11408
11409     /**
11410      * Sets the default sort column and order to be used by the next load operation.
11411      * @param {String} fieldName The name of the field to sort by.
11412      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11413      */
11414     setDefaultSort : function(field, dir){
11415         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11416     },
11417
11418     /**
11419      * Sort the Records.
11420      * If remote sorting is used, the sort is performed on the server, and the cache is
11421      * reloaded. If local sorting is used, the cache is sorted internally.
11422      * @param {String} fieldName The name of the field to sort by.
11423      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11424      */
11425     sort : function(fieldName, dir){
11426         var f = this.fields.get(fieldName);
11427         if(!dir){
11428             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11429             
11430             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11431                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11432             }else{
11433                 dir = f.sortDir;
11434             }
11435         }
11436         this.sortToggle[f.name] = dir;
11437         this.sortInfo = {field: f.name, direction: dir};
11438         if(!this.remoteSort){
11439             this.applySort();
11440             this.fireEvent("datachanged", this);
11441         }else{
11442             this.load(this.lastOptions);
11443         }
11444     },
11445
11446     /**
11447      * Calls the specified function for each of the Records in the cache.
11448      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11449      * Returning <em>false</em> aborts and exits the iteration.
11450      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11451      */
11452     each : function(fn, scope){
11453         this.data.each(fn, scope);
11454     },
11455
11456     /**
11457      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11458      * (e.g., during paging).
11459      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11460      */
11461     getModifiedRecords : function(){
11462         return this.modified;
11463     },
11464
11465     // private
11466     createFilterFn : function(property, value, anyMatch){
11467         if(!value.exec){ // not a regex
11468             value = String(value);
11469             if(value.length == 0){
11470                 return false;
11471             }
11472             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11473         }
11474         return function(r){
11475             return value.test(r.data[property]);
11476         };
11477     },
11478
11479     /**
11480      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11481      * @param {String} property A field on your records
11482      * @param {Number} start The record index to start at (defaults to 0)
11483      * @param {Number} end The last record index to include (defaults to length - 1)
11484      * @return {Number} The sum
11485      */
11486     sum : function(property, start, end){
11487         var rs = this.data.items, v = 0;
11488         start = start || 0;
11489         end = (end || end === 0) ? end : rs.length-1;
11490
11491         for(var i = start; i <= end; i++){
11492             v += (rs[i].data[property] || 0);
11493         }
11494         return v;
11495     },
11496
11497     /**
11498      * Filter the records by a specified property.
11499      * @param {String} field A field on your records
11500      * @param {String/RegExp} value Either a string that the field
11501      * should start with or a RegExp to test against the field
11502      * @param {Boolean} anyMatch True to match any part not just the beginning
11503      */
11504     filter : function(property, value, anyMatch){
11505         var fn = this.createFilterFn(property, value, anyMatch);
11506         return fn ? this.filterBy(fn) : this.clearFilter();
11507     },
11508
11509     /**
11510      * Filter by a function. The specified function will be called with each
11511      * record in this data source. If the function returns true the record is included,
11512      * otherwise it is filtered.
11513      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11514      * @param {Object} scope (optional) The scope of the function (defaults to this)
11515      */
11516     filterBy : function(fn, scope){
11517         this.snapshot = this.snapshot || this.data;
11518         this.data = this.queryBy(fn, scope||this);
11519         this.fireEvent("datachanged", this);
11520     },
11521
11522     /**
11523      * Query the records by a specified property.
11524      * @param {String} field A field on your records
11525      * @param {String/RegExp} value Either a string that the field
11526      * should start with or a RegExp to test against the field
11527      * @param {Boolean} anyMatch True to match any part not just the beginning
11528      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11529      */
11530     query : function(property, value, anyMatch){
11531         var fn = this.createFilterFn(property, value, anyMatch);
11532         return fn ? this.queryBy(fn) : this.data.clone();
11533     },
11534
11535     /**
11536      * Query by a function. The specified function will be called with each
11537      * record in this data source. If the function returns true the record is included
11538      * in the results.
11539      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11540      * @param {Object} scope (optional) The scope of the function (defaults to this)
11541       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11542      **/
11543     queryBy : function(fn, scope){
11544         var data = this.snapshot || this.data;
11545         return data.filterBy(fn, scope||this);
11546     },
11547
11548     /**
11549      * Collects unique values for a particular dataIndex from this store.
11550      * @param {String} dataIndex The property to collect
11551      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11552      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11553      * @return {Array} An array of the unique values
11554      **/
11555     collect : function(dataIndex, allowNull, bypassFilter){
11556         var d = (bypassFilter === true && this.snapshot) ?
11557                 this.snapshot.items : this.data.items;
11558         var v, sv, r = [], l = {};
11559         for(var i = 0, len = d.length; i < len; i++){
11560             v = d[i].data[dataIndex];
11561             sv = String(v);
11562             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11563                 l[sv] = true;
11564                 r[r.length] = v;
11565             }
11566         }
11567         return r;
11568     },
11569
11570     /**
11571      * Revert to a view of the Record cache with no filtering applied.
11572      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11573      */
11574     clearFilter : function(suppressEvent){
11575         if(this.snapshot && this.snapshot != this.data){
11576             this.data = this.snapshot;
11577             delete this.snapshot;
11578             if(suppressEvent !== true){
11579                 this.fireEvent("datachanged", this);
11580             }
11581         }
11582     },
11583
11584     // private
11585     afterEdit : function(record){
11586         if(this.modified.indexOf(record) == -1){
11587             this.modified.push(record);
11588         }
11589         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11590     },
11591     
11592     // private
11593     afterReject : function(record){
11594         this.modified.remove(record);
11595         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11596     },
11597
11598     // private
11599     afterCommit : function(record){
11600         this.modified.remove(record);
11601         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11602     },
11603
11604     /**
11605      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11606      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11607      */
11608     commitChanges : function(){
11609         var m = this.modified.slice(0);
11610         this.modified = [];
11611         for(var i = 0, len = m.length; i < len; i++){
11612             m[i].commit();
11613         }
11614     },
11615
11616     /**
11617      * Cancel outstanding changes on all changed records.
11618      */
11619     rejectChanges : function(){
11620         var m = this.modified.slice(0);
11621         this.modified = [];
11622         for(var i = 0, len = m.length; i < len; i++){
11623             m[i].reject();
11624         }
11625     },
11626
11627     onMetaChange : function(meta, rtype, o){
11628         this.recordType = rtype;
11629         this.fields = rtype.prototype.fields;
11630         delete this.snapshot;
11631         this.sortInfo = meta.sortInfo || this.sortInfo;
11632         this.modified = [];
11633         this.fireEvent('metachange', this, this.reader.meta);
11634     },
11635     
11636     moveIndex : function(data, type)
11637     {
11638         var index = this.indexOf(data);
11639         
11640         var newIndex = index + type;
11641         
11642         this.remove(data);
11643         
11644         this.insert(newIndex, data);
11645         
11646     }
11647 });/*
11648  * Based on:
11649  * Ext JS Library 1.1.1
11650  * Copyright(c) 2006-2007, Ext JS, LLC.
11651  *
11652  * Originally Released Under LGPL - original licence link has changed is not relivant.
11653  *
11654  * Fork - LGPL
11655  * <script type="text/javascript">
11656  */
11657
11658 /**
11659  * @class Roo.data.SimpleStore
11660  * @extends Roo.data.Store
11661  * Small helper class to make creating Stores from Array data easier.
11662  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11663  * @cfg {Array} fields An array of field definition objects, or field name strings.
11664  * @cfg {Array} data The multi-dimensional array of data
11665  * @constructor
11666  * @param {Object} config
11667  */
11668 Roo.data.SimpleStore = function(config){
11669     Roo.data.SimpleStore.superclass.constructor.call(this, {
11670         isLocal : true,
11671         reader: new Roo.data.ArrayReader({
11672                 id: config.id
11673             },
11674             Roo.data.Record.create(config.fields)
11675         ),
11676         proxy : new Roo.data.MemoryProxy(config.data)
11677     });
11678     this.load();
11679 };
11680 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11681  * Based on:
11682  * Ext JS Library 1.1.1
11683  * Copyright(c) 2006-2007, Ext JS, LLC.
11684  *
11685  * Originally Released Under LGPL - original licence link has changed is not relivant.
11686  *
11687  * Fork - LGPL
11688  * <script type="text/javascript">
11689  */
11690
11691 /**
11692 /**
11693  * @extends Roo.data.Store
11694  * @class Roo.data.JsonStore
11695  * Small helper class to make creating Stores for JSON data easier. <br/>
11696 <pre><code>
11697 var store = new Roo.data.JsonStore({
11698     url: 'get-images.php',
11699     root: 'images',
11700     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11701 });
11702 </code></pre>
11703  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11704  * JsonReader and HttpProxy (unless inline data is provided).</b>
11705  * @cfg {Array} fields An array of field definition objects, or field name strings.
11706  * @constructor
11707  * @param {Object} config
11708  */
11709 Roo.data.JsonStore = function(c){
11710     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11711         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11712         reader: new Roo.data.JsonReader(c, c.fields)
11713     }));
11714 };
11715 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11716  * Based on:
11717  * Ext JS Library 1.1.1
11718  * Copyright(c) 2006-2007, Ext JS, LLC.
11719  *
11720  * Originally Released Under LGPL - original licence link has changed is not relivant.
11721  *
11722  * Fork - LGPL
11723  * <script type="text/javascript">
11724  */
11725
11726  
11727 Roo.data.Field = function(config){
11728     if(typeof config == "string"){
11729         config = {name: config};
11730     }
11731     Roo.apply(this, config);
11732     
11733     if(!this.type){
11734         this.type = "auto";
11735     }
11736     
11737     var st = Roo.data.SortTypes;
11738     // named sortTypes are supported, here we look them up
11739     if(typeof this.sortType == "string"){
11740         this.sortType = st[this.sortType];
11741     }
11742     
11743     // set default sortType for strings and dates
11744     if(!this.sortType){
11745         switch(this.type){
11746             case "string":
11747                 this.sortType = st.asUCString;
11748                 break;
11749             case "date":
11750                 this.sortType = st.asDate;
11751                 break;
11752             default:
11753                 this.sortType = st.none;
11754         }
11755     }
11756
11757     // define once
11758     var stripRe = /[\$,%]/g;
11759
11760     // prebuilt conversion function for this field, instead of
11761     // switching every time we're reading a value
11762     if(!this.convert){
11763         var cv, dateFormat = this.dateFormat;
11764         switch(this.type){
11765             case "":
11766             case "auto":
11767             case undefined:
11768                 cv = function(v){ return v; };
11769                 break;
11770             case "string":
11771                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11772                 break;
11773             case "int":
11774                 cv = function(v){
11775                     return v !== undefined && v !== null && v !== '' ?
11776                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11777                     };
11778                 break;
11779             case "float":
11780                 cv = function(v){
11781                     return v !== undefined && v !== null && v !== '' ?
11782                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11783                     };
11784                 break;
11785             case "bool":
11786             case "boolean":
11787                 cv = function(v){ return v === true || v === "true" || v == 1; };
11788                 break;
11789             case "date":
11790                 cv = function(v){
11791                     if(!v){
11792                         return '';
11793                     }
11794                     if(v instanceof Date){
11795                         return v;
11796                     }
11797                     if(dateFormat){
11798                         if(dateFormat == "timestamp"){
11799                             return new Date(v*1000);
11800                         }
11801                         return Date.parseDate(v, dateFormat);
11802                     }
11803                     var parsed = Date.parse(v);
11804                     return parsed ? new Date(parsed) : null;
11805                 };
11806              break;
11807             
11808         }
11809         this.convert = cv;
11810     }
11811 };
11812
11813 Roo.data.Field.prototype = {
11814     dateFormat: null,
11815     defaultValue: "",
11816     mapping: null,
11817     sortType : null,
11818     sortDir : "ASC"
11819 };/*
11820  * Based on:
11821  * Ext JS Library 1.1.1
11822  * Copyright(c) 2006-2007, Ext JS, LLC.
11823  *
11824  * Originally Released Under LGPL - original licence link has changed is not relivant.
11825  *
11826  * Fork - LGPL
11827  * <script type="text/javascript">
11828  */
11829  
11830 // Base class for reading structured data from a data source.  This class is intended to be
11831 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11832
11833 /**
11834  * @class Roo.data.DataReader
11835  * Base class for reading structured data from a data source.  This class is intended to be
11836  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11837  */
11838
11839 Roo.data.DataReader = function(meta, recordType){
11840     
11841     this.meta = meta;
11842     
11843     this.recordType = recordType instanceof Array ? 
11844         Roo.data.Record.create(recordType) : recordType;
11845 };
11846
11847 Roo.data.DataReader.prototype = {
11848      /**
11849      * Create an empty record
11850      * @param {Object} data (optional) - overlay some values
11851      * @return {Roo.data.Record} record created.
11852      */
11853     newRow :  function(d) {
11854         var da =  {};
11855         this.recordType.prototype.fields.each(function(c) {
11856             switch( c.type) {
11857                 case 'int' : da[c.name] = 0; break;
11858                 case 'date' : da[c.name] = new Date(); break;
11859                 case 'float' : da[c.name] = 0.0; break;
11860                 case 'boolean' : da[c.name] = false; break;
11861                 default : da[c.name] = ""; break;
11862             }
11863             
11864         });
11865         return new this.recordType(Roo.apply(da, d));
11866     }
11867     
11868 };/*
11869  * Based on:
11870  * Ext JS Library 1.1.1
11871  * Copyright(c) 2006-2007, Ext JS, LLC.
11872  *
11873  * Originally Released Under LGPL - original licence link has changed is not relivant.
11874  *
11875  * Fork - LGPL
11876  * <script type="text/javascript">
11877  */
11878
11879 /**
11880  * @class Roo.data.DataProxy
11881  * @extends Roo.data.Observable
11882  * This class is an abstract base class for implementations which provide retrieval of
11883  * unformatted data objects.<br>
11884  * <p>
11885  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11886  * (of the appropriate type which knows how to parse the data object) to provide a block of
11887  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11888  * <p>
11889  * Custom implementations must implement the load method as described in
11890  * {@link Roo.data.HttpProxy#load}.
11891  */
11892 Roo.data.DataProxy = function(){
11893     this.addEvents({
11894         /**
11895          * @event beforeload
11896          * Fires before a network request is made to retrieve a data object.
11897          * @param {Object} This DataProxy object.
11898          * @param {Object} params The params parameter to the load function.
11899          */
11900         beforeload : true,
11901         /**
11902          * @event load
11903          * Fires before the load method's callback is called.
11904          * @param {Object} This DataProxy object.
11905          * @param {Object} o The data object.
11906          * @param {Object} arg The callback argument object passed to the load function.
11907          */
11908         load : true,
11909         /**
11910          * @event loadexception
11911          * Fires if an Exception occurs during data retrieval.
11912          * @param {Object} This DataProxy object.
11913          * @param {Object} o The data object.
11914          * @param {Object} arg The callback argument object passed to the load function.
11915          * @param {Object} e The Exception.
11916          */
11917         loadexception : true
11918     });
11919     Roo.data.DataProxy.superclass.constructor.call(this);
11920 };
11921
11922 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11923
11924     /**
11925      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11926      */
11927 /*
11928  * Based on:
11929  * Ext JS Library 1.1.1
11930  * Copyright(c) 2006-2007, Ext JS, LLC.
11931  *
11932  * Originally Released Under LGPL - original licence link has changed is not relivant.
11933  *
11934  * Fork - LGPL
11935  * <script type="text/javascript">
11936  */
11937 /**
11938  * @class Roo.data.MemoryProxy
11939  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11940  * to the Reader when its load method is called.
11941  * @constructor
11942  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11943  */
11944 Roo.data.MemoryProxy = function(data){
11945     if (data.data) {
11946         data = data.data;
11947     }
11948     Roo.data.MemoryProxy.superclass.constructor.call(this);
11949     this.data = data;
11950 };
11951
11952 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11953     
11954     /**
11955      * Load data from the requested source (in this case an in-memory
11956      * data object passed to the constructor), read the data object into
11957      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11958      * process that block using the passed callback.
11959      * @param {Object} params This parameter is not used by the MemoryProxy class.
11960      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11961      * object into a block of Roo.data.Records.
11962      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11963      * The function must be passed <ul>
11964      * <li>The Record block object</li>
11965      * <li>The "arg" argument from the load function</li>
11966      * <li>A boolean success indicator</li>
11967      * </ul>
11968      * @param {Object} scope The scope in which to call the callback
11969      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11970      */
11971     load : function(params, reader, callback, scope, arg){
11972         params = params || {};
11973         var result;
11974         try {
11975             result = reader.readRecords(this.data);
11976         }catch(e){
11977             this.fireEvent("loadexception", this, arg, null, e);
11978             callback.call(scope, null, arg, false);
11979             return;
11980         }
11981         callback.call(scope, result, arg, true);
11982     },
11983     
11984     // private
11985     update : function(params, records){
11986         
11987     }
11988 });/*
11989  * Based on:
11990  * Ext JS Library 1.1.1
11991  * Copyright(c) 2006-2007, Ext JS, LLC.
11992  *
11993  * Originally Released Under LGPL - original licence link has changed is not relivant.
11994  *
11995  * Fork - LGPL
11996  * <script type="text/javascript">
11997  */
11998 /**
11999  * @class Roo.data.HttpProxy
12000  * @extends Roo.data.DataProxy
12001  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12002  * configured to reference a certain URL.<br><br>
12003  * <p>
12004  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12005  * from which the running page was served.<br><br>
12006  * <p>
12007  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12008  * <p>
12009  * Be aware that to enable the browser to parse an XML document, the server must set
12010  * the Content-Type header in the HTTP response to "text/xml".
12011  * @constructor
12012  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12013  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12014  * will be used to make the request.
12015  */
12016 Roo.data.HttpProxy = function(conn){
12017     Roo.data.HttpProxy.superclass.constructor.call(this);
12018     // is conn a conn config or a real conn?
12019     this.conn = conn;
12020     this.useAjax = !conn || !conn.events;
12021   
12022 };
12023
12024 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12025     // thse are take from connection...
12026     
12027     /**
12028      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12029      */
12030     /**
12031      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12032      * extra parameters to each request made by this object. (defaults to undefined)
12033      */
12034     /**
12035      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12036      *  to each request made by this object. (defaults to undefined)
12037      */
12038     /**
12039      * @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)
12040      */
12041     /**
12042      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12043      */
12044      /**
12045      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12046      * @type Boolean
12047      */
12048   
12049
12050     /**
12051      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12052      * @type Boolean
12053      */
12054     /**
12055      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12056      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12057      * a finer-grained basis than the DataProxy events.
12058      */
12059     getConnection : function(){
12060         return this.useAjax ? Roo.Ajax : this.conn;
12061     },
12062
12063     /**
12064      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12065      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12066      * process that block using the passed callback.
12067      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12068      * for the request to the remote server.
12069      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12070      * object into a block of Roo.data.Records.
12071      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12072      * The function must be passed <ul>
12073      * <li>The Record block object</li>
12074      * <li>The "arg" argument from the load function</li>
12075      * <li>A boolean success indicator</li>
12076      * </ul>
12077      * @param {Object} scope The scope in which to call the callback
12078      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12079      */
12080     load : function(params, reader, callback, scope, arg){
12081         if(this.fireEvent("beforeload", this, params) !== false){
12082             var  o = {
12083                 params : params || {},
12084                 request: {
12085                     callback : callback,
12086                     scope : scope,
12087                     arg : arg
12088                 },
12089                 reader: reader,
12090                 callback : this.loadResponse,
12091                 scope: this
12092             };
12093             if(this.useAjax){
12094                 Roo.applyIf(o, this.conn);
12095                 if(this.activeRequest){
12096                     Roo.Ajax.abort(this.activeRequest);
12097                 }
12098                 this.activeRequest = Roo.Ajax.request(o);
12099             }else{
12100                 this.conn.request(o);
12101             }
12102         }else{
12103             callback.call(scope||this, null, arg, false);
12104         }
12105     },
12106
12107     // private
12108     loadResponse : function(o, success, response){
12109         delete this.activeRequest;
12110         if(!success){
12111             this.fireEvent("loadexception", this, o, response);
12112             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12113             return;
12114         }
12115         var result;
12116         try {
12117             result = o.reader.read(response);
12118         }catch(e){
12119             this.fireEvent("loadexception", this, o, response, e);
12120             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12121             return;
12122         }
12123         
12124         this.fireEvent("load", this, o, o.request.arg);
12125         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12126     },
12127
12128     // private
12129     update : function(dataSet){
12130
12131     },
12132
12133     // private
12134     updateResponse : function(dataSet){
12135
12136     }
12137 });/*
12138  * Based on:
12139  * Ext JS Library 1.1.1
12140  * Copyright(c) 2006-2007, Ext JS, LLC.
12141  *
12142  * Originally Released Under LGPL - original licence link has changed is not relivant.
12143  *
12144  * Fork - LGPL
12145  * <script type="text/javascript">
12146  */
12147
12148 /**
12149  * @class Roo.data.ScriptTagProxy
12150  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12151  * other than the originating domain of the running page.<br><br>
12152  * <p>
12153  * <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
12154  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12155  * <p>
12156  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12157  * source code that is used as the source inside a &lt;script> tag.<br><br>
12158  * <p>
12159  * In order for the browser to process the returned data, the server must wrap the data object
12160  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12161  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12162  * depending on whether the callback name was passed:
12163  * <p>
12164  * <pre><code>
12165 boolean scriptTag = false;
12166 String cb = request.getParameter("callback");
12167 if (cb != null) {
12168     scriptTag = true;
12169     response.setContentType("text/javascript");
12170 } else {
12171     response.setContentType("application/x-json");
12172 }
12173 Writer out = response.getWriter();
12174 if (scriptTag) {
12175     out.write(cb + "(");
12176 }
12177 out.print(dataBlock.toJsonString());
12178 if (scriptTag) {
12179     out.write(");");
12180 }
12181 </pre></code>
12182  *
12183  * @constructor
12184  * @param {Object} config A configuration object.
12185  */
12186 Roo.data.ScriptTagProxy = function(config){
12187     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12188     Roo.apply(this, config);
12189     this.head = document.getElementsByTagName("head")[0];
12190 };
12191
12192 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12193
12194 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12195     /**
12196      * @cfg {String} url The URL from which to request the data object.
12197      */
12198     /**
12199      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12200      */
12201     timeout : 30000,
12202     /**
12203      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12204      * the server the name of the callback function set up by the load call to process the returned data object.
12205      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12206      * javascript output which calls this named function passing the data object as its only parameter.
12207      */
12208     callbackParam : "callback",
12209     /**
12210      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12211      * name to the request.
12212      */
12213     nocache : true,
12214
12215     /**
12216      * Load data from the configured URL, read the data object into
12217      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12218      * process that block using the passed callback.
12219      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12220      * for the request to the remote server.
12221      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12222      * object into a block of Roo.data.Records.
12223      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12224      * The function must be passed <ul>
12225      * <li>The Record block object</li>
12226      * <li>The "arg" argument from the load function</li>
12227      * <li>A boolean success indicator</li>
12228      * </ul>
12229      * @param {Object} scope The scope in which to call the callback
12230      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12231      */
12232     load : function(params, reader, callback, scope, arg){
12233         if(this.fireEvent("beforeload", this, params) !== false){
12234
12235             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12236
12237             var url = this.url;
12238             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12239             if(this.nocache){
12240                 url += "&_dc=" + (new Date().getTime());
12241             }
12242             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12243             var trans = {
12244                 id : transId,
12245                 cb : "stcCallback"+transId,
12246                 scriptId : "stcScript"+transId,
12247                 params : params,
12248                 arg : arg,
12249                 url : url,
12250                 callback : callback,
12251                 scope : scope,
12252                 reader : reader
12253             };
12254             var conn = this;
12255
12256             window[trans.cb] = function(o){
12257                 conn.handleResponse(o, trans);
12258             };
12259
12260             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12261
12262             if(this.autoAbort !== false){
12263                 this.abort();
12264             }
12265
12266             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12267
12268             var script = document.createElement("script");
12269             script.setAttribute("src", url);
12270             script.setAttribute("type", "text/javascript");
12271             script.setAttribute("id", trans.scriptId);
12272             this.head.appendChild(script);
12273
12274             this.trans = trans;
12275         }else{
12276             callback.call(scope||this, null, arg, false);
12277         }
12278     },
12279
12280     // private
12281     isLoading : function(){
12282         return this.trans ? true : false;
12283     },
12284
12285     /**
12286      * Abort the current server request.
12287      */
12288     abort : function(){
12289         if(this.isLoading()){
12290             this.destroyTrans(this.trans);
12291         }
12292     },
12293
12294     // private
12295     destroyTrans : function(trans, isLoaded){
12296         this.head.removeChild(document.getElementById(trans.scriptId));
12297         clearTimeout(trans.timeoutId);
12298         if(isLoaded){
12299             window[trans.cb] = undefined;
12300             try{
12301                 delete window[trans.cb];
12302             }catch(e){}
12303         }else{
12304             // if hasn't been loaded, wait for load to remove it to prevent script error
12305             window[trans.cb] = function(){
12306                 window[trans.cb] = undefined;
12307                 try{
12308                     delete window[trans.cb];
12309                 }catch(e){}
12310             };
12311         }
12312     },
12313
12314     // private
12315     handleResponse : function(o, trans){
12316         this.trans = false;
12317         this.destroyTrans(trans, true);
12318         var result;
12319         try {
12320             result = trans.reader.readRecords(o);
12321         }catch(e){
12322             this.fireEvent("loadexception", this, o, trans.arg, e);
12323             trans.callback.call(trans.scope||window, null, trans.arg, false);
12324             return;
12325         }
12326         this.fireEvent("load", this, o, trans.arg);
12327         trans.callback.call(trans.scope||window, result, trans.arg, true);
12328     },
12329
12330     // private
12331     handleFailure : function(trans){
12332         this.trans = false;
12333         this.destroyTrans(trans, false);
12334         this.fireEvent("loadexception", this, null, trans.arg);
12335         trans.callback.call(trans.scope||window, null, trans.arg, false);
12336     }
12337 });/*
12338  * Based on:
12339  * Ext JS Library 1.1.1
12340  * Copyright(c) 2006-2007, Ext JS, LLC.
12341  *
12342  * Originally Released Under LGPL - original licence link has changed is not relivant.
12343  *
12344  * Fork - LGPL
12345  * <script type="text/javascript">
12346  */
12347
12348 /**
12349  * @class Roo.data.JsonReader
12350  * @extends Roo.data.DataReader
12351  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12352  * based on mappings in a provided Roo.data.Record constructor.
12353  * 
12354  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12355  * in the reply previously. 
12356  * 
12357  * <p>
12358  * Example code:
12359  * <pre><code>
12360 var RecordDef = Roo.data.Record.create([
12361     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12362     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12363 ]);
12364 var myReader = new Roo.data.JsonReader({
12365     totalProperty: "results",    // The property which contains the total dataset size (optional)
12366     root: "rows",                // The property which contains an Array of row objects
12367     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12368 }, RecordDef);
12369 </code></pre>
12370  * <p>
12371  * This would consume a JSON file like this:
12372  * <pre><code>
12373 { 'results': 2, 'rows': [
12374     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12375     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12376 }
12377 </code></pre>
12378  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12379  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12380  * paged from the remote server.
12381  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12382  * @cfg {String} root name of the property which contains the Array of row objects.
12383  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12384  * @cfg {Array} fields Array of field definition objects
12385  * @constructor
12386  * Create a new JsonReader
12387  * @param {Object} meta Metadata configuration options
12388  * @param {Object} recordType Either an Array of field definition objects,
12389  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12390  */
12391 Roo.data.JsonReader = function(meta, recordType){
12392     
12393     meta = meta || {};
12394     // set some defaults:
12395     Roo.applyIf(meta, {
12396         totalProperty: 'total',
12397         successProperty : 'success',
12398         root : 'data',
12399         id : 'id'
12400     });
12401     
12402     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12403 };
12404 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12405     
12406     /**
12407      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12408      * Used by Store query builder to append _requestMeta to params.
12409      * 
12410      */
12411     metaFromRemote : false,
12412     /**
12413      * This method is only used by a DataProxy which has retrieved data from a remote server.
12414      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12415      * @return {Object} data A data block which is used by an Roo.data.Store object as
12416      * a cache of Roo.data.Records.
12417      */
12418     read : function(response){
12419         var json = response.responseText;
12420        
12421         var o = /* eval:var:o */ eval("("+json+")");
12422         if(!o) {
12423             throw {message: "JsonReader.read: Json object not found"};
12424         }
12425         
12426         if(o.metaData){
12427             
12428             delete this.ef;
12429             this.metaFromRemote = true;
12430             this.meta = o.metaData;
12431             this.recordType = Roo.data.Record.create(o.metaData.fields);
12432             this.onMetaChange(this.meta, this.recordType, o);
12433         }
12434         return this.readRecords(o);
12435     },
12436
12437     // private function a store will implement
12438     onMetaChange : function(meta, recordType, o){
12439
12440     },
12441
12442     /**
12443          * @ignore
12444          */
12445     simpleAccess: function(obj, subsc) {
12446         return obj[subsc];
12447     },
12448
12449         /**
12450          * @ignore
12451          */
12452     getJsonAccessor: function(){
12453         var re = /[\[\.]/;
12454         return function(expr) {
12455             try {
12456                 return(re.test(expr))
12457                     ? new Function("obj", "return obj." + expr)
12458                     : function(obj){
12459                         return obj[expr];
12460                     };
12461             } catch(e){}
12462             return Roo.emptyFn;
12463         };
12464     }(),
12465
12466     /**
12467      * Create a data block containing Roo.data.Records from an XML document.
12468      * @param {Object} o An object which contains an Array of row objects in the property specified
12469      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12470      * which contains the total size of the dataset.
12471      * @return {Object} data A data block which is used by an Roo.data.Store object as
12472      * a cache of Roo.data.Records.
12473      */
12474     readRecords : function(o){
12475         /**
12476          * After any data loads, the raw JSON data is available for further custom processing.
12477          * @type Object
12478          */
12479         this.o = o;
12480         var s = this.meta, Record = this.recordType,
12481             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12482
12483 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12484         if (!this.ef) {
12485             if(s.totalProperty) {
12486                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12487                 }
12488                 if(s.successProperty) {
12489                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12490                 }
12491                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12492                 if (s.id) {
12493                         var g = this.getJsonAccessor(s.id);
12494                         this.getId = function(rec) {
12495                                 var r = g(rec);  
12496                                 return (r === undefined || r === "") ? null : r;
12497                         };
12498                 } else {
12499                         this.getId = function(){return null;};
12500                 }
12501             this.ef = [];
12502             for(var jj = 0; jj < fl; jj++){
12503                 f = fi[jj];
12504                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12505                 this.ef[jj] = this.getJsonAccessor(map);
12506             }
12507         }
12508
12509         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12510         if(s.totalProperty){
12511             var vt = parseInt(this.getTotal(o), 10);
12512             if(!isNaN(vt)){
12513                 totalRecords = vt;
12514             }
12515         }
12516         if(s.successProperty){
12517             var vs = this.getSuccess(o);
12518             if(vs === false || vs === 'false'){
12519                 success = false;
12520             }
12521         }
12522         var records = [];
12523         for(var i = 0; i < c; i++){
12524                 var n = root[i];
12525             var values = {};
12526             var id = this.getId(n);
12527             for(var j = 0; j < fl; j++){
12528                 f = fi[j];
12529             var v = this.ef[j](n);
12530             if (!f.convert) {
12531                 Roo.log('missing convert for ' + f.name);
12532                 Roo.log(f);
12533                 continue;
12534             }
12535             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12536             }
12537             var record = new Record(values, id);
12538             record.json = n;
12539             records[i] = record;
12540         }
12541         return {
12542             raw : o,
12543             success : success,
12544             records : records,
12545             totalRecords : totalRecords
12546         };
12547     }
12548 });/*
12549  * Based on:
12550  * Ext JS Library 1.1.1
12551  * Copyright(c) 2006-2007, Ext JS, LLC.
12552  *
12553  * Originally Released Under LGPL - original licence link has changed is not relivant.
12554  *
12555  * Fork - LGPL
12556  * <script type="text/javascript">
12557  */
12558
12559 /**
12560  * @class Roo.data.ArrayReader
12561  * @extends Roo.data.DataReader
12562  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12563  * Each element of that Array represents a row of data fields. The
12564  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12565  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12566  * <p>
12567  * Example code:.
12568  * <pre><code>
12569 var RecordDef = Roo.data.Record.create([
12570     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12571     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12572 ]);
12573 var myReader = new Roo.data.ArrayReader({
12574     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12575 }, RecordDef);
12576 </code></pre>
12577  * <p>
12578  * This would consume an Array like this:
12579  * <pre><code>
12580 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12581   </code></pre>
12582  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12583  * @constructor
12584  * Create a new JsonReader
12585  * @param {Object} meta Metadata configuration options.
12586  * @param {Object} recordType Either an Array of field definition objects
12587  * as specified to {@link Roo.data.Record#create},
12588  * or an {@link Roo.data.Record} object
12589  * created using {@link Roo.data.Record#create}.
12590  */
12591 Roo.data.ArrayReader = function(meta, recordType){
12592     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12593 };
12594
12595 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12596     /**
12597      * Create a data block containing Roo.data.Records from an XML document.
12598      * @param {Object} o An Array of row objects which represents the dataset.
12599      * @return {Object} data A data block which is used by an Roo.data.Store object as
12600      * a cache of Roo.data.Records.
12601      */
12602     readRecords : function(o){
12603         var sid = this.meta ? this.meta.id : null;
12604         var recordType = this.recordType, fields = recordType.prototype.fields;
12605         var records = [];
12606         var root = o;
12607             for(var i = 0; i < root.length; i++){
12608                     var n = root[i];
12609                 var values = {};
12610                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12611                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12612                 var f = fields.items[j];
12613                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12614                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12615                 v = f.convert(v);
12616                 values[f.name] = v;
12617             }
12618                 var record = new recordType(values, id);
12619                 record.json = n;
12620                 records[records.length] = record;
12621             }
12622             return {
12623                 records : records,
12624                 totalRecords : records.length
12625             };
12626     }
12627 });/*
12628  * - LGPL
12629  * * 
12630  */
12631
12632 /**
12633  * @class Roo.bootstrap.ComboBox
12634  * @extends Roo.bootstrap.TriggerField
12635  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12636  * @cfg {Boolean} append (true|false) default false
12637  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12638  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12639  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12640  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12641  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12642  * @cfg {Boolean} animate default true
12643  * @cfg {Boolean} emptyResultText only for touch device
12644  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12645  * @cfg {String} emptyTitle default ''
12646  * @constructor
12647  * Create a new ComboBox.
12648  * @param {Object} config Configuration options
12649  */
12650 Roo.bootstrap.ComboBox = function(config){
12651     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12652     this.addEvents({
12653         /**
12654          * @event expand
12655          * Fires when the dropdown list is expanded
12656         * @param {Roo.bootstrap.ComboBox} combo This combo box
12657         */
12658         'expand' : true,
12659         /**
12660          * @event collapse
12661          * Fires when the dropdown list is collapsed
12662         * @param {Roo.bootstrap.ComboBox} combo This combo box
12663         */
12664         'collapse' : true,
12665         /**
12666          * @event beforeselect
12667          * Fires before a list item is selected. Return false to cancel the selection.
12668         * @param {Roo.bootstrap.ComboBox} combo This combo box
12669         * @param {Roo.data.Record} record The data record returned from the underlying store
12670         * @param {Number} index The index of the selected item in the dropdown list
12671         */
12672         'beforeselect' : true,
12673         /**
12674          * @event select
12675          * Fires when a list item is selected
12676         * @param {Roo.bootstrap.ComboBox} combo This combo box
12677         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12678         * @param {Number} index The index of the selected item in the dropdown list
12679         */
12680         'select' : true,
12681         /**
12682          * @event beforequery
12683          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12684          * The event object passed has these properties:
12685         * @param {Roo.bootstrap.ComboBox} combo This combo box
12686         * @param {String} query The query
12687         * @param {Boolean} forceAll true to force "all" query
12688         * @param {Boolean} cancel true to cancel the query
12689         * @param {Object} e The query event object
12690         */
12691         'beforequery': true,
12692          /**
12693          * @event add
12694          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12695         * @param {Roo.bootstrap.ComboBox} combo This combo box
12696         */
12697         'add' : true,
12698         /**
12699          * @event edit
12700          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12701         * @param {Roo.bootstrap.ComboBox} combo This combo box
12702         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12703         */
12704         'edit' : true,
12705         /**
12706          * @event remove
12707          * Fires when the remove value from the combobox array
12708         * @param {Roo.bootstrap.ComboBox} combo This combo box
12709         */
12710         'remove' : true,
12711         /**
12712          * @event afterremove
12713          * Fires when the remove value from the combobox array
12714         * @param {Roo.bootstrap.ComboBox} combo This combo box
12715         */
12716         'afterremove' : true,
12717         /**
12718          * @event specialfilter
12719          * Fires when specialfilter
12720             * @param {Roo.bootstrap.ComboBox} combo This combo box
12721             */
12722         'specialfilter' : true,
12723         /**
12724          * @event tick
12725          * Fires when tick the element
12726             * @param {Roo.bootstrap.ComboBox} combo This combo box
12727             */
12728         'tick' : true,
12729         /**
12730          * @event touchviewdisplay
12731          * Fires when touch view require special display (default is using displayField)
12732             * @param {Roo.bootstrap.ComboBox} combo This combo box
12733             * @param {Object} cfg set html .
12734             */
12735         'touchviewdisplay' : true
12736         
12737     });
12738     
12739     this.item = [];
12740     this.tickItems = [];
12741     
12742     this.selectedIndex = -1;
12743     if(this.mode == 'local'){
12744         if(config.queryDelay === undefined){
12745             this.queryDelay = 10;
12746         }
12747         if(config.minChars === undefined){
12748             this.minChars = 0;
12749         }
12750     }
12751 };
12752
12753 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12754      
12755     /**
12756      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12757      * rendering into an Roo.Editor, defaults to false)
12758      */
12759     /**
12760      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12761      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12762      */
12763     /**
12764      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12765      */
12766     /**
12767      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12768      * the dropdown list (defaults to undefined, with no header element)
12769      */
12770
12771      /**
12772      * @cfg {String/Roo.Template} tpl The template to use to render the output
12773      */
12774      
12775      /**
12776      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12777      */
12778     listWidth: undefined,
12779     /**
12780      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12781      * mode = 'remote' or 'text' if mode = 'local')
12782      */
12783     displayField: undefined,
12784     
12785     /**
12786      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12787      * mode = 'remote' or 'value' if mode = 'local'). 
12788      * Note: use of a valueField requires the user make a selection
12789      * in order for a value to be mapped.
12790      */
12791     valueField: undefined,
12792     /**
12793      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12794      */
12795     modalTitle : '',
12796     
12797     /**
12798      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12799      * field's data value (defaults to the underlying DOM element's name)
12800      */
12801     hiddenName: undefined,
12802     /**
12803      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12804      */
12805     listClass: '',
12806     /**
12807      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12808      */
12809     selectedClass: 'active',
12810     
12811     /**
12812      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12813      */
12814     shadow:'sides',
12815     /**
12816      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12817      * anchor positions (defaults to 'tl-bl')
12818      */
12819     listAlign: 'tl-bl?',
12820     /**
12821      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12822      */
12823     maxHeight: 300,
12824     /**
12825      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12826      * query specified by the allQuery config option (defaults to 'query')
12827      */
12828     triggerAction: 'query',
12829     /**
12830      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12831      * (defaults to 4, does not apply if editable = false)
12832      */
12833     minChars : 4,
12834     /**
12835      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12836      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12837      */
12838     typeAhead: false,
12839     /**
12840      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12841      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12842      */
12843     queryDelay: 500,
12844     /**
12845      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12846      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12847      */
12848     pageSize: 0,
12849     /**
12850      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12851      * when editable = true (defaults to false)
12852      */
12853     selectOnFocus:false,
12854     /**
12855      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12856      */
12857     queryParam: 'query',
12858     /**
12859      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12860      * when mode = 'remote' (defaults to 'Loading...')
12861      */
12862     loadingText: 'Loading...',
12863     /**
12864      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12865      */
12866     resizable: false,
12867     /**
12868      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12869      */
12870     handleHeight : 8,
12871     /**
12872      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12873      * traditional select (defaults to true)
12874      */
12875     editable: true,
12876     /**
12877      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12878      */
12879     allQuery: '',
12880     /**
12881      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12882      */
12883     mode: 'remote',
12884     /**
12885      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12886      * listWidth has a higher value)
12887      */
12888     minListWidth : 70,
12889     /**
12890      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12891      * allow the user to set arbitrary text into the field (defaults to false)
12892      */
12893     forceSelection:false,
12894     /**
12895      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12896      * if typeAhead = true (defaults to 250)
12897      */
12898     typeAheadDelay : 250,
12899     /**
12900      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12901      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12902      */
12903     valueNotFoundText : undefined,
12904     /**
12905      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12906      */
12907     blockFocus : false,
12908     
12909     /**
12910      * @cfg {Boolean} disableClear Disable showing of clear button.
12911      */
12912     disableClear : false,
12913     /**
12914      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12915      */
12916     alwaysQuery : false,
12917     
12918     /**
12919      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12920      */
12921     multiple : false,
12922     
12923     /**
12924      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12925      */
12926     invalidClass : "has-warning",
12927     
12928     /**
12929      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12930      */
12931     validClass : "has-success",
12932     
12933     /**
12934      * @cfg {Boolean} specialFilter (true|false) special filter default false
12935      */
12936     specialFilter : false,
12937     
12938     /**
12939      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12940      */
12941     mobileTouchView : true,
12942     
12943     /**
12944      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12945      */
12946     useNativeIOS : false,
12947     
12948     ios_options : false,
12949     
12950     //private
12951     addicon : false,
12952     editicon: false,
12953     
12954     page: 0,
12955     hasQuery: false,
12956     append: false,
12957     loadNext: false,
12958     autoFocus : true,
12959     tickable : false,
12960     btnPosition : 'right',
12961     triggerList : true,
12962     showToggleBtn : true,
12963     animate : true,
12964     emptyResultText: 'Empty',
12965     triggerText : 'Select',
12966     emptyTitle : '',
12967     
12968     // element that contains real text value.. (when hidden is used..)
12969     
12970     getAutoCreate : function()
12971     {   
12972         var cfg = false;
12973         //render
12974         /*
12975          * Render classic select for iso
12976          */
12977         
12978         if(Roo.isIOS && this.useNativeIOS){
12979             cfg = this.getAutoCreateNativeIOS();
12980             return cfg;
12981         }
12982         
12983         /*
12984          * Touch Devices
12985          */
12986         
12987         if(Roo.isTouch && this.mobileTouchView){
12988             cfg = this.getAutoCreateTouchView();
12989             return cfg;;
12990         }
12991         
12992         /*
12993          *  Normal ComboBox
12994          */
12995         if(!this.tickable){
12996             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12997             return cfg;
12998         }
12999         
13000         /*
13001          *  ComboBox with tickable selections
13002          */
13003              
13004         var align = this.labelAlign || this.parentLabelAlign();
13005         
13006         cfg = {
13007             cls : 'form-group roo-combobox-tickable' //input-group
13008         };
13009         
13010         var btn_text_select = '';
13011         var btn_text_done = '';
13012         var btn_text_cancel = '';
13013         
13014         if (this.btn_text_show) {
13015             btn_text_select = 'Select';
13016             btn_text_done = 'Done';
13017             btn_text_cancel = 'Cancel'; 
13018         }
13019         
13020         var buttons = {
13021             tag : 'div',
13022             cls : 'tickable-buttons',
13023             cn : [
13024                 {
13025                     tag : 'button',
13026                     type : 'button',
13027                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13028                     //html : this.triggerText
13029                     html: btn_text_select
13030                 },
13031                 {
13032                     tag : 'button',
13033                     type : 'button',
13034                     name : 'ok',
13035                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13036                     //html : 'Done'
13037                     html: btn_text_done
13038                 },
13039                 {
13040                     tag : 'button',
13041                     type : 'button',
13042                     name : 'cancel',
13043                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13044                     //html : 'Cancel'
13045                     html: btn_text_cancel
13046                 }
13047             ]
13048         };
13049         
13050         if(this.editable){
13051             buttons.cn.unshift({
13052                 tag: 'input',
13053                 cls: 'roo-select2-search-field-input'
13054             });
13055         }
13056         
13057         var _this = this;
13058         
13059         Roo.each(buttons.cn, function(c){
13060             if (_this.size) {
13061                 c.cls += ' btn-' + _this.size;
13062             }
13063
13064             if (_this.disabled) {
13065                 c.disabled = true;
13066             }
13067         });
13068         
13069         var box = {
13070             tag: 'div',
13071             cn: [
13072                 {
13073                     tag: 'input',
13074                     type : 'hidden',
13075                     cls: 'form-hidden-field'
13076                 },
13077                 {
13078                     tag: 'ul',
13079                     cls: 'roo-select2-choices',
13080                     cn:[
13081                         {
13082                             tag: 'li',
13083                             cls: 'roo-select2-search-field',
13084                             cn: [
13085                                 buttons
13086                             ]
13087                         }
13088                     ]
13089                 }
13090             ]
13091         };
13092         
13093         var combobox = {
13094             cls: 'roo-select2-container input-group roo-select2-container-multi',
13095             cn: [
13096                 box
13097 //                {
13098 //                    tag: 'ul',
13099 //                    cls: 'typeahead typeahead-long dropdown-menu',
13100 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13101 //                }
13102             ]
13103         };
13104         
13105         if(this.hasFeedback && !this.allowBlank){
13106             
13107             var feedback = {
13108                 tag: 'span',
13109                 cls: 'glyphicon form-control-feedback'
13110             };
13111
13112             combobox.cn.push(feedback);
13113         }
13114         
13115         
13116         if (align ==='left' && this.fieldLabel.length) {
13117             
13118             cfg.cls += ' roo-form-group-label-left';
13119             
13120             cfg.cn = [
13121                 {
13122                     tag : 'i',
13123                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13124                     tooltip : 'This field is required'
13125                 },
13126                 {
13127                     tag: 'label',
13128                     'for' :  id,
13129                     cls : 'control-label',
13130                     html : this.fieldLabel
13131
13132                 },
13133                 {
13134                     cls : "", 
13135                     cn: [
13136                         combobox
13137                     ]
13138                 }
13139
13140             ];
13141             
13142             var labelCfg = cfg.cn[1];
13143             var contentCfg = cfg.cn[2];
13144             
13145
13146             if(this.indicatorpos == 'right'){
13147                 
13148                 cfg.cn = [
13149                     {
13150                         tag: 'label',
13151                         'for' :  id,
13152                         cls : 'control-label',
13153                         cn : [
13154                             {
13155                                 tag : 'span',
13156                                 html : this.fieldLabel
13157                             },
13158                             {
13159                                 tag : 'i',
13160                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13161                                 tooltip : 'This field is required'
13162                             }
13163                         ]
13164                     },
13165                     {
13166                         cls : "",
13167                         cn: [
13168                             combobox
13169                         ]
13170                     }
13171
13172                 ];
13173                 
13174                 
13175                 
13176                 labelCfg = cfg.cn[0];
13177                 contentCfg = cfg.cn[1];
13178             
13179             }
13180             
13181             if(this.labelWidth > 12){
13182                 labelCfg.style = "width: " + this.labelWidth + 'px';
13183             }
13184             
13185             if(this.labelWidth < 13 && this.labelmd == 0){
13186                 this.labelmd = this.labelWidth;
13187             }
13188             
13189             if(this.labellg > 0){
13190                 labelCfg.cls += ' col-lg-' + this.labellg;
13191                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13192             }
13193             
13194             if(this.labelmd > 0){
13195                 labelCfg.cls += ' col-md-' + this.labelmd;
13196                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13197             }
13198             
13199             if(this.labelsm > 0){
13200                 labelCfg.cls += ' col-sm-' + this.labelsm;
13201                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13202             }
13203             
13204             if(this.labelxs > 0){
13205                 labelCfg.cls += ' col-xs-' + this.labelxs;
13206                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13207             }
13208                 
13209                 
13210         } else if ( this.fieldLabel.length) {
13211 //                Roo.log(" label");
13212                  cfg.cn = [
13213                     {
13214                         tag : 'i',
13215                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13216                         tooltip : 'This field is required'
13217                     },
13218                     {
13219                         tag: 'label',
13220                         //cls : 'input-group-addon',
13221                         html : this.fieldLabel
13222                     },
13223                     combobox
13224                 ];
13225                 
13226                 if(this.indicatorpos == 'right'){
13227                     cfg.cn = [
13228                         {
13229                             tag: 'label',
13230                             //cls : 'input-group-addon',
13231                             html : this.fieldLabel
13232                         },
13233                         {
13234                             tag : 'i',
13235                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13236                             tooltip : 'This field is required'
13237                         },
13238                         combobox
13239                     ];
13240                     
13241                 }
13242
13243         } else {
13244             
13245 //                Roo.log(" no label && no align");
13246                 cfg = combobox
13247                      
13248                 
13249         }
13250          
13251         var settings=this;
13252         ['xs','sm','md','lg'].map(function(size){
13253             if (settings[size]) {
13254                 cfg.cls += ' col-' + size + '-' + settings[size];
13255             }
13256         });
13257         
13258         return cfg;
13259         
13260     },
13261     
13262     _initEventsCalled : false,
13263     
13264     // private
13265     initEvents: function()
13266     {   
13267         if (this._initEventsCalled) { // as we call render... prevent looping...
13268             return;
13269         }
13270         this._initEventsCalled = true;
13271         
13272         if (!this.store) {
13273             throw "can not find store for combo";
13274         }
13275         
13276         this.indicator = this.indicatorEl();
13277         
13278         this.store = Roo.factory(this.store, Roo.data);
13279         this.store.parent = this;
13280         
13281         // if we are building from html. then this element is so complex, that we can not really
13282         // use the rendered HTML.
13283         // so we have to trash and replace the previous code.
13284         if (Roo.XComponent.build_from_html) {
13285             // remove this element....
13286             var e = this.el.dom, k=0;
13287             while (e ) { e = e.previousSibling;  ++k;}
13288
13289             this.el.remove();
13290             
13291             this.el=false;
13292             this.rendered = false;
13293             
13294             this.render(this.parent().getChildContainer(true), k);
13295         }
13296         
13297         if(Roo.isIOS && this.useNativeIOS){
13298             this.initIOSView();
13299             return;
13300         }
13301         
13302         /*
13303          * Touch Devices
13304          */
13305         
13306         if(Roo.isTouch && this.mobileTouchView){
13307             this.initTouchView();
13308             return;
13309         }
13310         
13311         if(this.tickable){
13312             this.initTickableEvents();
13313             return;
13314         }
13315         
13316         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13317         
13318         if(this.hiddenName){
13319             
13320             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13321             
13322             this.hiddenField.dom.value =
13323                 this.hiddenValue !== undefined ? this.hiddenValue :
13324                 this.value !== undefined ? this.value : '';
13325
13326             // prevent input submission
13327             this.el.dom.removeAttribute('name');
13328             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13329              
13330              
13331         }
13332         //if(Roo.isGecko){
13333         //    this.el.dom.setAttribute('autocomplete', 'off');
13334         //}
13335         
13336         var cls = 'x-combo-list';
13337         
13338         //this.list = new Roo.Layer({
13339         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13340         //});
13341         
13342         var _this = this;
13343         
13344         (function(){
13345             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13346             _this.list.setWidth(lw);
13347         }).defer(100);
13348         
13349         this.list.on('mouseover', this.onViewOver, this);
13350         this.list.on('mousemove', this.onViewMove, this);
13351         this.list.on('scroll', this.onViewScroll, this);
13352         
13353         /*
13354         this.list.swallowEvent('mousewheel');
13355         this.assetHeight = 0;
13356
13357         if(this.title){
13358             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13359             this.assetHeight += this.header.getHeight();
13360         }
13361
13362         this.innerList = this.list.createChild({cls:cls+'-inner'});
13363         this.innerList.on('mouseover', this.onViewOver, this);
13364         this.innerList.on('mousemove', this.onViewMove, this);
13365         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13366         
13367         if(this.allowBlank && !this.pageSize && !this.disableClear){
13368             this.footer = this.list.createChild({cls:cls+'-ft'});
13369             this.pageTb = new Roo.Toolbar(this.footer);
13370            
13371         }
13372         if(this.pageSize){
13373             this.footer = this.list.createChild({cls:cls+'-ft'});
13374             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13375                     {pageSize: this.pageSize});
13376             
13377         }
13378         
13379         if (this.pageTb && this.allowBlank && !this.disableClear) {
13380             var _this = this;
13381             this.pageTb.add(new Roo.Toolbar.Fill(), {
13382                 cls: 'x-btn-icon x-btn-clear',
13383                 text: '&#160;',
13384                 handler: function()
13385                 {
13386                     _this.collapse();
13387                     _this.clearValue();
13388                     _this.onSelect(false, -1);
13389                 }
13390             });
13391         }
13392         if (this.footer) {
13393             this.assetHeight += this.footer.getHeight();
13394         }
13395         */
13396             
13397         if(!this.tpl){
13398             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13399         }
13400
13401         this.view = new Roo.View(this.list, this.tpl, {
13402             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13403         });
13404         //this.view.wrapEl.setDisplayed(false);
13405         this.view.on('click', this.onViewClick, this);
13406         
13407         
13408         this.store.on('beforeload', this.onBeforeLoad, this);
13409         this.store.on('load', this.onLoad, this);
13410         this.store.on('loadexception', this.onLoadException, this);
13411         /*
13412         if(this.resizable){
13413             this.resizer = new Roo.Resizable(this.list,  {
13414                pinned:true, handles:'se'
13415             });
13416             this.resizer.on('resize', function(r, w, h){
13417                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13418                 this.listWidth = w;
13419                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13420                 this.restrictHeight();
13421             }, this);
13422             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13423         }
13424         */
13425         if(!this.editable){
13426             this.editable = true;
13427             this.setEditable(false);
13428         }
13429         
13430         /*
13431         
13432         if (typeof(this.events.add.listeners) != 'undefined') {
13433             
13434             this.addicon = this.wrap.createChild(
13435                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13436        
13437             this.addicon.on('click', function(e) {
13438                 this.fireEvent('add', this);
13439             }, this);
13440         }
13441         if (typeof(this.events.edit.listeners) != 'undefined') {
13442             
13443             this.editicon = this.wrap.createChild(
13444                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13445             if (this.addicon) {
13446                 this.editicon.setStyle('margin-left', '40px');
13447             }
13448             this.editicon.on('click', function(e) {
13449                 
13450                 // we fire even  if inothing is selected..
13451                 this.fireEvent('edit', this, this.lastData );
13452                 
13453             }, this);
13454         }
13455         */
13456         
13457         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13458             "up" : function(e){
13459                 this.inKeyMode = true;
13460                 this.selectPrev();
13461             },
13462
13463             "down" : function(e){
13464                 if(!this.isExpanded()){
13465                     this.onTriggerClick();
13466                 }else{
13467                     this.inKeyMode = true;
13468                     this.selectNext();
13469                 }
13470             },
13471
13472             "enter" : function(e){
13473 //                this.onViewClick();
13474                 //return true;
13475                 this.collapse();
13476                 
13477                 if(this.fireEvent("specialkey", this, e)){
13478                     this.onViewClick(false);
13479                 }
13480                 
13481                 return true;
13482             },
13483
13484             "esc" : function(e){
13485                 this.collapse();
13486             },
13487
13488             "tab" : function(e){
13489                 this.collapse();
13490                 
13491                 if(this.fireEvent("specialkey", this, e)){
13492                     this.onViewClick(false);
13493                 }
13494                 
13495                 return true;
13496             },
13497
13498             scope : this,
13499
13500             doRelay : function(foo, bar, hname){
13501                 if(hname == 'down' || this.scope.isExpanded()){
13502                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13503                 }
13504                 return true;
13505             },
13506
13507             forceKeyDown: true
13508         });
13509         
13510         
13511         this.queryDelay = Math.max(this.queryDelay || 10,
13512                 this.mode == 'local' ? 10 : 250);
13513         
13514         
13515         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13516         
13517         if(this.typeAhead){
13518             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13519         }
13520         if(this.editable !== false){
13521             this.inputEl().on("keyup", this.onKeyUp, this);
13522         }
13523         if(this.forceSelection){
13524             this.inputEl().on('blur', this.doForce, this);
13525         }
13526         
13527         if(this.multiple){
13528             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13529             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13530         }
13531     },
13532     
13533     initTickableEvents: function()
13534     {   
13535         this.createList();
13536         
13537         if(this.hiddenName){
13538             
13539             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13540             
13541             this.hiddenField.dom.value =
13542                 this.hiddenValue !== undefined ? this.hiddenValue :
13543                 this.value !== undefined ? this.value : '';
13544
13545             // prevent input submission
13546             this.el.dom.removeAttribute('name');
13547             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13548              
13549              
13550         }
13551         
13552 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13553         
13554         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13555         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13556         if(this.triggerList){
13557             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13558         }
13559          
13560         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13561         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13562         
13563         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13564         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13565         
13566         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13567         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13568         
13569         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13570         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13571         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13572         
13573         this.okBtn.hide();
13574         this.cancelBtn.hide();
13575         
13576         var _this = this;
13577         
13578         (function(){
13579             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13580             _this.list.setWidth(lw);
13581         }).defer(100);
13582         
13583         this.list.on('mouseover', this.onViewOver, this);
13584         this.list.on('mousemove', this.onViewMove, this);
13585         
13586         this.list.on('scroll', this.onViewScroll, this);
13587         
13588         if(!this.tpl){
13589             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>';
13590         }
13591
13592         this.view = new Roo.View(this.list, this.tpl, {
13593             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13594         });
13595         
13596         //this.view.wrapEl.setDisplayed(false);
13597         this.view.on('click', this.onViewClick, this);
13598         
13599         
13600         
13601         this.store.on('beforeload', this.onBeforeLoad, this);
13602         this.store.on('load', this.onLoad, this);
13603         this.store.on('loadexception', this.onLoadException, this);
13604         
13605         if(this.editable){
13606             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13607                 "up" : function(e){
13608                     this.inKeyMode = true;
13609                     this.selectPrev();
13610                 },
13611
13612                 "down" : function(e){
13613                     this.inKeyMode = true;
13614                     this.selectNext();
13615                 },
13616
13617                 "enter" : function(e){
13618                     if(this.fireEvent("specialkey", this, e)){
13619                         this.onViewClick(false);
13620                     }
13621                     
13622                     return true;
13623                 },
13624
13625                 "esc" : function(e){
13626                     this.onTickableFooterButtonClick(e, false, false);
13627                 },
13628
13629                 "tab" : function(e){
13630                     this.fireEvent("specialkey", this, e);
13631                     
13632                     this.onTickableFooterButtonClick(e, false, false);
13633                     
13634                     return true;
13635                 },
13636
13637                 scope : this,
13638
13639                 doRelay : function(e, fn, key){
13640                     if(this.scope.isExpanded()){
13641                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13642                     }
13643                     return true;
13644                 },
13645
13646                 forceKeyDown: true
13647             });
13648         }
13649         
13650         this.queryDelay = Math.max(this.queryDelay || 10,
13651                 this.mode == 'local' ? 10 : 250);
13652         
13653         
13654         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13655         
13656         if(this.typeAhead){
13657             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13658         }
13659         
13660         if(this.editable !== false){
13661             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13662         }
13663         
13664         this.indicator = this.indicatorEl();
13665         
13666         if(this.indicator){
13667             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13668             this.indicator.hide();
13669         }
13670         
13671     },
13672
13673     onDestroy : function(){
13674         if(this.view){
13675             this.view.setStore(null);
13676             this.view.el.removeAllListeners();
13677             this.view.el.remove();
13678             this.view.purgeListeners();
13679         }
13680         if(this.list){
13681             this.list.dom.innerHTML  = '';
13682         }
13683         
13684         if(this.store){
13685             this.store.un('beforeload', this.onBeforeLoad, this);
13686             this.store.un('load', this.onLoad, this);
13687             this.store.un('loadexception', this.onLoadException, this);
13688         }
13689         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13690     },
13691
13692     // private
13693     fireKey : function(e){
13694         if(e.isNavKeyPress() && !this.list.isVisible()){
13695             this.fireEvent("specialkey", this, e);
13696         }
13697     },
13698
13699     // private
13700     onResize: function(w, h){
13701 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13702 //        
13703 //        if(typeof w != 'number'){
13704 //            // we do not handle it!?!?
13705 //            return;
13706 //        }
13707 //        var tw = this.trigger.getWidth();
13708 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13709 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13710 //        var x = w - tw;
13711 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13712 //            
13713 //        //this.trigger.setStyle('left', x+'px');
13714 //        
13715 //        if(this.list && this.listWidth === undefined){
13716 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13717 //            this.list.setWidth(lw);
13718 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13719 //        }
13720         
13721     
13722         
13723     },
13724
13725     /**
13726      * Allow or prevent the user from directly editing the field text.  If false is passed,
13727      * the user will only be able to select from the items defined in the dropdown list.  This method
13728      * is the runtime equivalent of setting the 'editable' config option at config time.
13729      * @param {Boolean} value True to allow the user to directly edit the field text
13730      */
13731     setEditable : function(value){
13732         if(value == this.editable){
13733             return;
13734         }
13735         this.editable = value;
13736         if(!value){
13737             this.inputEl().dom.setAttribute('readOnly', true);
13738             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13739             this.inputEl().addClass('x-combo-noedit');
13740         }else{
13741             this.inputEl().dom.setAttribute('readOnly', false);
13742             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13743             this.inputEl().removeClass('x-combo-noedit');
13744         }
13745     },
13746
13747     // private
13748     
13749     onBeforeLoad : function(combo,opts){
13750         if(!this.hasFocus){
13751             return;
13752         }
13753          if (!opts.add) {
13754             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13755          }
13756         this.restrictHeight();
13757         this.selectedIndex = -1;
13758     },
13759
13760     // private
13761     onLoad : function(){
13762         
13763         this.hasQuery = false;
13764         
13765         if(!this.hasFocus){
13766             return;
13767         }
13768         
13769         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13770             this.loading.hide();
13771         }
13772         
13773         if(this.store.getCount() > 0){
13774             
13775             this.expand();
13776             this.restrictHeight();
13777             if(this.lastQuery == this.allQuery){
13778                 if(this.editable && !this.tickable){
13779                     this.inputEl().dom.select();
13780                 }
13781                 
13782                 if(
13783                     !this.selectByValue(this.value, true) &&
13784                     this.autoFocus && 
13785                     (
13786                         !this.store.lastOptions ||
13787                         typeof(this.store.lastOptions.add) == 'undefined' || 
13788                         this.store.lastOptions.add != true
13789                     )
13790                 ){
13791                     this.select(0, true);
13792                 }
13793             }else{
13794                 if(this.autoFocus){
13795                     this.selectNext();
13796                 }
13797                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13798                     this.taTask.delay(this.typeAheadDelay);
13799                 }
13800             }
13801         }else{
13802             this.onEmptyResults();
13803         }
13804         
13805         //this.el.focus();
13806     },
13807     // private
13808     onLoadException : function()
13809     {
13810         this.hasQuery = false;
13811         
13812         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13813             this.loading.hide();
13814         }
13815         
13816         if(this.tickable && this.editable){
13817             return;
13818         }
13819         
13820         this.collapse();
13821         // only causes errors at present
13822         //Roo.log(this.store.reader.jsonData);
13823         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13824             // fixme
13825             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13826         //}
13827         
13828         
13829     },
13830     // private
13831     onTypeAhead : function(){
13832         if(this.store.getCount() > 0){
13833             var r = this.store.getAt(0);
13834             var newValue = r.data[this.displayField];
13835             var len = newValue.length;
13836             var selStart = this.getRawValue().length;
13837             
13838             if(selStart != len){
13839                 this.setRawValue(newValue);
13840                 this.selectText(selStart, newValue.length);
13841             }
13842         }
13843     },
13844
13845     // private
13846     onSelect : function(record, index){
13847         
13848         if(this.fireEvent('beforeselect', this, record, index) !== false){
13849         
13850             this.setFromData(index > -1 ? record.data : false);
13851             
13852             this.collapse();
13853             this.fireEvent('select', this, record, index);
13854         }
13855     },
13856
13857     /**
13858      * Returns the currently selected field value or empty string if no value is set.
13859      * @return {String} value The selected value
13860      */
13861     getValue : function()
13862     {
13863         if(Roo.isIOS && this.useNativeIOS){
13864             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13865         }
13866         
13867         if(this.multiple){
13868             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13869         }
13870         
13871         if(this.valueField){
13872             return typeof this.value != 'undefined' ? this.value : '';
13873         }else{
13874             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13875         }
13876     },
13877     
13878     getRawValue : function()
13879     {
13880         if(Roo.isIOS && this.useNativeIOS){
13881             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13882         }
13883         
13884         var v = this.inputEl().getValue();
13885         
13886         return v;
13887     },
13888
13889     /**
13890      * Clears any text/value currently set in the field
13891      */
13892     clearValue : function(){
13893         
13894         if(this.hiddenField){
13895             this.hiddenField.dom.value = '';
13896         }
13897         this.value = '';
13898         this.setRawValue('');
13899         this.lastSelectionText = '';
13900         this.lastData = false;
13901         
13902         var close = this.closeTriggerEl();
13903         
13904         if(close){
13905             close.hide();
13906         }
13907         
13908         this.validate();
13909         
13910     },
13911
13912     /**
13913      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13914      * will be displayed in the field.  If the value does not match the data value of an existing item,
13915      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13916      * Otherwise the field will be blank (although the value will still be set).
13917      * @param {String} value The value to match
13918      */
13919     setValue : function(v)
13920     {
13921         if(Roo.isIOS && this.useNativeIOS){
13922             this.setIOSValue(v);
13923             return;
13924         }
13925         
13926         if(this.multiple){
13927             this.syncValue();
13928             return;
13929         }
13930         
13931         var text = v;
13932         if(this.valueField){
13933             var r = this.findRecord(this.valueField, v);
13934             if(r){
13935                 text = r.data[this.displayField];
13936             }else if(this.valueNotFoundText !== undefined){
13937                 text = this.valueNotFoundText;
13938             }
13939         }
13940         this.lastSelectionText = text;
13941         if(this.hiddenField){
13942             this.hiddenField.dom.value = v;
13943         }
13944         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13945         this.value = v;
13946         
13947         var close = this.closeTriggerEl();
13948         
13949         if(close){
13950             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13951         }
13952         
13953         this.validate();
13954     },
13955     /**
13956      * @property {Object} the last set data for the element
13957      */
13958     
13959     lastData : false,
13960     /**
13961      * Sets the value of the field based on a object which is related to the record format for the store.
13962      * @param {Object} value the value to set as. or false on reset?
13963      */
13964     setFromData : function(o){
13965         
13966         if(this.multiple){
13967             this.addItem(o);
13968             return;
13969         }
13970             
13971         var dv = ''; // display value
13972         var vv = ''; // value value..
13973         this.lastData = o;
13974         if (this.displayField) {
13975             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13976         } else {
13977             // this is an error condition!!!
13978             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13979         }
13980         
13981         if(this.valueField){
13982             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13983         }
13984         
13985         var close = this.closeTriggerEl();
13986         
13987         if(close){
13988             if(dv.length || vv * 1 > 0){
13989                 close.show() ;
13990                 this.blockFocus=true;
13991             } else {
13992                 close.hide();
13993             }             
13994         }
13995         
13996         if(this.hiddenField){
13997             this.hiddenField.dom.value = vv;
13998             
13999             this.lastSelectionText = dv;
14000             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14001             this.value = vv;
14002             return;
14003         }
14004         // no hidden field.. - we store the value in 'value', but still display
14005         // display field!!!!
14006         this.lastSelectionText = dv;
14007         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14008         this.value = vv;
14009         
14010         
14011         
14012     },
14013     // private
14014     reset : function(){
14015         // overridden so that last data is reset..
14016         
14017         if(this.multiple){
14018             this.clearItem();
14019             return;
14020         }
14021         
14022         this.setValue(this.originalValue);
14023         //this.clearInvalid();
14024         this.lastData = false;
14025         if (this.view) {
14026             this.view.clearSelections();
14027         }
14028         
14029         this.validate();
14030     },
14031     // private
14032     findRecord : function(prop, value){
14033         var record;
14034         if(this.store.getCount() > 0){
14035             this.store.each(function(r){
14036                 if(r.data[prop] == value){
14037                     record = r;
14038                     return false;
14039                 }
14040                 return true;
14041             });
14042         }
14043         return record;
14044     },
14045     
14046     getName: function()
14047     {
14048         // returns hidden if it's set..
14049         if (!this.rendered) {return ''};
14050         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14051         
14052     },
14053     // private
14054     onViewMove : function(e, t){
14055         this.inKeyMode = false;
14056     },
14057
14058     // private
14059     onViewOver : function(e, t){
14060         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14061             return;
14062         }
14063         var item = this.view.findItemFromChild(t);
14064         
14065         if(item){
14066             var index = this.view.indexOf(item);
14067             this.select(index, false);
14068         }
14069     },
14070
14071     // private
14072     onViewClick : function(view, doFocus, el, e)
14073     {
14074         var index = this.view.getSelectedIndexes()[0];
14075         
14076         var r = this.store.getAt(index);
14077         
14078         if(this.tickable){
14079             
14080             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14081                 return;
14082             }
14083             
14084             var rm = false;
14085             var _this = this;
14086             
14087             Roo.each(this.tickItems, function(v,k){
14088                 
14089                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14090                     Roo.log(v);
14091                     _this.tickItems.splice(k, 1);
14092                     
14093                     if(typeof(e) == 'undefined' && view == false){
14094                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14095                     }
14096                     
14097                     rm = true;
14098                     return;
14099                 }
14100             });
14101             
14102             if(rm){
14103                 return;
14104             }
14105             
14106             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14107                 this.tickItems.push(r.data);
14108             }
14109             
14110             if(typeof(e) == 'undefined' && view == false){
14111                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14112             }
14113                     
14114             return;
14115         }
14116         
14117         if(r){
14118             this.onSelect(r, index);
14119         }
14120         if(doFocus !== false && !this.blockFocus){
14121             this.inputEl().focus();
14122         }
14123     },
14124
14125     // private
14126     restrictHeight : function(){
14127         //this.innerList.dom.style.height = '';
14128         //var inner = this.innerList.dom;
14129         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14130         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14131         //this.list.beginUpdate();
14132         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14133         this.list.alignTo(this.inputEl(), this.listAlign);
14134         this.list.alignTo(this.inputEl(), this.listAlign);
14135         //this.list.endUpdate();
14136     },
14137
14138     // private
14139     onEmptyResults : function(){
14140         
14141         if(this.tickable && this.editable){
14142             this.hasFocus = false;
14143             this.restrictHeight();
14144             return;
14145         }
14146         
14147         this.collapse();
14148     },
14149
14150     /**
14151      * Returns true if the dropdown list is expanded, else false.
14152      */
14153     isExpanded : function(){
14154         return this.list.isVisible();
14155     },
14156
14157     /**
14158      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14159      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14160      * @param {String} value The data value of the item to select
14161      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14162      * selected item if it is not currently in view (defaults to true)
14163      * @return {Boolean} True if the value matched an item in the list, else false
14164      */
14165     selectByValue : function(v, scrollIntoView){
14166         if(v !== undefined && v !== null){
14167             var r = this.findRecord(this.valueField || this.displayField, v);
14168             if(r){
14169                 this.select(this.store.indexOf(r), scrollIntoView);
14170                 return true;
14171             }
14172         }
14173         return false;
14174     },
14175
14176     /**
14177      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14178      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14179      * @param {Number} index The zero-based index of the list item to select
14180      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14181      * selected item if it is not currently in view (defaults to true)
14182      */
14183     select : function(index, scrollIntoView){
14184         this.selectedIndex = index;
14185         this.view.select(index);
14186         if(scrollIntoView !== false){
14187             var el = this.view.getNode(index);
14188             /*
14189              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14190              */
14191             if(el){
14192                 this.list.scrollChildIntoView(el, false);
14193             }
14194         }
14195     },
14196
14197     // private
14198     selectNext : function(){
14199         var ct = this.store.getCount();
14200         if(ct > 0){
14201             if(this.selectedIndex == -1){
14202                 this.select(0);
14203             }else if(this.selectedIndex < ct-1){
14204                 this.select(this.selectedIndex+1);
14205             }
14206         }
14207     },
14208
14209     // private
14210     selectPrev : function(){
14211         var ct = this.store.getCount();
14212         if(ct > 0){
14213             if(this.selectedIndex == -1){
14214                 this.select(0);
14215             }else if(this.selectedIndex != 0){
14216                 this.select(this.selectedIndex-1);
14217             }
14218         }
14219     },
14220
14221     // private
14222     onKeyUp : function(e){
14223         if(this.editable !== false && !e.isSpecialKey()){
14224             this.lastKey = e.getKey();
14225             this.dqTask.delay(this.queryDelay);
14226         }
14227     },
14228
14229     // private
14230     validateBlur : function(){
14231         return !this.list || !this.list.isVisible();   
14232     },
14233
14234     // private
14235     initQuery : function(){
14236         
14237         var v = this.getRawValue();
14238         
14239         if(this.tickable && this.editable){
14240             v = this.tickableInputEl().getValue();
14241         }
14242         
14243         this.doQuery(v);
14244     },
14245
14246     // private
14247     doForce : function(){
14248         if(this.inputEl().dom.value.length > 0){
14249             this.inputEl().dom.value =
14250                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14251              
14252         }
14253     },
14254
14255     /**
14256      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14257      * query allowing the query action to be canceled if needed.
14258      * @param {String} query The SQL query to execute
14259      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14260      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14261      * saved in the current store (defaults to false)
14262      */
14263     doQuery : function(q, forceAll){
14264         
14265         if(q === undefined || q === null){
14266             q = '';
14267         }
14268         var qe = {
14269             query: q,
14270             forceAll: forceAll,
14271             combo: this,
14272             cancel:false
14273         };
14274         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14275             return false;
14276         }
14277         q = qe.query;
14278         
14279         forceAll = qe.forceAll;
14280         if(forceAll === true || (q.length >= this.minChars)){
14281             
14282             this.hasQuery = true;
14283             
14284             if(this.lastQuery != q || this.alwaysQuery){
14285                 this.lastQuery = q;
14286                 if(this.mode == 'local'){
14287                     this.selectedIndex = -1;
14288                     if(forceAll){
14289                         this.store.clearFilter();
14290                     }else{
14291                         
14292                         if(this.specialFilter){
14293                             this.fireEvent('specialfilter', this);
14294                             this.onLoad();
14295                             return;
14296                         }
14297                         
14298                         this.store.filter(this.displayField, q);
14299                     }
14300                     
14301                     this.store.fireEvent("datachanged", this.store);
14302                     
14303                     this.onLoad();
14304                     
14305                     
14306                 }else{
14307                     
14308                     this.store.baseParams[this.queryParam] = q;
14309                     
14310                     var options = {params : this.getParams(q)};
14311                     
14312                     if(this.loadNext){
14313                         options.add = true;
14314                         options.params.start = this.page * this.pageSize;
14315                     }
14316                     
14317                     this.store.load(options);
14318                     
14319                     /*
14320                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14321                      *  we should expand the list on onLoad
14322                      *  so command out it
14323                      */
14324 //                    this.expand();
14325                 }
14326             }else{
14327                 this.selectedIndex = -1;
14328                 this.onLoad();   
14329             }
14330         }
14331         
14332         this.loadNext = false;
14333     },
14334     
14335     // private
14336     getParams : function(q){
14337         var p = {};
14338         //p[this.queryParam] = q;
14339         
14340         if(this.pageSize){
14341             p.start = 0;
14342             p.limit = this.pageSize;
14343         }
14344         return p;
14345     },
14346
14347     /**
14348      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14349      */
14350     collapse : function(){
14351         if(!this.isExpanded()){
14352             return;
14353         }
14354         
14355         this.list.hide();
14356         
14357         this.hasFocus = false;
14358         
14359         if(this.tickable){
14360             this.okBtn.hide();
14361             this.cancelBtn.hide();
14362             this.trigger.show();
14363             
14364             if(this.editable){
14365                 this.tickableInputEl().dom.value = '';
14366                 this.tickableInputEl().blur();
14367             }
14368             
14369         }
14370         
14371         Roo.get(document).un('mousedown', this.collapseIf, this);
14372         Roo.get(document).un('mousewheel', this.collapseIf, this);
14373         if (!this.editable) {
14374             Roo.get(document).un('keydown', this.listKeyPress, this);
14375         }
14376         this.fireEvent('collapse', this);
14377         
14378         this.validate();
14379     },
14380
14381     // private
14382     collapseIf : function(e){
14383         var in_combo  = e.within(this.el);
14384         var in_list =  e.within(this.list);
14385         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14386         
14387         if (in_combo || in_list || is_list) {
14388             //e.stopPropagation();
14389             return;
14390         }
14391         
14392         if(this.tickable){
14393             this.onTickableFooterButtonClick(e, false, false);
14394         }
14395
14396         this.collapse();
14397         
14398     },
14399
14400     /**
14401      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14402      */
14403     expand : function(){
14404        
14405         if(this.isExpanded() || !this.hasFocus){
14406             return;
14407         }
14408         
14409         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14410         this.list.setWidth(lw);
14411         
14412         Roo.log('expand');
14413         
14414         this.list.show();
14415         
14416         this.restrictHeight();
14417         
14418         if(this.tickable){
14419             
14420             this.tickItems = Roo.apply([], this.item);
14421             
14422             this.okBtn.show();
14423             this.cancelBtn.show();
14424             this.trigger.hide();
14425             
14426             if(this.editable){
14427                 this.tickableInputEl().focus();
14428             }
14429             
14430         }
14431         
14432         Roo.get(document).on('mousedown', this.collapseIf, this);
14433         Roo.get(document).on('mousewheel', this.collapseIf, this);
14434         if (!this.editable) {
14435             Roo.get(document).on('keydown', this.listKeyPress, this);
14436         }
14437         
14438         this.fireEvent('expand', this);
14439     },
14440
14441     // private
14442     // Implements the default empty TriggerField.onTriggerClick function
14443     onTriggerClick : function(e)
14444     {
14445         Roo.log('trigger click');
14446         
14447         if(this.disabled || !this.triggerList){
14448             return;
14449         }
14450         
14451         this.page = 0;
14452         this.loadNext = false;
14453         
14454         if(this.isExpanded()){
14455             this.collapse();
14456             if (!this.blockFocus) {
14457                 this.inputEl().focus();
14458             }
14459             
14460         }else {
14461             this.hasFocus = true;
14462             if(this.triggerAction == 'all') {
14463                 this.doQuery(this.allQuery, true);
14464             } else {
14465                 this.doQuery(this.getRawValue());
14466             }
14467             if (!this.blockFocus) {
14468                 this.inputEl().focus();
14469             }
14470         }
14471     },
14472     
14473     onTickableTriggerClick : function(e)
14474     {
14475         if(this.disabled){
14476             return;
14477         }
14478         
14479         this.page = 0;
14480         this.loadNext = false;
14481         this.hasFocus = true;
14482         
14483         if(this.triggerAction == 'all') {
14484             this.doQuery(this.allQuery, true);
14485         } else {
14486             this.doQuery(this.getRawValue());
14487         }
14488     },
14489     
14490     onSearchFieldClick : function(e)
14491     {
14492         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14493             this.onTickableFooterButtonClick(e, false, false);
14494             return;
14495         }
14496         
14497         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14498             return;
14499         }
14500         
14501         this.page = 0;
14502         this.loadNext = false;
14503         this.hasFocus = true;
14504         
14505         if(this.triggerAction == 'all') {
14506             this.doQuery(this.allQuery, true);
14507         } else {
14508             this.doQuery(this.getRawValue());
14509         }
14510     },
14511     
14512     listKeyPress : function(e)
14513     {
14514         //Roo.log('listkeypress');
14515         // scroll to first matching element based on key pres..
14516         if (e.isSpecialKey()) {
14517             return false;
14518         }
14519         var k = String.fromCharCode(e.getKey()).toUpperCase();
14520         //Roo.log(k);
14521         var match  = false;
14522         var csel = this.view.getSelectedNodes();
14523         var cselitem = false;
14524         if (csel.length) {
14525             var ix = this.view.indexOf(csel[0]);
14526             cselitem  = this.store.getAt(ix);
14527             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14528                 cselitem = false;
14529             }
14530             
14531         }
14532         
14533         this.store.each(function(v) { 
14534             if (cselitem) {
14535                 // start at existing selection.
14536                 if (cselitem.id == v.id) {
14537                     cselitem = false;
14538                 }
14539                 return true;
14540             }
14541                 
14542             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14543                 match = this.store.indexOf(v);
14544                 return false;
14545             }
14546             return true;
14547         }, this);
14548         
14549         if (match === false) {
14550             return true; // no more action?
14551         }
14552         // scroll to?
14553         this.view.select(match);
14554         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14555         sn.scrollIntoView(sn.dom.parentNode, false);
14556     },
14557     
14558     onViewScroll : function(e, t){
14559         
14560         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){
14561             return;
14562         }
14563         
14564         this.hasQuery = true;
14565         
14566         this.loading = this.list.select('.loading', true).first();
14567         
14568         if(this.loading === null){
14569             this.list.createChild({
14570                 tag: 'div',
14571                 cls: 'loading roo-select2-more-results roo-select2-active',
14572                 html: 'Loading more results...'
14573             });
14574             
14575             this.loading = this.list.select('.loading', true).first();
14576             
14577             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14578             
14579             this.loading.hide();
14580         }
14581         
14582         this.loading.show();
14583         
14584         var _combo = this;
14585         
14586         this.page++;
14587         this.loadNext = true;
14588         
14589         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14590         
14591         return;
14592     },
14593     
14594     addItem : function(o)
14595     {   
14596         var dv = ''; // display value
14597         
14598         if (this.displayField) {
14599             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14600         } else {
14601             // this is an error condition!!!
14602             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14603         }
14604         
14605         if(!dv.length){
14606             return;
14607         }
14608         
14609         var choice = this.choices.createChild({
14610             tag: 'li',
14611             cls: 'roo-select2-search-choice',
14612             cn: [
14613                 {
14614                     tag: 'div',
14615                     html: dv
14616                 },
14617                 {
14618                     tag: 'a',
14619                     href: '#',
14620                     cls: 'roo-select2-search-choice-close fa fa-times',
14621                     tabindex: '-1'
14622                 }
14623             ]
14624             
14625         }, this.searchField);
14626         
14627         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14628         
14629         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14630         
14631         this.item.push(o);
14632         
14633         this.lastData = o;
14634         
14635         this.syncValue();
14636         
14637         this.inputEl().dom.value = '';
14638         
14639         this.validate();
14640     },
14641     
14642     onRemoveItem : function(e, _self, o)
14643     {
14644         e.preventDefault();
14645         
14646         this.lastItem = Roo.apply([], this.item);
14647         
14648         var index = this.item.indexOf(o.data) * 1;
14649         
14650         if( index < 0){
14651             Roo.log('not this item?!');
14652             return;
14653         }
14654         
14655         this.item.splice(index, 1);
14656         o.item.remove();
14657         
14658         this.syncValue();
14659         
14660         this.fireEvent('remove', this, e);
14661         
14662         this.validate();
14663         
14664     },
14665     
14666     syncValue : function()
14667     {
14668         if(!this.item.length){
14669             this.clearValue();
14670             return;
14671         }
14672             
14673         var value = [];
14674         var _this = this;
14675         Roo.each(this.item, function(i){
14676             if(_this.valueField){
14677                 value.push(i[_this.valueField]);
14678                 return;
14679             }
14680
14681             value.push(i);
14682         });
14683
14684         this.value = value.join(',');
14685
14686         if(this.hiddenField){
14687             this.hiddenField.dom.value = this.value;
14688         }
14689         
14690         this.store.fireEvent("datachanged", this.store);
14691         
14692         this.validate();
14693     },
14694     
14695     clearItem : function()
14696     {
14697         if(!this.multiple){
14698             return;
14699         }
14700         
14701         this.item = [];
14702         
14703         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14704            c.remove();
14705         });
14706         
14707         this.syncValue();
14708         
14709         this.validate();
14710         
14711         if(this.tickable && !Roo.isTouch){
14712             this.view.refresh();
14713         }
14714     },
14715     
14716     inputEl: function ()
14717     {
14718         if(Roo.isIOS && this.useNativeIOS){
14719             return this.el.select('select.roo-ios-select', true).first();
14720         }
14721         
14722         if(Roo.isTouch && this.mobileTouchView){
14723             return this.el.select('input.form-control',true).first();
14724         }
14725         
14726         if(this.tickable){
14727             return this.searchField;
14728         }
14729         
14730         return this.el.select('input.form-control',true).first();
14731     },
14732     
14733     onTickableFooterButtonClick : function(e, btn, el)
14734     {
14735         e.preventDefault();
14736         
14737         this.lastItem = Roo.apply([], this.item);
14738         
14739         if(btn && btn.name == 'cancel'){
14740             this.tickItems = Roo.apply([], this.item);
14741             this.collapse();
14742             return;
14743         }
14744         
14745         this.clearItem();
14746         
14747         var _this = this;
14748         
14749         Roo.each(this.tickItems, function(o){
14750             _this.addItem(o);
14751         });
14752         
14753         this.collapse();
14754         
14755     },
14756     
14757     validate : function()
14758     {
14759         if(this.getVisibilityEl().hasClass('hidden')){
14760             return true;
14761         }
14762         
14763         var v = this.getRawValue();
14764         
14765         if(this.multiple){
14766             v = this.getValue();
14767         }
14768         
14769         if(this.disabled || this.allowBlank || v.length){
14770             this.markValid();
14771             return true;
14772         }
14773         
14774         this.markInvalid();
14775         return false;
14776     },
14777     
14778     tickableInputEl : function()
14779     {
14780         if(!this.tickable || !this.editable){
14781             return this.inputEl();
14782         }
14783         
14784         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14785     },
14786     
14787     
14788     getAutoCreateTouchView : function()
14789     {
14790         var id = Roo.id();
14791         
14792         var cfg = {
14793             cls: 'form-group' //input-group
14794         };
14795         
14796         var input =  {
14797             tag: 'input',
14798             id : id,
14799             type : this.inputType,
14800             cls : 'form-control x-combo-noedit',
14801             autocomplete: 'new-password',
14802             placeholder : this.placeholder || '',
14803             readonly : true
14804         };
14805         
14806         if (this.name) {
14807             input.name = this.name;
14808         }
14809         
14810         if (this.size) {
14811             input.cls += ' input-' + this.size;
14812         }
14813         
14814         if (this.disabled) {
14815             input.disabled = true;
14816         }
14817         
14818         var inputblock = {
14819             cls : '',
14820             cn : [
14821                 input
14822             ]
14823         };
14824         
14825         if(this.before){
14826             inputblock.cls += ' input-group';
14827             
14828             inputblock.cn.unshift({
14829                 tag :'span',
14830                 cls : 'input-group-addon',
14831                 html : this.before
14832             });
14833         }
14834         
14835         if(this.removable && !this.multiple){
14836             inputblock.cls += ' roo-removable';
14837             
14838             inputblock.cn.push({
14839                 tag: 'button',
14840                 html : 'x',
14841                 cls : 'roo-combo-removable-btn close'
14842             });
14843         }
14844
14845         if(this.hasFeedback && !this.allowBlank){
14846             
14847             inputblock.cls += ' has-feedback';
14848             
14849             inputblock.cn.push({
14850                 tag: 'span',
14851                 cls: 'glyphicon form-control-feedback'
14852             });
14853             
14854         }
14855         
14856         if (this.after) {
14857             
14858             inputblock.cls += (this.before) ? '' : ' input-group';
14859             
14860             inputblock.cn.push({
14861                 tag :'span',
14862                 cls : 'input-group-addon',
14863                 html : this.after
14864             });
14865         }
14866
14867         var box = {
14868             tag: 'div',
14869             cn: [
14870                 {
14871                     tag: 'input',
14872                     type : 'hidden',
14873                     cls: 'form-hidden-field'
14874                 },
14875                 inputblock
14876             ]
14877             
14878         };
14879         
14880         if(this.multiple){
14881             box = {
14882                 tag: 'div',
14883                 cn: [
14884                     {
14885                         tag: 'input',
14886                         type : 'hidden',
14887                         cls: 'form-hidden-field'
14888                     },
14889                     {
14890                         tag: 'ul',
14891                         cls: 'roo-select2-choices',
14892                         cn:[
14893                             {
14894                                 tag: 'li',
14895                                 cls: 'roo-select2-search-field',
14896                                 cn: [
14897
14898                                     inputblock
14899                                 ]
14900                             }
14901                         ]
14902                     }
14903                 ]
14904             }
14905         };
14906         
14907         var combobox = {
14908             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14909             cn: [
14910                 box
14911             ]
14912         };
14913         
14914         if(!this.multiple && this.showToggleBtn){
14915             
14916             var caret = {
14917                         tag: 'span',
14918                         cls: 'caret'
14919             };
14920             
14921             if (this.caret != false) {
14922                 caret = {
14923                      tag: 'i',
14924                      cls: 'fa fa-' + this.caret
14925                 };
14926                 
14927             }
14928             
14929             combobox.cn.push({
14930                 tag :'span',
14931                 cls : 'input-group-addon btn dropdown-toggle',
14932                 cn : [
14933                     caret,
14934                     {
14935                         tag: 'span',
14936                         cls: 'combobox-clear',
14937                         cn  : [
14938                             {
14939                                 tag : 'i',
14940                                 cls: 'icon-remove'
14941                             }
14942                         ]
14943                     }
14944                 ]
14945
14946             })
14947         }
14948         
14949         if(this.multiple){
14950             combobox.cls += ' roo-select2-container-multi';
14951         }
14952         
14953         var align = this.labelAlign || this.parentLabelAlign();
14954         
14955         if (align ==='left' && this.fieldLabel.length) {
14956
14957             cfg.cn = [
14958                 {
14959                    tag : 'i',
14960                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14961                    tooltip : 'This field is required'
14962                 },
14963                 {
14964                     tag: 'label',
14965                     cls : 'control-label',
14966                     html : this.fieldLabel
14967
14968                 },
14969                 {
14970                     cls : '', 
14971                     cn: [
14972                         combobox
14973                     ]
14974                 }
14975             ];
14976             
14977             var labelCfg = cfg.cn[1];
14978             var contentCfg = cfg.cn[2];
14979             
14980
14981             if(this.indicatorpos == 'right'){
14982                 cfg.cn = [
14983                     {
14984                         tag: 'label',
14985                         'for' :  id,
14986                         cls : 'control-label',
14987                         cn : [
14988                             {
14989                                 tag : 'span',
14990                                 html : this.fieldLabel
14991                             },
14992                             {
14993                                 tag : 'i',
14994                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14995                                 tooltip : 'This field is required'
14996                             }
14997                         ]
14998                     },
14999                     {
15000                         cls : "",
15001                         cn: [
15002                             combobox
15003                         ]
15004                     }
15005
15006                 ];
15007                 
15008                 labelCfg = cfg.cn[0];
15009                 contentCfg = cfg.cn[1];
15010             }
15011             
15012            
15013             
15014             if(this.labelWidth > 12){
15015                 labelCfg.style = "width: " + this.labelWidth + 'px';
15016             }
15017             
15018             if(this.labelWidth < 13 && this.labelmd == 0){
15019                 this.labelmd = this.labelWidth;
15020             }
15021             
15022             if(this.labellg > 0){
15023                 labelCfg.cls += ' col-lg-' + this.labellg;
15024                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15025             }
15026             
15027             if(this.labelmd > 0){
15028                 labelCfg.cls += ' col-md-' + this.labelmd;
15029                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15030             }
15031             
15032             if(this.labelsm > 0){
15033                 labelCfg.cls += ' col-sm-' + this.labelsm;
15034                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15035             }
15036             
15037             if(this.labelxs > 0){
15038                 labelCfg.cls += ' col-xs-' + this.labelxs;
15039                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15040             }
15041                 
15042                 
15043         } else if ( this.fieldLabel.length) {
15044             cfg.cn = [
15045                 {
15046                    tag : 'i',
15047                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15048                    tooltip : 'This field is required'
15049                 },
15050                 {
15051                     tag: 'label',
15052                     cls : 'control-label',
15053                     html : this.fieldLabel
15054
15055                 },
15056                 {
15057                     cls : '', 
15058                     cn: [
15059                         combobox
15060                     ]
15061                 }
15062             ];
15063             
15064             if(this.indicatorpos == 'right'){
15065                 cfg.cn = [
15066                     {
15067                         tag: 'label',
15068                         cls : 'control-label',
15069                         html : this.fieldLabel,
15070                         cn : [
15071                             {
15072                                tag : 'i',
15073                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15074                                tooltip : 'This field is required'
15075                             }
15076                         ]
15077                     },
15078                     {
15079                         cls : '', 
15080                         cn: [
15081                             combobox
15082                         ]
15083                     }
15084                 ];
15085             }
15086         } else {
15087             cfg.cn = combobox;    
15088         }
15089         
15090         
15091         var settings = this;
15092         
15093         ['xs','sm','md','lg'].map(function(size){
15094             if (settings[size]) {
15095                 cfg.cls += ' col-' + size + '-' + settings[size];
15096             }
15097         });
15098         
15099         return cfg;
15100     },
15101     
15102     initTouchView : function()
15103     {
15104         this.renderTouchView();
15105         
15106         this.touchViewEl.on('scroll', function(){
15107             this.el.dom.scrollTop = 0;
15108         }, this);
15109         
15110         this.originalValue = this.getValue();
15111         
15112         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15113         
15114         this.inputEl().on("click", this.showTouchView, this);
15115         if (this.triggerEl) {
15116             this.triggerEl.on("click", this.showTouchView, this);
15117         }
15118         
15119         
15120         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15121         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15122         
15123         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15124         
15125         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15126         this.store.on('load', this.onTouchViewLoad, this);
15127         this.store.on('loadexception', this.onTouchViewLoadException, this);
15128         
15129         if(this.hiddenName){
15130             
15131             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15132             
15133             this.hiddenField.dom.value =
15134                 this.hiddenValue !== undefined ? this.hiddenValue :
15135                 this.value !== undefined ? this.value : '';
15136         
15137             this.el.dom.removeAttribute('name');
15138             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15139         }
15140         
15141         if(this.multiple){
15142             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15143             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15144         }
15145         
15146         if(this.removable && !this.multiple){
15147             var close = this.closeTriggerEl();
15148             if(close){
15149                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15150                 close.on('click', this.removeBtnClick, this, close);
15151             }
15152         }
15153         /*
15154          * fix the bug in Safari iOS8
15155          */
15156         this.inputEl().on("focus", function(e){
15157             document.activeElement.blur();
15158         }, this);
15159         
15160         return;
15161         
15162         
15163     },
15164     
15165     renderTouchView : function()
15166     {
15167         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15168         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15169         
15170         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15171         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15172         
15173         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15174         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15175         this.touchViewBodyEl.setStyle('overflow', 'auto');
15176         
15177         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15178         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15179         
15180         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15181         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15182         
15183     },
15184     
15185     showTouchView : function()
15186     {
15187         if(this.disabled){
15188             return;
15189         }
15190         
15191         this.touchViewHeaderEl.hide();
15192
15193         if(this.modalTitle.length){
15194             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15195             this.touchViewHeaderEl.show();
15196         }
15197
15198         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15199         this.touchViewEl.show();
15200
15201         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15202         
15203         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15204         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15205
15206         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15207
15208         if(this.modalTitle.length){
15209             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15210         }
15211         
15212         this.touchViewBodyEl.setHeight(bodyHeight);
15213
15214         if(this.animate){
15215             var _this = this;
15216             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15217         }else{
15218             this.touchViewEl.addClass('in');
15219         }
15220
15221         this.doTouchViewQuery();
15222         
15223     },
15224     
15225     hideTouchView : function()
15226     {
15227         this.touchViewEl.removeClass('in');
15228
15229         if(this.animate){
15230             var _this = this;
15231             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15232         }else{
15233             this.touchViewEl.setStyle('display', 'none');
15234         }
15235         
15236     },
15237     
15238     setTouchViewValue : function()
15239     {
15240         if(this.multiple){
15241             this.clearItem();
15242         
15243             var _this = this;
15244
15245             Roo.each(this.tickItems, function(o){
15246                 this.addItem(o);
15247             }, this);
15248         }
15249         
15250         this.hideTouchView();
15251     },
15252     
15253     doTouchViewQuery : function()
15254     {
15255         var qe = {
15256             query: '',
15257             forceAll: true,
15258             combo: this,
15259             cancel:false
15260         };
15261         
15262         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15263             return false;
15264         }
15265         
15266         if(!this.alwaysQuery || this.mode == 'local'){
15267             this.onTouchViewLoad();
15268             return;
15269         }
15270         
15271         this.store.load();
15272     },
15273     
15274     onTouchViewBeforeLoad : function(combo,opts)
15275     {
15276         return;
15277     },
15278
15279     // private
15280     onTouchViewLoad : function()
15281     {
15282         if(this.store.getCount() < 1){
15283             this.onTouchViewEmptyResults();
15284             return;
15285         }
15286         
15287         this.clearTouchView();
15288         
15289         var rawValue = this.getRawValue();
15290         
15291         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15292         
15293         this.tickItems = [];
15294         
15295         this.store.data.each(function(d, rowIndex){
15296             var row = this.touchViewListGroup.createChild(template);
15297             
15298             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15299                 row.addClass(d.data.cls);
15300             }
15301             
15302             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15303                 var cfg = {
15304                     data : d.data,
15305                     html : d.data[this.displayField]
15306                 };
15307                 
15308                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15309                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15310                 }
15311             }
15312             row.removeClass('selected');
15313             if(!this.multiple && this.valueField &&
15314                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15315             {
15316                 // radio buttons..
15317                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15318                 row.addClass('selected');
15319             }
15320             
15321             if(this.multiple && this.valueField &&
15322                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15323             {
15324                 
15325                 // checkboxes...
15326                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15327                 this.tickItems.push(d.data);
15328             }
15329             
15330             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15331             
15332         }, this);
15333         
15334         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15335         
15336         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15337
15338         if(this.modalTitle.length){
15339             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15340         }
15341
15342         var listHeight = this.touchViewListGroup.getHeight();
15343         
15344         var _this = this;
15345         
15346         if(firstChecked && listHeight > bodyHeight){
15347             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15348         }
15349         
15350     },
15351     
15352     onTouchViewLoadException : function()
15353     {
15354         this.hideTouchView();
15355     },
15356     
15357     onTouchViewEmptyResults : function()
15358     {
15359         this.clearTouchView();
15360         
15361         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15362         
15363         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15364         
15365     },
15366     
15367     clearTouchView : function()
15368     {
15369         this.touchViewListGroup.dom.innerHTML = '';
15370     },
15371     
15372     onTouchViewClick : function(e, el, o)
15373     {
15374         e.preventDefault();
15375         
15376         var row = o.row;
15377         var rowIndex = o.rowIndex;
15378         
15379         var r = this.store.getAt(rowIndex);
15380         
15381         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15382             
15383             if(!this.multiple){
15384                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15385                     c.dom.removeAttribute('checked');
15386                 }, this);
15387
15388                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15389
15390                 this.setFromData(r.data);
15391
15392                 var close = this.closeTriggerEl();
15393
15394                 if(close){
15395                     close.show();
15396                 }
15397
15398                 this.hideTouchView();
15399
15400                 this.fireEvent('select', this, r, rowIndex);
15401
15402                 return;
15403             }
15404
15405             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15406                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15407                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15408                 return;
15409             }
15410
15411             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15412             this.addItem(r.data);
15413             this.tickItems.push(r.data);
15414         }
15415     },
15416     
15417     getAutoCreateNativeIOS : function()
15418     {
15419         var cfg = {
15420             cls: 'form-group' //input-group,
15421         };
15422         
15423         var combobox =  {
15424             tag: 'select',
15425             cls : 'roo-ios-select'
15426         };
15427         
15428         if (this.name) {
15429             combobox.name = this.name;
15430         }
15431         
15432         if (this.disabled) {
15433             combobox.disabled = true;
15434         }
15435         
15436         var settings = this;
15437         
15438         ['xs','sm','md','lg'].map(function(size){
15439             if (settings[size]) {
15440                 cfg.cls += ' col-' + size + '-' + settings[size];
15441             }
15442         });
15443         
15444         cfg.cn = combobox;
15445         
15446         return cfg;
15447         
15448     },
15449     
15450     initIOSView : function()
15451     {
15452         this.store.on('load', this.onIOSViewLoad, this);
15453         
15454         return;
15455     },
15456     
15457     onIOSViewLoad : function()
15458     {
15459         if(this.store.getCount() < 1){
15460             return;
15461         }
15462         
15463         this.clearIOSView();
15464         
15465         if(this.allowBlank) {
15466             
15467             var default_text = '-- SELECT --';
15468             
15469             if(this.placeholder.length){
15470                 default_text = this.placeholder;
15471             }
15472             
15473             if(this.emptyTitle.length){
15474                 default_text += ' - ' + this.emptyTitle + ' -';
15475             }
15476             
15477             var opt = this.inputEl().createChild({
15478                 tag: 'option',
15479                 value : 0,
15480                 html : default_text
15481             });
15482             
15483             var o = {};
15484             o[this.valueField] = 0;
15485             o[this.displayField] = default_text;
15486             
15487             this.ios_options.push({
15488                 data : o,
15489                 el : opt
15490             });
15491             
15492         }
15493         
15494         this.store.data.each(function(d, rowIndex){
15495             
15496             var html = '';
15497             
15498             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15499                 html = d.data[this.displayField];
15500             }
15501             
15502             var value = '';
15503             
15504             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15505                 value = d.data[this.valueField];
15506             }
15507             
15508             var option = {
15509                 tag: 'option',
15510                 value : value,
15511                 html : html
15512             };
15513             
15514             if(this.value == d.data[this.valueField]){
15515                 option['selected'] = true;
15516             }
15517             
15518             var opt = this.inputEl().createChild(option);
15519             
15520             this.ios_options.push({
15521                 data : d.data,
15522                 el : opt
15523             });
15524             
15525         }, this);
15526         
15527         this.inputEl().on('change', function(){
15528            this.fireEvent('select', this);
15529         }, this);
15530         
15531     },
15532     
15533     clearIOSView: function()
15534     {
15535         this.inputEl().dom.innerHTML = '';
15536         
15537         this.ios_options = [];
15538     },
15539     
15540     setIOSValue: function(v)
15541     {
15542         this.value = v;
15543         
15544         if(!this.ios_options){
15545             return;
15546         }
15547         
15548         Roo.each(this.ios_options, function(opts){
15549            
15550            opts.el.dom.removeAttribute('selected');
15551            
15552            if(opts.data[this.valueField] != v){
15553                return;
15554            }
15555            
15556            opts.el.dom.setAttribute('selected', true);
15557            
15558         }, this);
15559     }
15560
15561     /** 
15562     * @cfg {Boolean} grow 
15563     * @hide 
15564     */
15565     /** 
15566     * @cfg {Number} growMin 
15567     * @hide 
15568     */
15569     /** 
15570     * @cfg {Number} growMax 
15571     * @hide 
15572     */
15573     /**
15574      * @hide
15575      * @method autoSize
15576      */
15577 });
15578
15579 Roo.apply(Roo.bootstrap.ComboBox,  {
15580     
15581     header : {
15582         tag: 'div',
15583         cls: 'modal-header',
15584         cn: [
15585             {
15586                 tag: 'h4',
15587                 cls: 'modal-title'
15588             }
15589         ]
15590     },
15591     
15592     body : {
15593         tag: 'div',
15594         cls: 'modal-body',
15595         cn: [
15596             {
15597                 tag: 'ul',
15598                 cls: 'list-group'
15599             }
15600         ]
15601     },
15602     
15603     listItemRadio : {
15604         tag: 'li',
15605         cls: 'list-group-item',
15606         cn: [
15607             {
15608                 tag: 'span',
15609                 cls: 'roo-combobox-list-group-item-value'
15610             },
15611             {
15612                 tag: 'div',
15613                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15614                 cn: [
15615                     {
15616                         tag: 'input',
15617                         type: 'radio'
15618                     },
15619                     {
15620                         tag: 'label'
15621                     }
15622                 ]
15623             }
15624         ]
15625     },
15626     
15627     listItemCheckbox : {
15628         tag: 'li',
15629         cls: 'list-group-item',
15630         cn: [
15631             {
15632                 tag: 'span',
15633                 cls: 'roo-combobox-list-group-item-value'
15634             },
15635             {
15636                 tag: 'div',
15637                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15638                 cn: [
15639                     {
15640                         tag: 'input',
15641                         type: 'checkbox'
15642                     },
15643                     {
15644                         tag: 'label'
15645                     }
15646                 ]
15647             }
15648         ]
15649     },
15650     
15651     emptyResult : {
15652         tag: 'div',
15653         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15654     },
15655     
15656     footer : {
15657         tag: 'div',
15658         cls: 'modal-footer',
15659         cn: [
15660             {
15661                 tag: 'div',
15662                 cls: 'row',
15663                 cn: [
15664                     {
15665                         tag: 'div',
15666                         cls: 'col-xs-6 text-left',
15667                         cn: {
15668                             tag: 'button',
15669                             cls: 'btn btn-danger roo-touch-view-cancel',
15670                             html: 'Cancel'
15671                         }
15672                     },
15673                     {
15674                         tag: 'div',
15675                         cls: 'col-xs-6 text-right',
15676                         cn: {
15677                             tag: 'button',
15678                             cls: 'btn btn-success roo-touch-view-ok',
15679                             html: 'OK'
15680                         }
15681                     }
15682                 ]
15683             }
15684         ]
15685         
15686     }
15687 });
15688
15689 Roo.apply(Roo.bootstrap.ComboBox,  {
15690     
15691     touchViewTemplate : {
15692         tag: 'div',
15693         cls: 'modal fade roo-combobox-touch-view',
15694         cn: [
15695             {
15696                 tag: 'div',
15697                 cls: 'modal-dialog',
15698                 style : 'position:fixed', // we have to fix position....
15699                 cn: [
15700                     {
15701                         tag: 'div',
15702                         cls: 'modal-content',
15703                         cn: [
15704                             Roo.bootstrap.ComboBox.header,
15705                             Roo.bootstrap.ComboBox.body,
15706                             Roo.bootstrap.ComboBox.footer
15707                         ]
15708                     }
15709                 ]
15710             }
15711         ]
15712     }
15713 });/*
15714  * Based on:
15715  * Ext JS Library 1.1.1
15716  * Copyright(c) 2006-2007, Ext JS, LLC.
15717  *
15718  * Originally Released Under LGPL - original licence link has changed is not relivant.
15719  *
15720  * Fork - LGPL
15721  * <script type="text/javascript">
15722  */
15723
15724 /**
15725  * @class Roo.View
15726  * @extends Roo.util.Observable
15727  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15728  * This class also supports single and multi selection modes. <br>
15729  * Create a data model bound view:
15730  <pre><code>
15731  var store = new Roo.data.Store(...);
15732
15733  var view = new Roo.View({
15734     el : "my-element",
15735     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15736  
15737     singleSelect: true,
15738     selectedClass: "ydataview-selected",
15739     store: store
15740  });
15741
15742  // listen for node click?
15743  view.on("click", function(vw, index, node, e){
15744  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15745  });
15746
15747  // load XML data
15748  dataModel.load("foobar.xml");
15749  </code></pre>
15750  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15751  * <br><br>
15752  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15753  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15754  * 
15755  * Note: old style constructor is still suported (container, template, config)
15756  * 
15757  * @constructor
15758  * Create a new View
15759  * @param {Object} config The config object
15760  * 
15761  */
15762 Roo.View = function(config, depreciated_tpl, depreciated_config){
15763     
15764     this.parent = false;
15765     
15766     if (typeof(depreciated_tpl) == 'undefined') {
15767         // new way.. - universal constructor.
15768         Roo.apply(this, config);
15769         this.el  = Roo.get(this.el);
15770     } else {
15771         // old format..
15772         this.el  = Roo.get(config);
15773         this.tpl = depreciated_tpl;
15774         Roo.apply(this, depreciated_config);
15775     }
15776     this.wrapEl  = this.el.wrap().wrap();
15777     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15778     
15779     
15780     if(typeof(this.tpl) == "string"){
15781         this.tpl = new Roo.Template(this.tpl);
15782     } else {
15783         // support xtype ctors..
15784         this.tpl = new Roo.factory(this.tpl, Roo);
15785     }
15786     
15787     
15788     this.tpl.compile();
15789     
15790     /** @private */
15791     this.addEvents({
15792         /**
15793          * @event beforeclick
15794          * Fires before a click is processed. Returns false to cancel the default action.
15795          * @param {Roo.View} this
15796          * @param {Number} index The index of the target node
15797          * @param {HTMLElement} node The target node
15798          * @param {Roo.EventObject} e The raw event object
15799          */
15800             "beforeclick" : true,
15801         /**
15802          * @event click
15803          * Fires when a template node is clicked.
15804          * @param {Roo.View} this
15805          * @param {Number} index The index of the target node
15806          * @param {HTMLElement} node The target node
15807          * @param {Roo.EventObject} e The raw event object
15808          */
15809             "click" : true,
15810         /**
15811          * @event dblclick
15812          * Fires when a template node is double clicked.
15813          * @param {Roo.View} this
15814          * @param {Number} index The index of the target node
15815          * @param {HTMLElement} node The target node
15816          * @param {Roo.EventObject} e The raw event object
15817          */
15818             "dblclick" : true,
15819         /**
15820          * @event contextmenu
15821          * Fires when a template node is right clicked.
15822          * @param {Roo.View} this
15823          * @param {Number} index The index of the target node
15824          * @param {HTMLElement} node The target node
15825          * @param {Roo.EventObject} e The raw event object
15826          */
15827             "contextmenu" : true,
15828         /**
15829          * @event selectionchange
15830          * Fires when the selected nodes change.
15831          * @param {Roo.View} this
15832          * @param {Array} selections Array of the selected nodes
15833          */
15834             "selectionchange" : true,
15835     
15836         /**
15837          * @event beforeselect
15838          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15839          * @param {Roo.View} this
15840          * @param {HTMLElement} node The node to be selected
15841          * @param {Array} selections Array of currently selected nodes
15842          */
15843             "beforeselect" : true,
15844         /**
15845          * @event preparedata
15846          * Fires on every row to render, to allow you to change the data.
15847          * @param {Roo.View} this
15848          * @param {Object} data to be rendered (change this)
15849          */
15850           "preparedata" : true
15851           
15852           
15853         });
15854
15855
15856
15857     this.el.on({
15858         "click": this.onClick,
15859         "dblclick": this.onDblClick,
15860         "contextmenu": this.onContextMenu,
15861         scope:this
15862     });
15863
15864     this.selections = [];
15865     this.nodes = [];
15866     this.cmp = new Roo.CompositeElementLite([]);
15867     if(this.store){
15868         this.store = Roo.factory(this.store, Roo.data);
15869         this.setStore(this.store, true);
15870     }
15871     
15872     if ( this.footer && this.footer.xtype) {
15873            
15874          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15875         
15876         this.footer.dataSource = this.store;
15877         this.footer.container = fctr;
15878         this.footer = Roo.factory(this.footer, Roo);
15879         fctr.insertFirst(this.el);
15880         
15881         // this is a bit insane - as the paging toolbar seems to detach the el..
15882 //        dom.parentNode.parentNode.parentNode
15883          // they get detached?
15884     }
15885     
15886     
15887     Roo.View.superclass.constructor.call(this);
15888     
15889     
15890 };
15891
15892 Roo.extend(Roo.View, Roo.util.Observable, {
15893     
15894      /**
15895      * @cfg {Roo.data.Store} store Data store to load data from.
15896      */
15897     store : false,
15898     
15899     /**
15900      * @cfg {String|Roo.Element} el The container element.
15901      */
15902     el : '',
15903     
15904     /**
15905      * @cfg {String|Roo.Template} tpl The template used by this View 
15906      */
15907     tpl : false,
15908     /**
15909      * @cfg {String} dataName the named area of the template to use as the data area
15910      *                          Works with domtemplates roo-name="name"
15911      */
15912     dataName: false,
15913     /**
15914      * @cfg {String} selectedClass The css class to add to selected nodes
15915      */
15916     selectedClass : "x-view-selected",
15917      /**
15918      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15919      */
15920     emptyText : "",
15921     
15922     /**
15923      * @cfg {String} text to display on mask (default Loading)
15924      */
15925     mask : false,
15926     /**
15927      * @cfg {Boolean} multiSelect Allow multiple selection
15928      */
15929     multiSelect : false,
15930     /**
15931      * @cfg {Boolean} singleSelect Allow single selection
15932      */
15933     singleSelect:  false,
15934     
15935     /**
15936      * @cfg {Boolean} toggleSelect - selecting 
15937      */
15938     toggleSelect : false,
15939     
15940     /**
15941      * @cfg {Boolean} tickable - selecting 
15942      */
15943     tickable : false,
15944     
15945     /**
15946      * Returns the element this view is bound to.
15947      * @return {Roo.Element}
15948      */
15949     getEl : function(){
15950         return this.wrapEl;
15951     },
15952     
15953     
15954
15955     /**
15956      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15957      */
15958     refresh : function(){
15959         //Roo.log('refresh');
15960         var t = this.tpl;
15961         
15962         // if we are using something like 'domtemplate', then
15963         // the what gets used is:
15964         // t.applySubtemplate(NAME, data, wrapping data..)
15965         // the outer template then get' applied with
15966         //     the store 'extra data'
15967         // and the body get's added to the
15968         //      roo-name="data" node?
15969         //      <span class='roo-tpl-{name}'></span> ?????
15970         
15971         
15972         
15973         this.clearSelections();
15974         this.el.update("");
15975         var html = [];
15976         var records = this.store.getRange();
15977         if(records.length < 1) {
15978             
15979             // is this valid??  = should it render a template??
15980             
15981             this.el.update(this.emptyText);
15982             return;
15983         }
15984         var el = this.el;
15985         if (this.dataName) {
15986             this.el.update(t.apply(this.store.meta)); //????
15987             el = this.el.child('.roo-tpl-' + this.dataName);
15988         }
15989         
15990         for(var i = 0, len = records.length; i < len; i++){
15991             var data = this.prepareData(records[i].data, i, records[i]);
15992             this.fireEvent("preparedata", this, data, i, records[i]);
15993             
15994             var d = Roo.apply({}, data);
15995             
15996             if(this.tickable){
15997                 Roo.apply(d, {'roo-id' : Roo.id()});
15998                 
15999                 var _this = this;
16000             
16001                 Roo.each(this.parent.item, function(item){
16002                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16003                         return;
16004                     }
16005                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16006                 });
16007             }
16008             
16009             html[html.length] = Roo.util.Format.trim(
16010                 this.dataName ?
16011                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16012                     t.apply(d)
16013             );
16014         }
16015         
16016         
16017         
16018         el.update(html.join(""));
16019         this.nodes = el.dom.childNodes;
16020         this.updateIndexes(0);
16021     },
16022     
16023
16024     /**
16025      * Function to override to reformat the data that is sent to
16026      * the template for each node.
16027      * DEPRICATED - use the preparedata event handler.
16028      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16029      * a JSON object for an UpdateManager bound view).
16030      */
16031     prepareData : function(data, index, record)
16032     {
16033         this.fireEvent("preparedata", this, data, index, record);
16034         return data;
16035     },
16036
16037     onUpdate : function(ds, record){
16038         // Roo.log('on update');   
16039         this.clearSelections();
16040         var index = this.store.indexOf(record);
16041         var n = this.nodes[index];
16042         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16043         n.parentNode.removeChild(n);
16044         this.updateIndexes(index, index);
16045     },
16046
16047     
16048     
16049 // --------- FIXME     
16050     onAdd : function(ds, records, index)
16051     {
16052         //Roo.log(['on Add', ds, records, index] );        
16053         this.clearSelections();
16054         if(this.nodes.length == 0){
16055             this.refresh();
16056             return;
16057         }
16058         var n = this.nodes[index];
16059         for(var i = 0, len = records.length; i < len; i++){
16060             var d = this.prepareData(records[i].data, i, records[i]);
16061             if(n){
16062                 this.tpl.insertBefore(n, d);
16063             }else{
16064                 
16065                 this.tpl.append(this.el, d);
16066             }
16067         }
16068         this.updateIndexes(index);
16069     },
16070
16071     onRemove : function(ds, record, index){
16072        // Roo.log('onRemove');
16073         this.clearSelections();
16074         var el = this.dataName  ?
16075             this.el.child('.roo-tpl-' + this.dataName) :
16076             this.el; 
16077         
16078         el.dom.removeChild(this.nodes[index]);
16079         this.updateIndexes(index);
16080     },
16081
16082     /**
16083      * Refresh an individual node.
16084      * @param {Number} index
16085      */
16086     refreshNode : function(index){
16087         this.onUpdate(this.store, this.store.getAt(index));
16088     },
16089
16090     updateIndexes : function(startIndex, endIndex){
16091         var ns = this.nodes;
16092         startIndex = startIndex || 0;
16093         endIndex = endIndex || ns.length - 1;
16094         for(var i = startIndex; i <= endIndex; i++){
16095             ns[i].nodeIndex = i;
16096         }
16097     },
16098
16099     /**
16100      * Changes the data store this view uses and refresh the view.
16101      * @param {Store} store
16102      */
16103     setStore : function(store, initial){
16104         if(!initial && this.store){
16105             this.store.un("datachanged", this.refresh);
16106             this.store.un("add", this.onAdd);
16107             this.store.un("remove", this.onRemove);
16108             this.store.un("update", this.onUpdate);
16109             this.store.un("clear", this.refresh);
16110             this.store.un("beforeload", this.onBeforeLoad);
16111             this.store.un("load", this.onLoad);
16112             this.store.un("loadexception", this.onLoad);
16113         }
16114         if(store){
16115           
16116             store.on("datachanged", this.refresh, this);
16117             store.on("add", this.onAdd, this);
16118             store.on("remove", this.onRemove, this);
16119             store.on("update", this.onUpdate, this);
16120             store.on("clear", this.refresh, this);
16121             store.on("beforeload", this.onBeforeLoad, this);
16122             store.on("load", this.onLoad, this);
16123             store.on("loadexception", this.onLoad, this);
16124         }
16125         
16126         if(store){
16127             this.refresh();
16128         }
16129     },
16130     /**
16131      * onbeforeLoad - masks the loading area.
16132      *
16133      */
16134     onBeforeLoad : function(store,opts)
16135     {
16136          //Roo.log('onBeforeLoad');   
16137         if (!opts.add) {
16138             this.el.update("");
16139         }
16140         this.el.mask(this.mask ? this.mask : "Loading" ); 
16141     },
16142     onLoad : function ()
16143     {
16144         this.el.unmask();
16145     },
16146     
16147
16148     /**
16149      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16150      * @param {HTMLElement} node
16151      * @return {HTMLElement} The template node
16152      */
16153     findItemFromChild : function(node){
16154         var el = this.dataName  ?
16155             this.el.child('.roo-tpl-' + this.dataName,true) :
16156             this.el.dom; 
16157         
16158         if(!node || node.parentNode == el){
16159                     return node;
16160             }
16161             var p = node.parentNode;
16162             while(p && p != el){
16163             if(p.parentNode == el){
16164                 return p;
16165             }
16166             p = p.parentNode;
16167         }
16168             return null;
16169     },
16170
16171     /** @ignore */
16172     onClick : function(e){
16173         var item = this.findItemFromChild(e.getTarget());
16174         if(item){
16175             var index = this.indexOf(item);
16176             if(this.onItemClick(item, index, e) !== false){
16177                 this.fireEvent("click", this, index, item, e);
16178             }
16179         }else{
16180             this.clearSelections();
16181         }
16182     },
16183
16184     /** @ignore */
16185     onContextMenu : function(e){
16186         var item = this.findItemFromChild(e.getTarget());
16187         if(item){
16188             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16189         }
16190     },
16191
16192     /** @ignore */
16193     onDblClick : function(e){
16194         var item = this.findItemFromChild(e.getTarget());
16195         if(item){
16196             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16197         }
16198     },
16199
16200     onItemClick : function(item, index, e)
16201     {
16202         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16203             return false;
16204         }
16205         if (this.toggleSelect) {
16206             var m = this.isSelected(item) ? 'unselect' : 'select';
16207             //Roo.log(m);
16208             var _t = this;
16209             _t[m](item, true, false);
16210             return true;
16211         }
16212         if(this.multiSelect || this.singleSelect){
16213             if(this.multiSelect && e.shiftKey && this.lastSelection){
16214                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16215             }else{
16216                 this.select(item, this.multiSelect && e.ctrlKey);
16217                 this.lastSelection = item;
16218             }
16219             
16220             if(!this.tickable){
16221                 e.preventDefault();
16222             }
16223             
16224         }
16225         return true;
16226     },
16227
16228     /**
16229      * Get the number of selected nodes.
16230      * @return {Number}
16231      */
16232     getSelectionCount : function(){
16233         return this.selections.length;
16234     },
16235
16236     /**
16237      * Get the currently selected nodes.
16238      * @return {Array} An array of HTMLElements
16239      */
16240     getSelectedNodes : function(){
16241         return this.selections;
16242     },
16243
16244     /**
16245      * Get the indexes of the selected nodes.
16246      * @return {Array}
16247      */
16248     getSelectedIndexes : function(){
16249         var indexes = [], s = this.selections;
16250         for(var i = 0, len = s.length; i < len; i++){
16251             indexes.push(s[i].nodeIndex);
16252         }
16253         return indexes;
16254     },
16255
16256     /**
16257      * Clear all selections
16258      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16259      */
16260     clearSelections : function(suppressEvent){
16261         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16262             this.cmp.elements = this.selections;
16263             this.cmp.removeClass(this.selectedClass);
16264             this.selections = [];
16265             if(!suppressEvent){
16266                 this.fireEvent("selectionchange", this, this.selections);
16267             }
16268         }
16269     },
16270
16271     /**
16272      * Returns true if the passed node is selected
16273      * @param {HTMLElement/Number} node The node or node index
16274      * @return {Boolean}
16275      */
16276     isSelected : function(node){
16277         var s = this.selections;
16278         if(s.length < 1){
16279             return false;
16280         }
16281         node = this.getNode(node);
16282         return s.indexOf(node) !== -1;
16283     },
16284
16285     /**
16286      * Selects nodes.
16287      * @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
16288      * @param {Boolean} keepExisting (optional) true to keep existing selections
16289      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16290      */
16291     select : function(nodeInfo, keepExisting, suppressEvent){
16292         if(nodeInfo instanceof Array){
16293             if(!keepExisting){
16294                 this.clearSelections(true);
16295             }
16296             for(var i = 0, len = nodeInfo.length; i < len; i++){
16297                 this.select(nodeInfo[i], true, true);
16298             }
16299             return;
16300         } 
16301         var node = this.getNode(nodeInfo);
16302         if(!node || this.isSelected(node)){
16303             return; // already selected.
16304         }
16305         if(!keepExisting){
16306             this.clearSelections(true);
16307         }
16308         
16309         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16310             Roo.fly(node).addClass(this.selectedClass);
16311             this.selections.push(node);
16312             if(!suppressEvent){
16313                 this.fireEvent("selectionchange", this, this.selections);
16314             }
16315         }
16316         
16317         
16318     },
16319       /**
16320      * Unselects nodes.
16321      * @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
16322      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16323      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16324      */
16325     unselect : function(nodeInfo, keepExisting, suppressEvent)
16326     {
16327         if(nodeInfo instanceof Array){
16328             Roo.each(this.selections, function(s) {
16329                 this.unselect(s, nodeInfo);
16330             }, this);
16331             return;
16332         }
16333         var node = this.getNode(nodeInfo);
16334         if(!node || !this.isSelected(node)){
16335             //Roo.log("not selected");
16336             return; // not selected.
16337         }
16338         // fireevent???
16339         var ns = [];
16340         Roo.each(this.selections, function(s) {
16341             if (s == node ) {
16342                 Roo.fly(node).removeClass(this.selectedClass);
16343
16344                 return;
16345             }
16346             ns.push(s);
16347         },this);
16348         
16349         this.selections= ns;
16350         this.fireEvent("selectionchange", this, this.selections);
16351     },
16352
16353     /**
16354      * Gets a template node.
16355      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16356      * @return {HTMLElement} The node or null if it wasn't found
16357      */
16358     getNode : function(nodeInfo){
16359         if(typeof nodeInfo == "string"){
16360             return document.getElementById(nodeInfo);
16361         }else if(typeof nodeInfo == "number"){
16362             return this.nodes[nodeInfo];
16363         }
16364         return nodeInfo;
16365     },
16366
16367     /**
16368      * Gets a range template nodes.
16369      * @param {Number} startIndex
16370      * @param {Number} endIndex
16371      * @return {Array} An array of nodes
16372      */
16373     getNodes : function(start, end){
16374         var ns = this.nodes;
16375         start = start || 0;
16376         end = typeof end == "undefined" ? ns.length - 1 : end;
16377         var nodes = [];
16378         if(start <= end){
16379             for(var i = start; i <= end; i++){
16380                 nodes.push(ns[i]);
16381             }
16382         } else{
16383             for(var i = start; i >= end; i--){
16384                 nodes.push(ns[i]);
16385             }
16386         }
16387         return nodes;
16388     },
16389
16390     /**
16391      * Finds the index of the passed node
16392      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16393      * @return {Number} The index of the node or -1
16394      */
16395     indexOf : function(node){
16396         node = this.getNode(node);
16397         if(typeof node.nodeIndex == "number"){
16398             return node.nodeIndex;
16399         }
16400         var ns = this.nodes;
16401         for(var i = 0, len = ns.length; i < len; i++){
16402             if(ns[i] == node){
16403                 return i;
16404             }
16405         }
16406         return -1;
16407     }
16408 });
16409 /*
16410  * - LGPL
16411  *
16412  * based on jquery fullcalendar
16413  * 
16414  */
16415
16416 Roo.bootstrap = Roo.bootstrap || {};
16417 /**
16418  * @class Roo.bootstrap.Calendar
16419  * @extends Roo.bootstrap.Component
16420  * Bootstrap Calendar class
16421  * @cfg {Boolean} loadMask (true|false) default false
16422  * @cfg {Object} header generate the user specific header of the calendar, default false
16423
16424  * @constructor
16425  * Create a new Container
16426  * @param {Object} config The config object
16427  */
16428
16429
16430
16431 Roo.bootstrap.Calendar = function(config){
16432     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16433      this.addEvents({
16434         /**
16435              * @event select
16436              * Fires when a date is selected
16437              * @param {DatePicker} this
16438              * @param {Date} date The selected date
16439              */
16440         'select': true,
16441         /**
16442              * @event monthchange
16443              * Fires when the displayed month changes 
16444              * @param {DatePicker} this
16445              * @param {Date} date The selected month
16446              */
16447         'monthchange': true,
16448         /**
16449              * @event evententer
16450              * Fires when mouse over an event
16451              * @param {Calendar} this
16452              * @param {event} Event
16453              */
16454         'evententer': true,
16455         /**
16456              * @event eventleave
16457              * Fires when the mouse leaves an
16458              * @param {Calendar} this
16459              * @param {event}
16460              */
16461         'eventleave': true,
16462         /**
16463              * @event eventclick
16464              * Fires when the mouse click an
16465              * @param {Calendar} this
16466              * @param {event}
16467              */
16468         'eventclick': true
16469         
16470     });
16471
16472 };
16473
16474 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16475     
16476      /**
16477      * @cfg {Number} startDay
16478      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16479      */
16480     startDay : 0,
16481     
16482     loadMask : false,
16483     
16484     header : false,
16485       
16486     getAutoCreate : function(){
16487         
16488         
16489         var fc_button = function(name, corner, style, content ) {
16490             return Roo.apply({},{
16491                 tag : 'span',
16492                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16493                          (corner.length ?
16494                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16495                             ''
16496                         ),
16497                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16498                 unselectable: 'on'
16499             });
16500         };
16501         
16502         var header = {};
16503         
16504         if(!this.header){
16505             header = {
16506                 tag : 'table',
16507                 cls : 'fc-header',
16508                 style : 'width:100%',
16509                 cn : [
16510                     {
16511                         tag: 'tr',
16512                         cn : [
16513                             {
16514                                 tag : 'td',
16515                                 cls : 'fc-header-left',
16516                                 cn : [
16517                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16518                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16519                                     { tag: 'span', cls: 'fc-header-space' },
16520                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16521
16522
16523                                 ]
16524                             },
16525
16526                             {
16527                                 tag : 'td',
16528                                 cls : 'fc-header-center',
16529                                 cn : [
16530                                     {
16531                                         tag: 'span',
16532                                         cls: 'fc-header-title',
16533                                         cn : {
16534                                             tag: 'H2',
16535                                             html : 'month / year'
16536                                         }
16537                                     }
16538
16539                                 ]
16540                             },
16541                             {
16542                                 tag : 'td',
16543                                 cls : 'fc-header-right',
16544                                 cn : [
16545                               /*      fc_button('month', 'left', '', 'month' ),
16546                                     fc_button('week', '', '', 'week' ),
16547                                     fc_button('day', 'right', '', 'day' )
16548                                 */    
16549
16550                                 ]
16551                             }
16552
16553                         ]
16554                     }
16555                 ]
16556             };
16557         }
16558         
16559         header = this.header;
16560         
16561        
16562         var cal_heads = function() {
16563             var ret = [];
16564             // fixme - handle this.
16565             
16566             for (var i =0; i < Date.dayNames.length; i++) {
16567                 var d = Date.dayNames[i];
16568                 ret.push({
16569                     tag: 'th',
16570                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16571                     html : d.substring(0,3)
16572                 });
16573                 
16574             }
16575             ret[0].cls += ' fc-first';
16576             ret[6].cls += ' fc-last';
16577             return ret;
16578         };
16579         var cal_cell = function(n) {
16580             return  {
16581                 tag: 'td',
16582                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16583                 cn : [
16584                     {
16585                         cn : [
16586                             {
16587                                 cls: 'fc-day-number',
16588                                 html: 'D'
16589                             },
16590                             {
16591                                 cls: 'fc-day-content',
16592                              
16593                                 cn : [
16594                                      {
16595                                         style: 'position: relative;' // height: 17px;
16596                                     }
16597                                 ]
16598                             }
16599                             
16600                             
16601                         ]
16602                     }
16603                 ]
16604                 
16605             }
16606         };
16607         var cal_rows = function() {
16608             
16609             var ret = [];
16610             for (var r = 0; r < 6; r++) {
16611                 var row= {
16612                     tag : 'tr',
16613                     cls : 'fc-week',
16614                     cn : []
16615                 };
16616                 
16617                 for (var i =0; i < Date.dayNames.length; i++) {
16618                     var d = Date.dayNames[i];
16619                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16620
16621                 }
16622                 row.cn[0].cls+=' fc-first';
16623                 row.cn[0].cn[0].style = 'min-height:90px';
16624                 row.cn[6].cls+=' fc-last';
16625                 ret.push(row);
16626                 
16627             }
16628             ret[0].cls += ' fc-first';
16629             ret[4].cls += ' fc-prev-last';
16630             ret[5].cls += ' fc-last';
16631             return ret;
16632             
16633         };
16634         
16635         var cal_table = {
16636             tag: 'table',
16637             cls: 'fc-border-separate',
16638             style : 'width:100%',
16639             cellspacing  : 0,
16640             cn : [
16641                 { 
16642                     tag: 'thead',
16643                     cn : [
16644                         { 
16645                             tag: 'tr',
16646                             cls : 'fc-first fc-last',
16647                             cn : cal_heads()
16648                         }
16649                     ]
16650                 },
16651                 { 
16652                     tag: 'tbody',
16653                     cn : cal_rows()
16654                 }
16655                   
16656             ]
16657         };
16658          
16659          var cfg = {
16660             cls : 'fc fc-ltr',
16661             cn : [
16662                 header,
16663                 {
16664                     cls : 'fc-content',
16665                     style : "position: relative;",
16666                     cn : [
16667                         {
16668                             cls : 'fc-view fc-view-month fc-grid',
16669                             style : 'position: relative',
16670                             unselectable : 'on',
16671                             cn : [
16672                                 {
16673                                     cls : 'fc-event-container',
16674                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16675                                 },
16676                                 cal_table
16677                             ]
16678                         }
16679                     ]
16680     
16681                 }
16682            ] 
16683             
16684         };
16685         
16686          
16687         
16688         return cfg;
16689     },
16690     
16691     
16692     initEvents : function()
16693     {
16694         if(!this.store){
16695             throw "can not find store for calendar";
16696         }
16697         
16698         var mark = {
16699             tag: "div",
16700             cls:"x-dlg-mask",
16701             style: "text-align:center",
16702             cn: [
16703                 {
16704                     tag: "div",
16705                     style: "background-color:white;width:50%;margin:250 auto",
16706                     cn: [
16707                         {
16708                             tag: "img",
16709                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16710                         },
16711                         {
16712                             tag: "span",
16713                             html: "Loading"
16714                         }
16715                         
16716                     ]
16717                 }
16718             ]
16719         };
16720         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16721         
16722         var size = this.el.select('.fc-content', true).first().getSize();
16723         this.maskEl.setSize(size.width, size.height);
16724         this.maskEl.enableDisplayMode("block");
16725         if(!this.loadMask){
16726             this.maskEl.hide();
16727         }
16728         
16729         this.store = Roo.factory(this.store, Roo.data);
16730         this.store.on('load', this.onLoad, this);
16731         this.store.on('beforeload', this.onBeforeLoad, this);
16732         
16733         this.resize();
16734         
16735         this.cells = this.el.select('.fc-day',true);
16736         //Roo.log(this.cells);
16737         this.textNodes = this.el.query('.fc-day-number');
16738         this.cells.addClassOnOver('fc-state-hover');
16739         
16740         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16741         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16742         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16743         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16744         
16745         this.on('monthchange', this.onMonthChange, this);
16746         
16747         this.update(new Date().clearTime());
16748     },
16749     
16750     resize : function() {
16751         var sz  = this.el.getSize();
16752         
16753         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16754         this.el.select('.fc-day-content div',true).setHeight(34);
16755     },
16756     
16757     
16758     // private
16759     showPrevMonth : function(e){
16760         this.update(this.activeDate.add("mo", -1));
16761     },
16762     showToday : function(e){
16763         this.update(new Date().clearTime());
16764     },
16765     // private
16766     showNextMonth : function(e){
16767         this.update(this.activeDate.add("mo", 1));
16768     },
16769
16770     // private
16771     showPrevYear : function(){
16772         this.update(this.activeDate.add("y", -1));
16773     },
16774
16775     // private
16776     showNextYear : function(){
16777         this.update(this.activeDate.add("y", 1));
16778     },
16779
16780     
16781    // private
16782     update : function(date)
16783     {
16784         var vd = this.activeDate;
16785         this.activeDate = date;
16786 //        if(vd && this.el){
16787 //            var t = date.getTime();
16788 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16789 //                Roo.log('using add remove');
16790 //                
16791 //                this.fireEvent('monthchange', this, date);
16792 //                
16793 //                this.cells.removeClass("fc-state-highlight");
16794 //                this.cells.each(function(c){
16795 //                   if(c.dateValue == t){
16796 //                       c.addClass("fc-state-highlight");
16797 //                       setTimeout(function(){
16798 //                            try{c.dom.firstChild.focus();}catch(e){}
16799 //                       }, 50);
16800 //                       return false;
16801 //                   }
16802 //                   return true;
16803 //                });
16804 //                return;
16805 //            }
16806 //        }
16807         
16808         var days = date.getDaysInMonth();
16809         
16810         var firstOfMonth = date.getFirstDateOfMonth();
16811         var startingPos = firstOfMonth.getDay()-this.startDay;
16812         
16813         if(startingPos < this.startDay){
16814             startingPos += 7;
16815         }
16816         
16817         var pm = date.add(Date.MONTH, -1);
16818         var prevStart = pm.getDaysInMonth()-startingPos;
16819 //        
16820         this.cells = this.el.select('.fc-day',true);
16821         this.textNodes = this.el.query('.fc-day-number');
16822         this.cells.addClassOnOver('fc-state-hover');
16823         
16824         var cells = this.cells.elements;
16825         var textEls = this.textNodes;
16826         
16827         Roo.each(cells, function(cell){
16828             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16829         });
16830         
16831         days += startingPos;
16832
16833         // convert everything to numbers so it's fast
16834         var day = 86400000;
16835         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16836         //Roo.log(d);
16837         //Roo.log(pm);
16838         //Roo.log(prevStart);
16839         
16840         var today = new Date().clearTime().getTime();
16841         var sel = date.clearTime().getTime();
16842         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16843         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16844         var ddMatch = this.disabledDatesRE;
16845         var ddText = this.disabledDatesText;
16846         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16847         var ddaysText = this.disabledDaysText;
16848         var format = this.format;
16849         
16850         var setCellClass = function(cal, cell){
16851             cell.row = 0;
16852             cell.events = [];
16853             cell.more = [];
16854             //Roo.log('set Cell Class');
16855             cell.title = "";
16856             var t = d.getTime();
16857             
16858             //Roo.log(d);
16859             
16860             cell.dateValue = t;
16861             if(t == today){
16862                 cell.className += " fc-today";
16863                 cell.className += " fc-state-highlight";
16864                 cell.title = cal.todayText;
16865             }
16866             if(t == sel){
16867                 // disable highlight in other month..
16868                 //cell.className += " fc-state-highlight";
16869                 
16870             }
16871             // disabling
16872             if(t < min) {
16873                 cell.className = " fc-state-disabled";
16874                 cell.title = cal.minText;
16875                 return;
16876             }
16877             if(t > max) {
16878                 cell.className = " fc-state-disabled";
16879                 cell.title = cal.maxText;
16880                 return;
16881             }
16882             if(ddays){
16883                 if(ddays.indexOf(d.getDay()) != -1){
16884                     cell.title = ddaysText;
16885                     cell.className = " fc-state-disabled";
16886                 }
16887             }
16888             if(ddMatch && format){
16889                 var fvalue = d.dateFormat(format);
16890                 if(ddMatch.test(fvalue)){
16891                     cell.title = ddText.replace("%0", fvalue);
16892                     cell.className = " fc-state-disabled";
16893                 }
16894             }
16895             
16896             if (!cell.initialClassName) {
16897                 cell.initialClassName = cell.dom.className;
16898             }
16899             
16900             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16901         };
16902
16903         var i = 0;
16904         
16905         for(; i < startingPos; i++) {
16906             textEls[i].innerHTML = (++prevStart);
16907             d.setDate(d.getDate()+1);
16908             
16909             cells[i].className = "fc-past fc-other-month";
16910             setCellClass(this, cells[i]);
16911         }
16912         
16913         var intDay = 0;
16914         
16915         for(; i < days; i++){
16916             intDay = i - startingPos + 1;
16917             textEls[i].innerHTML = (intDay);
16918             d.setDate(d.getDate()+1);
16919             
16920             cells[i].className = ''; // "x-date-active";
16921             setCellClass(this, cells[i]);
16922         }
16923         var extraDays = 0;
16924         
16925         for(; i < 42; i++) {
16926             textEls[i].innerHTML = (++extraDays);
16927             d.setDate(d.getDate()+1);
16928             
16929             cells[i].className = "fc-future fc-other-month";
16930             setCellClass(this, cells[i]);
16931         }
16932         
16933         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16934         
16935         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16936         
16937         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16938         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16939         
16940         if(totalRows != 6){
16941             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16942             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16943         }
16944         
16945         this.fireEvent('monthchange', this, date);
16946         
16947         
16948         /*
16949         if(!this.internalRender){
16950             var main = this.el.dom.firstChild;
16951             var w = main.offsetWidth;
16952             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16953             Roo.fly(main).setWidth(w);
16954             this.internalRender = true;
16955             // opera does not respect the auto grow header center column
16956             // then, after it gets a width opera refuses to recalculate
16957             // without a second pass
16958             if(Roo.isOpera && !this.secondPass){
16959                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16960                 this.secondPass = true;
16961                 this.update.defer(10, this, [date]);
16962             }
16963         }
16964         */
16965         
16966     },
16967     
16968     findCell : function(dt) {
16969         dt = dt.clearTime().getTime();
16970         var ret = false;
16971         this.cells.each(function(c){
16972             //Roo.log("check " +c.dateValue + '?=' + dt);
16973             if(c.dateValue == dt){
16974                 ret = c;
16975                 return false;
16976             }
16977             return true;
16978         });
16979         
16980         return ret;
16981     },
16982     
16983     findCells : function(ev) {
16984         var s = ev.start.clone().clearTime().getTime();
16985        // Roo.log(s);
16986         var e= ev.end.clone().clearTime().getTime();
16987        // Roo.log(e);
16988         var ret = [];
16989         this.cells.each(function(c){
16990              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16991             
16992             if(c.dateValue > e){
16993                 return ;
16994             }
16995             if(c.dateValue < s){
16996                 return ;
16997             }
16998             ret.push(c);
16999         });
17000         
17001         return ret;    
17002     },
17003     
17004 //    findBestRow: function(cells)
17005 //    {
17006 //        var ret = 0;
17007 //        
17008 //        for (var i =0 ; i < cells.length;i++) {
17009 //            ret  = Math.max(cells[i].rows || 0,ret);
17010 //        }
17011 //        return ret;
17012 //        
17013 //    },
17014     
17015     
17016     addItem : function(ev)
17017     {
17018         // look for vertical location slot in
17019         var cells = this.findCells(ev);
17020         
17021 //        ev.row = this.findBestRow(cells);
17022         
17023         // work out the location.
17024         
17025         var crow = false;
17026         var rows = [];
17027         for(var i =0; i < cells.length; i++) {
17028             
17029             cells[i].row = cells[0].row;
17030             
17031             if(i == 0){
17032                 cells[i].row = cells[i].row + 1;
17033             }
17034             
17035             if (!crow) {
17036                 crow = {
17037                     start : cells[i],
17038                     end :  cells[i]
17039                 };
17040                 continue;
17041             }
17042             if (crow.start.getY() == cells[i].getY()) {
17043                 // on same row.
17044                 crow.end = cells[i];
17045                 continue;
17046             }
17047             // different row.
17048             rows.push(crow);
17049             crow = {
17050                 start: cells[i],
17051                 end : cells[i]
17052             };
17053             
17054         }
17055         
17056         rows.push(crow);
17057         ev.els = [];
17058         ev.rows = rows;
17059         ev.cells = cells;
17060         
17061         cells[0].events.push(ev);
17062         
17063         this.calevents.push(ev);
17064     },
17065     
17066     clearEvents: function() {
17067         
17068         if(!this.calevents){
17069             return;
17070         }
17071         
17072         Roo.each(this.cells.elements, function(c){
17073             c.row = 0;
17074             c.events = [];
17075             c.more = [];
17076         });
17077         
17078         Roo.each(this.calevents, function(e) {
17079             Roo.each(e.els, function(el) {
17080                 el.un('mouseenter' ,this.onEventEnter, this);
17081                 el.un('mouseleave' ,this.onEventLeave, this);
17082                 el.remove();
17083             },this);
17084         },this);
17085         
17086         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17087             e.remove();
17088         });
17089         
17090     },
17091     
17092     renderEvents: function()
17093     {   
17094         var _this = this;
17095         
17096         this.cells.each(function(c) {
17097             
17098             if(c.row < 5){
17099                 return;
17100             }
17101             
17102             var ev = c.events;
17103             
17104             var r = 4;
17105             if(c.row != c.events.length){
17106                 r = 4 - (4 - (c.row - c.events.length));
17107             }
17108             
17109             c.events = ev.slice(0, r);
17110             c.more = ev.slice(r);
17111             
17112             if(c.more.length && c.more.length == 1){
17113                 c.events.push(c.more.pop());
17114             }
17115             
17116             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17117             
17118         });
17119             
17120         this.cells.each(function(c) {
17121             
17122             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17123             
17124             
17125             for (var e = 0; e < c.events.length; e++){
17126                 var ev = c.events[e];
17127                 var rows = ev.rows;
17128                 
17129                 for(var i = 0; i < rows.length; i++) {
17130                 
17131                     // how many rows should it span..
17132
17133                     var  cfg = {
17134                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17135                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17136
17137                         unselectable : "on",
17138                         cn : [
17139                             {
17140                                 cls: 'fc-event-inner',
17141                                 cn : [
17142     //                                {
17143     //                                  tag:'span',
17144     //                                  cls: 'fc-event-time',
17145     //                                  html : cells.length > 1 ? '' : ev.time
17146     //                                },
17147                                     {
17148                                       tag:'span',
17149                                       cls: 'fc-event-title',
17150                                       html : String.format('{0}', ev.title)
17151                                     }
17152
17153
17154                                 ]
17155                             },
17156                             {
17157                                 cls: 'ui-resizable-handle ui-resizable-e',
17158                                 html : '&nbsp;&nbsp;&nbsp'
17159                             }
17160
17161                         ]
17162                     };
17163
17164                     if (i == 0) {
17165                         cfg.cls += ' fc-event-start';
17166                     }
17167                     if ((i+1) == rows.length) {
17168                         cfg.cls += ' fc-event-end';
17169                     }
17170
17171                     var ctr = _this.el.select('.fc-event-container',true).first();
17172                     var cg = ctr.createChild(cfg);
17173
17174                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17175                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17176
17177                     var r = (c.more.length) ? 1 : 0;
17178                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17179                     cg.setWidth(ebox.right - sbox.x -2);
17180
17181                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17182                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17183                     cg.on('click', _this.onEventClick, _this, ev);
17184
17185                     ev.els.push(cg);
17186                     
17187                 }
17188                 
17189             }
17190             
17191             
17192             if(c.more.length){
17193                 var  cfg = {
17194                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17195                     style : 'position: absolute',
17196                     unselectable : "on",
17197                     cn : [
17198                         {
17199                             cls: 'fc-event-inner',
17200                             cn : [
17201                                 {
17202                                   tag:'span',
17203                                   cls: 'fc-event-title',
17204                                   html : 'More'
17205                                 }
17206
17207
17208                             ]
17209                         },
17210                         {
17211                             cls: 'ui-resizable-handle ui-resizable-e',
17212                             html : '&nbsp;&nbsp;&nbsp'
17213                         }
17214
17215                     ]
17216                 };
17217
17218                 var ctr = _this.el.select('.fc-event-container',true).first();
17219                 var cg = ctr.createChild(cfg);
17220
17221                 var sbox = c.select('.fc-day-content',true).first().getBox();
17222                 var ebox = c.select('.fc-day-content',true).first().getBox();
17223                 //Roo.log(cg);
17224                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17225                 cg.setWidth(ebox.right - sbox.x -2);
17226
17227                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17228                 
17229             }
17230             
17231         });
17232         
17233         
17234         
17235     },
17236     
17237     onEventEnter: function (e, el,event,d) {
17238         this.fireEvent('evententer', this, el, event);
17239     },
17240     
17241     onEventLeave: function (e, el,event,d) {
17242         this.fireEvent('eventleave', this, el, event);
17243     },
17244     
17245     onEventClick: function (e, el,event,d) {
17246         this.fireEvent('eventclick', this, el, event);
17247     },
17248     
17249     onMonthChange: function () {
17250         this.store.load();
17251     },
17252     
17253     onMoreEventClick: function(e, el, more)
17254     {
17255         var _this = this;
17256         
17257         this.calpopover.placement = 'right';
17258         this.calpopover.setTitle('More');
17259         
17260         this.calpopover.setContent('');
17261         
17262         var ctr = this.calpopover.el.select('.popover-content', true).first();
17263         
17264         Roo.each(more, function(m){
17265             var cfg = {
17266                 cls : 'fc-event-hori fc-event-draggable',
17267                 html : m.title
17268             };
17269             var cg = ctr.createChild(cfg);
17270             
17271             cg.on('click', _this.onEventClick, _this, m);
17272         });
17273         
17274         this.calpopover.show(el);
17275         
17276         
17277     },
17278     
17279     onLoad: function () 
17280     {   
17281         this.calevents = [];
17282         var cal = this;
17283         
17284         if(this.store.getCount() > 0){
17285             this.store.data.each(function(d){
17286                cal.addItem({
17287                     id : d.data.id,
17288                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17289                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17290                     time : d.data.start_time,
17291                     title : d.data.title,
17292                     description : d.data.description,
17293                     venue : d.data.venue
17294                 });
17295             });
17296         }
17297         
17298         this.renderEvents();
17299         
17300         if(this.calevents.length && this.loadMask){
17301             this.maskEl.hide();
17302         }
17303     },
17304     
17305     onBeforeLoad: function()
17306     {
17307         this.clearEvents();
17308         if(this.loadMask){
17309             this.maskEl.show();
17310         }
17311     }
17312 });
17313
17314  
17315  /*
17316  * - LGPL
17317  *
17318  * element
17319  * 
17320  */
17321
17322 /**
17323  * @class Roo.bootstrap.Popover
17324  * @extends Roo.bootstrap.Component
17325  * Bootstrap Popover class
17326  * @cfg {String} html contents of the popover   (or false to use children..)
17327  * @cfg {String} title of popover (or false to hide)
17328  * @cfg {String} placement how it is placed
17329  * @cfg {String} trigger click || hover (or false to trigger manually)
17330  * @cfg {String} over what (parent or false to trigger manually.)
17331  * @cfg {Number} delay - delay before showing
17332  
17333  * @constructor
17334  * Create a new Popover
17335  * @param {Object} config The config object
17336  */
17337
17338 Roo.bootstrap.Popover = function(config){
17339     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17340     
17341     this.addEvents({
17342         // raw events
17343          /**
17344          * @event show
17345          * After the popover show
17346          * 
17347          * @param {Roo.bootstrap.Popover} this
17348          */
17349         "show" : true,
17350         /**
17351          * @event hide
17352          * After the popover hide
17353          * 
17354          * @param {Roo.bootstrap.Popover} this
17355          */
17356         "hide" : true
17357     });
17358 };
17359
17360 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17361     
17362     title: 'Fill in a title',
17363     html: false,
17364     
17365     placement : 'right',
17366     trigger : 'hover', // hover
17367     
17368     delay : 0,
17369     
17370     over: 'parent',
17371     
17372     can_build_overlaid : false,
17373     
17374     getChildContainer : function()
17375     {
17376         return this.el.select('.popover-content',true).first();
17377     },
17378     
17379     getAutoCreate : function(){
17380          
17381         var cfg = {
17382            cls : 'popover roo-dynamic',
17383            style: 'display:block',
17384            cn : [
17385                 {
17386                     cls : 'arrow'
17387                 },
17388                 {
17389                     cls : 'popover-inner',
17390                     cn : [
17391                         {
17392                             tag: 'h3',
17393                             cls: 'popover-title',
17394                             html : this.title
17395                         },
17396                         {
17397                             cls : 'popover-content',
17398                             html : this.html
17399                         }
17400                     ]
17401                     
17402                 }
17403            ]
17404         };
17405         
17406         return cfg;
17407     },
17408     setTitle: function(str)
17409     {
17410         this.title = str;
17411         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17412     },
17413     setContent: function(str)
17414     {
17415         this.html = str;
17416         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17417     },
17418     // as it get's added to the bottom of the page.
17419     onRender : function(ct, position)
17420     {
17421         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17422         if(!this.el){
17423             var cfg = Roo.apply({},  this.getAutoCreate());
17424             cfg.id = Roo.id();
17425             
17426             if (this.cls) {
17427                 cfg.cls += ' ' + this.cls;
17428             }
17429             if (this.style) {
17430                 cfg.style = this.style;
17431             }
17432             //Roo.log("adding to ");
17433             this.el = Roo.get(document.body).createChild(cfg, position);
17434 //            Roo.log(this.el);
17435         }
17436         this.initEvents();
17437     },
17438     
17439     initEvents : function()
17440     {
17441         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17442         this.el.enableDisplayMode('block');
17443         this.el.hide();
17444         if (this.over === false) {
17445             return; 
17446         }
17447         if (this.triggers === false) {
17448             return;
17449         }
17450         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17451         var triggers = this.trigger ? this.trigger.split(' ') : [];
17452         Roo.each(triggers, function(trigger) {
17453         
17454             if (trigger == 'click') {
17455                 on_el.on('click', this.toggle, this);
17456             } else if (trigger != 'manual') {
17457                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17458                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17459       
17460                 on_el.on(eventIn  ,this.enter, this);
17461                 on_el.on(eventOut, this.leave, this);
17462             }
17463         }, this);
17464         
17465     },
17466     
17467     
17468     // private
17469     timeout : null,
17470     hoverState : null,
17471     
17472     toggle : function () {
17473         this.hoverState == 'in' ? this.leave() : this.enter();
17474     },
17475     
17476     enter : function () {
17477         
17478         clearTimeout(this.timeout);
17479     
17480         this.hoverState = 'in';
17481     
17482         if (!this.delay || !this.delay.show) {
17483             this.show();
17484             return;
17485         }
17486         var _t = this;
17487         this.timeout = setTimeout(function () {
17488             if (_t.hoverState == 'in') {
17489                 _t.show();
17490             }
17491         }, this.delay.show)
17492     },
17493     
17494     leave : function() {
17495         clearTimeout(this.timeout);
17496     
17497         this.hoverState = 'out';
17498     
17499         if (!this.delay || !this.delay.hide) {
17500             this.hide();
17501             return;
17502         }
17503         var _t = this;
17504         this.timeout = setTimeout(function () {
17505             if (_t.hoverState == 'out') {
17506                 _t.hide();
17507             }
17508         }, this.delay.hide)
17509     },
17510     
17511     show : function (on_el)
17512     {
17513         if (!on_el) {
17514             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17515         }
17516         
17517         // set content.
17518         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17519         if (this.html !== false) {
17520             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17521         }
17522         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17523         if (!this.title.length) {
17524             this.el.select('.popover-title',true).hide();
17525         }
17526         
17527         var placement = typeof this.placement == 'function' ?
17528             this.placement.call(this, this.el, on_el) :
17529             this.placement;
17530             
17531         var autoToken = /\s?auto?\s?/i;
17532         var autoPlace = autoToken.test(placement);
17533         if (autoPlace) {
17534             placement = placement.replace(autoToken, '') || 'top';
17535         }
17536         
17537         //this.el.detach()
17538         //this.el.setXY([0,0]);
17539         this.el.show();
17540         this.el.dom.style.display='block';
17541         this.el.addClass(placement);
17542         
17543         //this.el.appendTo(on_el);
17544         
17545         var p = this.getPosition();
17546         var box = this.el.getBox();
17547         
17548         if (autoPlace) {
17549             // fixme..
17550         }
17551         var align = Roo.bootstrap.Popover.alignment[placement];
17552         
17553 //        Roo.log(align);
17554         this.el.alignTo(on_el, align[0],align[1]);
17555         //var arrow = this.el.select('.arrow',true).first();
17556         //arrow.set(align[2], 
17557         
17558         this.el.addClass('in');
17559         
17560         
17561         if (this.el.hasClass('fade')) {
17562             // fade it?
17563         }
17564         
17565         this.hoverState = 'in';
17566         
17567         this.fireEvent('show', this);
17568         
17569     },
17570     hide : function()
17571     {
17572         this.el.setXY([0,0]);
17573         this.el.removeClass('in');
17574         this.el.hide();
17575         this.hoverState = null;
17576         
17577         this.fireEvent('hide', this);
17578     }
17579     
17580 });
17581
17582 Roo.bootstrap.Popover.alignment = {
17583     'left' : ['r-l', [-10,0], 'right'],
17584     'right' : ['l-r', [10,0], 'left'],
17585     'bottom' : ['t-b', [0,10], 'top'],
17586     'top' : [ 'b-t', [0,-10], 'bottom']
17587 };
17588
17589  /*
17590  * - LGPL
17591  *
17592  * Progress
17593  * 
17594  */
17595
17596 /**
17597  * @class Roo.bootstrap.Progress
17598  * @extends Roo.bootstrap.Component
17599  * Bootstrap Progress class
17600  * @cfg {Boolean} striped striped of the progress bar
17601  * @cfg {Boolean} active animated of the progress bar
17602  * 
17603  * 
17604  * @constructor
17605  * Create a new Progress
17606  * @param {Object} config The config object
17607  */
17608
17609 Roo.bootstrap.Progress = function(config){
17610     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17611 };
17612
17613 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17614     
17615     striped : false,
17616     active: false,
17617     
17618     getAutoCreate : function(){
17619         var cfg = {
17620             tag: 'div',
17621             cls: 'progress'
17622         };
17623         
17624         
17625         if(this.striped){
17626             cfg.cls += ' progress-striped';
17627         }
17628       
17629         if(this.active){
17630             cfg.cls += ' active';
17631         }
17632         
17633         
17634         return cfg;
17635     }
17636    
17637 });
17638
17639  
17640
17641  /*
17642  * - LGPL
17643  *
17644  * ProgressBar
17645  * 
17646  */
17647
17648 /**
17649  * @class Roo.bootstrap.ProgressBar
17650  * @extends Roo.bootstrap.Component
17651  * Bootstrap ProgressBar class
17652  * @cfg {Number} aria_valuenow aria-value now
17653  * @cfg {Number} aria_valuemin aria-value min
17654  * @cfg {Number} aria_valuemax aria-value max
17655  * @cfg {String} label label for the progress bar
17656  * @cfg {String} panel (success | info | warning | danger )
17657  * @cfg {String} role role of the progress bar
17658  * @cfg {String} sr_only text
17659  * 
17660  * 
17661  * @constructor
17662  * Create a new ProgressBar
17663  * @param {Object} config The config object
17664  */
17665
17666 Roo.bootstrap.ProgressBar = function(config){
17667     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17668 };
17669
17670 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17671     
17672     aria_valuenow : 0,
17673     aria_valuemin : 0,
17674     aria_valuemax : 100,
17675     label : false,
17676     panel : false,
17677     role : false,
17678     sr_only: false,
17679     
17680     getAutoCreate : function()
17681     {
17682         
17683         var cfg = {
17684             tag: 'div',
17685             cls: 'progress-bar',
17686             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17687         };
17688         
17689         if(this.sr_only){
17690             cfg.cn = {
17691                 tag: 'span',
17692                 cls: 'sr-only',
17693                 html: this.sr_only
17694             }
17695         }
17696         
17697         if(this.role){
17698             cfg.role = this.role;
17699         }
17700         
17701         if(this.aria_valuenow){
17702             cfg['aria-valuenow'] = this.aria_valuenow;
17703         }
17704         
17705         if(this.aria_valuemin){
17706             cfg['aria-valuemin'] = this.aria_valuemin;
17707         }
17708         
17709         if(this.aria_valuemax){
17710             cfg['aria-valuemax'] = this.aria_valuemax;
17711         }
17712         
17713         if(this.label && !this.sr_only){
17714             cfg.html = this.label;
17715         }
17716         
17717         if(this.panel){
17718             cfg.cls += ' progress-bar-' + this.panel;
17719         }
17720         
17721         return cfg;
17722     },
17723     
17724     update : function(aria_valuenow)
17725     {
17726         this.aria_valuenow = aria_valuenow;
17727         
17728         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17729     }
17730    
17731 });
17732
17733  
17734
17735  /*
17736  * - LGPL
17737  *
17738  * column
17739  * 
17740  */
17741
17742 /**
17743  * @class Roo.bootstrap.TabGroup
17744  * @extends Roo.bootstrap.Column
17745  * Bootstrap Column class
17746  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17747  * @cfg {Boolean} carousel true to make the group behave like a carousel
17748  * @cfg {Boolean} bullets show bullets for the panels
17749  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17750  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17751  * @cfg {Boolean} showarrow (true|false) show arrow default true
17752  * 
17753  * @constructor
17754  * Create a new TabGroup
17755  * @param {Object} config The config object
17756  */
17757
17758 Roo.bootstrap.TabGroup = function(config){
17759     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17760     if (!this.navId) {
17761         this.navId = Roo.id();
17762     }
17763     this.tabs = [];
17764     Roo.bootstrap.TabGroup.register(this);
17765     
17766 };
17767
17768 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17769     
17770     carousel : false,
17771     transition : false,
17772     bullets : 0,
17773     timer : 0,
17774     autoslide : false,
17775     slideFn : false,
17776     slideOnTouch : false,
17777     showarrow : true,
17778     
17779     getAutoCreate : function()
17780     {
17781         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17782         
17783         cfg.cls += ' tab-content';
17784         
17785         if (this.carousel) {
17786             cfg.cls += ' carousel slide';
17787             
17788             cfg.cn = [{
17789                cls : 'carousel-inner',
17790                cn : []
17791             }];
17792         
17793             if(this.bullets  && !Roo.isTouch){
17794                 
17795                 var bullets = {
17796                     cls : 'carousel-bullets',
17797                     cn : []
17798                 };
17799                
17800                 if(this.bullets_cls){
17801                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17802                 }
17803                 
17804                 bullets.cn.push({
17805                     cls : 'clear'
17806                 });
17807                 
17808                 cfg.cn[0].cn.push(bullets);
17809             }
17810             
17811             if(this.showarrow){
17812                 cfg.cn[0].cn.push({
17813                     tag : 'div',
17814                     class : 'carousel-arrow',
17815                     cn : [
17816                         {
17817                             tag : 'div',
17818                             class : 'carousel-prev',
17819                             cn : [
17820                                 {
17821                                     tag : 'i',
17822                                     class : 'fa fa-chevron-left'
17823                                 }
17824                             ]
17825                         },
17826                         {
17827                             tag : 'div',
17828                             class : 'carousel-next',
17829                             cn : [
17830                                 {
17831                                     tag : 'i',
17832                                     class : 'fa fa-chevron-right'
17833                                 }
17834                             ]
17835                         }
17836                     ]
17837                 });
17838             }
17839             
17840         }
17841         
17842         return cfg;
17843     },
17844     
17845     initEvents:  function()
17846     {
17847 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17848 //            this.el.on("touchstart", this.onTouchStart, this);
17849 //        }
17850         
17851         if(this.autoslide){
17852             var _this = this;
17853             
17854             this.slideFn = window.setInterval(function() {
17855                 _this.showPanelNext();
17856             }, this.timer);
17857         }
17858         
17859         if(this.showarrow){
17860             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17861             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17862         }
17863         
17864         
17865     },
17866     
17867 //    onTouchStart : function(e, el, o)
17868 //    {
17869 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17870 //            return;
17871 //        }
17872 //        
17873 //        this.showPanelNext();
17874 //    },
17875     
17876     
17877     getChildContainer : function()
17878     {
17879         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17880     },
17881     
17882     /**
17883     * register a Navigation item
17884     * @param {Roo.bootstrap.NavItem} the navitem to add
17885     */
17886     register : function(item)
17887     {
17888         this.tabs.push( item);
17889         item.navId = this.navId; // not really needed..
17890         this.addBullet();
17891     
17892     },
17893     
17894     getActivePanel : function()
17895     {
17896         var r = false;
17897         Roo.each(this.tabs, function(t) {
17898             if (t.active) {
17899                 r = t;
17900                 return false;
17901             }
17902             return null;
17903         });
17904         return r;
17905         
17906     },
17907     getPanelByName : function(n)
17908     {
17909         var r = false;
17910         Roo.each(this.tabs, function(t) {
17911             if (t.tabId == n) {
17912                 r = t;
17913                 return false;
17914             }
17915             return null;
17916         });
17917         return r;
17918     },
17919     indexOfPanel : function(p)
17920     {
17921         var r = false;
17922         Roo.each(this.tabs, function(t,i) {
17923             if (t.tabId == p.tabId) {
17924                 r = i;
17925                 return false;
17926             }
17927             return null;
17928         });
17929         return r;
17930     },
17931     /**
17932      * show a specific panel
17933      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17934      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17935      */
17936     showPanel : function (pan)
17937     {
17938         if(this.transition || typeof(pan) == 'undefined'){
17939             Roo.log("waiting for the transitionend");
17940             return;
17941         }
17942         
17943         if (typeof(pan) == 'number') {
17944             pan = this.tabs[pan];
17945         }
17946         
17947         if (typeof(pan) == 'string') {
17948             pan = this.getPanelByName(pan);
17949         }
17950         
17951         var cur = this.getActivePanel();
17952         
17953         if(!pan || !cur){
17954             Roo.log('pan or acitve pan is undefined');
17955             return false;
17956         }
17957         
17958         if (pan.tabId == this.getActivePanel().tabId) {
17959             return true;
17960         }
17961         
17962         if (false === cur.fireEvent('beforedeactivate')) {
17963             return false;
17964         }
17965         
17966         if(this.bullets > 0 && !Roo.isTouch){
17967             this.setActiveBullet(this.indexOfPanel(pan));
17968         }
17969         
17970         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17971             
17972             this.transition = true;
17973             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17974             var lr = dir == 'next' ? 'left' : 'right';
17975             pan.el.addClass(dir); // or prev
17976             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17977             cur.el.addClass(lr); // or right
17978             pan.el.addClass(lr);
17979             
17980             var _this = this;
17981             cur.el.on('transitionend', function() {
17982                 Roo.log("trans end?");
17983                 
17984                 pan.el.removeClass([lr,dir]);
17985                 pan.setActive(true);
17986                 
17987                 cur.el.removeClass([lr]);
17988                 cur.setActive(false);
17989                 
17990                 _this.transition = false;
17991                 
17992             }, this, { single:  true } );
17993             
17994             return true;
17995         }
17996         
17997         cur.setActive(false);
17998         pan.setActive(true);
17999         
18000         return true;
18001         
18002     },
18003     showPanelNext : function()
18004     {
18005         var i = this.indexOfPanel(this.getActivePanel());
18006         
18007         if (i >= this.tabs.length - 1 && !this.autoslide) {
18008             return;
18009         }
18010         
18011         if (i >= this.tabs.length - 1 && this.autoslide) {
18012             i = -1;
18013         }
18014         
18015         this.showPanel(this.tabs[i+1]);
18016     },
18017     
18018     showPanelPrev : function()
18019     {
18020         var i = this.indexOfPanel(this.getActivePanel());
18021         
18022         if (i  < 1 && !this.autoslide) {
18023             return;
18024         }
18025         
18026         if (i < 1 && this.autoslide) {
18027             i = this.tabs.length;
18028         }
18029         
18030         this.showPanel(this.tabs[i-1]);
18031     },
18032     
18033     
18034     addBullet: function()
18035     {
18036         if(!this.bullets || Roo.isTouch){
18037             return;
18038         }
18039         var ctr = this.el.select('.carousel-bullets',true).first();
18040         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18041         var bullet = ctr.createChild({
18042             cls : 'bullet bullet-' + i
18043         },ctr.dom.lastChild);
18044         
18045         
18046         var _this = this;
18047         
18048         bullet.on('click', (function(e, el, o, ii, t){
18049
18050             e.preventDefault();
18051
18052             this.showPanel(ii);
18053
18054             if(this.autoslide && this.slideFn){
18055                 clearInterval(this.slideFn);
18056                 this.slideFn = window.setInterval(function() {
18057                     _this.showPanelNext();
18058                 }, this.timer);
18059             }
18060
18061         }).createDelegate(this, [i, bullet], true));
18062                 
18063         
18064     },
18065      
18066     setActiveBullet : function(i)
18067     {
18068         if(Roo.isTouch){
18069             return;
18070         }
18071         
18072         Roo.each(this.el.select('.bullet', true).elements, function(el){
18073             el.removeClass('selected');
18074         });
18075
18076         var bullet = this.el.select('.bullet-' + i, true).first();
18077         
18078         if(!bullet){
18079             return;
18080         }
18081         
18082         bullet.addClass('selected');
18083     }
18084     
18085     
18086   
18087 });
18088
18089  
18090
18091  
18092  
18093 Roo.apply(Roo.bootstrap.TabGroup, {
18094     
18095     groups: {},
18096      /**
18097     * register a Navigation Group
18098     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18099     */
18100     register : function(navgrp)
18101     {
18102         this.groups[navgrp.navId] = navgrp;
18103         
18104     },
18105     /**
18106     * fetch a Navigation Group based on the navigation ID
18107     * if one does not exist , it will get created.
18108     * @param {string} the navgroup to add
18109     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18110     */
18111     get: function(navId) {
18112         if (typeof(this.groups[navId]) == 'undefined') {
18113             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18114         }
18115         return this.groups[navId] ;
18116     }
18117     
18118     
18119     
18120 });
18121
18122  /*
18123  * - LGPL
18124  *
18125  * TabPanel
18126  * 
18127  */
18128
18129 /**
18130  * @class Roo.bootstrap.TabPanel
18131  * @extends Roo.bootstrap.Component
18132  * Bootstrap TabPanel class
18133  * @cfg {Boolean} active panel active
18134  * @cfg {String} html panel content
18135  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18136  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18137  * @cfg {String} href click to link..
18138  * 
18139  * 
18140  * @constructor
18141  * Create a new TabPanel
18142  * @param {Object} config The config object
18143  */
18144
18145 Roo.bootstrap.TabPanel = function(config){
18146     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18147     this.addEvents({
18148         /**
18149              * @event changed
18150              * Fires when the active status changes
18151              * @param {Roo.bootstrap.TabPanel} this
18152              * @param {Boolean} state the new state
18153             
18154          */
18155         'changed': true,
18156         /**
18157              * @event beforedeactivate
18158              * Fires before a tab is de-activated - can be used to do validation on a form.
18159              * @param {Roo.bootstrap.TabPanel} this
18160              * @return {Boolean} false if there is an error
18161             
18162          */
18163         'beforedeactivate': true
18164      });
18165     
18166     this.tabId = this.tabId || Roo.id();
18167   
18168 };
18169
18170 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18171     
18172     active: false,
18173     html: false,
18174     tabId: false,
18175     navId : false,
18176     href : '',
18177     
18178     getAutoCreate : function(){
18179         var cfg = {
18180             tag: 'div',
18181             // item is needed for carousel - not sure if it has any effect otherwise
18182             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18183             html: this.html || ''
18184         };
18185         
18186         if(this.active){
18187             cfg.cls += ' active';
18188         }
18189         
18190         if(this.tabId){
18191             cfg.tabId = this.tabId;
18192         }
18193         
18194         
18195         return cfg;
18196     },
18197     
18198     initEvents:  function()
18199     {
18200         var p = this.parent();
18201         
18202         this.navId = this.navId || p.navId;
18203         
18204         if (typeof(this.navId) != 'undefined') {
18205             // not really needed.. but just in case.. parent should be a NavGroup.
18206             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18207             
18208             tg.register(this);
18209             
18210             var i = tg.tabs.length - 1;
18211             
18212             if(this.active && tg.bullets > 0 && i < tg.bullets){
18213                 tg.setActiveBullet(i);
18214             }
18215         }
18216         
18217         this.el.on('click', this.onClick, this);
18218         
18219         if(Roo.isTouch){
18220             this.el.on("touchstart", this.onTouchStart, this);
18221             this.el.on("touchmove", this.onTouchMove, this);
18222             this.el.on("touchend", this.onTouchEnd, this);
18223         }
18224         
18225     },
18226     
18227     onRender : function(ct, position)
18228     {
18229         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18230     },
18231     
18232     setActive : function(state)
18233     {
18234         Roo.log("panel - set active " + this.tabId + "=" + state);
18235         
18236         this.active = state;
18237         if (!state) {
18238             this.el.removeClass('active');
18239             
18240         } else  if (!this.el.hasClass('active')) {
18241             this.el.addClass('active');
18242         }
18243         
18244         this.fireEvent('changed', this, state);
18245     },
18246     
18247     onClick : function(e)
18248     {
18249         e.preventDefault();
18250         
18251         if(!this.href.length){
18252             return;
18253         }
18254         
18255         window.location.href = this.href;
18256     },
18257     
18258     startX : 0,
18259     startY : 0,
18260     endX : 0,
18261     endY : 0,
18262     swiping : false,
18263     
18264     onTouchStart : function(e)
18265     {
18266         this.swiping = false;
18267         
18268         this.startX = e.browserEvent.touches[0].clientX;
18269         this.startY = e.browserEvent.touches[0].clientY;
18270     },
18271     
18272     onTouchMove : function(e)
18273     {
18274         this.swiping = true;
18275         
18276         this.endX = e.browserEvent.touches[0].clientX;
18277         this.endY = e.browserEvent.touches[0].clientY;
18278     },
18279     
18280     onTouchEnd : function(e)
18281     {
18282         if(!this.swiping){
18283             this.onClick(e);
18284             return;
18285         }
18286         
18287         var tabGroup = this.parent();
18288         
18289         if(this.endX > this.startX){ // swiping right
18290             tabGroup.showPanelPrev();
18291             return;
18292         }
18293         
18294         if(this.startX > this.endX){ // swiping left
18295             tabGroup.showPanelNext();
18296             return;
18297         }
18298     }
18299     
18300     
18301 });
18302  
18303
18304  
18305
18306  /*
18307  * - LGPL
18308  *
18309  * DateField
18310  * 
18311  */
18312
18313 /**
18314  * @class Roo.bootstrap.DateField
18315  * @extends Roo.bootstrap.Input
18316  * Bootstrap DateField class
18317  * @cfg {Number} weekStart default 0
18318  * @cfg {String} viewMode default empty, (months|years)
18319  * @cfg {String} minViewMode default empty, (months|years)
18320  * @cfg {Number} startDate default -Infinity
18321  * @cfg {Number} endDate default Infinity
18322  * @cfg {Boolean} todayHighlight default false
18323  * @cfg {Boolean} todayBtn default false
18324  * @cfg {Boolean} calendarWeeks default false
18325  * @cfg {Object} daysOfWeekDisabled default empty
18326  * @cfg {Boolean} singleMode default false (true | false)
18327  * 
18328  * @cfg {Boolean} keyboardNavigation default true
18329  * @cfg {String} language default en
18330  * 
18331  * @constructor
18332  * Create a new DateField
18333  * @param {Object} config The config object
18334  */
18335
18336 Roo.bootstrap.DateField = function(config){
18337     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18338      this.addEvents({
18339             /**
18340              * @event show
18341              * Fires when this field show.
18342              * @param {Roo.bootstrap.DateField} this
18343              * @param {Mixed} date The date value
18344              */
18345             show : true,
18346             /**
18347              * @event show
18348              * Fires when this field hide.
18349              * @param {Roo.bootstrap.DateField} this
18350              * @param {Mixed} date The date value
18351              */
18352             hide : true,
18353             /**
18354              * @event select
18355              * Fires when select a date.
18356              * @param {Roo.bootstrap.DateField} this
18357              * @param {Mixed} date The date value
18358              */
18359             select : true,
18360             /**
18361              * @event beforeselect
18362              * Fires when before select a date.
18363              * @param {Roo.bootstrap.DateField} this
18364              * @param {Mixed} date The date value
18365              */
18366             beforeselect : true
18367         });
18368 };
18369
18370 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18371     
18372     /**
18373      * @cfg {String} format
18374      * The default date format string which can be overriden for localization support.  The format must be
18375      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18376      */
18377     format : "m/d/y",
18378     /**
18379      * @cfg {String} altFormats
18380      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18381      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18382      */
18383     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18384     
18385     weekStart : 0,
18386     
18387     viewMode : '',
18388     
18389     minViewMode : '',
18390     
18391     todayHighlight : false,
18392     
18393     todayBtn: false,
18394     
18395     language: 'en',
18396     
18397     keyboardNavigation: true,
18398     
18399     calendarWeeks: false,
18400     
18401     startDate: -Infinity,
18402     
18403     endDate: Infinity,
18404     
18405     daysOfWeekDisabled: [],
18406     
18407     _events: [],
18408     
18409     singleMode : false,
18410     
18411     UTCDate: function()
18412     {
18413         return new Date(Date.UTC.apply(Date, arguments));
18414     },
18415     
18416     UTCToday: function()
18417     {
18418         var today = new Date();
18419         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18420     },
18421     
18422     getDate: function() {
18423             var d = this.getUTCDate();
18424             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18425     },
18426     
18427     getUTCDate: function() {
18428             return this.date;
18429     },
18430     
18431     setDate: function(d) {
18432             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18433     },
18434     
18435     setUTCDate: function(d) {
18436             this.date = d;
18437             this.setValue(this.formatDate(this.date));
18438     },
18439         
18440     onRender: function(ct, position)
18441     {
18442         
18443         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18444         
18445         this.language = this.language || 'en';
18446         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18447         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18448         
18449         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18450         this.format = this.format || 'm/d/y';
18451         this.isInline = false;
18452         this.isInput = true;
18453         this.component = this.el.select('.add-on', true).first() || false;
18454         this.component = (this.component && this.component.length === 0) ? false : this.component;
18455         this.hasInput = this.component && this.inputEl().length;
18456         
18457         if (typeof(this.minViewMode === 'string')) {
18458             switch (this.minViewMode) {
18459                 case 'months':
18460                     this.minViewMode = 1;
18461                     break;
18462                 case 'years':
18463                     this.minViewMode = 2;
18464                     break;
18465                 default:
18466                     this.minViewMode = 0;
18467                     break;
18468             }
18469         }
18470         
18471         if (typeof(this.viewMode === 'string')) {
18472             switch (this.viewMode) {
18473                 case 'months':
18474                     this.viewMode = 1;
18475                     break;
18476                 case 'years':
18477                     this.viewMode = 2;
18478                     break;
18479                 default:
18480                     this.viewMode = 0;
18481                     break;
18482             }
18483         }
18484                 
18485         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18486         
18487 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18488         
18489         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18490         
18491         this.picker().on('mousedown', this.onMousedown, this);
18492         this.picker().on('click', this.onClick, this);
18493         
18494         this.picker().addClass('datepicker-dropdown');
18495         
18496         this.startViewMode = this.viewMode;
18497         
18498         if(this.singleMode){
18499             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18500                 v.setVisibilityMode(Roo.Element.DISPLAY);
18501                 v.hide();
18502             });
18503             
18504             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18505                 v.setStyle('width', '189px');
18506             });
18507         }
18508         
18509         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18510             if(!this.calendarWeeks){
18511                 v.remove();
18512                 return;
18513             }
18514             
18515             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18516             v.attr('colspan', function(i, val){
18517                 return parseInt(val) + 1;
18518             });
18519         });
18520                         
18521         
18522         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18523         
18524         this.setStartDate(this.startDate);
18525         this.setEndDate(this.endDate);
18526         
18527         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18528         
18529         this.fillDow();
18530         this.fillMonths();
18531         this.update();
18532         this.showMode();
18533         
18534         if(this.isInline) {
18535             this.show();
18536         }
18537     },
18538     
18539     picker : function()
18540     {
18541         return this.pickerEl;
18542 //        return this.el.select('.datepicker', true).first();
18543     },
18544     
18545     fillDow: function()
18546     {
18547         var dowCnt = this.weekStart;
18548         
18549         var dow = {
18550             tag: 'tr',
18551             cn: [
18552                 
18553             ]
18554         };
18555         
18556         if(this.calendarWeeks){
18557             dow.cn.push({
18558                 tag: 'th',
18559                 cls: 'cw',
18560                 html: '&nbsp;'
18561             })
18562         }
18563         
18564         while (dowCnt < this.weekStart + 7) {
18565             dow.cn.push({
18566                 tag: 'th',
18567                 cls: 'dow',
18568                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18569             });
18570         }
18571         
18572         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18573     },
18574     
18575     fillMonths: function()
18576     {    
18577         var i = 0;
18578         var months = this.picker().select('>.datepicker-months td', true).first();
18579         
18580         months.dom.innerHTML = '';
18581         
18582         while (i < 12) {
18583             var month = {
18584                 tag: 'span',
18585                 cls: 'month',
18586                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18587             };
18588             
18589             months.createChild(month);
18590         }
18591         
18592     },
18593     
18594     update: function()
18595     {
18596         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;
18597         
18598         if (this.date < this.startDate) {
18599             this.viewDate = new Date(this.startDate);
18600         } else if (this.date > this.endDate) {
18601             this.viewDate = new Date(this.endDate);
18602         } else {
18603             this.viewDate = new Date(this.date);
18604         }
18605         
18606         this.fill();
18607     },
18608     
18609     fill: function() 
18610     {
18611         var d = new Date(this.viewDate),
18612                 year = d.getUTCFullYear(),
18613                 month = d.getUTCMonth(),
18614                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18615                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18616                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18617                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18618                 currentDate = this.date && this.date.valueOf(),
18619                 today = this.UTCToday();
18620         
18621         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18622         
18623 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18624         
18625 //        this.picker.select('>tfoot th.today').
18626 //                                              .text(dates[this.language].today)
18627 //                                              .toggle(this.todayBtn !== false);
18628     
18629         this.updateNavArrows();
18630         this.fillMonths();
18631                                                 
18632         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18633         
18634         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18635          
18636         prevMonth.setUTCDate(day);
18637         
18638         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18639         
18640         var nextMonth = new Date(prevMonth);
18641         
18642         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18643         
18644         nextMonth = nextMonth.valueOf();
18645         
18646         var fillMonths = false;
18647         
18648         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18649         
18650         while(prevMonth.valueOf() < nextMonth) {
18651             var clsName = '';
18652             
18653             if (prevMonth.getUTCDay() === this.weekStart) {
18654                 if(fillMonths){
18655                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18656                 }
18657                     
18658                 fillMonths = {
18659                     tag: 'tr',
18660                     cn: []
18661                 };
18662                 
18663                 if(this.calendarWeeks){
18664                     // ISO 8601: First week contains first thursday.
18665                     // ISO also states week starts on Monday, but we can be more abstract here.
18666                     var
18667                     // Start of current week: based on weekstart/current date
18668                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18669                     // Thursday of this week
18670                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18671                     // First Thursday of year, year from thursday
18672                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18673                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18674                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18675                     
18676                     fillMonths.cn.push({
18677                         tag: 'td',
18678                         cls: 'cw',
18679                         html: calWeek
18680                     });
18681                 }
18682             }
18683             
18684             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18685                 clsName += ' old';
18686             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18687                 clsName += ' new';
18688             }
18689             if (this.todayHighlight &&
18690                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18691                 prevMonth.getUTCMonth() == today.getMonth() &&
18692                 prevMonth.getUTCDate() == today.getDate()) {
18693                 clsName += ' today';
18694             }
18695             
18696             if (currentDate && prevMonth.valueOf() === currentDate) {
18697                 clsName += ' active';
18698             }
18699             
18700             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18701                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18702                     clsName += ' disabled';
18703             }
18704             
18705             fillMonths.cn.push({
18706                 tag: 'td',
18707                 cls: 'day ' + clsName,
18708                 html: prevMonth.getDate()
18709             });
18710             
18711             prevMonth.setDate(prevMonth.getDate()+1);
18712         }
18713           
18714         var currentYear = this.date && this.date.getUTCFullYear();
18715         var currentMonth = this.date && this.date.getUTCMonth();
18716         
18717         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18718         
18719         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18720             v.removeClass('active');
18721             
18722             if(currentYear === year && k === currentMonth){
18723                 v.addClass('active');
18724             }
18725             
18726             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18727                 v.addClass('disabled');
18728             }
18729             
18730         });
18731         
18732         
18733         year = parseInt(year/10, 10) * 10;
18734         
18735         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18736         
18737         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18738         
18739         year -= 1;
18740         for (var i = -1; i < 11; i++) {
18741             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18742                 tag: 'span',
18743                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18744                 html: year
18745             });
18746             
18747             year += 1;
18748         }
18749     },
18750     
18751     showMode: function(dir) 
18752     {
18753         if (dir) {
18754             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18755         }
18756         
18757         Roo.each(this.picker().select('>div',true).elements, function(v){
18758             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18759             v.hide();
18760         });
18761         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18762     },
18763     
18764     place: function()
18765     {
18766         if(this.isInline) {
18767             return;
18768         }
18769         
18770         this.picker().removeClass(['bottom', 'top']);
18771         
18772         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18773             /*
18774              * place to the top of element!
18775              *
18776              */
18777             
18778             this.picker().addClass('top');
18779             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18780             
18781             return;
18782         }
18783         
18784         this.picker().addClass('bottom');
18785         
18786         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18787     },
18788     
18789     parseDate : function(value)
18790     {
18791         if(!value || value instanceof Date){
18792             return value;
18793         }
18794         var v = Date.parseDate(value, this.format);
18795         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18796             v = Date.parseDate(value, 'Y-m-d');
18797         }
18798         if(!v && this.altFormats){
18799             if(!this.altFormatsArray){
18800                 this.altFormatsArray = this.altFormats.split("|");
18801             }
18802             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18803                 v = Date.parseDate(value, this.altFormatsArray[i]);
18804             }
18805         }
18806         return v;
18807     },
18808     
18809     formatDate : function(date, fmt)
18810     {   
18811         return (!date || !(date instanceof Date)) ?
18812         date : date.dateFormat(fmt || this.format);
18813     },
18814     
18815     onFocus : function()
18816     {
18817         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18818         this.show();
18819     },
18820     
18821     onBlur : function()
18822     {
18823         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18824         
18825         var d = this.inputEl().getValue();
18826         
18827         this.setValue(d);
18828                 
18829         this.hide();
18830     },
18831     
18832     show : function()
18833     {
18834         this.picker().show();
18835         this.update();
18836         this.place();
18837         
18838         this.fireEvent('show', this, this.date);
18839     },
18840     
18841     hide : function()
18842     {
18843         if(this.isInline) {
18844             return;
18845         }
18846         this.picker().hide();
18847         this.viewMode = this.startViewMode;
18848         this.showMode();
18849         
18850         this.fireEvent('hide', this, this.date);
18851         
18852     },
18853     
18854     onMousedown: function(e)
18855     {
18856         e.stopPropagation();
18857         e.preventDefault();
18858     },
18859     
18860     keyup: function(e)
18861     {
18862         Roo.bootstrap.DateField.superclass.keyup.call(this);
18863         this.update();
18864     },
18865
18866     setValue: function(v)
18867     {
18868         if(this.fireEvent('beforeselect', this, v) !== false){
18869             var d = new Date(this.parseDate(v) ).clearTime();
18870         
18871             if(isNaN(d.getTime())){
18872                 this.date = this.viewDate = '';
18873                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18874                 return;
18875             }
18876
18877             v = this.formatDate(d);
18878
18879             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18880
18881             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18882
18883             this.update();
18884
18885             this.fireEvent('select', this, this.date);
18886         }
18887     },
18888     
18889     getValue: function()
18890     {
18891         return this.formatDate(this.date);
18892     },
18893     
18894     fireKey: function(e)
18895     {
18896         if (!this.picker().isVisible()){
18897             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18898                 this.show();
18899             }
18900             return;
18901         }
18902         
18903         var dateChanged = false,
18904         dir, day, month,
18905         newDate, newViewDate;
18906         
18907         switch(e.keyCode){
18908             case 27: // escape
18909                 this.hide();
18910                 e.preventDefault();
18911                 break;
18912             case 37: // left
18913             case 39: // right
18914                 if (!this.keyboardNavigation) {
18915                     break;
18916                 }
18917                 dir = e.keyCode == 37 ? -1 : 1;
18918                 
18919                 if (e.ctrlKey){
18920                     newDate = this.moveYear(this.date, dir);
18921                     newViewDate = this.moveYear(this.viewDate, dir);
18922                 } else if (e.shiftKey){
18923                     newDate = this.moveMonth(this.date, dir);
18924                     newViewDate = this.moveMonth(this.viewDate, dir);
18925                 } else {
18926                     newDate = new Date(this.date);
18927                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18928                     newViewDate = new Date(this.viewDate);
18929                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18930                 }
18931                 if (this.dateWithinRange(newDate)){
18932                     this.date = newDate;
18933                     this.viewDate = newViewDate;
18934                     this.setValue(this.formatDate(this.date));
18935 //                    this.update();
18936                     e.preventDefault();
18937                     dateChanged = true;
18938                 }
18939                 break;
18940             case 38: // up
18941             case 40: // down
18942                 if (!this.keyboardNavigation) {
18943                     break;
18944                 }
18945                 dir = e.keyCode == 38 ? -1 : 1;
18946                 if (e.ctrlKey){
18947                     newDate = this.moveYear(this.date, dir);
18948                     newViewDate = this.moveYear(this.viewDate, dir);
18949                 } else if (e.shiftKey){
18950                     newDate = this.moveMonth(this.date, dir);
18951                     newViewDate = this.moveMonth(this.viewDate, dir);
18952                 } else {
18953                     newDate = new Date(this.date);
18954                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18955                     newViewDate = new Date(this.viewDate);
18956                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18957                 }
18958                 if (this.dateWithinRange(newDate)){
18959                     this.date = newDate;
18960                     this.viewDate = newViewDate;
18961                     this.setValue(this.formatDate(this.date));
18962 //                    this.update();
18963                     e.preventDefault();
18964                     dateChanged = true;
18965                 }
18966                 break;
18967             case 13: // enter
18968                 this.setValue(this.formatDate(this.date));
18969                 this.hide();
18970                 e.preventDefault();
18971                 break;
18972             case 9: // tab
18973                 this.setValue(this.formatDate(this.date));
18974                 this.hide();
18975                 break;
18976             case 16: // shift
18977             case 17: // ctrl
18978             case 18: // alt
18979                 break;
18980             default :
18981                 this.hide();
18982                 
18983         }
18984     },
18985     
18986     
18987     onClick: function(e) 
18988     {
18989         e.stopPropagation();
18990         e.preventDefault();
18991         
18992         var target = e.getTarget();
18993         
18994         if(target.nodeName.toLowerCase() === 'i'){
18995             target = Roo.get(target).dom.parentNode;
18996         }
18997         
18998         var nodeName = target.nodeName;
18999         var className = target.className;
19000         var html = target.innerHTML;
19001         //Roo.log(nodeName);
19002         
19003         switch(nodeName.toLowerCase()) {
19004             case 'th':
19005                 switch(className) {
19006                     case 'switch':
19007                         this.showMode(1);
19008                         break;
19009                     case 'prev':
19010                     case 'next':
19011                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19012                         switch(this.viewMode){
19013                                 case 0:
19014                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19015                                         break;
19016                                 case 1:
19017                                 case 2:
19018                                         this.viewDate = this.moveYear(this.viewDate, dir);
19019                                         break;
19020                         }
19021                         this.fill();
19022                         break;
19023                     case 'today':
19024                         var date = new Date();
19025                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19026 //                        this.fill()
19027                         this.setValue(this.formatDate(this.date));
19028                         
19029                         this.hide();
19030                         break;
19031                 }
19032                 break;
19033             case 'span':
19034                 if (className.indexOf('disabled') < 0) {
19035                     this.viewDate.setUTCDate(1);
19036                     if (className.indexOf('month') > -1) {
19037                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19038                     } else {
19039                         var year = parseInt(html, 10) || 0;
19040                         this.viewDate.setUTCFullYear(year);
19041                         
19042                     }
19043                     
19044                     if(this.singleMode){
19045                         this.setValue(this.formatDate(this.viewDate));
19046                         this.hide();
19047                         return;
19048                     }
19049                     
19050                     this.showMode(-1);
19051                     this.fill();
19052                 }
19053                 break;
19054                 
19055             case 'td':
19056                 //Roo.log(className);
19057                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19058                     var day = parseInt(html, 10) || 1;
19059                     var year = this.viewDate.getUTCFullYear(),
19060                         month = this.viewDate.getUTCMonth();
19061
19062                     if (className.indexOf('old') > -1) {
19063                         if(month === 0 ){
19064                             month = 11;
19065                             year -= 1;
19066                         }else{
19067                             month -= 1;
19068                         }
19069                     } else if (className.indexOf('new') > -1) {
19070                         if (month == 11) {
19071                             month = 0;
19072                             year += 1;
19073                         } else {
19074                             month += 1;
19075                         }
19076                     }
19077                     //Roo.log([year,month,day]);
19078                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19079                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19080 //                    this.fill();
19081                     //Roo.log(this.formatDate(this.date));
19082                     this.setValue(this.formatDate(this.date));
19083                     this.hide();
19084                 }
19085                 break;
19086         }
19087     },
19088     
19089     setStartDate: function(startDate)
19090     {
19091         this.startDate = startDate || -Infinity;
19092         if (this.startDate !== -Infinity) {
19093             this.startDate = this.parseDate(this.startDate);
19094         }
19095         this.update();
19096         this.updateNavArrows();
19097     },
19098
19099     setEndDate: function(endDate)
19100     {
19101         this.endDate = endDate || Infinity;
19102         if (this.endDate !== Infinity) {
19103             this.endDate = this.parseDate(this.endDate);
19104         }
19105         this.update();
19106         this.updateNavArrows();
19107     },
19108     
19109     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19110     {
19111         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19112         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19113             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19114         }
19115         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19116             return parseInt(d, 10);
19117         });
19118         this.update();
19119         this.updateNavArrows();
19120     },
19121     
19122     updateNavArrows: function() 
19123     {
19124         if(this.singleMode){
19125             return;
19126         }
19127         
19128         var d = new Date(this.viewDate),
19129         year = d.getUTCFullYear(),
19130         month = d.getUTCMonth();
19131         
19132         Roo.each(this.picker().select('.prev', true).elements, function(v){
19133             v.show();
19134             switch (this.viewMode) {
19135                 case 0:
19136
19137                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19138                         v.hide();
19139                     }
19140                     break;
19141                 case 1:
19142                 case 2:
19143                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19144                         v.hide();
19145                     }
19146                     break;
19147             }
19148         });
19149         
19150         Roo.each(this.picker().select('.next', true).elements, function(v){
19151             v.show();
19152             switch (this.viewMode) {
19153                 case 0:
19154
19155                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19156                         v.hide();
19157                     }
19158                     break;
19159                 case 1:
19160                 case 2:
19161                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19162                         v.hide();
19163                     }
19164                     break;
19165             }
19166         })
19167     },
19168     
19169     moveMonth: function(date, dir)
19170     {
19171         if (!dir) {
19172             return date;
19173         }
19174         var new_date = new Date(date.valueOf()),
19175         day = new_date.getUTCDate(),
19176         month = new_date.getUTCMonth(),
19177         mag = Math.abs(dir),
19178         new_month, test;
19179         dir = dir > 0 ? 1 : -1;
19180         if (mag == 1){
19181             test = dir == -1
19182             // If going back one month, make sure month is not current month
19183             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19184             ? function(){
19185                 return new_date.getUTCMonth() == month;
19186             }
19187             // If going forward one month, make sure month is as expected
19188             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19189             : function(){
19190                 return new_date.getUTCMonth() != new_month;
19191             };
19192             new_month = month + dir;
19193             new_date.setUTCMonth(new_month);
19194             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19195             if (new_month < 0 || new_month > 11) {
19196                 new_month = (new_month + 12) % 12;
19197             }
19198         } else {
19199             // For magnitudes >1, move one month at a time...
19200             for (var i=0; i<mag; i++) {
19201                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19202                 new_date = this.moveMonth(new_date, dir);
19203             }
19204             // ...then reset the day, keeping it in the new month
19205             new_month = new_date.getUTCMonth();
19206             new_date.setUTCDate(day);
19207             test = function(){
19208                 return new_month != new_date.getUTCMonth();
19209             };
19210         }
19211         // Common date-resetting loop -- if date is beyond end of month, make it
19212         // end of month
19213         while (test()){
19214             new_date.setUTCDate(--day);
19215             new_date.setUTCMonth(new_month);
19216         }
19217         return new_date;
19218     },
19219
19220     moveYear: function(date, dir)
19221     {
19222         return this.moveMonth(date, dir*12);
19223     },
19224
19225     dateWithinRange: function(date)
19226     {
19227         return date >= this.startDate && date <= this.endDate;
19228     },
19229
19230     
19231     remove: function() 
19232     {
19233         this.picker().remove();
19234     },
19235     
19236     validateValue : function(value)
19237     {
19238         if(this.getVisibilityEl().hasClass('hidden')){
19239             return true;
19240         }
19241         
19242         if(value.length < 1)  {
19243             if(this.allowBlank){
19244                 return true;
19245             }
19246             return false;
19247         }
19248         
19249         if(value.length < this.minLength){
19250             return false;
19251         }
19252         if(value.length > this.maxLength){
19253             return false;
19254         }
19255         if(this.vtype){
19256             var vt = Roo.form.VTypes;
19257             if(!vt[this.vtype](value, this)){
19258                 return false;
19259             }
19260         }
19261         if(typeof this.validator == "function"){
19262             var msg = this.validator(value);
19263             if(msg !== true){
19264                 return false;
19265             }
19266         }
19267         
19268         if(this.regex && !this.regex.test(value)){
19269             return false;
19270         }
19271         
19272         if(typeof(this.parseDate(value)) == 'undefined'){
19273             return false;
19274         }
19275         
19276         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19277             return false;
19278         }      
19279         
19280         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19281             return false;
19282         } 
19283         
19284         
19285         return true;
19286     },
19287     
19288     setVisible : function(visible)
19289     {
19290         if(!this.getEl()){
19291             return;
19292         }
19293         
19294         this.getEl().removeClass('hidden');
19295         
19296         if(visible){
19297             return;
19298         }
19299         
19300         this.getEl().addClass('hidden');
19301     }
19302    
19303 });
19304
19305 Roo.apply(Roo.bootstrap.DateField,  {
19306     
19307     head : {
19308         tag: 'thead',
19309         cn: [
19310         {
19311             tag: 'tr',
19312             cn: [
19313             {
19314                 tag: 'th',
19315                 cls: 'prev',
19316                 html: '<i class="fa fa-arrow-left"/>'
19317             },
19318             {
19319                 tag: 'th',
19320                 cls: 'switch',
19321                 colspan: '5'
19322             },
19323             {
19324                 tag: 'th',
19325                 cls: 'next',
19326                 html: '<i class="fa fa-arrow-right"/>'
19327             }
19328
19329             ]
19330         }
19331         ]
19332     },
19333     
19334     content : {
19335         tag: 'tbody',
19336         cn: [
19337         {
19338             tag: 'tr',
19339             cn: [
19340             {
19341                 tag: 'td',
19342                 colspan: '7'
19343             }
19344             ]
19345         }
19346         ]
19347     },
19348     
19349     footer : {
19350         tag: 'tfoot',
19351         cn: [
19352         {
19353             tag: 'tr',
19354             cn: [
19355             {
19356                 tag: 'th',
19357                 colspan: '7',
19358                 cls: 'today'
19359             }
19360                     
19361             ]
19362         }
19363         ]
19364     },
19365     
19366     dates:{
19367         en: {
19368             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19369             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19370             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19371             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19372             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19373             today: "Today"
19374         }
19375     },
19376     
19377     modes: [
19378     {
19379         clsName: 'days',
19380         navFnc: 'Month',
19381         navStep: 1
19382     },
19383     {
19384         clsName: 'months',
19385         navFnc: 'FullYear',
19386         navStep: 1
19387     },
19388     {
19389         clsName: 'years',
19390         navFnc: 'FullYear',
19391         navStep: 10
19392     }]
19393 });
19394
19395 Roo.apply(Roo.bootstrap.DateField,  {
19396   
19397     template : {
19398         tag: 'div',
19399         cls: 'datepicker dropdown-menu roo-dynamic',
19400         cn: [
19401         {
19402             tag: 'div',
19403             cls: 'datepicker-days',
19404             cn: [
19405             {
19406                 tag: 'table',
19407                 cls: 'table-condensed',
19408                 cn:[
19409                 Roo.bootstrap.DateField.head,
19410                 {
19411                     tag: 'tbody'
19412                 },
19413                 Roo.bootstrap.DateField.footer
19414                 ]
19415             }
19416             ]
19417         },
19418         {
19419             tag: 'div',
19420             cls: 'datepicker-months',
19421             cn: [
19422             {
19423                 tag: 'table',
19424                 cls: 'table-condensed',
19425                 cn:[
19426                 Roo.bootstrap.DateField.head,
19427                 Roo.bootstrap.DateField.content,
19428                 Roo.bootstrap.DateField.footer
19429                 ]
19430             }
19431             ]
19432         },
19433         {
19434             tag: 'div',
19435             cls: 'datepicker-years',
19436             cn: [
19437             {
19438                 tag: 'table',
19439                 cls: 'table-condensed',
19440                 cn:[
19441                 Roo.bootstrap.DateField.head,
19442                 Roo.bootstrap.DateField.content,
19443                 Roo.bootstrap.DateField.footer
19444                 ]
19445             }
19446             ]
19447         }
19448         ]
19449     }
19450 });
19451
19452  
19453
19454  /*
19455  * - LGPL
19456  *
19457  * TimeField
19458  * 
19459  */
19460
19461 /**
19462  * @class Roo.bootstrap.TimeField
19463  * @extends Roo.bootstrap.Input
19464  * Bootstrap DateField class
19465  * 
19466  * 
19467  * @constructor
19468  * Create a new TimeField
19469  * @param {Object} config The config object
19470  */
19471
19472 Roo.bootstrap.TimeField = function(config){
19473     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19474     this.addEvents({
19475             /**
19476              * @event show
19477              * Fires when this field show.
19478              * @param {Roo.bootstrap.DateField} thisthis
19479              * @param {Mixed} date The date value
19480              */
19481             show : true,
19482             /**
19483              * @event show
19484              * Fires when this field hide.
19485              * @param {Roo.bootstrap.DateField} this
19486              * @param {Mixed} date The date value
19487              */
19488             hide : true,
19489             /**
19490              * @event select
19491              * Fires when select a date.
19492              * @param {Roo.bootstrap.DateField} this
19493              * @param {Mixed} date The date value
19494              */
19495             select : true
19496         });
19497 };
19498
19499 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19500     
19501     /**
19502      * @cfg {String} format
19503      * The default time format string which can be overriden for localization support.  The format must be
19504      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19505      */
19506     format : "H:i",
19507        
19508     onRender: function(ct, position)
19509     {
19510         
19511         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19512                 
19513         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19514         
19515         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19516         
19517         this.pop = this.picker().select('>.datepicker-time',true).first();
19518         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19519         
19520         this.picker().on('mousedown', this.onMousedown, this);
19521         this.picker().on('click', this.onClick, this);
19522         
19523         this.picker().addClass('datepicker-dropdown');
19524     
19525         this.fillTime();
19526         this.update();
19527             
19528         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19529         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19530         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19531         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19532         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19533         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19534
19535     },
19536     
19537     fireKey: function(e){
19538         if (!this.picker().isVisible()){
19539             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19540                 this.show();
19541             }
19542             return;
19543         }
19544
19545         e.preventDefault();
19546         
19547         switch(e.keyCode){
19548             case 27: // escape
19549                 this.hide();
19550                 break;
19551             case 37: // left
19552             case 39: // right
19553                 this.onTogglePeriod();
19554                 break;
19555             case 38: // up
19556                 this.onIncrementMinutes();
19557                 break;
19558             case 40: // down
19559                 this.onDecrementMinutes();
19560                 break;
19561             case 13: // enter
19562             case 9: // tab
19563                 this.setTime();
19564                 break;
19565         }
19566     },
19567     
19568     onClick: function(e) {
19569         e.stopPropagation();
19570         e.preventDefault();
19571     },
19572     
19573     picker : function()
19574     {
19575         return this.el.select('.datepicker', true).first();
19576     },
19577     
19578     fillTime: function()
19579     {    
19580         var time = this.pop.select('tbody', true).first();
19581         
19582         time.dom.innerHTML = '';
19583         
19584         time.createChild({
19585             tag: 'tr',
19586             cn: [
19587                 {
19588                     tag: 'td',
19589                     cn: [
19590                         {
19591                             tag: 'a',
19592                             href: '#',
19593                             cls: 'btn',
19594                             cn: [
19595                                 {
19596                                     tag: 'span',
19597                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19598                                 }
19599                             ]
19600                         } 
19601                     ]
19602                 },
19603                 {
19604                     tag: 'td',
19605                     cls: 'separator'
19606                 },
19607                 {
19608                     tag: 'td',
19609                     cn: [
19610                         {
19611                             tag: 'a',
19612                             href: '#',
19613                             cls: 'btn',
19614                             cn: [
19615                                 {
19616                                     tag: 'span',
19617                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19618                                 }
19619                             ]
19620                         }
19621                     ]
19622                 },
19623                 {
19624                     tag: 'td',
19625                     cls: 'separator'
19626                 }
19627             ]
19628         });
19629         
19630         time.createChild({
19631             tag: 'tr',
19632             cn: [
19633                 {
19634                     tag: 'td',
19635                     cn: [
19636                         {
19637                             tag: 'span',
19638                             cls: 'timepicker-hour',
19639                             html: '00'
19640                         }  
19641                     ]
19642                 },
19643                 {
19644                     tag: 'td',
19645                     cls: 'separator',
19646                     html: ':'
19647                 },
19648                 {
19649                     tag: 'td',
19650                     cn: [
19651                         {
19652                             tag: 'span',
19653                             cls: 'timepicker-minute',
19654                             html: '00'
19655                         }  
19656                     ]
19657                 },
19658                 {
19659                     tag: 'td',
19660                     cls: 'separator'
19661                 },
19662                 {
19663                     tag: 'td',
19664                     cn: [
19665                         {
19666                             tag: 'button',
19667                             type: 'button',
19668                             cls: 'btn btn-primary period',
19669                             html: 'AM'
19670                             
19671                         }
19672                     ]
19673                 }
19674             ]
19675         });
19676         
19677         time.createChild({
19678             tag: 'tr',
19679             cn: [
19680                 {
19681                     tag: 'td',
19682                     cn: [
19683                         {
19684                             tag: 'a',
19685                             href: '#',
19686                             cls: 'btn',
19687                             cn: [
19688                                 {
19689                                     tag: 'span',
19690                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19691                                 }
19692                             ]
19693                         }
19694                     ]
19695                 },
19696                 {
19697                     tag: 'td',
19698                     cls: 'separator'
19699                 },
19700                 {
19701                     tag: 'td',
19702                     cn: [
19703                         {
19704                             tag: 'a',
19705                             href: '#',
19706                             cls: 'btn',
19707                             cn: [
19708                                 {
19709                                     tag: 'span',
19710                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19711                                 }
19712                             ]
19713                         }
19714                     ]
19715                 },
19716                 {
19717                     tag: 'td',
19718                     cls: 'separator'
19719                 }
19720             ]
19721         });
19722         
19723     },
19724     
19725     update: function()
19726     {
19727         
19728         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19729         
19730         this.fill();
19731     },
19732     
19733     fill: function() 
19734     {
19735         var hours = this.time.getHours();
19736         var minutes = this.time.getMinutes();
19737         var period = 'AM';
19738         
19739         if(hours > 11){
19740             period = 'PM';
19741         }
19742         
19743         if(hours == 0){
19744             hours = 12;
19745         }
19746         
19747         
19748         if(hours > 12){
19749             hours = hours - 12;
19750         }
19751         
19752         if(hours < 10){
19753             hours = '0' + hours;
19754         }
19755         
19756         if(minutes < 10){
19757             minutes = '0' + minutes;
19758         }
19759         
19760         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19761         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19762         this.pop.select('button', true).first().dom.innerHTML = period;
19763         
19764     },
19765     
19766     place: function()
19767     {   
19768         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19769         
19770         var cls = ['bottom'];
19771         
19772         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19773             cls.pop();
19774             cls.push('top');
19775         }
19776         
19777         cls.push('right');
19778         
19779         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19780             cls.pop();
19781             cls.push('left');
19782         }
19783         
19784         this.picker().addClass(cls.join('-'));
19785         
19786         var _this = this;
19787         
19788         Roo.each(cls, function(c){
19789             if(c == 'bottom'){
19790                 _this.picker().setTop(_this.inputEl().getHeight());
19791                 return;
19792             }
19793             if(c == 'top'){
19794                 _this.picker().setTop(0 - _this.picker().getHeight());
19795                 return;
19796             }
19797             
19798             if(c == 'left'){
19799                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19800                 return;
19801             }
19802             if(c == 'right'){
19803                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19804                 return;
19805             }
19806         });
19807         
19808     },
19809   
19810     onFocus : function()
19811     {
19812         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19813         this.show();
19814     },
19815     
19816     onBlur : function()
19817     {
19818         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19819         this.hide();
19820     },
19821     
19822     show : function()
19823     {
19824         this.picker().show();
19825         this.pop.show();
19826         this.update();
19827         this.place();
19828         
19829         this.fireEvent('show', this, this.date);
19830     },
19831     
19832     hide : function()
19833     {
19834         this.picker().hide();
19835         this.pop.hide();
19836         
19837         this.fireEvent('hide', this, this.date);
19838     },
19839     
19840     setTime : function()
19841     {
19842         this.hide();
19843         this.setValue(this.time.format(this.format));
19844         
19845         this.fireEvent('select', this, this.date);
19846         
19847         
19848     },
19849     
19850     onMousedown: function(e){
19851         e.stopPropagation();
19852         e.preventDefault();
19853     },
19854     
19855     onIncrementHours: function()
19856     {
19857         Roo.log('onIncrementHours');
19858         this.time = this.time.add(Date.HOUR, 1);
19859         this.update();
19860         
19861     },
19862     
19863     onDecrementHours: function()
19864     {
19865         Roo.log('onDecrementHours');
19866         this.time = this.time.add(Date.HOUR, -1);
19867         this.update();
19868     },
19869     
19870     onIncrementMinutes: function()
19871     {
19872         Roo.log('onIncrementMinutes');
19873         this.time = this.time.add(Date.MINUTE, 1);
19874         this.update();
19875     },
19876     
19877     onDecrementMinutes: function()
19878     {
19879         Roo.log('onDecrementMinutes');
19880         this.time = this.time.add(Date.MINUTE, -1);
19881         this.update();
19882     },
19883     
19884     onTogglePeriod: function()
19885     {
19886         Roo.log('onTogglePeriod');
19887         this.time = this.time.add(Date.HOUR, 12);
19888         this.update();
19889     }
19890     
19891    
19892 });
19893
19894 Roo.apply(Roo.bootstrap.TimeField,  {
19895     
19896     content : {
19897         tag: 'tbody',
19898         cn: [
19899             {
19900                 tag: 'tr',
19901                 cn: [
19902                 {
19903                     tag: 'td',
19904                     colspan: '7'
19905                 }
19906                 ]
19907             }
19908         ]
19909     },
19910     
19911     footer : {
19912         tag: 'tfoot',
19913         cn: [
19914             {
19915                 tag: 'tr',
19916                 cn: [
19917                 {
19918                     tag: 'th',
19919                     colspan: '7',
19920                     cls: '',
19921                     cn: [
19922                         {
19923                             tag: 'button',
19924                             cls: 'btn btn-info ok',
19925                             html: 'OK'
19926                         }
19927                     ]
19928                 }
19929
19930                 ]
19931             }
19932         ]
19933     }
19934 });
19935
19936 Roo.apply(Roo.bootstrap.TimeField,  {
19937   
19938     template : {
19939         tag: 'div',
19940         cls: 'datepicker dropdown-menu',
19941         cn: [
19942             {
19943                 tag: 'div',
19944                 cls: 'datepicker-time',
19945                 cn: [
19946                 {
19947                     tag: 'table',
19948                     cls: 'table-condensed',
19949                     cn:[
19950                     Roo.bootstrap.TimeField.content,
19951                     Roo.bootstrap.TimeField.footer
19952                     ]
19953                 }
19954                 ]
19955             }
19956         ]
19957     }
19958 });
19959
19960  
19961
19962  /*
19963  * - LGPL
19964  *
19965  * MonthField
19966  * 
19967  */
19968
19969 /**
19970  * @class Roo.bootstrap.MonthField
19971  * @extends Roo.bootstrap.Input
19972  * Bootstrap MonthField class
19973  * 
19974  * @cfg {String} language default en
19975  * 
19976  * @constructor
19977  * Create a new MonthField
19978  * @param {Object} config The config object
19979  */
19980
19981 Roo.bootstrap.MonthField = function(config){
19982     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19983     
19984     this.addEvents({
19985         /**
19986          * @event show
19987          * Fires when this field show.
19988          * @param {Roo.bootstrap.MonthField} this
19989          * @param {Mixed} date The date value
19990          */
19991         show : true,
19992         /**
19993          * @event show
19994          * Fires when this field hide.
19995          * @param {Roo.bootstrap.MonthField} this
19996          * @param {Mixed} date The date value
19997          */
19998         hide : true,
19999         /**
20000          * @event select
20001          * Fires when select a date.
20002          * @param {Roo.bootstrap.MonthField} this
20003          * @param {String} oldvalue The old value
20004          * @param {String} newvalue The new value
20005          */
20006         select : true
20007     });
20008 };
20009
20010 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20011     
20012     onRender: function(ct, position)
20013     {
20014         
20015         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20016         
20017         this.language = this.language || 'en';
20018         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20019         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20020         
20021         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20022         this.isInline = false;
20023         this.isInput = true;
20024         this.component = this.el.select('.add-on', true).first() || false;
20025         this.component = (this.component && this.component.length === 0) ? false : this.component;
20026         this.hasInput = this.component && this.inputEL().length;
20027         
20028         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20029         
20030         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20031         
20032         this.picker().on('mousedown', this.onMousedown, this);
20033         this.picker().on('click', this.onClick, this);
20034         
20035         this.picker().addClass('datepicker-dropdown');
20036         
20037         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20038             v.setStyle('width', '189px');
20039         });
20040         
20041         this.fillMonths();
20042         
20043         this.update();
20044         
20045         if(this.isInline) {
20046             this.show();
20047         }
20048         
20049     },
20050     
20051     setValue: function(v, suppressEvent)
20052     {   
20053         var o = this.getValue();
20054         
20055         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20056         
20057         this.update();
20058
20059         if(suppressEvent !== true){
20060             this.fireEvent('select', this, o, v);
20061         }
20062         
20063     },
20064     
20065     getValue: function()
20066     {
20067         return this.value;
20068     },
20069     
20070     onClick: function(e) 
20071     {
20072         e.stopPropagation();
20073         e.preventDefault();
20074         
20075         var target = e.getTarget();
20076         
20077         if(target.nodeName.toLowerCase() === 'i'){
20078             target = Roo.get(target).dom.parentNode;
20079         }
20080         
20081         var nodeName = target.nodeName;
20082         var className = target.className;
20083         var html = target.innerHTML;
20084         
20085         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20086             return;
20087         }
20088         
20089         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20090         
20091         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20092         
20093         this.hide();
20094                         
20095     },
20096     
20097     picker : function()
20098     {
20099         return this.pickerEl;
20100     },
20101     
20102     fillMonths: function()
20103     {    
20104         var i = 0;
20105         var months = this.picker().select('>.datepicker-months td', true).first();
20106         
20107         months.dom.innerHTML = '';
20108         
20109         while (i < 12) {
20110             var month = {
20111                 tag: 'span',
20112                 cls: 'month',
20113                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20114             };
20115             
20116             months.createChild(month);
20117         }
20118         
20119     },
20120     
20121     update: function()
20122     {
20123         var _this = this;
20124         
20125         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20126             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20127         }
20128         
20129         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20130             e.removeClass('active');
20131             
20132             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20133                 e.addClass('active');
20134             }
20135         })
20136     },
20137     
20138     place: function()
20139     {
20140         if(this.isInline) {
20141             return;
20142         }
20143         
20144         this.picker().removeClass(['bottom', 'top']);
20145         
20146         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20147             /*
20148              * place to the top of element!
20149              *
20150              */
20151             
20152             this.picker().addClass('top');
20153             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20154             
20155             return;
20156         }
20157         
20158         this.picker().addClass('bottom');
20159         
20160         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20161     },
20162     
20163     onFocus : function()
20164     {
20165         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20166         this.show();
20167     },
20168     
20169     onBlur : function()
20170     {
20171         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20172         
20173         var d = this.inputEl().getValue();
20174         
20175         this.setValue(d);
20176                 
20177         this.hide();
20178     },
20179     
20180     show : function()
20181     {
20182         this.picker().show();
20183         this.picker().select('>.datepicker-months', true).first().show();
20184         this.update();
20185         this.place();
20186         
20187         this.fireEvent('show', this, this.date);
20188     },
20189     
20190     hide : function()
20191     {
20192         if(this.isInline) {
20193             return;
20194         }
20195         this.picker().hide();
20196         this.fireEvent('hide', this, this.date);
20197         
20198     },
20199     
20200     onMousedown: function(e)
20201     {
20202         e.stopPropagation();
20203         e.preventDefault();
20204     },
20205     
20206     keyup: function(e)
20207     {
20208         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20209         this.update();
20210     },
20211
20212     fireKey: function(e)
20213     {
20214         if (!this.picker().isVisible()){
20215             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20216                 this.show();
20217             }
20218             return;
20219         }
20220         
20221         var dir;
20222         
20223         switch(e.keyCode){
20224             case 27: // escape
20225                 this.hide();
20226                 e.preventDefault();
20227                 break;
20228             case 37: // left
20229             case 39: // right
20230                 dir = e.keyCode == 37 ? -1 : 1;
20231                 
20232                 this.vIndex = this.vIndex + dir;
20233                 
20234                 if(this.vIndex < 0){
20235                     this.vIndex = 0;
20236                 }
20237                 
20238                 if(this.vIndex > 11){
20239                     this.vIndex = 11;
20240                 }
20241                 
20242                 if(isNaN(this.vIndex)){
20243                     this.vIndex = 0;
20244                 }
20245                 
20246                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20247                 
20248                 break;
20249             case 38: // up
20250             case 40: // down
20251                 
20252                 dir = e.keyCode == 38 ? -1 : 1;
20253                 
20254                 this.vIndex = this.vIndex + dir * 4;
20255                 
20256                 if(this.vIndex < 0){
20257                     this.vIndex = 0;
20258                 }
20259                 
20260                 if(this.vIndex > 11){
20261                     this.vIndex = 11;
20262                 }
20263                 
20264                 if(isNaN(this.vIndex)){
20265                     this.vIndex = 0;
20266                 }
20267                 
20268                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20269                 break;
20270                 
20271             case 13: // enter
20272                 
20273                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20274                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20275                 }
20276                 
20277                 this.hide();
20278                 e.preventDefault();
20279                 break;
20280             case 9: // tab
20281                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20282                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20283                 }
20284                 this.hide();
20285                 break;
20286             case 16: // shift
20287             case 17: // ctrl
20288             case 18: // alt
20289                 break;
20290             default :
20291                 this.hide();
20292                 
20293         }
20294     },
20295     
20296     remove: function() 
20297     {
20298         this.picker().remove();
20299     }
20300    
20301 });
20302
20303 Roo.apply(Roo.bootstrap.MonthField,  {
20304     
20305     content : {
20306         tag: 'tbody',
20307         cn: [
20308         {
20309             tag: 'tr',
20310             cn: [
20311             {
20312                 tag: 'td',
20313                 colspan: '7'
20314             }
20315             ]
20316         }
20317         ]
20318     },
20319     
20320     dates:{
20321         en: {
20322             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20323             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20324         }
20325     }
20326 });
20327
20328 Roo.apply(Roo.bootstrap.MonthField,  {
20329   
20330     template : {
20331         tag: 'div',
20332         cls: 'datepicker dropdown-menu roo-dynamic',
20333         cn: [
20334             {
20335                 tag: 'div',
20336                 cls: 'datepicker-months',
20337                 cn: [
20338                 {
20339                     tag: 'table',
20340                     cls: 'table-condensed',
20341                     cn:[
20342                         Roo.bootstrap.DateField.content
20343                     ]
20344                 }
20345                 ]
20346             }
20347         ]
20348     }
20349 });
20350
20351  
20352
20353  
20354  /*
20355  * - LGPL
20356  *
20357  * CheckBox
20358  * 
20359  */
20360
20361 /**
20362  * @class Roo.bootstrap.CheckBox
20363  * @extends Roo.bootstrap.Input
20364  * Bootstrap CheckBox class
20365  * 
20366  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20367  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20368  * @cfg {String} boxLabel The text that appears beside the checkbox
20369  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20370  * @cfg {Boolean} checked initnal the element
20371  * @cfg {Boolean} inline inline the element (default false)
20372  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20373  * @cfg {String} tooltip label tooltip
20374  * 
20375  * @constructor
20376  * Create a new CheckBox
20377  * @param {Object} config The config object
20378  */
20379
20380 Roo.bootstrap.CheckBox = function(config){
20381     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20382    
20383     this.addEvents({
20384         /**
20385         * @event check
20386         * Fires when the element is checked or unchecked.
20387         * @param {Roo.bootstrap.CheckBox} this This input
20388         * @param {Boolean} checked The new checked value
20389         */
20390        check : true,
20391        /**
20392         * @event click
20393         * Fires when the element is click.
20394         * @param {Roo.bootstrap.CheckBox} this This input
20395         */
20396        click : true
20397     });
20398     
20399 };
20400
20401 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20402   
20403     inputType: 'checkbox',
20404     inputValue: 1,
20405     valueOff: 0,
20406     boxLabel: false,
20407     checked: false,
20408     weight : false,
20409     inline: false,
20410     tooltip : '',
20411     
20412     getAutoCreate : function()
20413     {
20414         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20415         
20416         var id = Roo.id();
20417         
20418         var cfg = {};
20419         
20420         cfg.cls = 'form-group ' + this.inputType; //input-group
20421         
20422         if(this.inline){
20423             cfg.cls += ' ' + this.inputType + '-inline';
20424         }
20425         
20426         var input =  {
20427             tag: 'input',
20428             id : id,
20429             type : this.inputType,
20430             value : this.inputValue,
20431             cls : 'roo-' + this.inputType, //'form-box',
20432             placeholder : this.placeholder || ''
20433             
20434         };
20435         
20436         if(this.inputType != 'radio'){
20437             var hidden =  {
20438                 tag: 'input',
20439                 type : 'hidden',
20440                 cls : 'roo-hidden-value',
20441                 value : this.checked ? this.inputValue : this.valueOff
20442             };
20443         }
20444         
20445             
20446         if (this.weight) { // Validity check?
20447             cfg.cls += " " + this.inputType + "-" + this.weight;
20448         }
20449         
20450         if (this.disabled) {
20451             input.disabled=true;
20452         }
20453         
20454         if(this.checked){
20455             input.checked = this.checked;
20456         }
20457         
20458         if (this.name) {
20459             
20460             input.name = this.name;
20461             
20462             if(this.inputType != 'radio'){
20463                 hidden.name = this.name;
20464                 input.name = '_hidden_' + this.name;
20465             }
20466         }
20467         
20468         if (this.size) {
20469             input.cls += ' input-' + this.size;
20470         }
20471         
20472         var settings=this;
20473         
20474         ['xs','sm','md','lg'].map(function(size){
20475             if (settings[size]) {
20476                 cfg.cls += ' col-' + size + '-' + settings[size];
20477             }
20478         });
20479         
20480         var inputblock = input;
20481          
20482         if (this.before || this.after) {
20483             
20484             inputblock = {
20485                 cls : 'input-group',
20486                 cn :  [] 
20487             };
20488             
20489             if (this.before) {
20490                 inputblock.cn.push({
20491                     tag :'span',
20492                     cls : 'input-group-addon',
20493                     html : this.before
20494                 });
20495             }
20496             
20497             inputblock.cn.push(input);
20498             
20499             if(this.inputType != 'radio'){
20500                 inputblock.cn.push(hidden);
20501             }
20502             
20503             if (this.after) {
20504                 inputblock.cn.push({
20505                     tag :'span',
20506                     cls : 'input-group-addon',
20507                     html : this.after
20508                 });
20509             }
20510             
20511         }
20512         
20513         if (align ==='left' && this.fieldLabel.length) {
20514 //                Roo.log("left and has label");
20515             cfg.cn = [
20516                 {
20517                     tag: 'label',
20518                     'for' :  id,
20519                     cls : 'control-label',
20520                     html : this.fieldLabel
20521                 },
20522                 {
20523                     cls : "", 
20524                     cn: [
20525                         inputblock
20526                     ]
20527                 }
20528             ];
20529             
20530             if(this.labelWidth > 12){
20531                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20532             }
20533             
20534             if(this.labelWidth < 13 && this.labelmd == 0){
20535                 this.labelmd = this.labelWidth;
20536             }
20537             
20538             if(this.labellg > 0){
20539                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20540                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20541             }
20542             
20543             if(this.labelmd > 0){
20544                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20545                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20546             }
20547             
20548             if(this.labelsm > 0){
20549                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20550                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20551             }
20552             
20553             if(this.labelxs > 0){
20554                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20555                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20556             }
20557             
20558         } else if ( this.fieldLabel.length) {
20559 //                Roo.log(" label");
20560                 cfg.cn = [
20561                    
20562                     {
20563                         tag: this.boxLabel ? 'span' : 'label',
20564                         'for': id,
20565                         cls: 'control-label box-input-label',
20566                         //cls : 'input-group-addon',
20567                         html : this.fieldLabel
20568                     },
20569                     
20570                     inputblock
20571                     
20572                 ];
20573
20574         } else {
20575             
20576 //                Roo.log(" no label && no align");
20577                 cfg.cn = [  inputblock ] ;
20578                 
20579                 
20580         }
20581         
20582         if(this.boxLabel){
20583              var boxLabelCfg = {
20584                 tag: 'label',
20585                 //'for': id, // box label is handled by onclick - so no for...
20586                 cls: 'box-label',
20587                 html: this.boxLabel
20588             };
20589             
20590             if(this.tooltip){
20591                 boxLabelCfg.tooltip = this.tooltip;
20592             }
20593              
20594             cfg.cn.push(boxLabelCfg);
20595         }
20596         
20597         if(this.inputType != 'radio'){
20598             cfg.cn.push(hidden);
20599         }
20600         
20601         return cfg;
20602         
20603     },
20604     
20605     /**
20606      * return the real input element.
20607      */
20608     inputEl: function ()
20609     {
20610         return this.el.select('input.roo-' + this.inputType,true).first();
20611     },
20612     hiddenEl: function ()
20613     {
20614         return this.el.select('input.roo-hidden-value',true).first();
20615     },
20616     
20617     labelEl: function()
20618     {
20619         return this.el.select('label.control-label',true).first();
20620     },
20621     /* depricated... */
20622     
20623     label: function()
20624     {
20625         return this.labelEl();
20626     },
20627     
20628     boxLabelEl: function()
20629     {
20630         return this.el.select('label.box-label',true).first();
20631     },
20632     
20633     initEvents : function()
20634     {
20635 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20636         
20637         this.inputEl().on('click', this.onClick,  this);
20638         
20639         if (this.boxLabel) { 
20640             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20641         }
20642         
20643         this.startValue = this.getValue();
20644         
20645         if(this.groupId){
20646             Roo.bootstrap.CheckBox.register(this);
20647         }
20648     },
20649     
20650     onClick : function(e)
20651     {   
20652         if(this.fireEvent('click', this, e) !== false){
20653             this.setChecked(!this.checked);
20654         }
20655         
20656     },
20657     
20658     setChecked : function(state,suppressEvent)
20659     {
20660         this.startValue = this.getValue();
20661
20662         if(this.inputType == 'radio'){
20663             
20664             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20665                 e.dom.checked = false;
20666             });
20667             
20668             this.inputEl().dom.checked = true;
20669             
20670             this.inputEl().dom.value = this.inputValue;
20671             
20672             if(suppressEvent !== true){
20673                 this.fireEvent('check', this, true);
20674             }
20675             
20676             this.validate();
20677             
20678             return;
20679         }
20680         
20681         this.checked = state;
20682         
20683         this.inputEl().dom.checked = state;
20684         
20685         
20686         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20687         
20688         if(suppressEvent !== true){
20689             this.fireEvent('check', this, state);
20690         }
20691         
20692         this.validate();
20693     },
20694     
20695     getValue : function()
20696     {
20697         if(this.inputType == 'radio'){
20698             return this.getGroupValue();
20699         }
20700         
20701         return this.hiddenEl().dom.value;
20702         
20703     },
20704     
20705     getGroupValue : function()
20706     {
20707         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20708             return '';
20709         }
20710         
20711         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20712     },
20713     
20714     setValue : function(v,suppressEvent)
20715     {
20716         if(this.inputType == 'radio'){
20717             this.setGroupValue(v, suppressEvent);
20718             return;
20719         }
20720         
20721         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20722         
20723         this.validate();
20724     },
20725     
20726     setGroupValue : function(v, suppressEvent)
20727     {
20728         this.startValue = this.getValue();
20729         
20730         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20731             e.dom.checked = false;
20732             
20733             if(e.dom.value == v){
20734                 e.dom.checked = true;
20735             }
20736         });
20737         
20738         if(suppressEvent !== true){
20739             this.fireEvent('check', this, true);
20740         }
20741
20742         this.validate();
20743         
20744         return;
20745     },
20746     
20747     validate : function()
20748     {
20749         if(this.getVisibilityEl().hasClass('hidden')){
20750             return true;
20751         }
20752         
20753         if(
20754                 this.disabled || 
20755                 (this.inputType == 'radio' && this.validateRadio()) ||
20756                 (this.inputType == 'checkbox' && this.validateCheckbox())
20757         ){
20758             this.markValid();
20759             return true;
20760         }
20761         
20762         this.markInvalid();
20763         return false;
20764     },
20765     
20766     validateRadio : function()
20767     {
20768         if(this.getVisibilityEl().hasClass('hidden')){
20769             return true;
20770         }
20771         
20772         if(this.allowBlank){
20773             return true;
20774         }
20775         
20776         var valid = false;
20777         
20778         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20779             if(!e.dom.checked){
20780                 return;
20781             }
20782             
20783             valid = true;
20784             
20785             return false;
20786         });
20787         
20788         return valid;
20789     },
20790     
20791     validateCheckbox : function()
20792     {
20793         if(!this.groupId){
20794             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20795             //return (this.getValue() == this.inputValue) ? true : false;
20796         }
20797         
20798         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20799         
20800         if(!group){
20801             return false;
20802         }
20803         
20804         var r = false;
20805         
20806         for(var i in group){
20807             if(group[i].el.isVisible(true)){
20808                 r = false;
20809                 break;
20810             }
20811             
20812             r = true;
20813         }
20814         
20815         for(var i in group){
20816             if(r){
20817                 break;
20818             }
20819             
20820             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20821         }
20822         
20823         return r;
20824     },
20825     
20826     /**
20827      * Mark this field as valid
20828      */
20829     markValid : function()
20830     {
20831         var _this = this;
20832         
20833         this.fireEvent('valid', this);
20834         
20835         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20836         
20837         if(this.groupId){
20838             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20839         }
20840         
20841         if(label){
20842             label.markValid();
20843         }
20844
20845         if(this.inputType == 'radio'){
20846             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20847                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20848                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20849             });
20850             
20851             return;
20852         }
20853
20854         if(!this.groupId){
20855             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20856             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20857             return;
20858         }
20859         
20860         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20861         
20862         if(!group){
20863             return;
20864         }
20865         
20866         for(var i in group){
20867             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20868             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20869         }
20870     },
20871     
20872      /**
20873      * Mark this field as invalid
20874      * @param {String} msg The validation message
20875      */
20876     markInvalid : function(msg)
20877     {
20878         if(this.allowBlank){
20879             return;
20880         }
20881         
20882         var _this = this;
20883         
20884         this.fireEvent('invalid', this, msg);
20885         
20886         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20887         
20888         if(this.groupId){
20889             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20890         }
20891         
20892         if(label){
20893             label.markInvalid();
20894         }
20895             
20896         if(this.inputType == 'radio'){
20897             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20898                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20899                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20900             });
20901             
20902             return;
20903         }
20904         
20905         if(!this.groupId){
20906             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20907             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20908             return;
20909         }
20910         
20911         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20912         
20913         if(!group){
20914             return;
20915         }
20916         
20917         for(var i in group){
20918             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20919             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20920         }
20921         
20922     },
20923     
20924     clearInvalid : function()
20925     {
20926         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20927         
20928         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20929         
20930         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20931         
20932         if (label && label.iconEl) {
20933             label.iconEl.removeClass(label.validClass);
20934             label.iconEl.removeClass(label.invalidClass);
20935         }
20936     },
20937     
20938     disable : function()
20939     {
20940         if(this.inputType != 'radio'){
20941             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20942             return;
20943         }
20944         
20945         var _this = this;
20946         
20947         if(this.rendered){
20948             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20949                 _this.getActionEl().addClass(this.disabledClass);
20950                 e.dom.disabled = true;
20951             });
20952         }
20953         
20954         this.disabled = true;
20955         this.fireEvent("disable", this);
20956         return this;
20957     },
20958
20959     enable : function()
20960     {
20961         if(this.inputType != 'radio'){
20962             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20963             return;
20964         }
20965         
20966         var _this = this;
20967         
20968         if(this.rendered){
20969             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20970                 _this.getActionEl().removeClass(this.disabledClass);
20971                 e.dom.disabled = false;
20972             });
20973         }
20974         
20975         this.disabled = false;
20976         this.fireEvent("enable", this);
20977         return this;
20978     },
20979     
20980     setBoxLabel : function(v)
20981     {
20982         this.boxLabel = v;
20983         
20984         if(this.rendered){
20985             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20986         }
20987     }
20988
20989 });
20990
20991 Roo.apply(Roo.bootstrap.CheckBox, {
20992     
20993     groups: {},
20994     
20995      /**
20996     * register a CheckBox Group
20997     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20998     */
20999     register : function(checkbox)
21000     {
21001         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21002             this.groups[checkbox.groupId] = {};
21003         }
21004         
21005         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21006             return;
21007         }
21008         
21009         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21010         
21011     },
21012     /**
21013     * fetch a CheckBox Group based on the group ID
21014     * @param {string} the group ID
21015     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21016     */
21017     get: function(groupId) {
21018         if (typeof(this.groups[groupId]) == 'undefined') {
21019             return false;
21020         }
21021         
21022         return this.groups[groupId] ;
21023     }
21024     
21025     
21026 });
21027 /*
21028  * - LGPL
21029  *
21030  * RadioItem
21031  * 
21032  */
21033
21034 /**
21035  * @class Roo.bootstrap.Radio
21036  * @extends Roo.bootstrap.Component
21037  * Bootstrap Radio class
21038  * @cfg {String} boxLabel - the label associated
21039  * @cfg {String} value - the value of radio
21040  * 
21041  * @constructor
21042  * Create a new Radio
21043  * @param {Object} config The config object
21044  */
21045 Roo.bootstrap.Radio = function(config){
21046     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21047     
21048 };
21049
21050 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21051     
21052     boxLabel : '',
21053     
21054     value : '',
21055     
21056     getAutoCreate : function()
21057     {
21058         var cfg = {
21059             tag : 'div',
21060             cls : 'form-group radio',
21061             cn : [
21062                 {
21063                     tag : 'label',
21064                     cls : 'box-label',
21065                     html : this.boxLabel
21066                 }
21067             ]
21068         };
21069         
21070         return cfg;
21071     },
21072     
21073     initEvents : function() 
21074     {
21075         this.parent().register(this);
21076         
21077         this.el.on('click', this.onClick, this);
21078         
21079     },
21080     
21081     onClick : function(e)
21082     {
21083         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21084             this.setChecked(true);
21085         }
21086     },
21087     
21088     setChecked : function(state, suppressEvent)
21089     {
21090         this.parent().setValue(this.value, suppressEvent);
21091         
21092     },
21093     
21094     setBoxLabel : function(v)
21095     {
21096         this.boxLabel = v;
21097         
21098         if(this.rendered){
21099             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21100         }
21101     }
21102     
21103 });
21104  
21105
21106  /*
21107  * - LGPL
21108  *
21109  * Input
21110  * 
21111  */
21112
21113 /**
21114  * @class Roo.bootstrap.SecurePass
21115  * @extends Roo.bootstrap.Input
21116  * Bootstrap SecurePass class
21117  *
21118  * 
21119  * @constructor
21120  * Create a new SecurePass
21121  * @param {Object} config The config object
21122  */
21123  
21124 Roo.bootstrap.SecurePass = function (config) {
21125     // these go here, so the translation tool can replace them..
21126     this.errors = {
21127         PwdEmpty: "Please type a password, and then retype it to confirm.",
21128         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21129         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21130         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21131         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21132         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21133         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21134         TooWeak: "Your password is Too Weak."
21135     },
21136     this.meterLabel = "Password strength:";
21137     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21138     this.meterClass = [
21139         "roo-password-meter-tooweak", 
21140         "roo-password-meter-weak", 
21141         "roo-password-meter-medium", 
21142         "roo-password-meter-strong", 
21143         "roo-password-meter-grey"
21144     ];
21145     
21146     this.errors = {};
21147     
21148     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21149 }
21150
21151 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21152     /**
21153      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21154      * {
21155      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21156      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21157      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21158      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21159      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21160      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21161      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21162      * })
21163      */
21164     // private
21165     
21166     meterWidth: 300,
21167     errorMsg :'',    
21168     errors: false,
21169     imageRoot: '/',
21170     /**
21171      * @cfg {String/Object} Label for the strength meter (defaults to
21172      * 'Password strength:')
21173      */
21174     // private
21175     meterLabel: '',
21176     /**
21177      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21178      * ['Weak', 'Medium', 'Strong'])
21179      */
21180     // private    
21181     pwdStrengths: false,    
21182     // private
21183     strength: 0,
21184     // private
21185     _lastPwd: null,
21186     // private
21187     kCapitalLetter: 0,
21188     kSmallLetter: 1,
21189     kDigit: 2,
21190     kPunctuation: 3,
21191     
21192     insecure: false,
21193     // private
21194     initEvents: function ()
21195     {
21196         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21197
21198         if (this.el.is('input[type=password]') && Roo.isSafari) {
21199             this.el.on('keydown', this.SafariOnKeyDown, this);
21200         }
21201
21202         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21203     },
21204     // private
21205     onRender: function (ct, position)
21206     {
21207         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21208         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21209         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21210
21211         this.trigger.createChild({
21212                    cn: [
21213                     {
21214                     //id: 'PwdMeter',
21215                     tag: 'div',
21216                     cls: 'roo-password-meter-grey col-xs-12',
21217                     style: {
21218                         //width: 0,
21219                         //width: this.meterWidth + 'px'                                                
21220                         }
21221                     },
21222                     {                            
21223                          cls: 'roo-password-meter-text'                          
21224                     }
21225                 ]            
21226         });
21227
21228          
21229         if (this.hideTrigger) {
21230             this.trigger.setDisplayed(false);
21231         }
21232         this.setSize(this.width || '', this.height || '');
21233     },
21234     // private
21235     onDestroy: function ()
21236     {
21237         if (this.trigger) {
21238             this.trigger.removeAllListeners();
21239             this.trigger.remove();
21240         }
21241         if (this.wrap) {
21242             this.wrap.remove();
21243         }
21244         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21245     },
21246     // private
21247     checkStrength: function ()
21248     {
21249         var pwd = this.inputEl().getValue();
21250         if (pwd == this._lastPwd) {
21251             return;
21252         }
21253
21254         var strength;
21255         if (this.ClientSideStrongPassword(pwd)) {
21256             strength = 3;
21257         } else if (this.ClientSideMediumPassword(pwd)) {
21258             strength = 2;
21259         } else if (this.ClientSideWeakPassword(pwd)) {
21260             strength = 1;
21261         } else {
21262             strength = 0;
21263         }
21264         
21265         Roo.log('strength1: ' + strength);
21266         
21267         //var pm = this.trigger.child('div/div/div').dom;
21268         var pm = this.trigger.child('div/div');
21269         pm.removeClass(this.meterClass);
21270         pm.addClass(this.meterClass[strength]);
21271                 
21272         
21273         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21274                 
21275         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21276         
21277         this._lastPwd = pwd;
21278     },
21279     reset: function ()
21280     {
21281         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21282         
21283         this._lastPwd = '';
21284         
21285         var pm = this.trigger.child('div/div');
21286         pm.removeClass(this.meterClass);
21287         pm.addClass('roo-password-meter-grey');        
21288         
21289         
21290         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21291         
21292         pt.innerHTML = '';
21293         this.inputEl().dom.type='password';
21294     },
21295     // private
21296     validateValue: function (value)
21297     {
21298         
21299         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21300             return false;
21301         }
21302         if (value.length == 0) {
21303             if (this.allowBlank) {
21304                 this.clearInvalid();
21305                 return true;
21306             }
21307
21308             this.markInvalid(this.errors.PwdEmpty);
21309             this.errorMsg = this.errors.PwdEmpty;
21310             return false;
21311         }
21312         
21313         if(this.insecure){
21314             return true;
21315         }
21316         
21317         if ('[\x21-\x7e]*'.match(value)) {
21318             this.markInvalid(this.errors.PwdBadChar);
21319             this.errorMsg = this.errors.PwdBadChar;
21320             return false;
21321         }
21322         if (value.length < 6) {
21323             this.markInvalid(this.errors.PwdShort);
21324             this.errorMsg = this.errors.PwdShort;
21325             return false;
21326         }
21327         if (value.length > 16) {
21328             this.markInvalid(this.errors.PwdLong);
21329             this.errorMsg = this.errors.PwdLong;
21330             return false;
21331         }
21332         var strength;
21333         if (this.ClientSideStrongPassword(value)) {
21334             strength = 3;
21335         } else if (this.ClientSideMediumPassword(value)) {
21336             strength = 2;
21337         } else if (this.ClientSideWeakPassword(value)) {
21338             strength = 1;
21339         } else {
21340             strength = 0;
21341         }
21342
21343         
21344         if (strength < 2) {
21345             //this.markInvalid(this.errors.TooWeak);
21346             this.errorMsg = this.errors.TooWeak;
21347             //return false;
21348         }
21349         
21350         
21351         console.log('strength2: ' + strength);
21352         
21353         //var pm = this.trigger.child('div/div/div').dom;
21354         
21355         var pm = this.trigger.child('div/div');
21356         pm.removeClass(this.meterClass);
21357         pm.addClass(this.meterClass[strength]);
21358                 
21359         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21360                 
21361         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21362         
21363         this.errorMsg = ''; 
21364         return true;
21365     },
21366     // private
21367     CharacterSetChecks: function (type)
21368     {
21369         this.type = type;
21370         this.fResult = false;
21371     },
21372     // private
21373     isctype: function (character, type)
21374     {
21375         switch (type) {  
21376             case this.kCapitalLetter:
21377                 if (character >= 'A' && character <= 'Z') {
21378                     return true;
21379                 }
21380                 break;
21381             
21382             case this.kSmallLetter:
21383                 if (character >= 'a' && character <= 'z') {
21384                     return true;
21385                 }
21386                 break;
21387             
21388             case this.kDigit:
21389                 if (character >= '0' && character <= '9') {
21390                     return true;
21391                 }
21392                 break;
21393             
21394             case this.kPunctuation:
21395                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21396                     return true;
21397                 }
21398                 break;
21399             
21400             default:
21401                 return false;
21402         }
21403
21404     },
21405     // private
21406     IsLongEnough: function (pwd, size)
21407     {
21408         return !(pwd == null || isNaN(size) || pwd.length < size);
21409     },
21410     // private
21411     SpansEnoughCharacterSets: function (word, nb)
21412     {
21413         if (!this.IsLongEnough(word, nb))
21414         {
21415             return false;
21416         }
21417
21418         var characterSetChecks = new Array(
21419             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21420             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21421         );
21422         
21423         for (var index = 0; index < word.length; ++index) {
21424             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21425                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21426                     characterSetChecks[nCharSet].fResult = true;
21427                     break;
21428                 }
21429             }
21430         }
21431
21432         var nCharSets = 0;
21433         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21434             if (characterSetChecks[nCharSet].fResult) {
21435                 ++nCharSets;
21436             }
21437         }
21438
21439         if (nCharSets < nb) {
21440             return false;
21441         }
21442         return true;
21443     },
21444     // private
21445     ClientSideStrongPassword: function (pwd)
21446     {
21447         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21448     },
21449     // private
21450     ClientSideMediumPassword: function (pwd)
21451     {
21452         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21453     },
21454     // private
21455     ClientSideWeakPassword: function (pwd)
21456     {
21457         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21458     }
21459           
21460 })//<script type="text/javascript">
21461
21462 /*
21463  * Based  Ext JS Library 1.1.1
21464  * Copyright(c) 2006-2007, Ext JS, LLC.
21465  * LGPL
21466  *
21467  */
21468  
21469 /**
21470  * @class Roo.HtmlEditorCore
21471  * @extends Roo.Component
21472  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21473  *
21474  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21475  */
21476
21477 Roo.HtmlEditorCore = function(config){
21478     
21479     
21480     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21481     
21482     
21483     this.addEvents({
21484         /**
21485          * @event initialize
21486          * Fires when the editor is fully initialized (including the iframe)
21487          * @param {Roo.HtmlEditorCore} this
21488          */
21489         initialize: true,
21490         /**
21491          * @event activate
21492          * Fires when the editor is first receives the focus. Any insertion must wait
21493          * until after this event.
21494          * @param {Roo.HtmlEditorCore} this
21495          */
21496         activate: true,
21497          /**
21498          * @event beforesync
21499          * Fires before the textarea is updated with content from the editor iframe. Return false
21500          * to cancel the sync.
21501          * @param {Roo.HtmlEditorCore} this
21502          * @param {String} html
21503          */
21504         beforesync: true,
21505          /**
21506          * @event beforepush
21507          * Fires before the iframe editor is updated with content from the textarea. Return false
21508          * to cancel the push.
21509          * @param {Roo.HtmlEditorCore} this
21510          * @param {String} html
21511          */
21512         beforepush: true,
21513          /**
21514          * @event sync
21515          * Fires when the textarea is updated with content from the editor iframe.
21516          * @param {Roo.HtmlEditorCore} this
21517          * @param {String} html
21518          */
21519         sync: true,
21520          /**
21521          * @event push
21522          * Fires when the iframe editor is updated with content from the textarea.
21523          * @param {Roo.HtmlEditorCore} this
21524          * @param {String} html
21525          */
21526         push: true,
21527         
21528         /**
21529          * @event editorevent
21530          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21531          * @param {Roo.HtmlEditorCore} this
21532          */
21533         editorevent: true
21534         
21535     });
21536     
21537     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21538     
21539     // defaults : white / black...
21540     this.applyBlacklists();
21541     
21542     
21543     
21544 };
21545
21546
21547 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21548
21549
21550      /**
21551      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21552      */
21553     
21554     owner : false,
21555     
21556      /**
21557      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21558      *                        Roo.resizable.
21559      */
21560     resizable : false,
21561      /**
21562      * @cfg {Number} height (in pixels)
21563      */   
21564     height: 300,
21565    /**
21566      * @cfg {Number} width (in pixels)
21567      */   
21568     width: 500,
21569     
21570     /**
21571      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21572      * 
21573      */
21574     stylesheets: false,
21575     
21576     // id of frame..
21577     frameId: false,
21578     
21579     // private properties
21580     validationEvent : false,
21581     deferHeight: true,
21582     initialized : false,
21583     activated : false,
21584     sourceEditMode : false,
21585     onFocus : Roo.emptyFn,
21586     iframePad:3,
21587     hideMode:'offsets',
21588     
21589     clearUp: true,
21590     
21591     // blacklist + whitelisted elements..
21592     black: false,
21593     white: false,
21594      
21595     bodyCls : '',
21596
21597     /**
21598      * Protected method that will not generally be called directly. It
21599      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21600      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21601      */
21602     getDocMarkup : function(){
21603         // body styles..
21604         var st = '';
21605         
21606         // inherit styels from page...?? 
21607         if (this.stylesheets === false) {
21608             
21609             Roo.get(document.head).select('style').each(function(node) {
21610                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21611             });
21612             
21613             Roo.get(document.head).select('link').each(function(node) { 
21614                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21615             });
21616             
21617         } else if (!this.stylesheets.length) {
21618                 // simple..
21619                 st = '<style type="text/css">' +
21620                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21621                    '</style>';
21622         } else { 
21623             st = '<style type="text/css">' +
21624                     this.stylesheets +
21625                 '</style>';
21626         }
21627         
21628         st +=  '<style type="text/css">' +
21629             'IMG { cursor: pointer } ' +
21630         '</style>';
21631
21632         var cls = 'roo-htmleditor-body';
21633         
21634         if(this.bodyCls.length){
21635             cls += ' ' + this.bodyCls;
21636         }
21637         
21638         return '<html><head>' + st  +
21639             //<style type="text/css">' +
21640             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21641             //'</style>' +
21642             ' </head><body class="' +  cls + '"></body></html>';
21643     },
21644
21645     // private
21646     onRender : function(ct, position)
21647     {
21648         var _t = this;
21649         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21650         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21651         
21652         
21653         this.el.dom.style.border = '0 none';
21654         this.el.dom.setAttribute('tabIndex', -1);
21655         this.el.addClass('x-hidden hide');
21656         
21657         
21658         
21659         if(Roo.isIE){ // fix IE 1px bogus margin
21660             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21661         }
21662        
21663         
21664         this.frameId = Roo.id();
21665         
21666          
21667         
21668         var iframe = this.owner.wrap.createChild({
21669             tag: 'iframe',
21670             cls: 'form-control', // bootstrap..
21671             id: this.frameId,
21672             name: this.frameId,
21673             frameBorder : 'no',
21674             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21675         }, this.el
21676         );
21677         
21678         
21679         this.iframe = iframe.dom;
21680
21681          this.assignDocWin();
21682         
21683         this.doc.designMode = 'on';
21684        
21685         this.doc.open();
21686         this.doc.write(this.getDocMarkup());
21687         this.doc.close();
21688
21689         
21690         var task = { // must defer to wait for browser to be ready
21691             run : function(){
21692                 //console.log("run task?" + this.doc.readyState);
21693                 this.assignDocWin();
21694                 if(this.doc.body || this.doc.readyState == 'complete'){
21695                     try {
21696                         this.doc.designMode="on";
21697                     } catch (e) {
21698                         return;
21699                     }
21700                     Roo.TaskMgr.stop(task);
21701                     this.initEditor.defer(10, this);
21702                 }
21703             },
21704             interval : 10,
21705             duration: 10000,
21706             scope: this
21707         };
21708         Roo.TaskMgr.start(task);
21709
21710     },
21711
21712     // private
21713     onResize : function(w, h)
21714     {
21715          Roo.log('resize: ' +w + ',' + h );
21716         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21717         if(!this.iframe){
21718             return;
21719         }
21720         if(typeof w == 'number'){
21721             
21722             this.iframe.style.width = w + 'px';
21723         }
21724         if(typeof h == 'number'){
21725             
21726             this.iframe.style.height = h + 'px';
21727             if(this.doc){
21728                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21729             }
21730         }
21731         
21732     },
21733
21734     /**
21735      * Toggles the editor between standard and source edit mode.
21736      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21737      */
21738     toggleSourceEdit : function(sourceEditMode){
21739         
21740         this.sourceEditMode = sourceEditMode === true;
21741         
21742         if(this.sourceEditMode){
21743  
21744             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21745             
21746         }else{
21747             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21748             //this.iframe.className = '';
21749             this.deferFocus();
21750         }
21751         //this.setSize(this.owner.wrap.getSize());
21752         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21753     },
21754
21755     
21756   
21757
21758     /**
21759      * Protected method that will not generally be called directly. If you need/want
21760      * custom HTML cleanup, this is the method you should override.
21761      * @param {String} html The HTML to be cleaned
21762      * return {String} The cleaned HTML
21763      */
21764     cleanHtml : function(html){
21765         html = String(html);
21766         if(html.length > 5){
21767             if(Roo.isSafari){ // strip safari nonsense
21768                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21769             }
21770         }
21771         if(html == '&nbsp;'){
21772             html = '';
21773         }
21774         return html;
21775     },
21776
21777     /**
21778      * HTML Editor -> Textarea
21779      * Protected method that will not generally be called directly. Syncs the contents
21780      * of the editor iframe with the textarea.
21781      */
21782     syncValue : function(){
21783         if(this.initialized){
21784             var bd = (this.doc.body || this.doc.documentElement);
21785             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21786             var html = bd.innerHTML;
21787             if(Roo.isSafari){
21788                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21789                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21790                 if(m && m[1]){
21791                     html = '<div style="'+m[0]+'">' + html + '</div>';
21792                 }
21793             }
21794             html = this.cleanHtml(html);
21795             // fix up the special chars.. normaly like back quotes in word...
21796             // however we do not want to do this with chinese..
21797             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21798                 var cc = b.charCodeAt();
21799                 if (
21800                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21801                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21802                     (cc >= 0xf900 && cc < 0xfb00 )
21803                 ) {
21804                         return b;
21805                 }
21806                 return "&#"+cc+";" 
21807             });
21808             if(this.owner.fireEvent('beforesync', this, html) !== false){
21809                 this.el.dom.value = html;
21810                 this.owner.fireEvent('sync', this, html);
21811             }
21812         }
21813     },
21814
21815     /**
21816      * Protected method that will not generally be called directly. Pushes the value of the textarea
21817      * into the iframe editor.
21818      */
21819     pushValue : function(){
21820         if(this.initialized){
21821             var v = this.el.dom.value.trim();
21822             
21823 //            if(v.length < 1){
21824 //                v = '&#160;';
21825 //            }
21826             
21827             if(this.owner.fireEvent('beforepush', this, v) !== false){
21828                 var d = (this.doc.body || this.doc.documentElement);
21829                 d.innerHTML = v;
21830                 this.cleanUpPaste();
21831                 this.el.dom.value = d.innerHTML;
21832                 this.owner.fireEvent('push', this, v);
21833             }
21834         }
21835     },
21836
21837     // private
21838     deferFocus : function(){
21839         this.focus.defer(10, this);
21840     },
21841
21842     // doc'ed in Field
21843     focus : function(){
21844         if(this.win && !this.sourceEditMode){
21845             this.win.focus();
21846         }else{
21847             this.el.focus();
21848         }
21849     },
21850     
21851     assignDocWin: function()
21852     {
21853         var iframe = this.iframe;
21854         
21855          if(Roo.isIE){
21856             this.doc = iframe.contentWindow.document;
21857             this.win = iframe.contentWindow;
21858         } else {
21859 //            if (!Roo.get(this.frameId)) {
21860 //                return;
21861 //            }
21862 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21863 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21864             
21865             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21866                 return;
21867             }
21868             
21869             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21870             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21871         }
21872     },
21873     
21874     // private
21875     initEditor : function(){
21876         //console.log("INIT EDITOR");
21877         this.assignDocWin();
21878         
21879         
21880         
21881         this.doc.designMode="on";
21882         this.doc.open();
21883         this.doc.write(this.getDocMarkup());
21884         this.doc.close();
21885         
21886         var dbody = (this.doc.body || this.doc.documentElement);
21887         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21888         // this copies styles from the containing element into thsi one..
21889         // not sure why we need all of this..
21890         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21891         
21892         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21893         //ss['background-attachment'] = 'fixed'; // w3c
21894         dbody.bgProperties = 'fixed'; // ie
21895         //Roo.DomHelper.applyStyles(dbody, ss);
21896         Roo.EventManager.on(this.doc, {
21897             //'mousedown': this.onEditorEvent,
21898             'mouseup': this.onEditorEvent,
21899             'dblclick': this.onEditorEvent,
21900             'click': this.onEditorEvent,
21901             'keyup': this.onEditorEvent,
21902             buffer:100,
21903             scope: this
21904         });
21905         if(Roo.isGecko){
21906             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21907         }
21908         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21909             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21910         }
21911         this.initialized = true;
21912
21913         this.owner.fireEvent('initialize', this);
21914         this.pushValue();
21915     },
21916
21917     // private
21918     onDestroy : function(){
21919         
21920         
21921         
21922         if(this.rendered){
21923             
21924             //for (var i =0; i < this.toolbars.length;i++) {
21925             //    // fixme - ask toolbars for heights?
21926             //    this.toolbars[i].onDestroy();
21927            // }
21928             
21929             //this.wrap.dom.innerHTML = '';
21930             //this.wrap.remove();
21931         }
21932     },
21933
21934     // private
21935     onFirstFocus : function(){
21936         
21937         this.assignDocWin();
21938         
21939         
21940         this.activated = true;
21941          
21942     
21943         if(Roo.isGecko){ // prevent silly gecko errors
21944             this.win.focus();
21945             var s = this.win.getSelection();
21946             if(!s.focusNode || s.focusNode.nodeType != 3){
21947                 var r = s.getRangeAt(0);
21948                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21949                 r.collapse(true);
21950                 this.deferFocus();
21951             }
21952             try{
21953                 this.execCmd('useCSS', true);
21954                 this.execCmd('styleWithCSS', false);
21955             }catch(e){}
21956         }
21957         this.owner.fireEvent('activate', this);
21958     },
21959
21960     // private
21961     adjustFont: function(btn){
21962         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21963         //if(Roo.isSafari){ // safari
21964         //    adjust *= 2;
21965        // }
21966         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21967         if(Roo.isSafari){ // safari
21968             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21969             v =  (v < 10) ? 10 : v;
21970             v =  (v > 48) ? 48 : v;
21971             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21972             
21973         }
21974         
21975         
21976         v = Math.max(1, v+adjust);
21977         
21978         this.execCmd('FontSize', v  );
21979     },
21980
21981     onEditorEvent : function(e)
21982     {
21983         this.owner.fireEvent('editorevent', this, e);
21984       //  this.updateToolbar();
21985         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21986     },
21987
21988     insertTag : function(tg)
21989     {
21990         // could be a bit smarter... -> wrap the current selected tRoo..
21991         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21992             
21993             range = this.createRange(this.getSelection());
21994             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21995             wrappingNode.appendChild(range.extractContents());
21996             range.insertNode(wrappingNode);
21997
21998             return;
21999             
22000             
22001             
22002         }
22003         this.execCmd("formatblock",   tg);
22004         
22005     },
22006     
22007     insertText : function(txt)
22008     {
22009         
22010         
22011         var range = this.createRange();
22012         range.deleteContents();
22013                //alert(Sender.getAttribute('label'));
22014                
22015         range.insertNode(this.doc.createTextNode(txt));
22016     } ,
22017     
22018      
22019
22020     /**
22021      * Executes a Midas editor command on the editor document and performs necessary focus and
22022      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22023      * @param {String} cmd The Midas command
22024      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22025      */
22026     relayCmd : function(cmd, value){
22027         this.win.focus();
22028         this.execCmd(cmd, value);
22029         this.owner.fireEvent('editorevent', this);
22030         //this.updateToolbar();
22031         this.owner.deferFocus();
22032     },
22033
22034     /**
22035      * Executes a Midas editor command directly on the editor document.
22036      * For visual commands, you should use {@link #relayCmd} instead.
22037      * <b>This should only be called after the editor is initialized.</b>
22038      * @param {String} cmd The Midas command
22039      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22040      */
22041     execCmd : function(cmd, value){
22042         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22043         this.syncValue();
22044     },
22045  
22046  
22047    
22048     /**
22049      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22050      * to insert tRoo.
22051      * @param {String} text | dom node.. 
22052      */
22053     insertAtCursor : function(text)
22054     {
22055         
22056         if(!this.activated){
22057             return;
22058         }
22059         /*
22060         if(Roo.isIE){
22061             this.win.focus();
22062             var r = this.doc.selection.createRange();
22063             if(r){
22064                 r.collapse(true);
22065                 r.pasteHTML(text);
22066                 this.syncValue();
22067                 this.deferFocus();
22068             
22069             }
22070             return;
22071         }
22072         */
22073         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22074             this.win.focus();
22075             
22076             
22077             // from jquery ui (MIT licenced)
22078             var range, node;
22079             var win = this.win;
22080             
22081             if (win.getSelection && win.getSelection().getRangeAt) {
22082                 range = win.getSelection().getRangeAt(0);
22083                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22084                 range.insertNode(node);
22085             } else if (win.document.selection && win.document.selection.createRange) {
22086                 // no firefox support
22087                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22088                 win.document.selection.createRange().pasteHTML(txt);
22089             } else {
22090                 // no firefox support
22091                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22092                 this.execCmd('InsertHTML', txt);
22093             } 
22094             
22095             this.syncValue();
22096             
22097             this.deferFocus();
22098         }
22099     },
22100  // private
22101     mozKeyPress : function(e){
22102         if(e.ctrlKey){
22103             var c = e.getCharCode(), cmd;
22104           
22105             if(c > 0){
22106                 c = String.fromCharCode(c).toLowerCase();
22107                 switch(c){
22108                     case 'b':
22109                         cmd = 'bold';
22110                         break;
22111                     case 'i':
22112                         cmd = 'italic';
22113                         break;
22114                     
22115                     case 'u':
22116                         cmd = 'underline';
22117                         break;
22118                     
22119                     case 'v':
22120                         this.cleanUpPaste.defer(100, this);
22121                         return;
22122                         
22123                 }
22124                 if(cmd){
22125                     this.win.focus();
22126                     this.execCmd(cmd);
22127                     this.deferFocus();
22128                     e.preventDefault();
22129                 }
22130                 
22131             }
22132         }
22133     },
22134
22135     // private
22136     fixKeys : function(){ // load time branching for fastest keydown performance
22137         if(Roo.isIE){
22138             return function(e){
22139                 var k = e.getKey(), r;
22140                 if(k == e.TAB){
22141                     e.stopEvent();
22142                     r = this.doc.selection.createRange();
22143                     if(r){
22144                         r.collapse(true);
22145                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22146                         this.deferFocus();
22147                     }
22148                     return;
22149                 }
22150                 
22151                 if(k == e.ENTER){
22152                     r = this.doc.selection.createRange();
22153                     if(r){
22154                         var target = r.parentElement();
22155                         if(!target || target.tagName.toLowerCase() != 'li'){
22156                             e.stopEvent();
22157                             r.pasteHTML('<br />');
22158                             r.collapse(false);
22159                             r.select();
22160                         }
22161                     }
22162                 }
22163                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22164                     this.cleanUpPaste.defer(100, this);
22165                     return;
22166                 }
22167                 
22168                 
22169             };
22170         }else if(Roo.isOpera){
22171             return function(e){
22172                 var k = e.getKey();
22173                 if(k == e.TAB){
22174                     e.stopEvent();
22175                     this.win.focus();
22176                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22177                     this.deferFocus();
22178                 }
22179                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22180                     this.cleanUpPaste.defer(100, this);
22181                     return;
22182                 }
22183                 
22184             };
22185         }else if(Roo.isSafari){
22186             return function(e){
22187                 var k = e.getKey();
22188                 
22189                 if(k == e.TAB){
22190                     e.stopEvent();
22191                     this.execCmd('InsertText','\t');
22192                     this.deferFocus();
22193                     return;
22194                 }
22195                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22196                     this.cleanUpPaste.defer(100, this);
22197                     return;
22198                 }
22199                 
22200              };
22201         }
22202     }(),
22203     
22204     getAllAncestors: function()
22205     {
22206         var p = this.getSelectedNode();
22207         var a = [];
22208         if (!p) {
22209             a.push(p); // push blank onto stack..
22210             p = this.getParentElement();
22211         }
22212         
22213         
22214         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22215             a.push(p);
22216             p = p.parentNode;
22217         }
22218         a.push(this.doc.body);
22219         return a;
22220     },
22221     lastSel : false,
22222     lastSelNode : false,
22223     
22224     
22225     getSelection : function() 
22226     {
22227         this.assignDocWin();
22228         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22229     },
22230     
22231     getSelectedNode: function() 
22232     {
22233         // this may only work on Gecko!!!
22234         
22235         // should we cache this!!!!
22236         
22237         
22238         
22239          
22240         var range = this.createRange(this.getSelection()).cloneRange();
22241         
22242         if (Roo.isIE) {
22243             var parent = range.parentElement();
22244             while (true) {
22245                 var testRange = range.duplicate();
22246                 testRange.moveToElementText(parent);
22247                 if (testRange.inRange(range)) {
22248                     break;
22249                 }
22250                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22251                     break;
22252                 }
22253                 parent = parent.parentElement;
22254             }
22255             return parent;
22256         }
22257         
22258         // is ancestor a text element.
22259         var ac =  range.commonAncestorContainer;
22260         if (ac.nodeType == 3) {
22261             ac = ac.parentNode;
22262         }
22263         
22264         var ar = ac.childNodes;
22265          
22266         var nodes = [];
22267         var other_nodes = [];
22268         var has_other_nodes = false;
22269         for (var i=0;i<ar.length;i++) {
22270             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22271                 continue;
22272             }
22273             // fullly contained node.
22274             
22275             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22276                 nodes.push(ar[i]);
22277                 continue;
22278             }
22279             
22280             // probably selected..
22281             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22282                 other_nodes.push(ar[i]);
22283                 continue;
22284             }
22285             // outer..
22286             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22287                 continue;
22288             }
22289             
22290             
22291             has_other_nodes = true;
22292         }
22293         if (!nodes.length && other_nodes.length) {
22294             nodes= other_nodes;
22295         }
22296         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22297             return false;
22298         }
22299         
22300         return nodes[0];
22301     },
22302     createRange: function(sel)
22303     {
22304         // this has strange effects when using with 
22305         // top toolbar - not sure if it's a great idea.
22306         //this.editor.contentWindow.focus();
22307         if (typeof sel != "undefined") {
22308             try {
22309                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22310             } catch(e) {
22311                 return this.doc.createRange();
22312             }
22313         } else {
22314             return this.doc.createRange();
22315         }
22316     },
22317     getParentElement: function()
22318     {
22319         
22320         this.assignDocWin();
22321         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22322         
22323         var range = this.createRange(sel);
22324          
22325         try {
22326             var p = range.commonAncestorContainer;
22327             while (p.nodeType == 3) { // text node
22328                 p = p.parentNode;
22329             }
22330             return p;
22331         } catch (e) {
22332             return null;
22333         }
22334     
22335     },
22336     /***
22337      *
22338      * Range intersection.. the hard stuff...
22339      *  '-1' = before
22340      *  '0' = hits..
22341      *  '1' = after.
22342      *         [ -- selected range --- ]
22343      *   [fail]                        [fail]
22344      *
22345      *    basically..
22346      *      if end is before start or  hits it. fail.
22347      *      if start is after end or hits it fail.
22348      *
22349      *   if either hits (but other is outside. - then it's not 
22350      *   
22351      *    
22352      **/
22353     
22354     
22355     // @see http://www.thismuchiknow.co.uk/?p=64.
22356     rangeIntersectsNode : function(range, node)
22357     {
22358         var nodeRange = node.ownerDocument.createRange();
22359         try {
22360             nodeRange.selectNode(node);
22361         } catch (e) {
22362             nodeRange.selectNodeContents(node);
22363         }
22364     
22365         var rangeStartRange = range.cloneRange();
22366         rangeStartRange.collapse(true);
22367     
22368         var rangeEndRange = range.cloneRange();
22369         rangeEndRange.collapse(false);
22370     
22371         var nodeStartRange = nodeRange.cloneRange();
22372         nodeStartRange.collapse(true);
22373     
22374         var nodeEndRange = nodeRange.cloneRange();
22375         nodeEndRange.collapse(false);
22376     
22377         return rangeStartRange.compareBoundaryPoints(
22378                  Range.START_TO_START, nodeEndRange) == -1 &&
22379                rangeEndRange.compareBoundaryPoints(
22380                  Range.START_TO_START, nodeStartRange) == 1;
22381         
22382          
22383     },
22384     rangeCompareNode : function(range, node)
22385     {
22386         var nodeRange = node.ownerDocument.createRange();
22387         try {
22388             nodeRange.selectNode(node);
22389         } catch (e) {
22390             nodeRange.selectNodeContents(node);
22391         }
22392         
22393         
22394         range.collapse(true);
22395     
22396         nodeRange.collapse(true);
22397      
22398         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22399         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22400          
22401         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22402         
22403         var nodeIsBefore   =  ss == 1;
22404         var nodeIsAfter    = ee == -1;
22405         
22406         if (nodeIsBefore && nodeIsAfter) {
22407             return 0; // outer
22408         }
22409         if (!nodeIsBefore && nodeIsAfter) {
22410             return 1; //right trailed.
22411         }
22412         
22413         if (nodeIsBefore && !nodeIsAfter) {
22414             return 2;  // left trailed.
22415         }
22416         // fully contined.
22417         return 3;
22418     },
22419
22420     // private? - in a new class?
22421     cleanUpPaste :  function()
22422     {
22423         // cleans up the whole document..
22424         Roo.log('cleanuppaste');
22425         
22426         this.cleanUpChildren(this.doc.body);
22427         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22428         if (clean != this.doc.body.innerHTML) {
22429             this.doc.body.innerHTML = clean;
22430         }
22431         
22432     },
22433     
22434     cleanWordChars : function(input) {// change the chars to hex code
22435         var he = Roo.HtmlEditorCore;
22436         
22437         var output = input;
22438         Roo.each(he.swapCodes, function(sw) { 
22439             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22440             
22441             output = output.replace(swapper, sw[1]);
22442         });
22443         
22444         return output;
22445     },
22446     
22447     
22448     cleanUpChildren : function (n)
22449     {
22450         if (!n.childNodes.length) {
22451             return;
22452         }
22453         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22454            this.cleanUpChild(n.childNodes[i]);
22455         }
22456     },
22457     
22458     
22459         
22460     
22461     cleanUpChild : function (node)
22462     {
22463         var ed = this;
22464         //console.log(node);
22465         if (node.nodeName == "#text") {
22466             // clean up silly Windows -- stuff?
22467             return; 
22468         }
22469         if (node.nodeName == "#comment") {
22470             node.parentNode.removeChild(node);
22471             // clean up silly Windows -- stuff?
22472             return; 
22473         }
22474         var lcname = node.tagName.toLowerCase();
22475         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22476         // whitelist of tags..
22477         
22478         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22479             // remove node.
22480             node.parentNode.removeChild(node);
22481             return;
22482             
22483         }
22484         
22485         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22486         
22487         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22488         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22489         
22490         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22491         //    remove_keep_children = true;
22492         //}
22493         
22494         if (remove_keep_children) {
22495             this.cleanUpChildren(node);
22496             // inserts everything just before this node...
22497             while (node.childNodes.length) {
22498                 var cn = node.childNodes[0];
22499                 node.removeChild(cn);
22500                 node.parentNode.insertBefore(cn, node);
22501             }
22502             node.parentNode.removeChild(node);
22503             return;
22504         }
22505         
22506         if (!node.attributes || !node.attributes.length) {
22507             this.cleanUpChildren(node);
22508             return;
22509         }
22510         
22511         function cleanAttr(n,v)
22512         {
22513             
22514             if (v.match(/^\./) || v.match(/^\//)) {
22515                 return;
22516             }
22517             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22518                 return;
22519             }
22520             if (v.match(/^#/)) {
22521                 return;
22522             }
22523 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22524             node.removeAttribute(n);
22525             
22526         }
22527         
22528         var cwhite = this.cwhite;
22529         var cblack = this.cblack;
22530             
22531         function cleanStyle(n,v)
22532         {
22533             if (v.match(/expression/)) { //XSS?? should we even bother..
22534                 node.removeAttribute(n);
22535                 return;
22536             }
22537             
22538             var parts = v.split(/;/);
22539             var clean = [];
22540             
22541             Roo.each(parts, function(p) {
22542                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22543                 if (!p.length) {
22544                     return true;
22545                 }
22546                 var l = p.split(':').shift().replace(/\s+/g,'');
22547                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22548                 
22549                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22550 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22551                     //node.removeAttribute(n);
22552                     return true;
22553                 }
22554                 //Roo.log()
22555                 // only allow 'c whitelisted system attributes'
22556                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22557 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22558                     //node.removeAttribute(n);
22559                     return true;
22560                 }
22561                 
22562                 
22563                  
22564                 
22565                 clean.push(p);
22566                 return true;
22567             });
22568             if (clean.length) { 
22569                 node.setAttribute(n, clean.join(';'));
22570             } else {
22571                 node.removeAttribute(n);
22572             }
22573             
22574         }
22575         
22576         
22577         for (var i = node.attributes.length-1; i > -1 ; i--) {
22578             var a = node.attributes[i];
22579             //console.log(a);
22580             
22581             if (a.name.toLowerCase().substr(0,2)=='on')  {
22582                 node.removeAttribute(a.name);
22583                 continue;
22584             }
22585             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22586                 node.removeAttribute(a.name);
22587                 continue;
22588             }
22589             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22590                 cleanAttr(a.name,a.value); // fixme..
22591                 continue;
22592             }
22593             if (a.name == 'style') {
22594                 cleanStyle(a.name,a.value);
22595                 continue;
22596             }
22597             /// clean up MS crap..
22598             // tecnically this should be a list of valid class'es..
22599             
22600             
22601             if (a.name == 'class') {
22602                 if (a.value.match(/^Mso/)) {
22603                     node.className = '';
22604                 }
22605                 
22606                 if (a.value.match(/^body$/)) {
22607                     node.className = '';
22608                 }
22609                 continue;
22610             }
22611             
22612             // style cleanup!?
22613             // class cleanup?
22614             
22615         }
22616         
22617         
22618         this.cleanUpChildren(node);
22619         
22620         
22621     },
22622     
22623     /**
22624      * Clean up MS wordisms...
22625      */
22626     cleanWord : function(node)
22627     {
22628         
22629         
22630         if (!node) {
22631             this.cleanWord(this.doc.body);
22632             return;
22633         }
22634         if (node.nodeName == "#text") {
22635             // clean up silly Windows -- stuff?
22636             return; 
22637         }
22638         if (node.nodeName == "#comment") {
22639             node.parentNode.removeChild(node);
22640             // clean up silly Windows -- stuff?
22641             return; 
22642         }
22643         
22644         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22645             node.parentNode.removeChild(node);
22646             return;
22647         }
22648         
22649         // remove - but keep children..
22650         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22651             while (node.childNodes.length) {
22652                 var cn = node.childNodes[0];
22653                 node.removeChild(cn);
22654                 node.parentNode.insertBefore(cn, node);
22655             }
22656             node.parentNode.removeChild(node);
22657             this.iterateChildren(node, this.cleanWord);
22658             return;
22659         }
22660         // clean styles
22661         if (node.className.length) {
22662             
22663             var cn = node.className.split(/\W+/);
22664             var cna = [];
22665             Roo.each(cn, function(cls) {
22666                 if (cls.match(/Mso[a-zA-Z]+/)) {
22667                     return;
22668                 }
22669                 cna.push(cls);
22670             });
22671             node.className = cna.length ? cna.join(' ') : '';
22672             if (!cna.length) {
22673                 node.removeAttribute("class");
22674             }
22675         }
22676         
22677         if (node.hasAttribute("lang")) {
22678             node.removeAttribute("lang");
22679         }
22680         
22681         if (node.hasAttribute("style")) {
22682             
22683             var styles = node.getAttribute("style").split(";");
22684             var nstyle = [];
22685             Roo.each(styles, function(s) {
22686                 if (!s.match(/:/)) {
22687                     return;
22688                 }
22689                 var kv = s.split(":");
22690                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22691                     return;
22692                 }
22693                 // what ever is left... we allow.
22694                 nstyle.push(s);
22695             });
22696             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22697             if (!nstyle.length) {
22698                 node.removeAttribute('style');
22699             }
22700         }
22701         this.iterateChildren(node, this.cleanWord);
22702         
22703         
22704         
22705     },
22706     /**
22707      * iterateChildren of a Node, calling fn each time, using this as the scole..
22708      * @param {DomNode} node node to iterate children of.
22709      * @param {Function} fn method of this class to call on each item.
22710      */
22711     iterateChildren : function(node, fn)
22712     {
22713         if (!node.childNodes.length) {
22714                 return;
22715         }
22716         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22717            fn.call(this, node.childNodes[i])
22718         }
22719     },
22720     
22721     
22722     /**
22723      * cleanTableWidths.
22724      *
22725      * Quite often pasting from word etc.. results in tables with column and widths.
22726      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22727      *
22728      */
22729     cleanTableWidths : function(node)
22730     {
22731          
22732          
22733         if (!node) {
22734             this.cleanTableWidths(this.doc.body);
22735             return;
22736         }
22737         
22738         // ignore list...
22739         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22740             return; 
22741         }
22742         Roo.log(node.tagName);
22743         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22744             this.iterateChildren(node, this.cleanTableWidths);
22745             return;
22746         }
22747         if (node.hasAttribute('width')) {
22748             node.removeAttribute('width');
22749         }
22750         
22751          
22752         if (node.hasAttribute("style")) {
22753             // pretty basic...
22754             
22755             var styles = node.getAttribute("style").split(";");
22756             var nstyle = [];
22757             Roo.each(styles, function(s) {
22758                 if (!s.match(/:/)) {
22759                     return;
22760                 }
22761                 var kv = s.split(":");
22762                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22763                     return;
22764                 }
22765                 // what ever is left... we allow.
22766                 nstyle.push(s);
22767             });
22768             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22769             if (!nstyle.length) {
22770                 node.removeAttribute('style');
22771             }
22772         }
22773         
22774         this.iterateChildren(node, this.cleanTableWidths);
22775         
22776         
22777     },
22778     
22779     
22780     
22781     
22782     domToHTML : function(currentElement, depth, nopadtext) {
22783         
22784         depth = depth || 0;
22785         nopadtext = nopadtext || false;
22786     
22787         if (!currentElement) {
22788             return this.domToHTML(this.doc.body);
22789         }
22790         
22791         //Roo.log(currentElement);
22792         var j;
22793         var allText = false;
22794         var nodeName = currentElement.nodeName;
22795         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22796         
22797         if  (nodeName == '#text') {
22798             
22799             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22800         }
22801         
22802         
22803         var ret = '';
22804         if (nodeName != 'BODY') {
22805              
22806             var i = 0;
22807             // Prints the node tagName, such as <A>, <IMG>, etc
22808             if (tagName) {
22809                 var attr = [];
22810                 for(i = 0; i < currentElement.attributes.length;i++) {
22811                     // quoting?
22812                     var aname = currentElement.attributes.item(i).name;
22813                     if (!currentElement.attributes.item(i).value.length) {
22814                         continue;
22815                     }
22816                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22817                 }
22818                 
22819                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22820             } 
22821             else {
22822                 
22823                 // eack
22824             }
22825         } else {
22826             tagName = false;
22827         }
22828         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22829             return ret;
22830         }
22831         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22832             nopadtext = true;
22833         }
22834         
22835         
22836         // Traverse the tree
22837         i = 0;
22838         var currentElementChild = currentElement.childNodes.item(i);
22839         var allText = true;
22840         var innerHTML  = '';
22841         lastnode = '';
22842         while (currentElementChild) {
22843             // Formatting code (indent the tree so it looks nice on the screen)
22844             var nopad = nopadtext;
22845             if (lastnode == 'SPAN') {
22846                 nopad  = true;
22847             }
22848             // text
22849             if  (currentElementChild.nodeName == '#text') {
22850                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22851                 toadd = nopadtext ? toadd : toadd.trim();
22852                 if (!nopad && toadd.length > 80) {
22853                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22854                 }
22855                 innerHTML  += toadd;
22856                 
22857                 i++;
22858                 currentElementChild = currentElement.childNodes.item(i);
22859                 lastNode = '';
22860                 continue;
22861             }
22862             allText = false;
22863             
22864             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22865                 
22866             // Recursively traverse the tree structure of the child node
22867             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22868             lastnode = currentElementChild.nodeName;
22869             i++;
22870             currentElementChild=currentElement.childNodes.item(i);
22871         }
22872         
22873         ret += innerHTML;
22874         
22875         if (!allText) {
22876                 // The remaining code is mostly for formatting the tree
22877             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22878         }
22879         
22880         
22881         if (tagName) {
22882             ret+= "</"+tagName+">";
22883         }
22884         return ret;
22885         
22886     },
22887         
22888     applyBlacklists : function()
22889     {
22890         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22891         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22892         
22893         this.white = [];
22894         this.black = [];
22895         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22896             if (b.indexOf(tag) > -1) {
22897                 return;
22898             }
22899             this.white.push(tag);
22900             
22901         }, this);
22902         
22903         Roo.each(w, function(tag) {
22904             if (b.indexOf(tag) > -1) {
22905                 return;
22906             }
22907             if (this.white.indexOf(tag) > -1) {
22908                 return;
22909             }
22910             this.white.push(tag);
22911             
22912         }, this);
22913         
22914         
22915         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22916             if (w.indexOf(tag) > -1) {
22917                 return;
22918             }
22919             this.black.push(tag);
22920             
22921         }, this);
22922         
22923         Roo.each(b, function(tag) {
22924             if (w.indexOf(tag) > -1) {
22925                 return;
22926             }
22927             if (this.black.indexOf(tag) > -1) {
22928                 return;
22929             }
22930             this.black.push(tag);
22931             
22932         }, this);
22933         
22934         
22935         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22936         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22937         
22938         this.cwhite = [];
22939         this.cblack = [];
22940         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22941             if (b.indexOf(tag) > -1) {
22942                 return;
22943             }
22944             this.cwhite.push(tag);
22945             
22946         }, this);
22947         
22948         Roo.each(w, function(tag) {
22949             if (b.indexOf(tag) > -1) {
22950                 return;
22951             }
22952             if (this.cwhite.indexOf(tag) > -1) {
22953                 return;
22954             }
22955             this.cwhite.push(tag);
22956             
22957         }, this);
22958         
22959         
22960         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22961             if (w.indexOf(tag) > -1) {
22962                 return;
22963             }
22964             this.cblack.push(tag);
22965             
22966         }, this);
22967         
22968         Roo.each(b, function(tag) {
22969             if (w.indexOf(tag) > -1) {
22970                 return;
22971             }
22972             if (this.cblack.indexOf(tag) > -1) {
22973                 return;
22974             }
22975             this.cblack.push(tag);
22976             
22977         }, this);
22978     },
22979     
22980     setStylesheets : function(stylesheets)
22981     {
22982         if(typeof(stylesheets) == 'string'){
22983             Roo.get(this.iframe.contentDocument.head).createChild({
22984                 tag : 'link',
22985                 rel : 'stylesheet',
22986                 type : 'text/css',
22987                 href : stylesheets
22988             });
22989             
22990             return;
22991         }
22992         var _this = this;
22993      
22994         Roo.each(stylesheets, function(s) {
22995             if(!s.length){
22996                 return;
22997             }
22998             
22999             Roo.get(_this.iframe.contentDocument.head).createChild({
23000                 tag : 'link',
23001                 rel : 'stylesheet',
23002                 type : 'text/css',
23003                 href : s
23004             });
23005         });
23006
23007         
23008     },
23009     
23010     removeStylesheets : function()
23011     {
23012         var _this = this;
23013         
23014         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23015             s.remove();
23016         });
23017     },
23018     
23019     setStyle : function(style)
23020     {
23021         Roo.get(this.iframe.contentDocument.head).createChild({
23022             tag : 'style',
23023             type : 'text/css',
23024             html : style
23025         });
23026
23027         return;
23028     }
23029     
23030     // hide stuff that is not compatible
23031     /**
23032      * @event blur
23033      * @hide
23034      */
23035     /**
23036      * @event change
23037      * @hide
23038      */
23039     /**
23040      * @event focus
23041      * @hide
23042      */
23043     /**
23044      * @event specialkey
23045      * @hide
23046      */
23047     /**
23048      * @cfg {String} fieldClass @hide
23049      */
23050     /**
23051      * @cfg {String} focusClass @hide
23052      */
23053     /**
23054      * @cfg {String} autoCreate @hide
23055      */
23056     /**
23057      * @cfg {String} inputType @hide
23058      */
23059     /**
23060      * @cfg {String} invalidClass @hide
23061      */
23062     /**
23063      * @cfg {String} invalidText @hide
23064      */
23065     /**
23066      * @cfg {String} msgFx @hide
23067      */
23068     /**
23069      * @cfg {String} validateOnBlur @hide
23070      */
23071 });
23072
23073 Roo.HtmlEditorCore.white = [
23074         'area', 'br', 'img', 'input', 'hr', 'wbr',
23075         
23076        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23077        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23078        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23079        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23080        'table',   'ul',         'xmp', 
23081        
23082        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23083       'thead',   'tr', 
23084      
23085       'dir', 'menu', 'ol', 'ul', 'dl',
23086        
23087       'embed',  'object'
23088 ];
23089
23090
23091 Roo.HtmlEditorCore.black = [
23092     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23093         'applet', // 
23094         'base',   'basefont', 'bgsound', 'blink',  'body', 
23095         'frame',  'frameset', 'head',    'html',   'ilayer', 
23096         'iframe', 'layer',  'link',     'meta',    'object',   
23097         'script', 'style' ,'title',  'xml' // clean later..
23098 ];
23099 Roo.HtmlEditorCore.clean = [
23100     'script', 'style', 'title', 'xml'
23101 ];
23102 Roo.HtmlEditorCore.remove = [
23103     'font'
23104 ];
23105 // attributes..
23106
23107 Roo.HtmlEditorCore.ablack = [
23108     'on'
23109 ];
23110     
23111 Roo.HtmlEditorCore.aclean = [ 
23112     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23113 ];
23114
23115 // protocols..
23116 Roo.HtmlEditorCore.pwhite= [
23117         'http',  'https',  'mailto'
23118 ];
23119
23120 // white listed style attributes.
23121 Roo.HtmlEditorCore.cwhite= [
23122       //  'text-align', /// default is to allow most things..
23123       
23124          
23125 //        'font-size'//??
23126 ];
23127
23128 // black listed style attributes.
23129 Roo.HtmlEditorCore.cblack= [
23130       //  'font-size' -- this can be set by the project 
23131 ];
23132
23133
23134 Roo.HtmlEditorCore.swapCodes   =[ 
23135     [    8211, "--" ], 
23136     [    8212, "--" ], 
23137     [    8216,  "'" ],  
23138     [    8217, "'" ],  
23139     [    8220, '"' ],  
23140     [    8221, '"' ],  
23141     [    8226, "*" ],  
23142     [    8230, "..." ]
23143 ]; 
23144
23145     /*
23146  * - LGPL
23147  *
23148  * HtmlEditor
23149  * 
23150  */
23151
23152 /**
23153  * @class Roo.bootstrap.HtmlEditor
23154  * @extends Roo.bootstrap.TextArea
23155  * Bootstrap HtmlEditor class
23156
23157  * @constructor
23158  * Create a new HtmlEditor
23159  * @param {Object} config The config object
23160  */
23161
23162 Roo.bootstrap.HtmlEditor = function(config){
23163     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23164     if (!this.toolbars) {
23165         this.toolbars = [];
23166     }
23167     
23168     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23169     this.addEvents({
23170             /**
23171              * @event initialize
23172              * Fires when the editor is fully initialized (including the iframe)
23173              * @param {HtmlEditor} this
23174              */
23175             initialize: true,
23176             /**
23177              * @event activate
23178              * Fires when the editor is first receives the focus. Any insertion must wait
23179              * until after this event.
23180              * @param {HtmlEditor} this
23181              */
23182             activate: true,
23183              /**
23184              * @event beforesync
23185              * Fires before the textarea is updated with content from the editor iframe. Return false
23186              * to cancel the sync.
23187              * @param {HtmlEditor} this
23188              * @param {String} html
23189              */
23190             beforesync: true,
23191              /**
23192              * @event beforepush
23193              * Fires before the iframe editor is updated with content from the textarea. Return false
23194              * to cancel the push.
23195              * @param {HtmlEditor} this
23196              * @param {String} html
23197              */
23198             beforepush: true,
23199              /**
23200              * @event sync
23201              * Fires when the textarea is updated with content from the editor iframe.
23202              * @param {HtmlEditor} this
23203              * @param {String} html
23204              */
23205             sync: true,
23206              /**
23207              * @event push
23208              * Fires when the iframe editor is updated with content from the textarea.
23209              * @param {HtmlEditor} this
23210              * @param {String} html
23211              */
23212             push: true,
23213              /**
23214              * @event editmodechange
23215              * Fires when the editor switches edit modes
23216              * @param {HtmlEditor} this
23217              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23218              */
23219             editmodechange: true,
23220             /**
23221              * @event editorevent
23222              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23223              * @param {HtmlEditor} this
23224              */
23225             editorevent: true,
23226             /**
23227              * @event firstfocus
23228              * Fires when on first focus - needed by toolbars..
23229              * @param {HtmlEditor} this
23230              */
23231             firstfocus: true,
23232             /**
23233              * @event autosave
23234              * Auto save the htmlEditor value as a file into Events
23235              * @param {HtmlEditor} this
23236              */
23237             autosave: true,
23238             /**
23239              * @event savedpreview
23240              * preview the saved version of htmlEditor
23241              * @param {HtmlEditor} this
23242              */
23243             savedpreview: true
23244         });
23245 };
23246
23247
23248 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23249     
23250     
23251       /**
23252      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23253      */
23254     toolbars : false,
23255     
23256      /**
23257     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23258     */
23259     btns : [],
23260    
23261      /**
23262      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23263      *                        Roo.resizable.
23264      */
23265     resizable : false,
23266      /**
23267      * @cfg {Number} height (in pixels)
23268      */   
23269     height: 300,
23270    /**
23271      * @cfg {Number} width (in pixels)
23272      */   
23273     width: false,
23274     
23275     /**
23276      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23277      * 
23278      */
23279     stylesheets: false,
23280     
23281     // id of frame..
23282     frameId: false,
23283     
23284     // private properties
23285     validationEvent : false,
23286     deferHeight: true,
23287     initialized : false,
23288     activated : false,
23289     
23290     onFocus : Roo.emptyFn,
23291     iframePad:3,
23292     hideMode:'offsets',
23293     
23294     tbContainer : false,
23295     
23296     bodyCls : '',
23297     
23298     toolbarContainer :function() {
23299         return this.wrap.select('.x-html-editor-tb',true).first();
23300     },
23301
23302     /**
23303      * Protected method that will not generally be called directly. It
23304      * is called when the editor creates its toolbar. Override this method if you need to
23305      * add custom toolbar buttons.
23306      * @param {HtmlEditor} editor
23307      */
23308     createToolbar : function(){
23309         Roo.log('renewing');
23310         Roo.log("create toolbars");
23311         
23312         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23313         this.toolbars[0].render(this.toolbarContainer());
23314         
23315         return;
23316         
23317 //        if (!editor.toolbars || !editor.toolbars.length) {
23318 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23319 //        }
23320 //        
23321 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23322 //            editor.toolbars[i] = Roo.factory(
23323 //                    typeof(editor.toolbars[i]) == 'string' ?
23324 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23325 //                Roo.bootstrap.HtmlEditor);
23326 //            editor.toolbars[i].init(editor);
23327 //        }
23328     },
23329
23330      
23331     // private
23332     onRender : function(ct, position)
23333     {
23334        // Roo.log("Call onRender: " + this.xtype);
23335         var _t = this;
23336         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23337       
23338         this.wrap = this.inputEl().wrap({
23339             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23340         });
23341         
23342         this.editorcore.onRender(ct, position);
23343          
23344         if (this.resizable) {
23345             this.resizeEl = new Roo.Resizable(this.wrap, {
23346                 pinned : true,
23347                 wrap: true,
23348                 dynamic : true,
23349                 minHeight : this.height,
23350                 height: this.height,
23351                 handles : this.resizable,
23352                 width: this.width,
23353                 listeners : {
23354                     resize : function(r, w, h) {
23355                         _t.onResize(w,h); // -something
23356                     }
23357                 }
23358             });
23359             
23360         }
23361         this.createToolbar(this);
23362        
23363         
23364         if(!this.width && this.resizable){
23365             this.setSize(this.wrap.getSize());
23366         }
23367         if (this.resizeEl) {
23368             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23369             // should trigger onReize..
23370         }
23371         
23372     },
23373
23374     // private
23375     onResize : function(w, h)
23376     {
23377         Roo.log('resize: ' +w + ',' + h );
23378         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23379         var ew = false;
23380         var eh = false;
23381         
23382         if(this.inputEl() ){
23383             if(typeof w == 'number'){
23384                 var aw = w - this.wrap.getFrameWidth('lr');
23385                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23386                 ew = aw;
23387             }
23388             if(typeof h == 'number'){
23389                  var tbh = -11;  // fixme it needs to tool bar size!
23390                 for (var i =0; i < this.toolbars.length;i++) {
23391                     // fixme - ask toolbars for heights?
23392                     tbh += this.toolbars[i].el.getHeight();
23393                     //if (this.toolbars[i].footer) {
23394                     //    tbh += this.toolbars[i].footer.el.getHeight();
23395                     //}
23396                 }
23397               
23398                 
23399                 
23400                 
23401                 
23402                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23403                 ah -= 5; // knock a few pixes off for look..
23404                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23405                 var eh = ah;
23406             }
23407         }
23408         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23409         this.editorcore.onResize(ew,eh);
23410         
23411     },
23412
23413     /**
23414      * Toggles the editor between standard and source edit mode.
23415      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23416      */
23417     toggleSourceEdit : function(sourceEditMode)
23418     {
23419         this.editorcore.toggleSourceEdit(sourceEditMode);
23420         
23421         if(this.editorcore.sourceEditMode){
23422             Roo.log('editor - showing textarea');
23423             
23424 //            Roo.log('in');
23425 //            Roo.log(this.syncValue());
23426             this.syncValue();
23427             this.inputEl().removeClass(['hide', 'x-hidden']);
23428             this.inputEl().dom.removeAttribute('tabIndex');
23429             this.inputEl().focus();
23430         }else{
23431             Roo.log('editor - hiding textarea');
23432 //            Roo.log('out')
23433 //            Roo.log(this.pushValue()); 
23434             this.pushValue();
23435             
23436             this.inputEl().addClass(['hide', 'x-hidden']);
23437             this.inputEl().dom.setAttribute('tabIndex', -1);
23438             //this.deferFocus();
23439         }
23440          
23441         if(this.resizable){
23442             this.setSize(this.wrap.getSize());
23443         }
23444         
23445         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23446     },
23447  
23448     // private (for BoxComponent)
23449     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23450
23451     // private (for BoxComponent)
23452     getResizeEl : function(){
23453         return this.wrap;
23454     },
23455
23456     // private (for BoxComponent)
23457     getPositionEl : function(){
23458         return this.wrap;
23459     },
23460
23461     // private
23462     initEvents : function(){
23463         this.originalValue = this.getValue();
23464     },
23465
23466 //    /**
23467 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23468 //     * @method
23469 //     */
23470 //    markInvalid : Roo.emptyFn,
23471 //    /**
23472 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23473 //     * @method
23474 //     */
23475 //    clearInvalid : Roo.emptyFn,
23476
23477     setValue : function(v){
23478         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23479         this.editorcore.pushValue();
23480     },
23481
23482      
23483     // private
23484     deferFocus : function(){
23485         this.focus.defer(10, this);
23486     },
23487
23488     // doc'ed in Field
23489     focus : function(){
23490         this.editorcore.focus();
23491         
23492     },
23493       
23494
23495     // private
23496     onDestroy : function(){
23497         
23498         
23499         
23500         if(this.rendered){
23501             
23502             for (var i =0; i < this.toolbars.length;i++) {
23503                 // fixme - ask toolbars for heights?
23504                 this.toolbars[i].onDestroy();
23505             }
23506             
23507             this.wrap.dom.innerHTML = '';
23508             this.wrap.remove();
23509         }
23510     },
23511
23512     // private
23513     onFirstFocus : function(){
23514         //Roo.log("onFirstFocus");
23515         this.editorcore.onFirstFocus();
23516          for (var i =0; i < this.toolbars.length;i++) {
23517             this.toolbars[i].onFirstFocus();
23518         }
23519         
23520     },
23521     
23522     // private
23523     syncValue : function()
23524     {   
23525         this.editorcore.syncValue();
23526     },
23527     
23528     pushValue : function()
23529     {   
23530         this.editorcore.pushValue();
23531     }
23532      
23533     
23534     // hide stuff that is not compatible
23535     /**
23536      * @event blur
23537      * @hide
23538      */
23539     /**
23540      * @event change
23541      * @hide
23542      */
23543     /**
23544      * @event focus
23545      * @hide
23546      */
23547     /**
23548      * @event specialkey
23549      * @hide
23550      */
23551     /**
23552      * @cfg {String} fieldClass @hide
23553      */
23554     /**
23555      * @cfg {String} focusClass @hide
23556      */
23557     /**
23558      * @cfg {String} autoCreate @hide
23559      */
23560     /**
23561      * @cfg {String} inputType @hide
23562      */
23563     /**
23564      * @cfg {String} invalidClass @hide
23565      */
23566     /**
23567      * @cfg {String} invalidText @hide
23568      */
23569     /**
23570      * @cfg {String} msgFx @hide
23571      */
23572     /**
23573      * @cfg {String} validateOnBlur @hide
23574      */
23575 });
23576  
23577     
23578    
23579    
23580    
23581       
23582 Roo.namespace('Roo.bootstrap.htmleditor');
23583 /**
23584  * @class Roo.bootstrap.HtmlEditorToolbar1
23585  * Basic Toolbar
23586  * 
23587  * Usage:
23588  *
23589  new Roo.bootstrap.HtmlEditor({
23590     ....
23591     toolbars : [
23592         new Roo.bootstrap.HtmlEditorToolbar1({
23593             disable : { fonts: 1 , format: 1, ..., ... , ...],
23594             btns : [ .... ]
23595         })
23596     }
23597      
23598  * 
23599  * @cfg {Object} disable List of elements to disable..
23600  * @cfg {Array} btns List of additional buttons.
23601  * 
23602  * 
23603  * NEEDS Extra CSS? 
23604  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23605  */
23606  
23607 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23608 {
23609     
23610     Roo.apply(this, config);
23611     
23612     // default disabled, based on 'good practice'..
23613     this.disable = this.disable || {};
23614     Roo.applyIf(this.disable, {
23615         fontSize : true,
23616         colors : true,
23617         specialElements : true
23618     });
23619     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23620     
23621     this.editor = config.editor;
23622     this.editorcore = config.editor.editorcore;
23623     
23624     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23625     
23626     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23627     // dont call parent... till later.
23628 }
23629 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23630      
23631     bar : true,
23632     
23633     editor : false,
23634     editorcore : false,
23635     
23636     
23637     formats : [
23638         "p" ,  
23639         "h1","h2","h3","h4","h5","h6", 
23640         "pre", "code", 
23641         "abbr", "acronym", "address", "cite", "samp", "var",
23642         'div','span'
23643     ],
23644     
23645     onRender : function(ct, position)
23646     {
23647        // Roo.log("Call onRender: " + this.xtype);
23648         
23649        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23650        Roo.log(this.el);
23651        this.el.dom.style.marginBottom = '0';
23652        var _this = this;
23653        var editorcore = this.editorcore;
23654        var editor= this.editor;
23655        
23656        var children = [];
23657        var btn = function(id,cmd , toggle, handler, html){
23658        
23659             var  event = toggle ? 'toggle' : 'click';
23660        
23661             var a = {
23662                 size : 'sm',
23663                 xtype: 'Button',
23664                 xns: Roo.bootstrap,
23665                 glyphicon : id,
23666                 cmd : id || cmd,
23667                 enableToggle:toggle !== false,
23668                 html : html || '',
23669                 pressed : toggle ? false : null,
23670                 listeners : {}
23671             };
23672             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23673                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23674             };
23675             children.push(a);
23676             return a;
23677        }
23678        
23679     //    var cb_box = function...
23680         
23681         var style = {
23682                 xtype: 'Button',
23683                 size : 'sm',
23684                 xns: Roo.bootstrap,
23685                 glyphicon : 'font',
23686                 //html : 'submit'
23687                 menu : {
23688                     xtype: 'Menu',
23689                     xns: Roo.bootstrap,
23690                     items:  []
23691                 }
23692         };
23693         Roo.each(this.formats, function(f) {
23694             style.menu.items.push({
23695                 xtype :'MenuItem',
23696                 xns: Roo.bootstrap,
23697                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23698                 tagname : f,
23699                 listeners : {
23700                     click : function()
23701                     {
23702                         editorcore.insertTag(this.tagname);
23703                         editor.focus();
23704                     }
23705                 }
23706                 
23707             });
23708         });
23709         children.push(style);   
23710         
23711         btn('bold',false,true);
23712         btn('italic',false,true);
23713         btn('align-left', 'justifyleft',true);
23714         btn('align-center', 'justifycenter',true);
23715         btn('align-right' , 'justifyright',true);
23716         btn('link', false, false, function(btn) {
23717             //Roo.log("create link?");
23718             var url = prompt(this.createLinkText, this.defaultLinkValue);
23719             if(url && url != 'http:/'+'/'){
23720                 this.editorcore.relayCmd('createlink', url);
23721             }
23722         }),
23723         btn('list','insertunorderedlist',true);
23724         btn('pencil', false,true, function(btn){
23725                 Roo.log(this);
23726                 this.toggleSourceEdit(btn.pressed);
23727         });
23728         
23729         if (this.editor.btns.length > 0) {
23730             for (var i = 0; i<this.editor.btns.length; i++) {
23731                 children.push(this.editor.btns[i]);
23732             }
23733         }
23734         
23735         /*
23736         var cog = {
23737                 xtype: 'Button',
23738                 size : 'sm',
23739                 xns: Roo.bootstrap,
23740                 glyphicon : 'cog',
23741                 //html : 'submit'
23742                 menu : {
23743                     xtype: 'Menu',
23744                     xns: Roo.bootstrap,
23745                     items:  []
23746                 }
23747         };
23748         
23749         cog.menu.items.push({
23750             xtype :'MenuItem',
23751             xns: Roo.bootstrap,
23752             html : Clean styles,
23753             tagname : f,
23754             listeners : {
23755                 click : function()
23756                 {
23757                     editorcore.insertTag(this.tagname);
23758                     editor.focus();
23759                 }
23760             }
23761             
23762         });
23763        */
23764         
23765          
23766        this.xtype = 'NavSimplebar';
23767         
23768         for(var i=0;i< children.length;i++) {
23769             
23770             this.buttons.add(this.addxtypeChild(children[i]));
23771             
23772         }
23773         
23774         editor.on('editorevent', this.updateToolbar, this);
23775     },
23776     onBtnClick : function(id)
23777     {
23778        this.editorcore.relayCmd(id);
23779        this.editorcore.focus();
23780     },
23781     
23782     /**
23783      * Protected method that will not generally be called directly. It triggers
23784      * a toolbar update by reading the markup state of the current selection in the editor.
23785      */
23786     updateToolbar: function(){
23787
23788         if(!this.editorcore.activated){
23789             this.editor.onFirstFocus(); // is this neeed?
23790             return;
23791         }
23792
23793         var btns = this.buttons; 
23794         var doc = this.editorcore.doc;
23795         btns.get('bold').setActive(doc.queryCommandState('bold'));
23796         btns.get('italic').setActive(doc.queryCommandState('italic'));
23797         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23798         
23799         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23800         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23801         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23802         
23803         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23804         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23805          /*
23806         
23807         var ans = this.editorcore.getAllAncestors();
23808         if (this.formatCombo) {
23809             
23810             
23811             var store = this.formatCombo.store;
23812             this.formatCombo.setValue("");
23813             for (var i =0; i < ans.length;i++) {
23814                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23815                     // select it..
23816                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23817                     break;
23818                 }
23819             }
23820         }
23821         
23822         
23823         
23824         // hides menus... - so this cant be on a menu...
23825         Roo.bootstrap.MenuMgr.hideAll();
23826         */
23827         Roo.bootstrap.MenuMgr.hideAll();
23828         //this.editorsyncValue();
23829     },
23830     onFirstFocus: function() {
23831         this.buttons.each(function(item){
23832            item.enable();
23833         });
23834     },
23835     toggleSourceEdit : function(sourceEditMode){
23836         
23837           
23838         if(sourceEditMode){
23839             Roo.log("disabling buttons");
23840            this.buttons.each( function(item){
23841                 if(item.cmd != 'pencil'){
23842                     item.disable();
23843                 }
23844             });
23845           
23846         }else{
23847             Roo.log("enabling buttons");
23848             if(this.editorcore.initialized){
23849                 this.buttons.each( function(item){
23850                     item.enable();
23851                 });
23852             }
23853             
23854         }
23855         Roo.log("calling toggole on editor");
23856         // tell the editor that it's been pressed..
23857         this.editor.toggleSourceEdit(sourceEditMode);
23858        
23859     }
23860 });
23861
23862
23863
23864
23865
23866 /**
23867  * @class Roo.bootstrap.Table.AbstractSelectionModel
23868  * @extends Roo.util.Observable
23869  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23870  * implemented by descendant classes.  This class should not be directly instantiated.
23871  * @constructor
23872  */
23873 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23874     this.locked = false;
23875     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23876 };
23877
23878
23879 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23880     /** @ignore Called by the grid automatically. Do not call directly. */
23881     init : function(grid){
23882         this.grid = grid;
23883         this.initEvents();
23884     },
23885
23886     /**
23887      * Locks the selections.
23888      */
23889     lock : function(){
23890         this.locked = true;
23891     },
23892
23893     /**
23894      * Unlocks the selections.
23895      */
23896     unlock : function(){
23897         this.locked = false;
23898     },
23899
23900     /**
23901      * Returns true if the selections are locked.
23902      * @return {Boolean}
23903      */
23904     isLocked : function(){
23905         return this.locked;
23906     }
23907 });
23908 /**
23909  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23910  * @class Roo.bootstrap.Table.RowSelectionModel
23911  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23912  * It supports multiple selections and keyboard selection/navigation. 
23913  * @constructor
23914  * @param {Object} config
23915  */
23916
23917 Roo.bootstrap.Table.RowSelectionModel = function(config){
23918     Roo.apply(this, config);
23919     this.selections = new Roo.util.MixedCollection(false, function(o){
23920         return o.id;
23921     });
23922
23923     this.last = false;
23924     this.lastActive = false;
23925
23926     this.addEvents({
23927         /**
23928              * @event selectionchange
23929              * Fires when the selection changes
23930              * @param {SelectionModel} this
23931              */
23932             "selectionchange" : true,
23933         /**
23934              * @event afterselectionchange
23935              * Fires after the selection changes (eg. by key press or clicking)
23936              * @param {SelectionModel} this
23937              */
23938             "afterselectionchange" : true,
23939         /**
23940              * @event beforerowselect
23941              * Fires when a row is selected being selected, return false to cancel.
23942              * @param {SelectionModel} this
23943              * @param {Number} rowIndex The selected index
23944              * @param {Boolean} keepExisting False if other selections will be cleared
23945              */
23946             "beforerowselect" : true,
23947         /**
23948              * @event rowselect
23949              * Fires when a row is selected.
23950              * @param {SelectionModel} this
23951              * @param {Number} rowIndex The selected index
23952              * @param {Roo.data.Record} r The record
23953              */
23954             "rowselect" : true,
23955         /**
23956              * @event rowdeselect
23957              * Fires when a row is deselected.
23958              * @param {SelectionModel} this
23959              * @param {Number} rowIndex The selected index
23960              */
23961         "rowdeselect" : true
23962     });
23963     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23964     this.locked = false;
23965  };
23966
23967 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23968     /**
23969      * @cfg {Boolean} singleSelect
23970      * True to allow selection of only one row at a time (defaults to false)
23971      */
23972     singleSelect : false,
23973
23974     // private
23975     initEvents : function()
23976     {
23977
23978         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23979         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23980         //}else{ // allow click to work like normal
23981          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23982         //}
23983         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23984         this.grid.on("rowclick", this.handleMouseDown, this);
23985         
23986         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23987             "up" : function(e){
23988                 if(!e.shiftKey){
23989                     this.selectPrevious(e.shiftKey);
23990                 }else if(this.last !== false && this.lastActive !== false){
23991                     var last = this.last;
23992                     this.selectRange(this.last,  this.lastActive-1);
23993                     this.grid.getView().focusRow(this.lastActive);
23994                     if(last !== false){
23995                         this.last = last;
23996                     }
23997                 }else{
23998                     this.selectFirstRow();
23999                 }
24000                 this.fireEvent("afterselectionchange", this);
24001             },
24002             "down" : function(e){
24003                 if(!e.shiftKey){
24004                     this.selectNext(e.shiftKey);
24005                 }else if(this.last !== false && this.lastActive !== false){
24006                     var last = this.last;
24007                     this.selectRange(this.last,  this.lastActive+1);
24008                     this.grid.getView().focusRow(this.lastActive);
24009                     if(last !== false){
24010                         this.last = last;
24011                     }
24012                 }else{
24013                     this.selectFirstRow();
24014                 }
24015                 this.fireEvent("afterselectionchange", this);
24016             },
24017             scope: this
24018         });
24019         this.grid.store.on('load', function(){
24020             this.selections.clear();
24021         },this);
24022         /*
24023         var view = this.grid.view;
24024         view.on("refresh", this.onRefresh, this);
24025         view.on("rowupdated", this.onRowUpdated, this);
24026         view.on("rowremoved", this.onRemove, this);
24027         */
24028     },
24029
24030     // private
24031     onRefresh : function()
24032     {
24033         var ds = this.grid.store, i, v = this.grid.view;
24034         var s = this.selections;
24035         s.each(function(r){
24036             if((i = ds.indexOfId(r.id)) != -1){
24037                 v.onRowSelect(i);
24038             }else{
24039                 s.remove(r);
24040             }
24041         });
24042     },
24043
24044     // private
24045     onRemove : function(v, index, r){
24046         this.selections.remove(r);
24047     },
24048
24049     // private
24050     onRowUpdated : function(v, index, r){
24051         if(this.isSelected(r)){
24052             v.onRowSelect(index);
24053         }
24054     },
24055
24056     /**
24057      * Select records.
24058      * @param {Array} records The records to select
24059      * @param {Boolean} keepExisting (optional) True to keep existing selections
24060      */
24061     selectRecords : function(records, keepExisting)
24062     {
24063         if(!keepExisting){
24064             this.clearSelections();
24065         }
24066             var ds = this.grid.store;
24067         for(var i = 0, len = records.length; i < len; i++){
24068             this.selectRow(ds.indexOf(records[i]), true);
24069         }
24070     },
24071
24072     /**
24073      * Gets the number of selected rows.
24074      * @return {Number}
24075      */
24076     getCount : function(){
24077         return this.selections.length;
24078     },
24079
24080     /**
24081      * Selects the first row in the grid.
24082      */
24083     selectFirstRow : function(){
24084         this.selectRow(0);
24085     },
24086
24087     /**
24088      * Select the last row.
24089      * @param {Boolean} keepExisting (optional) True to keep existing selections
24090      */
24091     selectLastRow : function(keepExisting){
24092         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24093         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24094     },
24095
24096     /**
24097      * Selects the row immediately following the last selected row.
24098      * @param {Boolean} keepExisting (optional) True to keep existing selections
24099      */
24100     selectNext : function(keepExisting)
24101     {
24102             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24103             this.selectRow(this.last+1, keepExisting);
24104             this.grid.getView().focusRow(this.last);
24105         }
24106     },
24107
24108     /**
24109      * Selects the row that precedes the last selected row.
24110      * @param {Boolean} keepExisting (optional) True to keep existing selections
24111      */
24112     selectPrevious : function(keepExisting){
24113         if(this.last){
24114             this.selectRow(this.last-1, keepExisting);
24115             this.grid.getView().focusRow(this.last);
24116         }
24117     },
24118
24119     /**
24120      * Returns the selected records
24121      * @return {Array} Array of selected records
24122      */
24123     getSelections : function(){
24124         return [].concat(this.selections.items);
24125     },
24126
24127     /**
24128      * Returns the first selected record.
24129      * @return {Record}
24130      */
24131     getSelected : function(){
24132         return this.selections.itemAt(0);
24133     },
24134
24135
24136     /**
24137      * Clears all selections.
24138      */
24139     clearSelections : function(fast)
24140     {
24141         if(this.locked) {
24142             return;
24143         }
24144         if(fast !== true){
24145                 var ds = this.grid.store;
24146             var s = this.selections;
24147             s.each(function(r){
24148                 this.deselectRow(ds.indexOfId(r.id));
24149             }, this);
24150             s.clear();
24151         }else{
24152             this.selections.clear();
24153         }
24154         this.last = false;
24155     },
24156
24157
24158     /**
24159      * Selects all rows.
24160      */
24161     selectAll : function(){
24162         if(this.locked) {
24163             return;
24164         }
24165         this.selections.clear();
24166         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24167             this.selectRow(i, true);
24168         }
24169     },
24170
24171     /**
24172      * Returns True if there is a selection.
24173      * @return {Boolean}
24174      */
24175     hasSelection : function(){
24176         return this.selections.length > 0;
24177     },
24178
24179     /**
24180      * Returns True if the specified row is selected.
24181      * @param {Number/Record} record The record or index of the record to check
24182      * @return {Boolean}
24183      */
24184     isSelected : function(index){
24185             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24186         return (r && this.selections.key(r.id) ? true : false);
24187     },
24188
24189     /**
24190      * Returns True if the specified record id is selected.
24191      * @param {String} id The id of record to check
24192      * @return {Boolean}
24193      */
24194     isIdSelected : function(id){
24195         return (this.selections.key(id) ? true : false);
24196     },
24197
24198
24199     // private
24200     handleMouseDBClick : function(e, t){
24201         
24202     },
24203     // private
24204     handleMouseDown : function(e, t)
24205     {
24206             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24207         if(this.isLocked() || rowIndex < 0 ){
24208             return;
24209         };
24210         if(e.shiftKey && this.last !== false){
24211             var last = this.last;
24212             this.selectRange(last, rowIndex, e.ctrlKey);
24213             this.last = last; // reset the last
24214             t.focus();
24215     
24216         }else{
24217             var isSelected = this.isSelected(rowIndex);
24218             //Roo.log("select row:" + rowIndex);
24219             if(isSelected){
24220                 this.deselectRow(rowIndex);
24221             } else {
24222                         this.selectRow(rowIndex, true);
24223             }
24224     
24225             /*
24226                 if(e.button !== 0 && isSelected){
24227                 alert('rowIndex 2: ' + rowIndex);
24228                     view.focusRow(rowIndex);
24229                 }else if(e.ctrlKey && isSelected){
24230                     this.deselectRow(rowIndex);
24231                 }else if(!isSelected){
24232                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24233                     view.focusRow(rowIndex);
24234                 }
24235             */
24236         }
24237         this.fireEvent("afterselectionchange", this);
24238     },
24239     // private
24240     handleDragableRowClick :  function(grid, rowIndex, e) 
24241     {
24242         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24243             this.selectRow(rowIndex, false);
24244             grid.view.focusRow(rowIndex);
24245              this.fireEvent("afterselectionchange", this);
24246         }
24247     },
24248     
24249     /**
24250      * Selects multiple rows.
24251      * @param {Array} rows Array of the indexes of the row to select
24252      * @param {Boolean} keepExisting (optional) True to keep existing selections
24253      */
24254     selectRows : function(rows, keepExisting){
24255         if(!keepExisting){
24256             this.clearSelections();
24257         }
24258         for(var i = 0, len = rows.length; i < len; i++){
24259             this.selectRow(rows[i], true);
24260         }
24261     },
24262
24263     /**
24264      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24265      * @param {Number} startRow The index of the first row in the range
24266      * @param {Number} endRow The index of the last row in the range
24267      * @param {Boolean} keepExisting (optional) True to retain existing selections
24268      */
24269     selectRange : function(startRow, endRow, keepExisting){
24270         if(this.locked) {
24271             return;
24272         }
24273         if(!keepExisting){
24274             this.clearSelections();
24275         }
24276         if(startRow <= endRow){
24277             for(var i = startRow; i <= endRow; i++){
24278                 this.selectRow(i, true);
24279             }
24280         }else{
24281             for(var i = startRow; i >= endRow; i--){
24282                 this.selectRow(i, true);
24283             }
24284         }
24285     },
24286
24287     /**
24288      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24289      * @param {Number} startRow The index of the first row in the range
24290      * @param {Number} endRow The index of the last row in the range
24291      */
24292     deselectRange : function(startRow, endRow, preventViewNotify){
24293         if(this.locked) {
24294             return;
24295         }
24296         for(var i = startRow; i <= endRow; i++){
24297             this.deselectRow(i, preventViewNotify);
24298         }
24299     },
24300
24301     /**
24302      * Selects a row.
24303      * @param {Number} row The index of the row to select
24304      * @param {Boolean} keepExisting (optional) True to keep existing selections
24305      */
24306     selectRow : function(index, keepExisting, preventViewNotify)
24307     {
24308             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24309             return;
24310         }
24311         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24312             if(!keepExisting || this.singleSelect){
24313                 this.clearSelections();
24314             }
24315             
24316             var r = this.grid.store.getAt(index);
24317             //console.log('selectRow - record id :' + r.id);
24318             
24319             this.selections.add(r);
24320             this.last = this.lastActive = index;
24321             if(!preventViewNotify){
24322                 var proxy = new Roo.Element(
24323                                 this.grid.getRowDom(index)
24324                 );
24325                 proxy.addClass('bg-info info');
24326             }
24327             this.fireEvent("rowselect", this, index, r);
24328             this.fireEvent("selectionchange", this);
24329         }
24330     },
24331
24332     /**
24333      * Deselects a row.
24334      * @param {Number} row The index of the row to deselect
24335      */
24336     deselectRow : function(index, preventViewNotify)
24337     {
24338         if(this.locked) {
24339             return;
24340         }
24341         if(this.last == index){
24342             this.last = false;
24343         }
24344         if(this.lastActive == index){
24345             this.lastActive = false;
24346         }
24347         
24348         var r = this.grid.store.getAt(index);
24349         if (!r) {
24350             return;
24351         }
24352         
24353         this.selections.remove(r);
24354         //.console.log('deselectRow - record id :' + r.id);
24355         if(!preventViewNotify){
24356         
24357             var proxy = new Roo.Element(
24358                 this.grid.getRowDom(index)
24359             );
24360             proxy.removeClass('bg-info info');
24361         }
24362         this.fireEvent("rowdeselect", this, index);
24363         this.fireEvent("selectionchange", this);
24364     },
24365
24366     // private
24367     restoreLast : function(){
24368         if(this._last){
24369             this.last = this._last;
24370         }
24371     },
24372
24373     // private
24374     acceptsNav : function(row, col, cm){
24375         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24376     },
24377
24378     // private
24379     onEditorKey : function(field, e){
24380         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24381         if(k == e.TAB){
24382             e.stopEvent();
24383             ed.completeEdit();
24384             if(e.shiftKey){
24385                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24386             }else{
24387                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24388             }
24389         }else if(k == e.ENTER && !e.ctrlKey){
24390             e.stopEvent();
24391             ed.completeEdit();
24392             if(e.shiftKey){
24393                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24394             }else{
24395                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24396             }
24397         }else if(k == e.ESC){
24398             ed.cancelEdit();
24399         }
24400         if(newCell){
24401             g.startEditing(newCell[0], newCell[1]);
24402         }
24403     }
24404 });
24405 /*
24406  * Based on:
24407  * Ext JS Library 1.1.1
24408  * Copyright(c) 2006-2007, Ext JS, LLC.
24409  *
24410  * Originally Released Under LGPL - original licence link has changed is not relivant.
24411  *
24412  * Fork - LGPL
24413  * <script type="text/javascript">
24414  */
24415  
24416 /**
24417  * @class Roo.bootstrap.PagingToolbar
24418  * @extends Roo.bootstrap.NavSimplebar
24419  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24420  * @constructor
24421  * Create a new PagingToolbar
24422  * @param {Object} config The config object
24423  * @param {Roo.data.Store} store
24424  */
24425 Roo.bootstrap.PagingToolbar = function(config)
24426 {
24427     // old args format still supported... - xtype is prefered..
24428         // created from xtype...
24429     
24430     this.ds = config.dataSource;
24431     
24432     if (config.store && !this.ds) {
24433         this.store= Roo.factory(config.store, Roo.data);
24434         this.ds = this.store;
24435         this.ds.xmodule = this.xmodule || false;
24436     }
24437     
24438     this.toolbarItems = [];
24439     if (config.items) {
24440         this.toolbarItems = config.items;
24441     }
24442     
24443     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24444     
24445     this.cursor = 0;
24446     
24447     if (this.ds) { 
24448         this.bind(this.ds);
24449     }
24450     
24451     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24452     
24453 };
24454
24455 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24456     /**
24457      * @cfg {Roo.data.Store} dataSource
24458      * The underlying data store providing the paged data
24459      */
24460     /**
24461      * @cfg {String/HTMLElement/Element} container
24462      * container The id or element that will contain the toolbar
24463      */
24464     /**
24465      * @cfg {Boolean} displayInfo
24466      * True to display the displayMsg (defaults to false)
24467      */
24468     /**
24469      * @cfg {Number} pageSize
24470      * The number of records to display per page (defaults to 20)
24471      */
24472     pageSize: 20,
24473     /**
24474      * @cfg {String} displayMsg
24475      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24476      */
24477     displayMsg : 'Displaying {0} - {1} of {2}',
24478     /**
24479      * @cfg {String} emptyMsg
24480      * The message to display when no records are found (defaults to "No data to display")
24481      */
24482     emptyMsg : 'No data to display',
24483     /**
24484      * Customizable piece of the default paging text (defaults to "Page")
24485      * @type String
24486      */
24487     beforePageText : "Page",
24488     /**
24489      * Customizable piece of the default paging text (defaults to "of %0")
24490      * @type String
24491      */
24492     afterPageText : "of {0}",
24493     /**
24494      * Customizable piece of the default paging text (defaults to "First Page")
24495      * @type String
24496      */
24497     firstText : "First Page",
24498     /**
24499      * Customizable piece of the default paging text (defaults to "Previous Page")
24500      * @type String
24501      */
24502     prevText : "Previous Page",
24503     /**
24504      * Customizable piece of the default paging text (defaults to "Next Page")
24505      * @type String
24506      */
24507     nextText : "Next Page",
24508     /**
24509      * Customizable piece of the default paging text (defaults to "Last Page")
24510      * @type String
24511      */
24512     lastText : "Last Page",
24513     /**
24514      * Customizable piece of the default paging text (defaults to "Refresh")
24515      * @type String
24516      */
24517     refreshText : "Refresh",
24518
24519     buttons : false,
24520     // private
24521     onRender : function(ct, position) 
24522     {
24523         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24524         this.navgroup.parentId = this.id;
24525         this.navgroup.onRender(this.el, null);
24526         // add the buttons to the navgroup
24527         
24528         if(this.displayInfo){
24529             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24530             this.displayEl = this.el.select('.x-paging-info', true).first();
24531 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24532 //            this.displayEl = navel.el.select('span',true).first();
24533         }
24534         
24535         var _this = this;
24536         
24537         if(this.buttons){
24538             Roo.each(_this.buttons, function(e){ // this might need to use render????
24539                Roo.factory(e).render(_this.el);
24540             });
24541         }
24542             
24543         Roo.each(_this.toolbarItems, function(e) {
24544             _this.navgroup.addItem(e);
24545         });
24546         
24547         
24548         this.first = this.navgroup.addItem({
24549             tooltip: this.firstText,
24550             cls: "prev",
24551             icon : 'fa fa-backward',
24552             disabled: true,
24553             preventDefault: true,
24554             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24555         });
24556         
24557         this.prev =  this.navgroup.addItem({
24558             tooltip: this.prevText,
24559             cls: "prev",
24560             icon : 'fa fa-step-backward',
24561             disabled: true,
24562             preventDefault: true,
24563             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24564         });
24565     //this.addSeparator();
24566         
24567         
24568         var field = this.navgroup.addItem( {
24569             tagtype : 'span',
24570             cls : 'x-paging-position',
24571             
24572             html : this.beforePageText  +
24573                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24574                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24575          } ); //?? escaped?
24576         
24577         this.field = field.el.select('input', true).first();
24578         this.field.on("keydown", this.onPagingKeydown, this);
24579         this.field.on("focus", function(){this.dom.select();});
24580     
24581     
24582         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24583         //this.field.setHeight(18);
24584         //this.addSeparator();
24585         this.next = this.navgroup.addItem({
24586             tooltip: this.nextText,
24587             cls: "next",
24588             html : ' <i class="fa fa-step-forward">',
24589             disabled: true,
24590             preventDefault: true,
24591             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24592         });
24593         this.last = this.navgroup.addItem({
24594             tooltip: this.lastText,
24595             icon : 'fa fa-forward',
24596             cls: "next",
24597             disabled: true,
24598             preventDefault: true,
24599             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24600         });
24601     //this.addSeparator();
24602         this.loading = this.navgroup.addItem({
24603             tooltip: this.refreshText,
24604             icon: 'fa fa-refresh',
24605             preventDefault: true,
24606             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24607         });
24608         
24609     },
24610
24611     // private
24612     updateInfo : function(){
24613         if(this.displayEl){
24614             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24615             var msg = count == 0 ?
24616                 this.emptyMsg :
24617                 String.format(
24618                     this.displayMsg,
24619                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24620                 );
24621             this.displayEl.update(msg);
24622         }
24623     },
24624
24625     // private
24626     onLoad : function(ds, r, o)
24627     {
24628         this.cursor = o.params.start ? o.params.start : 0;
24629         
24630         var d = this.getPageData(),
24631             ap = d.activePage,
24632             ps = d.pages;
24633         
24634         
24635         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24636         this.field.dom.value = ap;
24637         this.first.setDisabled(ap == 1);
24638         this.prev.setDisabled(ap == 1);
24639         this.next.setDisabled(ap == ps);
24640         this.last.setDisabled(ap == ps);
24641         this.loading.enable();
24642         this.updateInfo();
24643     },
24644
24645     // private
24646     getPageData : function(){
24647         var total = this.ds.getTotalCount();
24648         return {
24649             total : total,
24650             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24651             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24652         };
24653     },
24654
24655     // private
24656     onLoadError : function(){
24657         this.loading.enable();
24658     },
24659
24660     // private
24661     onPagingKeydown : function(e){
24662         var k = e.getKey();
24663         var d = this.getPageData();
24664         if(k == e.RETURN){
24665             var v = this.field.dom.value, pageNum;
24666             if(!v || isNaN(pageNum = parseInt(v, 10))){
24667                 this.field.dom.value = d.activePage;
24668                 return;
24669             }
24670             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24671             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24672             e.stopEvent();
24673         }
24674         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))
24675         {
24676           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24677           this.field.dom.value = pageNum;
24678           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24679           e.stopEvent();
24680         }
24681         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24682         {
24683           var v = this.field.dom.value, pageNum; 
24684           var increment = (e.shiftKey) ? 10 : 1;
24685           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24686                 increment *= -1;
24687           }
24688           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24689             this.field.dom.value = d.activePage;
24690             return;
24691           }
24692           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24693           {
24694             this.field.dom.value = parseInt(v, 10) + increment;
24695             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24696             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24697           }
24698           e.stopEvent();
24699         }
24700     },
24701
24702     // private
24703     beforeLoad : function(){
24704         if(this.loading){
24705             this.loading.disable();
24706         }
24707     },
24708
24709     // private
24710     onClick : function(which){
24711         
24712         var ds = this.ds;
24713         if (!ds) {
24714             return;
24715         }
24716         
24717         switch(which){
24718             case "first":
24719                 ds.load({params:{start: 0, limit: this.pageSize}});
24720             break;
24721             case "prev":
24722                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24723             break;
24724             case "next":
24725                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24726             break;
24727             case "last":
24728                 var total = ds.getTotalCount();
24729                 var extra = total % this.pageSize;
24730                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24731                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24732             break;
24733             case "refresh":
24734                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24735             break;
24736         }
24737     },
24738
24739     /**
24740      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24741      * @param {Roo.data.Store} store The data store to unbind
24742      */
24743     unbind : function(ds){
24744         ds.un("beforeload", this.beforeLoad, this);
24745         ds.un("load", this.onLoad, this);
24746         ds.un("loadexception", this.onLoadError, this);
24747         ds.un("remove", this.updateInfo, this);
24748         ds.un("add", this.updateInfo, this);
24749         this.ds = undefined;
24750     },
24751
24752     /**
24753      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24754      * @param {Roo.data.Store} store The data store to bind
24755      */
24756     bind : function(ds){
24757         ds.on("beforeload", this.beforeLoad, this);
24758         ds.on("load", this.onLoad, this);
24759         ds.on("loadexception", this.onLoadError, this);
24760         ds.on("remove", this.updateInfo, this);
24761         ds.on("add", this.updateInfo, this);
24762         this.ds = ds;
24763     }
24764 });/*
24765  * - LGPL
24766  *
24767  * element
24768  * 
24769  */
24770
24771 /**
24772  * @class Roo.bootstrap.MessageBar
24773  * @extends Roo.bootstrap.Component
24774  * Bootstrap MessageBar class
24775  * @cfg {String} html contents of the MessageBar
24776  * @cfg {String} weight (info | success | warning | danger) default info
24777  * @cfg {String} beforeClass insert the bar before the given class
24778  * @cfg {Boolean} closable (true | false) default false
24779  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24780  * 
24781  * @constructor
24782  * Create a new Element
24783  * @param {Object} config The config object
24784  */
24785
24786 Roo.bootstrap.MessageBar = function(config){
24787     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24788 };
24789
24790 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24791     
24792     html: '',
24793     weight: 'info',
24794     closable: false,
24795     fixed: false,
24796     beforeClass: 'bootstrap-sticky-wrap',
24797     
24798     getAutoCreate : function(){
24799         
24800         var cfg = {
24801             tag: 'div',
24802             cls: 'alert alert-dismissable alert-' + this.weight,
24803             cn: [
24804                 {
24805                     tag: 'span',
24806                     cls: 'message',
24807                     html: this.html || ''
24808                 }
24809             ]
24810         };
24811         
24812         if(this.fixed){
24813             cfg.cls += ' alert-messages-fixed';
24814         }
24815         
24816         if(this.closable){
24817             cfg.cn.push({
24818                 tag: 'button',
24819                 cls: 'close',
24820                 html: 'x'
24821             });
24822         }
24823         
24824         return cfg;
24825     },
24826     
24827     onRender : function(ct, position)
24828     {
24829         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24830         
24831         if(!this.el){
24832             var cfg = Roo.apply({},  this.getAutoCreate());
24833             cfg.id = Roo.id();
24834             
24835             if (this.cls) {
24836                 cfg.cls += ' ' + this.cls;
24837             }
24838             if (this.style) {
24839                 cfg.style = this.style;
24840             }
24841             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24842             
24843             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24844         }
24845         
24846         this.el.select('>button.close').on('click', this.hide, this);
24847         
24848     },
24849     
24850     show : function()
24851     {
24852         if (!this.rendered) {
24853             this.render();
24854         }
24855         
24856         this.el.show();
24857         
24858         this.fireEvent('show', this);
24859         
24860     },
24861     
24862     hide : function()
24863     {
24864         if (!this.rendered) {
24865             this.render();
24866         }
24867         
24868         this.el.hide();
24869         
24870         this.fireEvent('hide', this);
24871     },
24872     
24873     update : function()
24874     {
24875 //        var e = this.el.dom.firstChild;
24876 //        
24877 //        if(this.closable){
24878 //            e = e.nextSibling;
24879 //        }
24880 //        
24881 //        e.data = this.html || '';
24882
24883         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24884     }
24885    
24886 });
24887
24888  
24889
24890      /*
24891  * - LGPL
24892  *
24893  * Graph
24894  * 
24895  */
24896
24897
24898 /**
24899  * @class Roo.bootstrap.Graph
24900  * @extends Roo.bootstrap.Component
24901  * Bootstrap Graph class
24902 > Prameters
24903  -sm {number} sm 4
24904  -md {number} md 5
24905  @cfg {String} graphtype  bar | vbar | pie
24906  @cfg {number} g_x coodinator | centre x (pie)
24907  @cfg {number} g_y coodinator | centre y (pie)
24908  @cfg {number} g_r radius (pie)
24909  @cfg {number} g_height height of the chart (respected by all elements in the set)
24910  @cfg {number} g_width width of the chart (respected by all elements in the set)
24911  @cfg {Object} title The title of the chart
24912     
24913  -{Array}  values
24914  -opts (object) options for the chart 
24915      o {
24916      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24917      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24918      o vgutter (number)
24919      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.
24920      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24921      o to
24922      o stretch (boolean)
24923      o }
24924  -opts (object) options for the pie
24925      o{
24926      o cut
24927      o startAngle (number)
24928      o endAngle (number)
24929      } 
24930  *
24931  * @constructor
24932  * Create a new Input
24933  * @param {Object} config The config object
24934  */
24935
24936 Roo.bootstrap.Graph = function(config){
24937     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24938     
24939     this.addEvents({
24940         // img events
24941         /**
24942          * @event click
24943          * The img click event for the img.
24944          * @param {Roo.EventObject} e
24945          */
24946         "click" : true
24947     });
24948 };
24949
24950 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24951     
24952     sm: 4,
24953     md: 5,
24954     graphtype: 'bar',
24955     g_height: 250,
24956     g_width: 400,
24957     g_x: 50,
24958     g_y: 50,
24959     g_r: 30,
24960     opts:{
24961         //g_colors: this.colors,
24962         g_type: 'soft',
24963         g_gutter: '20%'
24964
24965     },
24966     title : false,
24967
24968     getAutoCreate : function(){
24969         
24970         var cfg = {
24971             tag: 'div',
24972             html : null
24973         };
24974         
24975         
24976         return  cfg;
24977     },
24978
24979     onRender : function(ct,position){
24980         
24981         
24982         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24983         
24984         if (typeof(Raphael) == 'undefined') {
24985             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24986             return;
24987         }
24988         
24989         this.raphael = Raphael(this.el.dom);
24990         
24991                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24992                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24993                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24994                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24995                 /*
24996                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24997                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24998                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24999                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25000                 
25001                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25002                 r.barchart(330, 10, 300, 220, data1);
25003                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25004                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25005                 */
25006                 
25007                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25008                 // r.barchart(30, 30, 560, 250,  xdata, {
25009                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25010                 //     axis : "0 0 1 1",
25011                 //     axisxlabels :  xdata
25012                 //     //yvalues : cols,
25013                    
25014                 // });
25015 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25016 //        
25017 //        this.load(null,xdata,{
25018 //                axis : "0 0 1 1",
25019 //                axisxlabels :  xdata
25020 //                });
25021
25022     },
25023
25024     load : function(graphtype,xdata,opts)
25025     {
25026         this.raphael.clear();
25027         if(!graphtype) {
25028             graphtype = this.graphtype;
25029         }
25030         if(!opts){
25031             opts = this.opts;
25032         }
25033         var r = this.raphael,
25034             fin = function () {
25035                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25036             },
25037             fout = function () {
25038                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25039             },
25040             pfin = function() {
25041                 this.sector.stop();
25042                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25043
25044                 if (this.label) {
25045                     this.label[0].stop();
25046                     this.label[0].attr({ r: 7.5 });
25047                     this.label[1].attr({ "font-weight": 800 });
25048                 }
25049             },
25050             pfout = function() {
25051                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25052
25053                 if (this.label) {
25054                     this.label[0].animate({ r: 5 }, 500, "bounce");
25055                     this.label[1].attr({ "font-weight": 400 });
25056                 }
25057             };
25058
25059         switch(graphtype){
25060             case 'bar':
25061                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25062                 break;
25063             case 'hbar':
25064                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25065                 break;
25066             case 'pie':
25067 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25068 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25069 //            
25070                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25071                 
25072                 break;
25073
25074         }
25075         
25076         if(this.title){
25077             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25078         }
25079         
25080     },
25081     
25082     setTitle: function(o)
25083     {
25084         this.title = o;
25085     },
25086     
25087     initEvents: function() {
25088         
25089         if(!this.href){
25090             this.el.on('click', this.onClick, this);
25091         }
25092     },
25093     
25094     onClick : function(e)
25095     {
25096         Roo.log('img onclick');
25097         this.fireEvent('click', this, e);
25098     }
25099    
25100 });
25101
25102  
25103 /*
25104  * - LGPL
25105  *
25106  * numberBox
25107  * 
25108  */
25109 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25110
25111 /**
25112  * @class Roo.bootstrap.dash.NumberBox
25113  * @extends Roo.bootstrap.Component
25114  * Bootstrap NumberBox class
25115  * @cfg {String} headline Box headline
25116  * @cfg {String} content Box content
25117  * @cfg {String} icon Box icon
25118  * @cfg {String} footer Footer text
25119  * @cfg {String} fhref Footer href
25120  * 
25121  * @constructor
25122  * Create a new NumberBox
25123  * @param {Object} config The config object
25124  */
25125
25126
25127 Roo.bootstrap.dash.NumberBox = function(config){
25128     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25129     
25130 };
25131
25132 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25133     
25134     headline : '',
25135     content : '',
25136     icon : '',
25137     footer : '',
25138     fhref : '',
25139     ficon : '',
25140     
25141     getAutoCreate : function(){
25142         
25143         var cfg = {
25144             tag : 'div',
25145             cls : 'small-box ',
25146             cn : [
25147                 {
25148                     tag : 'div',
25149                     cls : 'inner',
25150                     cn :[
25151                         {
25152                             tag : 'h3',
25153                             cls : 'roo-headline',
25154                             html : this.headline
25155                         },
25156                         {
25157                             tag : 'p',
25158                             cls : 'roo-content',
25159                             html : this.content
25160                         }
25161                     ]
25162                 }
25163             ]
25164         };
25165         
25166         if(this.icon){
25167             cfg.cn.push({
25168                 tag : 'div',
25169                 cls : 'icon',
25170                 cn :[
25171                     {
25172                         tag : 'i',
25173                         cls : 'ion ' + this.icon
25174                     }
25175                 ]
25176             });
25177         }
25178         
25179         if(this.footer){
25180             var footer = {
25181                 tag : 'a',
25182                 cls : 'small-box-footer',
25183                 href : this.fhref || '#',
25184                 html : this.footer
25185             };
25186             
25187             cfg.cn.push(footer);
25188             
25189         }
25190         
25191         return  cfg;
25192     },
25193
25194     onRender : function(ct,position){
25195         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25196
25197
25198        
25199                 
25200     },
25201
25202     setHeadline: function (value)
25203     {
25204         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25205     },
25206     
25207     setFooter: function (value, href)
25208     {
25209         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25210         
25211         if(href){
25212             this.el.select('a.small-box-footer',true).first().attr('href', href);
25213         }
25214         
25215     },
25216
25217     setContent: function (value)
25218     {
25219         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25220     },
25221
25222     initEvents: function() 
25223     {   
25224         
25225     }
25226     
25227 });
25228
25229  
25230 /*
25231  * - LGPL
25232  *
25233  * TabBox
25234  * 
25235  */
25236 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25237
25238 /**
25239  * @class Roo.bootstrap.dash.TabBox
25240  * @extends Roo.bootstrap.Component
25241  * Bootstrap TabBox class
25242  * @cfg {String} title Title of the TabBox
25243  * @cfg {String} icon Icon of the TabBox
25244  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25245  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25246  * 
25247  * @constructor
25248  * Create a new TabBox
25249  * @param {Object} config The config object
25250  */
25251
25252
25253 Roo.bootstrap.dash.TabBox = function(config){
25254     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25255     this.addEvents({
25256         // raw events
25257         /**
25258          * @event addpane
25259          * When a pane is added
25260          * @param {Roo.bootstrap.dash.TabPane} pane
25261          */
25262         "addpane" : true,
25263         /**
25264          * @event activatepane
25265          * When a pane is activated
25266          * @param {Roo.bootstrap.dash.TabPane} pane
25267          */
25268         "activatepane" : true
25269         
25270          
25271     });
25272     
25273     this.panes = [];
25274 };
25275
25276 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25277
25278     title : '',
25279     icon : false,
25280     showtabs : true,
25281     tabScrollable : false,
25282     
25283     getChildContainer : function()
25284     {
25285         return this.el.select('.tab-content', true).first();
25286     },
25287     
25288     getAutoCreate : function(){
25289         
25290         var header = {
25291             tag: 'li',
25292             cls: 'pull-left header',
25293             html: this.title,
25294             cn : []
25295         };
25296         
25297         if(this.icon){
25298             header.cn.push({
25299                 tag: 'i',
25300                 cls: 'fa ' + this.icon
25301             });
25302         }
25303         
25304         var h = {
25305             tag: 'ul',
25306             cls: 'nav nav-tabs pull-right',
25307             cn: [
25308                 header
25309             ]
25310         };
25311         
25312         if(this.tabScrollable){
25313             h = {
25314                 tag: 'div',
25315                 cls: 'tab-header',
25316                 cn: [
25317                     {
25318                         tag: 'ul',
25319                         cls: 'nav nav-tabs pull-right',
25320                         cn: [
25321                             header
25322                         ]
25323                     }
25324                 ]
25325             };
25326         }
25327         
25328         var cfg = {
25329             tag: 'div',
25330             cls: 'nav-tabs-custom',
25331             cn: [
25332                 h,
25333                 {
25334                     tag: 'div',
25335                     cls: 'tab-content no-padding',
25336                     cn: []
25337                 }
25338             ]
25339         };
25340
25341         return  cfg;
25342     },
25343     initEvents : function()
25344     {
25345         //Roo.log('add add pane handler');
25346         this.on('addpane', this.onAddPane, this);
25347     },
25348      /**
25349      * Updates the box title
25350      * @param {String} html to set the title to.
25351      */
25352     setTitle : function(value)
25353     {
25354         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25355     },
25356     onAddPane : function(pane)
25357     {
25358         this.panes.push(pane);
25359         //Roo.log('addpane');
25360         //Roo.log(pane);
25361         // tabs are rendere left to right..
25362         if(!this.showtabs){
25363             return;
25364         }
25365         
25366         var ctr = this.el.select('.nav-tabs', true).first();
25367          
25368          
25369         var existing = ctr.select('.nav-tab',true);
25370         var qty = existing.getCount();;
25371         
25372         
25373         var tab = ctr.createChild({
25374             tag : 'li',
25375             cls : 'nav-tab' + (qty ? '' : ' active'),
25376             cn : [
25377                 {
25378                     tag : 'a',
25379                     href:'#',
25380                     html : pane.title
25381                 }
25382             ]
25383         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25384         pane.tab = tab;
25385         
25386         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25387         if (!qty) {
25388             pane.el.addClass('active');
25389         }
25390         
25391                 
25392     },
25393     onTabClick : function(ev,un,ob,pane)
25394     {
25395         //Roo.log('tab - prev default');
25396         ev.preventDefault();
25397         
25398         
25399         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25400         pane.tab.addClass('active');
25401         //Roo.log(pane.title);
25402         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25403         // technically we should have a deactivate event.. but maybe add later.
25404         // and it should not de-activate the selected tab...
25405         this.fireEvent('activatepane', pane);
25406         pane.el.addClass('active');
25407         pane.fireEvent('activate');
25408         
25409         
25410     },
25411     
25412     getActivePane : function()
25413     {
25414         var r = false;
25415         Roo.each(this.panes, function(p) {
25416             if(p.el.hasClass('active')){
25417                 r = p;
25418                 return false;
25419             }
25420             
25421             return;
25422         });
25423         
25424         return r;
25425     }
25426     
25427     
25428 });
25429
25430  
25431 /*
25432  * - LGPL
25433  *
25434  * Tab pane
25435  * 
25436  */
25437 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25438 /**
25439  * @class Roo.bootstrap.TabPane
25440  * @extends Roo.bootstrap.Component
25441  * Bootstrap TabPane class
25442  * @cfg {Boolean} active (false | true) Default false
25443  * @cfg {String} title title of panel
25444
25445  * 
25446  * @constructor
25447  * Create a new TabPane
25448  * @param {Object} config The config object
25449  */
25450
25451 Roo.bootstrap.dash.TabPane = function(config){
25452     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25453     
25454     this.addEvents({
25455         // raw events
25456         /**
25457          * @event activate
25458          * When a pane is activated
25459          * @param {Roo.bootstrap.dash.TabPane} pane
25460          */
25461         "activate" : true
25462          
25463     });
25464 };
25465
25466 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25467     
25468     active : false,
25469     title : '',
25470     
25471     // the tabBox that this is attached to.
25472     tab : false,
25473      
25474     getAutoCreate : function() 
25475     {
25476         var cfg = {
25477             tag: 'div',
25478             cls: 'tab-pane'
25479         };
25480         
25481         if(this.active){
25482             cfg.cls += ' active';
25483         }
25484         
25485         return cfg;
25486     },
25487     initEvents  : function()
25488     {
25489         //Roo.log('trigger add pane handler');
25490         this.parent().fireEvent('addpane', this)
25491     },
25492     
25493      /**
25494      * Updates the tab title 
25495      * @param {String} html to set the title to.
25496      */
25497     setTitle: function(str)
25498     {
25499         if (!this.tab) {
25500             return;
25501         }
25502         this.title = str;
25503         this.tab.select('a', true).first().dom.innerHTML = str;
25504         
25505     }
25506     
25507     
25508     
25509 });
25510
25511  
25512
25513
25514  /*
25515  * - LGPL
25516  *
25517  * menu
25518  * 
25519  */
25520 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25521
25522 /**
25523  * @class Roo.bootstrap.menu.Menu
25524  * @extends Roo.bootstrap.Component
25525  * Bootstrap Menu class - container for Menu
25526  * @cfg {String} html Text of the menu
25527  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25528  * @cfg {String} icon Font awesome icon
25529  * @cfg {String} pos Menu align to (top | bottom) default bottom
25530  * 
25531  * 
25532  * @constructor
25533  * Create a new Menu
25534  * @param {Object} config The config object
25535  */
25536
25537
25538 Roo.bootstrap.menu.Menu = function(config){
25539     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25540     
25541     this.addEvents({
25542         /**
25543          * @event beforeshow
25544          * Fires before this menu is displayed
25545          * @param {Roo.bootstrap.menu.Menu} this
25546          */
25547         beforeshow : true,
25548         /**
25549          * @event beforehide
25550          * Fires before this menu is hidden
25551          * @param {Roo.bootstrap.menu.Menu} this
25552          */
25553         beforehide : true,
25554         /**
25555          * @event show
25556          * Fires after this menu is displayed
25557          * @param {Roo.bootstrap.menu.Menu} this
25558          */
25559         show : true,
25560         /**
25561          * @event hide
25562          * Fires after this menu is hidden
25563          * @param {Roo.bootstrap.menu.Menu} this
25564          */
25565         hide : true,
25566         /**
25567          * @event click
25568          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25569          * @param {Roo.bootstrap.menu.Menu} this
25570          * @param {Roo.EventObject} e
25571          */
25572         click : true
25573     });
25574     
25575 };
25576
25577 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25578     
25579     submenu : false,
25580     html : '',
25581     weight : 'default',
25582     icon : false,
25583     pos : 'bottom',
25584     
25585     
25586     getChildContainer : function() {
25587         if(this.isSubMenu){
25588             return this.el;
25589         }
25590         
25591         return this.el.select('ul.dropdown-menu', true).first();  
25592     },
25593     
25594     getAutoCreate : function()
25595     {
25596         var text = [
25597             {
25598                 tag : 'span',
25599                 cls : 'roo-menu-text',
25600                 html : this.html
25601             }
25602         ];
25603         
25604         if(this.icon){
25605             text.unshift({
25606                 tag : 'i',
25607                 cls : 'fa ' + this.icon
25608             })
25609         }
25610         
25611         
25612         var cfg = {
25613             tag : 'div',
25614             cls : 'btn-group',
25615             cn : [
25616                 {
25617                     tag : 'button',
25618                     cls : 'dropdown-button btn btn-' + this.weight,
25619                     cn : text
25620                 },
25621                 {
25622                     tag : 'button',
25623                     cls : 'dropdown-toggle btn btn-' + this.weight,
25624                     cn : [
25625                         {
25626                             tag : 'span',
25627                             cls : 'caret'
25628                         }
25629                     ]
25630                 },
25631                 {
25632                     tag : 'ul',
25633                     cls : 'dropdown-menu'
25634                 }
25635             ]
25636             
25637         };
25638         
25639         if(this.pos == 'top'){
25640             cfg.cls += ' dropup';
25641         }
25642         
25643         if(this.isSubMenu){
25644             cfg = {
25645                 tag : 'ul',
25646                 cls : 'dropdown-menu'
25647             }
25648         }
25649         
25650         return cfg;
25651     },
25652     
25653     onRender : function(ct, position)
25654     {
25655         this.isSubMenu = ct.hasClass('dropdown-submenu');
25656         
25657         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25658     },
25659     
25660     initEvents : function() 
25661     {
25662         if(this.isSubMenu){
25663             return;
25664         }
25665         
25666         this.hidden = true;
25667         
25668         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25669         this.triggerEl.on('click', this.onTriggerPress, this);
25670         
25671         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25672         this.buttonEl.on('click', this.onClick, this);
25673         
25674     },
25675     
25676     list : function()
25677     {
25678         if(this.isSubMenu){
25679             return this.el;
25680         }
25681         
25682         return this.el.select('ul.dropdown-menu', true).first();
25683     },
25684     
25685     onClick : function(e)
25686     {
25687         this.fireEvent("click", this, e);
25688     },
25689     
25690     onTriggerPress  : function(e)
25691     {   
25692         if (this.isVisible()) {
25693             this.hide();
25694         } else {
25695             this.show();
25696         }
25697     },
25698     
25699     isVisible : function(){
25700         return !this.hidden;
25701     },
25702     
25703     show : function()
25704     {
25705         this.fireEvent("beforeshow", this);
25706         
25707         this.hidden = false;
25708         this.el.addClass('open');
25709         
25710         Roo.get(document).on("mouseup", this.onMouseUp, this);
25711         
25712         this.fireEvent("show", this);
25713         
25714         
25715     },
25716     
25717     hide : function()
25718     {
25719         this.fireEvent("beforehide", this);
25720         
25721         this.hidden = true;
25722         this.el.removeClass('open');
25723         
25724         Roo.get(document).un("mouseup", this.onMouseUp);
25725         
25726         this.fireEvent("hide", this);
25727     },
25728     
25729     onMouseUp : function()
25730     {
25731         this.hide();
25732     }
25733     
25734 });
25735
25736  
25737  /*
25738  * - LGPL
25739  *
25740  * menu item
25741  * 
25742  */
25743 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25744
25745 /**
25746  * @class Roo.bootstrap.menu.Item
25747  * @extends Roo.bootstrap.Component
25748  * Bootstrap MenuItem class
25749  * @cfg {Boolean} submenu (true | false) default false
25750  * @cfg {String} html text of the item
25751  * @cfg {String} href the link
25752  * @cfg {Boolean} disable (true | false) default false
25753  * @cfg {Boolean} preventDefault (true | false) default true
25754  * @cfg {String} icon Font awesome icon
25755  * @cfg {String} pos Submenu align to (left | right) default right 
25756  * 
25757  * 
25758  * @constructor
25759  * Create a new Item
25760  * @param {Object} config The config object
25761  */
25762
25763
25764 Roo.bootstrap.menu.Item = function(config){
25765     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25766     this.addEvents({
25767         /**
25768          * @event mouseover
25769          * Fires when the mouse is hovering over this menu
25770          * @param {Roo.bootstrap.menu.Item} this
25771          * @param {Roo.EventObject} e
25772          */
25773         mouseover : true,
25774         /**
25775          * @event mouseout
25776          * Fires when the mouse exits this menu
25777          * @param {Roo.bootstrap.menu.Item} this
25778          * @param {Roo.EventObject} e
25779          */
25780         mouseout : true,
25781         // raw events
25782         /**
25783          * @event click
25784          * The raw click event for the entire grid.
25785          * @param {Roo.EventObject} e
25786          */
25787         click : true
25788     });
25789 };
25790
25791 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25792     
25793     submenu : false,
25794     href : '',
25795     html : '',
25796     preventDefault: true,
25797     disable : false,
25798     icon : false,
25799     pos : 'right',
25800     
25801     getAutoCreate : function()
25802     {
25803         var text = [
25804             {
25805                 tag : 'span',
25806                 cls : 'roo-menu-item-text',
25807                 html : this.html
25808             }
25809         ];
25810         
25811         if(this.icon){
25812             text.unshift({
25813                 tag : 'i',
25814                 cls : 'fa ' + this.icon
25815             })
25816         }
25817         
25818         var cfg = {
25819             tag : 'li',
25820             cn : [
25821                 {
25822                     tag : 'a',
25823                     href : this.href || '#',
25824                     cn : text
25825                 }
25826             ]
25827         };
25828         
25829         if(this.disable){
25830             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25831         }
25832         
25833         if(this.submenu){
25834             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25835             
25836             if(this.pos == 'left'){
25837                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25838             }
25839         }
25840         
25841         return cfg;
25842     },
25843     
25844     initEvents : function() 
25845     {
25846         this.el.on('mouseover', this.onMouseOver, this);
25847         this.el.on('mouseout', this.onMouseOut, this);
25848         
25849         this.el.select('a', true).first().on('click', this.onClick, this);
25850         
25851     },
25852     
25853     onClick : function(e)
25854     {
25855         if(this.preventDefault){
25856             e.preventDefault();
25857         }
25858         
25859         this.fireEvent("click", this, e);
25860     },
25861     
25862     onMouseOver : function(e)
25863     {
25864         if(this.submenu && this.pos == 'left'){
25865             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25866         }
25867         
25868         this.fireEvent("mouseover", this, e);
25869     },
25870     
25871     onMouseOut : function(e)
25872     {
25873         this.fireEvent("mouseout", this, e);
25874     }
25875 });
25876
25877  
25878
25879  /*
25880  * - LGPL
25881  *
25882  * menu separator
25883  * 
25884  */
25885 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25886
25887 /**
25888  * @class Roo.bootstrap.menu.Separator
25889  * @extends Roo.bootstrap.Component
25890  * Bootstrap Separator class
25891  * 
25892  * @constructor
25893  * Create a new Separator
25894  * @param {Object} config The config object
25895  */
25896
25897
25898 Roo.bootstrap.menu.Separator = function(config){
25899     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25900 };
25901
25902 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25903     
25904     getAutoCreate : function(){
25905         var cfg = {
25906             tag : 'li',
25907             cls: 'divider'
25908         };
25909         
25910         return cfg;
25911     }
25912    
25913 });
25914
25915  
25916
25917  /*
25918  * - LGPL
25919  *
25920  * Tooltip
25921  * 
25922  */
25923
25924 /**
25925  * @class Roo.bootstrap.Tooltip
25926  * Bootstrap Tooltip class
25927  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25928  * to determine which dom element triggers the tooltip.
25929  * 
25930  * It needs to add support for additional attributes like tooltip-position
25931  * 
25932  * @constructor
25933  * Create a new Toolti
25934  * @param {Object} config The config object
25935  */
25936
25937 Roo.bootstrap.Tooltip = function(config){
25938     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25939     
25940     this.alignment = Roo.bootstrap.Tooltip.alignment;
25941     
25942     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25943         this.alignment = config.alignment;
25944     }
25945     
25946 };
25947
25948 Roo.apply(Roo.bootstrap.Tooltip, {
25949     /**
25950      * @function init initialize tooltip monitoring.
25951      * @static
25952      */
25953     currentEl : false,
25954     currentTip : false,
25955     currentRegion : false,
25956     
25957     //  init : delay?
25958     
25959     init : function()
25960     {
25961         Roo.get(document).on('mouseover', this.enter ,this);
25962         Roo.get(document).on('mouseout', this.leave, this);
25963          
25964         
25965         this.currentTip = new Roo.bootstrap.Tooltip();
25966     },
25967     
25968     enter : function(ev)
25969     {
25970         var dom = ev.getTarget();
25971         
25972         //Roo.log(['enter',dom]);
25973         var el = Roo.fly(dom);
25974         if (this.currentEl) {
25975             //Roo.log(dom);
25976             //Roo.log(this.currentEl);
25977             //Roo.log(this.currentEl.contains(dom));
25978             if (this.currentEl == el) {
25979                 return;
25980             }
25981             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25982                 return;
25983             }
25984
25985         }
25986         
25987         if (this.currentTip.el) {
25988             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25989         }    
25990         //Roo.log(ev);
25991         
25992         if(!el || el.dom == document){
25993             return;
25994         }
25995         
25996         var bindEl = el;
25997         
25998         // you can not look for children, as if el is the body.. then everythign is the child..
25999         if (!el.attr('tooltip')) { //
26000             if (!el.select("[tooltip]").elements.length) {
26001                 return;
26002             }
26003             // is the mouse over this child...?
26004             bindEl = el.select("[tooltip]").first();
26005             var xy = ev.getXY();
26006             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26007                 //Roo.log("not in region.");
26008                 return;
26009             }
26010             //Roo.log("child element over..");
26011             
26012         }
26013         this.currentEl = bindEl;
26014         this.currentTip.bind(bindEl);
26015         this.currentRegion = Roo.lib.Region.getRegion(dom);
26016         this.currentTip.enter();
26017         
26018     },
26019     leave : function(ev)
26020     {
26021         var dom = ev.getTarget();
26022         //Roo.log(['leave',dom]);
26023         if (!this.currentEl) {
26024             return;
26025         }
26026         
26027         
26028         if (dom != this.currentEl.dom) {
26029             return;
26030         }
26031         var xy = ev.getXY();
26032         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26033             return;
26034         }
26035         // only activate leave if mouse cursor is outside... bounding box..
26036         
26037         
26038         
26039         
26040         if (this.currentTip) {
26041             this.currentTip.leave();
26042         }
26043         //Roo.log('clear currentEl');
26044         this.currentEl = false;
26045         
26046         
26047     },
26048     alignment : {
26049         'left' : ['r-l', [-2,0], 'right'],
26050         'right' : ['l-r', [2,0], 'left'],
26051         'bottom' : ['t-b', [0,2], 'top'],
26052         'top' : [ 'b-t', [0,-2], 'bottom']
26053     }
26054     
26055 });
26056
26057
26058 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26059     
26060     
26061     bindEl : false,
26062     
26063     delay : null, // can be { show : 300 , hide: 500}
26064     
26065     timeout : null,
26066     
26067     hoverState : null, //???
26068     
26069     placement : 'bottom', 
26070     
26071     alignment : false,
26072     
26073     getAutoCreate : function(){
26074     
26075         var cfg = {
26076            cls : 'tooltip',
26077            role : 'tooltip',
26078            cn : [
26079                 {
26080                     cls : 'tooltip-arrow'
26081                 },
26082                 {
26083                     cls : 'tooltip-inner'
26084                 }
26085            ]
26086         };
26087         
26088         return cfg;
26089     },
26090     bind : function(el)
26091     {
26092         this.bindEl = el;
26093     },
26094       
26095     
26096     enter : function () {
26097        
26098         if (this.timeout != null) {
26099             clearTimeout(this.timeout);
26100         }
26101         
26102         this.hoverState = 'in';
26103          //Roo.log("enter - show");
26104         if (!this.delay || !this.delay.show) {
26105             this.show();
26106             return;
26107         }
26108         var _t = this;
26109         this.timeout = setTimeout(function () {
26110             if (_t.hoverState == 'in') {
26111                 _t.show();
26112             }
26113         }, this.delay.show);
26114     },
26115     leave : function()
26116     {
26117         clearTimeout(this.timeout);
26118     
26119         this.hoverState = 'out';
26120          if (!this.delay || !this.delay.hide) {
26121             this.hide();
26122             return;
26123         }
26124        
26125         var _t = this;
26126         this.timeout = setTimeout(function () {
26127             //Roo.log("leave - timeout");
26128             
26129             if (_t.hoverState == 'out') {
26130                 _t.hide();
26131                 Roo.bootstrap.Tooltip.currentEl = false;
26132             }
26133         }, delay);
26134     },
26135     
26136     show : function (msg)
26137     {
26138         if (!this.el) {
26139             this.render(document.body);
26140         }
26141         // set content.
26142         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26143         
26144         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26145         
26146         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26147         
26148         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26149         
26150         var placement = typeof this.placement == 'function' ?
26151             this.placement.call(this, this.el, on_el) :
26152             this.placement;
26153             
26154         var autoToken = /\s?auto?\s?/i;
26155         var autoPlace = autoToken.test(placement);
26156         if (autoPlace) {
26157             placement = placement.replace(autoToken, '') || 'top';
26158         }
26159         
26160         //this.el.detach()
26161         //this.el.setXY([0,0]);
26162         this.el.show();
26163         //this.el.dom.style.display='block';
26164         
26165         //this.el.appendTo(on_el);
26166         
26167         var p = this.getPosition();
26168         var box = this.el.getBox();
26169         
26170         if (autoPlace) {
26171             // fixme..
26172         }
26173         
26174         var align = this.alignment[placement];
26175         
26176         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26177         
26178         if(placement == 'top' || placement == 'bottom'){
26179             if(xy[0] < 0){
26180                 placement = 'right';
26181             }
26182             
26183             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26184                 placement = 'left';
26185             }
26186             
26187             var scroll = Roo.select('body', true).first().getScroll();
26188             
26189             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26190                 placement = 'top';
26191             }
26192             
26193         }
26194         
26195         this.el.alignTo(this.bindEl, align[0],align[1]);
26196         //var arrow = this.el.select('.arrow',true).first();
26197         //arrow.set(align[2], 
26198         
26199         this.el.addClass(placement);
26200         
26201         this.el.addClass('in fade');
26202         
26203         this.hoverState = null;
26204         
26205         if (this.el.hasClass('fade')) {
26206             // fade it?
26207         }
26208         
26209     },
26210     hide : function()
26211     {
26212          
26213         if (!this.el) {
26214             return;
26215         }
26216         //this.el.setXY([0,0]);
26217         this.el.removeClass('in');
26218         //this.el.hide();
26219         
26220     }
26221     
26222 });
26223  
26224
26225  /*
26226  * - LGPL
26227  *
26228  * Location Picker
26229  * 
26230  */
26231
26232 /**
26233  * @class Roo.bootstrap.LocationPicker
26234  * @extends Roo.bootstrap.Component
26235  * Bootstrap LocationPicker class
26236  * @cfg {Number} latitude Position when init default 0
26237  * @cfg {Number} longitude Position when init default 0
26238  * @cfg {Number} zoom default 15
26239  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26240  * @cfg {Boolean} mapTypeControl default false
26241  * @cfg {Boolean} disableDoubleClickZoom default false
26242  * @cfg {Boolean} scrollwheel default true
26243  * @cfg {Boolean} streetViewControl default false
26244  * @cfg {Number} radius default 0
26245  * @cfg {String} locationName
26246  * @cfg {Boolean} draggable default true
26247  * @cfg {Boolean} enableAutocomplete default false
26248  * @cfg {Boolean} enableReverseGeocode default true
26249  * @cfg {String} markerTitle
26250  * 
26251  * @constructor
26252  * Create a new LocationPicker
26253  * @param {Object} config The config object
26254  */
26255
26256
26257 Roo.bootstrap.LocationPicker = function(config){
26258     
26259     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26260     
26261     this.addEvents({
26262         /**
26263          * @event initial
26264          * Fires when the picker initialized.
26265          * @param {Roo.bootstrap.LocationPicker} this
26266          * @param {Google Location} location
26267          */
26268         initial : true,
26269         /**
26270          * @event positionchanged
26271          * Fires when the picker position changed.
26272          * @param {Roo.bootstrap.LocationPicker} this
26273          * @param {Google Location} location
26274          */
26275         positionchanged : true,
26276         /**
26277          * @event resize
26278          * Fires when the map resize.
26279          * @param {Roo.bootstrap.LocationPicker} this
26280          */
26281         resize : true,
26282         /**
26283          * @event show
26284          * Fires when the map show.
26285          * @param {Roo.bootstrap.LocationPicker} this
26286          */
26287         show : true,
26288         /**
26289          * @event hide
26290          * Fires when the map hide.
26291          * @param {Roo.bootstrap.LocationPicker} this
26292          */
26293         hide : true,
26294         /**
26295          * @event mapClick
26296          * Fires when click the map.
26297          * @param {Roo.bootstrap.LocationPicker} this
26298          * @param {Map event} e
26299          */
26300         mapClick : true,
26301         /**
26302          * @event mapRightClick
26303          * Fires when right click the map.
26304          * @param {Roo.bootstrap.LocationPicker} this
26305          * @param {Map event} e
26306          */
26307         mapRightClick : true,
26308         /**
26309          * @event markerClick
26310          * Fires when click the marker.
26311          * @param {Roo.bootstrap.LocationPicker} this
26312          * @param {Map event} e
26313          */
26314         markerClick : true,
26315         /**
26316          * @event markerRightClick
26317          * Fires when right click the marker.
26318          * @param {Roo.bootstrap.LocationPicker} this
26319          * @param {Map event} e
26320          */
26321         markerRightClick : true,
26322         /**
26323          * @event OverlayViewDraw
26324          * Fires when OverlayView Draw
26325          * @param {Roo.bootstrap.LocationPicker} this
26326          */
26327         OverlayViewDraw : true,
26328         /**
26329          * @event OverlayViewOnAdd
26330          * Fires when OverlayView Draw
26331          * @param {Roo.bootstrap.LocationPicker} this
26332          */
26333         OverlayViewOnAdd : true,
26334         /**
26335          * @event OverlayViewOnRemove
26336          * Fires when OverlayView Draw
26337          * @param {Roo.bootstrap.LocationPicker} this
26338          */
26339         OverlayViewOnRemove : true,
26340         /**
26341          * @event OverlayViewShow
26342          * Fires when OverlayView Draw
26343          * @param {Roo.bootstrap.LocationPicker} this
26344          * @param {Pixel} cpx
26345          */
26346         OverlayViewShow : true,
26347         /**
26348          * @event OverlayViewHide
26349          * Fires when OverlayView Draw
26350          * @param {Roo.bootstrap.LocationPicker} this
26351          */
26352         OverlayViewHide : true,
26353         /**
26354          * @event loadexception
26355          * Fires when load google lib failed.
26356          * @param {Roo.bootstrap.LocationPicker} this
26357          */
26358         loadexception : true
26359     });
26360         
26361 };
26362
26363 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26364     
26365     gMapContext: false,
26366     
26367     latitude: 0,
26368     longitude: 0,
26369     zoom: 15,
26370     mapTypeId: false,
26371     mapTypeControl: false,
26372     disableDoubleClickZoom: false,
26373     scrollwheel: true,
26374     streetViewControl: false,
26375     radius: 0,
26376     locationName: '',
26377     draggable: true,
26378     enableAutocomplete: false,
26379     enableReverseGeocode: true,
26380     markerTitle: '',
26381     
26382     getAutoCreate: function()
26383     {
26384
26385         var cfg = {
26386             tag: 'div',
26387             cls: 'roo-location-picker'
26388         };
26389         
26390         return cfg
26391     },
26392     
26393     initEvents: function(ct, position)
26394     {       
26395         if(!this.el.getWidth() || this.isApplied()){
26396             return;
26397         }
26398         
26399         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26400         
26401         this.initial();
26402     },
26403     
26404     initial: function()
26405     {
26406         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26407             this.fireEvent('loadexception', this);
26408             return;
26409         }
26410         
26411         if(!this.mapTypeId){
26412             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26413         }
26414         
26415         this.gMapContext = this.GMapContext();
26416         
26417         this.initOverlayView();
26418         
26419         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26420         
26421         var _this = this;
26422                 
26423         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26424             _this.setPosition(_this.gMapContext.marker.position);
26425         });
26426         
26427         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26428             _this.fireEvent('mapClick', this, event);
26429             
26430         });
26431
26432         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26433             _this.fireEvent('mapRightClick', this, event);
26434             
26435         });
26436         
26437         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26438             _this.fireEvent('markerClick', this, event);
26439             
26440         });
26441
26442         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26443             _this.fireEvent('markerRightClick', this, event);
26444             
26445         });
26446         
26447         this.setPosition(this.gMapContext.location);
26448         
26449         this.fireEvent('initial', this, this.gMapContext.location);
26450     },
26451     
26452     initOverlayView: function()
26453     {
26454         var _this = this;
26455         
26456         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26457             
26458             draw: function()
26459             {
26460                 _this.fireEvent('OverlayViewDraw', _this);
26461             },
26462             
26463             onAdd: function()
26464             {
26465                 _this.fireEvent('OverlayViewOnAdd', _this);
26466             },
26467             
26468             onRemove: function()
26469             {
26470                 _this.fireEvent('OverlayViewOnRemove', _this);
26471             },
26472             
26473             show: function(cpx)
26474             {
26475                 _this.fireEvent('OverlayViewShow', _this, cpx);
26476             },
26477             
26478             hide: function()
26479             {
26480                 _this.fireEvent('OverlayViewHide', _this);
26481             }
26482             
26483         });
26484     },
26485     
26486     fromLatLngToContainerPixel: function(event)
26487     {
26488         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26489     },
26490     
26491     isApplied: function() 
26492     {
26493         return this.getGmapContext() == false ? false : true;
26494     },
26495     
26496     getGmapContext: function() 
26497     {
26498         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26499     },
26500     
26501     GMapContext: function() 
26502     {
26503         var position = new google.maps.LatLng(this.latitude, this.longitude);
26504         
26505         var _map = new google.maps.Map(this.el.dom, {
26506             center: position,
26507             zoom: this.zoom,
26508             mapTypeId: this.mapTypeId,
26509             mapTypeControl: this.mapTypeControl,
26510             disableDoubleClickZoom: this.disableDoubleClickZoom,
26511             scrollwheel: this.scrollwheel,
26512             streetViewControl: this.streetViewControl,
26513             locationName: this.locationName,
26514             draggable: this.draggable,
26515             enableAutocomplete: this.enableAutocomplete,
26516             enableReverseGeocode: this.enableReverseGeocode
26517         });
26518         
26519         var _marker = new google.maps.Marker({
26520             position: position,
26521             map: _map,
26522             title: this.markerTitle,
26523             draggable: this.draggable
26524         });
26525         
26526         return {
26527             map: _map,
26528             marker: _marker,
26529             circle: null,
26530             location: position,
26531             radius: this.radius,
26532             locationName: this.locationName,
26533             addressComponents: {
26534                 formatted_address: null,
26535                 addressLine1: null,
26536                 addressLine2: null,
26537                 streetName: null,
26538                 streetNumber: null,
26539                 city: null,
26540                 district: null,
26541                 state: null,
26542                 stateOrProvince: null
26543             },
26544             settings: this,
26545             domContainer: this.el.dom,
26546             geodecoder: new google.maps.Geocoder()
26547         };
26548     },
26549     
26550     drawCircle: function(center, radius, options) 
26551     {
26552         if (this.gMapContext.circle != null) {
26553             this.gMapContext.circle.setMap(null);
26554         }
26555         if (radius > 0) {
26556             radius *= 1;
26557             options = Roo.apply({}, options, {
26558                 strokeColor: "#0000FF",
26559                 strokeOpacity: .35,
26560                 strokeWeight: 2,
26561                 fillColor: "#0000FF",
26562                 fillOpacity: .2
26563             });
26564             
26565             options.map = this.gMapContext.map;
26566             options.radius = radius;
26567             options.center = center;
26568             this.gMapContext.circle = new google.maps.Circle(options);
26569             return this.gMapContext.circle;
26570         }
26571         
26572         return null;
26573     },
26574     
26575     setPosition: function(location) 
26576     {
26577         this.gMapContext.location = location;
26578         this.gMapContext.marker.setPosition(location);
26579         this.gMapContext.map.panTo(location);
26580         this.drawCircle(location, this.gMapContext.radius, {});
26581         
26582         var _this = this;
26583         
26584         if (this.gMapContext.settings.enableReverseGeocode) {
26585             this.gMapContext.geodecoder.geocode({
26586                 latLng: this.gMapContext.location
26587             }, function(results, status) {
26588                 
26589                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26590                     _this.gMapContext.locationName = results[0].formatted_address;
26591                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26592                     
26593                     _this.fireEvent('positionchanged', this, location);
26594                 }
26595             });
26596             
26597             return;
26598         }
26599         
26600         this.fireEvent('positionchanged', this, location);
26601     },
26602     
26603     resize: function()
26604     {
26605         google.maps.event.trigger(this.gMapContext.map, "resize");
26606         
26607         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26608         
26609         this.fireEvent('resize', this);
26610     },
26611     
26612     setPositionByLatLng: function(latitude, longitude)
26613     {
26614         this.setPosition(new google.maps.LatLng(latitude, longitude));
26615     },
26616     
26617     getCurrentPosition: function() 
26618     {
26619         return {
26620             latitude: this.gMapContext.location.lat(),
26621             longitude: this.gMapContext.location.lng()
26622         };
26623     },
26624     
26625     getAddressName: function() 
26626     {
26627         return this.gMapContext.locationName;
26628     },
26629     
26630     getAddressComponents: function() 
26631     {
26632         return this.gMapContext.addressComponents;
26633     },
26634     
26635     address_component_from_google_geocode: function(address_components) 
26636     {
26637         var result = {};
26638         
26639         for (var i = 0; i < address_components.length; i++) {
26640             var component = address_components[i];
26641             if (component.types.indexOf("postal_code") >= 0) {
26642                 result.postalCode = component.short_name;
26643             } else if (component.types.indexOf("street_number") >= 0) {
26644                 result.streetNumber = component.short_name;
26645             } else if (component.types.indexOf("route") >= 0) {
26646                 result.streetName = component.short_name;
26647             } else if (component.types.indexOf("neighborhood") >= 0) {
26648                 result.city = component.short_name;
26649             } else if (component.types.indexOf("locality") >= 0) {
26650                 result.city = component.short_name;
26651             } else if (component.types.indexOf("sublocality") >= 0) {
26652                 result.district = component.short_name;
26653             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26654                 result.stateOrProvince = component.short_name;
26655             } else if (component.types.indexOf("country") >= 0) {
26656                 result.country = component.short_name;
26657             }
26658         }
26659         
26660         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26661         result.addressLine2 = "";
26662         return result;
26663     },
26664     
26665     setZoomLevel: function(zoom)
26666     {
26667         this.gMapContext.map.setZoom(zoom);
26668     },
26669     
26670     show: function()
26671     {
26672         if(!this.el){
26673             return;
26674         }
26675         
26676         this.el.show();
26677         
26678         this.resize();
26679         
26680         this.fireEvent('show', this);
26681     },
26682     
26683     hide: function()
26684     {
26685         if(!this.el){
26686             return;
26687         }
26688         
26689         this.el.hide();
26690         
26691         this.fireEvent('hide', this);
26692     }
26693     
26694 });
26695
26696 Roo.apply(Roo.bootstrap.LocationPicker, {
26697     
26698     OverlayView : function(map, options)
26699     {
26700         options = options || {};
26701         
26702         this.setMap(map);
26703     }
26704     
26705     
26706 });/*
26707  * - LGPL
26708  *
26709  * Alert
26710  * 
26711  */
26712
26713 /**
26714  * @class Roo.bootstrap.Alert
26715  * @extends Roo.bootstrap.Component
26716  * Bootstrap Alert class
26717  * @cfg {String} title The title of alert
26718  * @cfg {String} html The content of alert
26719  * @cfg {String} weight (  success | info | warning | danger )
26720  * @cfg {String} faicon font-awesomeicon
26721  * 
26722  * @constructor
26723  * Create a new alert
26724  * @param {Object} config The config object
26725  */
26726
26727
26728 Roo.bootstrap.Alert = function(config){
26729     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26730     
26731 };
26732
26733 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26734     
26735     title: '',
26736     html: '',
26737     weight: false,
26738     faicon: false,
26739     
26740     getAutoCreate : function()
26741     {
26742         
26743         var cfg = {
26744             tag : 'div',
26745             cls : 'alert',
26746             cn : [
26747                 {
26748                     tag : 'i',
26749                     cls : 'roo-alert-icon'
26750                     
26751                 },
26752                 {
26753                     tag : 'b',
26754                     cls : 'roo-alert-title',
26755                     html : this.title
26756                 },
26757                 {
26758                     tag : 'span',
26759                     cls : 'roo-alert-text',
26760                     html : this.html
26761                 }
26762             ]
26763         };
26764         
26765         if(this.faicon){
26766             cfg.cn[0].cls += ' fa ' + this.faicon;
26767         }
26768         
26769         if(this.weight){
26770             cfg.cls += ' alert-' + this.weight;
26771         }
26772         
26773         return cfg;
26774     },
26775     
26776     initEvents: function() 
26777     {
26778         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26779     },
26780     
26781     setTitle : function(str)
26782     {
26783         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26784     },
26785     
26786     setText : function(str)
26787     {
26788         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26789     },
26790     
26791     setWeight : function(weight)
26792     {
26793         if(this.weight){
26794             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26795         }
26796         
26797         this.weight = weight;
26798         
26799         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26800     },
26801     
26802     setIcon : function(icon)
26803     {
26804         if(this.faicon){
26805             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26806         }
26807         
26808         this.faicon = icon;
26809         
26810         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26811     },
26812     
26813     hide: function() 
26814     {
26815         this.el.hide();   
26816     },
26817     
26818     show: function() 
26819     {  
26820         this.el.show();   
26821     }
26822     
26823 });
26824
26825  
26826 /*
26827 * Licence: LGPL
26828 */
26829
26830 /**
26831  * @class Roo.bootstrap.UploadCropbox
26832  * @extends Roo.bootstrap.Component
26833  * Bootstrap UploadCropbox class
26834  * @cfg {String} emptyText show when image has been loaded
26835  * @cfg {String} rotateNotify show when image too small to rotate
26836  * @cfg {Number} errorTimeout default 3000
26837  * @cfg {Number} minWidth default 300
26838  * @cfg {Number} minHeight default 300
26839  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26840  * @cfg {Boolean} isDocument (true|false) default false
26841  * @cfg {String} url action url
26842  * @cfg {String} paramName default 'imageUpload'
26843  * @cfg {String} method default POST
26844  * @cfg {Boolean} loadMask (true|false) default true
26845  * @cfg {Boolean} loadingText default 'Loading...'
26846  * 
26847  * @constructor
26848  * Create a new UploadCropbox
26849  * @param {Object} config The config object
26850  */
26851
26852 Roo.bootstrap.UploadCropbox = function(config){
26853     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26854     
26855     this.addEvents({
26856         /**
26857          * @event beforeselectfile
26858          * Fire before select file
26859          * @param {Roo.bootstrap.UploadCropbox} this
26860          */
26861         "beforeselectfile" : true,
26862         /**
26863          * @event initial
26864          * Fire after initEvent
26865          * @param {Roo.bootstrap.UploadCropbox} this
26866          */
26867         "initial" : true,
26868         /**
26869          * @event crop
26870          * Fire after initEvent
26871          * @param {Roo.bootstrap.UploadCropbox} this
26872          * @param {String} data
26873          */
26874         "crop" : true,
26875         /**
26876          * @event prepare
26877          * Fire when preparing the file data
26878          * @param {Roo.bootstrap.UploadCropbox} this
26879          * @param {Object} file
26880          */
26881         "prepare" : true,
26882         /**
26883          * @event exception
26884          * Fire when get exception
26885          * @param {Roo.bootstrap.UploadCropbox} this
26886          * @param {XMLHttpRequest} xhr
26887          */
26888         "exception" : true,
26889         /**
26890          * @event beforeloadcanvas
26891          * Fire before load the canvas
26892          * @param {Roo.bootstrap.UploadCropbox} this
26893          * @param {String} src
26894          */
26895         "beforeloadcanvas" : true,
26896         /**
26897          * @event trash
26898          * Fire when trash image
26899          * @param {Roo.bootstrap.UploadCropbox} this
26900          */
26901         "trash" : true,
26902         /**
26903          * @event download
26904          * Fire when download the image
26905          * @param {Roo.bootstrap.UploadCropbox} this
26906          */
26907         "download" : true,
26908         /**
26909          * @event footerbuttonclick
26910          * Fire when footerbuttonclick
26911          * @param {Roo.bootstrap.UploadCropbox} this
26912          * @param {String} type
26913          */
26914         "footerbuttonclick" : true,
26915         /**
26916          * @event resize
26917          * Fire when resize
26918          * @param {Roo.bootstrap.UploadCropbox} this
26919          */
26920         "resize" : true,
26921         /**
26922          * @event rotate
26923          * Fire when rotate the image
26924          * @param {Roo.bootstrap.UploadCropbox} this
26925          * @param {String} pos
26926          */
26927         "rotate" : true,
26928         /**
26929          * @event inspect
26930          * Fire when inspect the file
26931          * @param {Roo.bootstrap.UploadCropbox} this
26932          * @param {Object} file
26933          */
26934         "inspect" : true,
26935         /**
26936          * @event upload
26937          * Fire when xhr upload the file
26938          * @param {Roo.bootstrap.UploadCropbox} this
26939          * @param {Object} data
26940          */
26941         "upload" : true,
26942         /**
26943          * @event arrange
26944          * Fire when arrange the file data
26945          * @param {Roo.bootstrap.UploadCropbox} this
26946          * @param {Object} formData
26947          */
26948         "arrange" : true
26949     });
26950     
26951     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26952 };
26953
26954 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26955     
26956     emptyText : 'Click to upload image',
26957     rotateNotify : 'Image is too small to rotate',
26958     errorTimeout : 3000,
26959     scale : 0,
26960     baseScale : 1,
26961     rotate : 0,
26962     dragable : false,
26963     pinching : false,
26964     mouseX : 0,
26965     mouseY : 0,
26966     cropData : false,
26967     minWidth : 300,
26968     minHeight : 300,
26969     file : false,
26970     exif : {},
26971     baseRotate : 1,
26972     cropType : 'image/jpeg',
26973     buttons : false,
26974     canvasLoaded : false,
26975     isDocument : false,
26976     method : 'POST',
26977     paramName : 'imageUpload',
26978     loadMask : true,
26979     loadingText : 'Loading...',
26980     maskEl : false,
26981     
26982     getAutoCreate : function()
26983     {
26984         var cfg = {
26985             tag : 'div',
26986             cls : 'roo-upload-cropbox',
26987             cn : [
26988                 {
26989                     tag : 'input',
26990                     cls : 'roo-upload-cropbox-selector',
26991                     type : 'file'
26992                 },
26993                 {
26994                     tag : 'div',
26995                     cls : 'roo-upload-cropbox-body',
26996                     style : 'cursor:pointer',
26997                     cn : [
26998                         {
26999                             tag : 'div',
27000                             cls : 'roo-upload-cropbox-preview'
27001                         },
27002                         {
27003                             tag : 'div',
27004                             cls : 'roo-upload-cropbox-thumb'
27005                         },
27006                         {
27007                             tag : 'div',
27008                             cls : 'roo-upload-cropbox-empty-notify',
27009                             html : this.emptyText
27010                         },
27011                         {
27012                             tag : 'div',
27013                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27014                             html : this.rotateNotify
27015                         }
27016                     ]
27017                 },
27018                 {
27019                     tag : 'div',
27020                     cls : 'roo-upload-cropbox-footer',
27021                     cn : {
27022                         tag : 'div',
27023                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27024                         cn : []
27025                     }
27026                 }
27027             ]
27028         };
27029         
27030         return cfg;
27031     },
27032     
27033     onRender : function(ct, position)
27034     {
27035         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27036         
27037         if (this.buttons.length) {
27038             
27039             Roo.each(this.buttons, function(bb) {
27040                 
27041                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27042                 
27043                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27044                 
27045             }, this);
27046         }
27047         
27048         if(this.loadMask){
27049             this.maskEl = this.el;
27050         }
27051     },
27052     
27053     initEvents : function()
27054     {
27055         this.urlAPI = (window.createObjectURL && window) || 
27056                                 (window.URL && URL.revokeObjectURL && URL) || 
27057                                 (window.webkitURL && webkitURL);
27058                         
27059         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27060         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27061         
27062         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27063         this.selectorEl.hide();
27064         
27065         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27066         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27067         
27068         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27069         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070         this.thumbEl.hide();
27071         
27072         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27073         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27074         
27075         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27076         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27077         this.errorEl.hide();
27078         
27079         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27080         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27081         this.footerEl.hide();
27082         
27083         this.setThumbBoxSize();
27084         
27085         this.bind();
27086         
27087         this.resize();
27088         
27089         this.fireEvent('initial', this);
27090     },
27091
27092     bind : function()
27093     {
27094         var _this = this;
27095         
27096         window.addEventListener("resize", function() { _this.resize(); } );
27097         
27098         this.bodyEl.on('click', this.beforeSelectFile, this);
27099         
27100         if(Roo.isTouch){
27101             this.bodyEl.on('touchstart', this.onTouchStart, this);
27102             this.bodyEl.on('touchmove', this.onTouchMove, this);
27103             this.bodyEl.on('touchend', this.onTouchEnd, this);
27104         }
27105         
27106         if(!Roo.isTouch){
27107             this.bodyEl.on('mousedown', this.onMouseDown, this);
27108             this.bodyEl.on('mousemove', this.onMouseMove, this);
27109             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27110             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27111             Roo.get(document).on('mouseup', this.onMouseUp, this);
27112         }
27113         
27114         this.selectorEl.on('change', this.onFileSelected, this);
27115     },
27116     
27117     reset : function()
27118     {    
27119         this.scale = 0;
27120         this.baseScale = 1;
27121         this.rotate = 0;
27122         this.baseRotate = 1;
27123         this.dragable = false;
27124         this.pinching = false;
27125         this.mouseX = 0;
27126         this.mouseY = 0;
27127         this.cropData = false;
27128         this.notifyEl.dom.innerHTML = this.emptyText;
27129         
27130         this.selectorEl.dom.value = '';
27131         
27132     },
27133     
27134     resize : function()
27135     {
27136         if(this.fireEvent('resize', this) != false){
27137             this.setThumbBoxPosition();
27138             this.setCanvasPosition();
27139         }
27140     },
27141     
27142     onFooterButtonClick : function(e, el, o, type)
27143     {
27144         switch (type) {
27145             case 'rotate-left' :
27146                 this.onRotateLeft(e);
27147                 break;
27148             case 'rotate-right' :
27149                 this.onRotateRight(e);
27150                 break;
27151             case 'picture' :
27152                 this.beforeSelectFile(e);
27153                 break;
27154             case 'trash' :
27155                 this.trash(e);
27156                 break;
27157             case 'crop' :
27158                 this.crop(e);
27159                 break;
27160             case 'download' :
27161                 this.download(e);
27162                 break;
27163             default :
27164                 break;
27165         }
27166         
27167         this.fireEvent('footerbuttonclick', this, type);
27168     },
27169     
27170     beforeSelectFile : function(e)
27171     {
27172         e.preventDefault();
27173         
27174         if(this.fireEvent('beforeselectfile', this) != false){
27175             this.selectorEl.dom.click();
27176         }
27177     },
27178     
27179     onFileSelected : function(e)
27180     {
27181         e.preventDefault();
27182         
27183         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27184             return;
27185         }
27186         
27187         var file = this.selectorEl.dom.files[0];
27188         
27189         if(this.fireEvent('inspect', this, file) != false){
27190             this.prepare(file);
27191         }
27192         
27193     },
27194     
27195     trash : function(e)
27196     {
27197         this.fireEvent('trash', this);
27198     },
27199     
27200     download : function(e)
27201     {
27202         this.fireEvent('download', this);
27203     },
27204     
27205     loadCanvas : function(src)
27206     {   
27207         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27208             
27209             this.reset();
27210             
27211             this.imageEl = document.createElement('img');
27212             
27213             var _this = this;
27214             
27215             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27216             
27217             this.imageEl.src = src;
27218         }
27219     },
27220     
27221     onLoadCanvas : function()
27222     {   
27223         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27224         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27225         
27226         this.bodyEl.un('click', this.beforeSelectFile, this);
27227         
27228         this.notifyEl.hide();
27229         this.thumbEl.show();
27230         this.footerEl.show();
27231         
27232         this.baseRotateLevel();
27233         
27234         if(this.isDocument){
27235             this.setThumbBoxSize();
27236         }
27237         
27238         this.setThumbBoxPosition();
27239         
27240         this.baseScaleLevel();
27241         
27242         this.draw();
27243         
27244         this.resize();
27245         
27246         this.canvasLoaded = true;
27247         
27248         if(this.loadMask){
27249             this.maskEl.unmask();
27250         }
27251         
27252     },
27253     
27254     setCanvasPosition : function()
27255     {   
27256         if(!this.canvasEl){
27257             return;
27258         }
27259         
27260         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27261         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27262         
27263         this.previewEl.setLeft(pw);
27264         this.previewEl.setTop(ph);
27265         
27266     },
27267     
27268     onMouseDown : function(e)
27269     {   
27270         e.stopEvent();
27271         
27272         this.dragable = true;
27273         this.pinching = false;
27274         
27275         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27276             this.dragable = false;
27277             return;
27278         }
27279         
27280         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27281         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27282         
27283     },
27284     
27285     onMouseMove : function(e)
27286     {   
27287         e.stopEvent();
27288         
27289         if(!this.canvasLoaded){
27290             return;
27291         }
27292         
27293         if (!this.dragable){
27294             return;
27295         }
27296         
27297         var minX = Math.ceil(this.thumbEl.getLeft(true));
27298         var minY = Math.ceil(this.thumbEl.getTop(true));
27299         
27300         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27301         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27302         
27303         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27304         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27305         
27306         x = x - this.mouseX;
27307         y = y - this.mouseY;
27308         
27309         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27310         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27311         
27312         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27313         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27314         
27315         this.previewEl.setLeft(bgX);
27316         this.previewEl.setTop(bgY);
27317         
27318         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27319         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27320     },
27321     
27322     onMouseUp : function(e)
27323     {   
27324         e.stopEvent();
27325         
27326         this.dragable = false;
27327     },
27328     
27329     onMouseWheel : function(e)
27330     {   
27331         e.stopEvent();
27332         
27333         this.startScale = this.scale;
27334         
27335         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27336         
27337         if(!this.zoomable()){
27338             this.scale = this.startScale;
27339             return;
27340         }
27341         
27342         this.draw();
27343         
27344         return;
27345     },
27346     
27347     zoomable : function()
27348     {
27349         var minScale = this.thumbEl.getWidth() / this.minWidth;
27350         
27351         if(this.minWidth < this.minHeight){
27352             minScale = this.thumbEl.getHeight() / this.minHeight;
27353         }
27354         
27355         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27356         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27357         
27358         if(
27359                 this.isDocument &&
27360                 (this.rotate == 0 || this.rotate == 180) && 
27361                 (
27362                     width > this.imageEl.OriginWidth || 
27363                     height > this.imageEl.OriginHeight ||
27364                     (width < this.minWidth && height < this.minHeight)
27365                 )
27366         ){
27367             return false;
27368         }
27369         
27370         if(
27371                 this.isDocument &&
27372                 (this.rotate == 90 || this.rotate == 270) && 
27373                 (
27374                     width > this.imageEl.OriginWidth || 
27375                     height > this.imageEl.OriginHeight ||
27376                     (width < this.minHeight && height < this.minWidth)
27377                 )
27378         ){
27379             return false;
27380         }
27381         
27382         if(
27383                 !this.isDocument &&
27384                 (this.rotate == 0 || this.rotate == 180) && 
27385                 (
27386                     width < this.minWidth || 
27387                     width > this.imageEl.OriginWidth || 
27388                     height < this.minHeight || 
27389                     height > this.imageEl.OriginHeight
27390                 )
27391         ){
27392             return false;
27393         }
27394         
27395         if(
27396                 !this.isDocument &&
27397                 (this.rotate == 90 || this.rotate == 270) && 
27398                 (
27399                     width < this.minHeight || 
27400                     width > this.imageEl.OriginWidth || 
27401                     height < this.minWidth || 
27402                     height > this.imageEl.OriginHeight
27403                 )
27404         ){
27405             return false;
27406         }
27407         
27408         return true;
27409         
27410     },
27411     
27412     onRotateLeft : function(e)
27413     {   
27414         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27415             
27416             var minScale = this.thumbEl.getWidth() / this.minWidth;
27417             
27418             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27419             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27420             
27421             this.startScale = this.scale;
27422             
27423             while (this.getScaleLevel() < minScale){
27424             
27425                 this.scale = this.scale + 1;
27426                 
27427                 if(!this.zoomable()){
27428                     break;
27429                 }
27430                 
27431                 if(
27432                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27433                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27434                 ){
27435                     continue;
27436                 }
27437                 
27438                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27439
27440                 this.draw();
27441                 
27442                 return;
27443             }
27444             
27445             this.scale = this.startScale;
27446             
27447             this.onRotateFail();
27448             
27449             return false;
27450         }
27451         
27452         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27453
27454         if(this.isDocument){
27455             this.setThumbBoxSize();
27456             this.setThumbBoxPosition();
27457             this.setCanvasPosition();
27458         }
27459         
27460         this.draw();
27461         
27462         this.fireEvent('rotate', this, 'left');
27463         
27464     },
27465     
27466     onRotateRight : function(e)
27467     {
27468         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27469             
27470             var minScale = this.thumbEl.getWidth() / this.minWidth;
27471         
27472             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27473             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27474             
27475             this.startScale = this.scale;
27476             
27477             while (this.getScaleLevel() < minScale){
27478             
27479                 this.scale = this.scale + 1;
27480                 
27481                 if(!this.zoomable()){
27482                     break;
27483                 }
27484                 
27485                 if(
27486                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27487                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27488                 ){
27489                     continue;
27490                 }
27491                 
27492                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27493
27494                 this.draw();
27495                 
27496                 return;
27497             }
27498             
27499             this.scale = this.startScale;
27500             
27501             this.onRotateFail();
27502             
27503             return false;
27504         }
27505         
27506         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27507
27508         if(this.isDocument){
27509             this.setThumbBoxSize();
27510             this.setThumbBoxPosition();
27511             this.setCanvasPosition();
27512         }
27513         
27514         this.draw();
27515         
27516         this.fireEvent('rotate', this, 'right');
27517     },
27518     
27519     onRotateFail : function()
27520     {
27521         this.errorEl.show(true);
27522         
27523         var _this = this;
27524         
27525         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27526     },
27527     
27528     draw : function()
27529     {
27530         this.previewEl.dom.innerHTML = '';
27531         
27532         var canvasEl = document.createElement("canvas");
27533         
27534         var contextEl = canvasEl.getContext("2d");
27535         
27536         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27537         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27538         var center = this.imageEl.OriginWidth / 2;
27539         
27540         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27541             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27542             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27543             center = this.imageEl.OriginHeight / 2;
27544         }
27545         
27546         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27547         
27548         contextEl.translate(center, center);
27549         contextEl.rotate(this.rotate * Math.PI / 180);
27550
27551         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27552         
27553         this.canvasEl = document.createElement("canvas");
27554         
27555         this.contextEl = this.canvasEl.getContext("2d");
27556         
27557         switch (this.rotate) {
27558             case 0 :
27559                 
27560                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27561                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27562                 
27563                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27564                 
27565                 break;
27566             case 90 : 
27567                 
27568                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27569                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27570                 
27571                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27572                     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);
27573                     break;
27574                 }
27575                 
27576                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27577                 
27578                 break;
27579             case 180 :
27580                 
27581                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27582                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27583                 
27584                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27585                     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);
27586                     break;
27587                 }
27588                 
27589                 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);
27590                 
27591                 break;
27592             case 270 :
27593                 
27594                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27595                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27596         
27597                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27598                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27599                     break;
27600                 }
27601                 
27602                 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);
27603                 
27604                 break;
27605             default : 
27606                 break;
27607         }
27608         
27609         this.previewEl.appendChild(this.canvasEl);
27610         
27611         this.setCanvasPosition();
27612     },
27613     
27614     crop : function()
27615     {
27616         if(!this.canvasLoaded){
27617             return;
27618         }
27619         
27620         var imageCanvas = document.createElement("canvas");
27621         
27622         var imageContext = imageCanvas.getContext("2d");
27623         
27624         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27625         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27626         
27627         var center = imageCanvas.width / 2;
27628         
27629         imageContext.translate(center, center);
27630         
27631         imageContext.rotate(this.rotate * Math.PI / 180);
27632         
27633         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27634         
27635         var canvas = document.createElement("canvas");
27636         
27637         var context = canvas.getContext("2d");
27638                 
27639         canvas.width = this.minWidth;
27640         canvas.height = this.minHeight;
27641
27642         switch (this.rotate) {
27643             case 0 :
27644                 
27645                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27646                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27647                 
27648                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27649                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27650                 
27651                 var targetWidth = this.minWidth - 2 * x;
27652                 var targetHeight = this.minHeight - 2 * y;
27653                 
27654                 var scale = 1;
27655                 
27656                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27657                     scale = targetWidth / width;
27658                 }
27659                 
27660                 if(x > 0 && y == 0){
27661                     scale = targetHeight / height;
27662                 }
27663                 
27664                 if(x > 0 && y > 0){
27665                     scale = targetWidth / width;
27666                     
27667                     if(width < height){
27668                         scale = targetHeight / height;
27669                     }
27670                 }
27671                 
27672                 context.scale(scale, scale);
27673                 
27674                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27675                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27676
27677                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27678                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27679
27680                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27681                 
27682                 break;
27683             case 90 : 
27684                 
27685                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27686                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27687                 
27688                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27689                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27690                 
27691                 var targetWidth = this.minWidth - 2 * x;
27692                 var targetHeight = this.minHeight - 2 * y;
27693                 
27694                 var scale = 1;
27695                 
27696                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27697                     scale = targetWidth / width;
27698                 }
27699                 
27700                 if(x > 0 && y == 0){
27701                     scale = targetHeight / height;
27702                 }
27703                 
27704                 if(x > 0 && y > 0){
27705                     scale = targetWidth / width;
27706                     
27707                     if(width < height){
27708                         scale = targetHeight / height;
27709                     }
27710                 }
27711                 
27712                 context.scale(scale, scale);
27713                 
27714                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27715                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27716
27717                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27718                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27719                 
27720                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27721                 
27722                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27723                 
27724                 break;
27725             case 180 :
27726                 
27727                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27728                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27729                 
27730                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27731                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27732                 
27733                 var targetWidth = this.minWidth - 2 * x;
27734                 var targetHeight = this.minHeight - 2 * y;
27735                 
27736                 var scale = 1;
27737                 
27738                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27739                     scale = targetWidth / width;
27740                 }
27741                 
27742                 if(x > 0 && y == 0){
27743                     scale = targetHeight / height;
27744                 }
27745                 
27746                 if(x > 0 && y > 0){
27747                     scale = targetWidth / width;
27748                     
27749                     if(width < height){
27750                         scale = targetHeight / height;
27751                     }
27752                 }
27753                 
27754                 context.scale(scale, scale);
27755                 
27756                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27757                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27758
27759                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27760                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27761
27762                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27763                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27764                 
27765                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27766                 
27767                 break;
27768             case 270 :
27769                 
27770                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27771                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27772                 
27773                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27774                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27775                 
27776                 var targetWidth = this.minWidth - 2 * x;
27777                 var targetHeight = this.minHeight - 2 * y;
27778                 
27779                 var scale = 1;
27780                 
27781                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27782                     scale = targetWidth / width;
27783                 }
27784                 
27785                 if(x > 0 && y == 0){
27786                     scale = targetHeight / height;
27787                 }
27788                 
27789                 if(x > 0 && y > 0){
27790                     scale = targetWidth / width;
27791                     
27792                     if(width < height){
27793                         scale = targetHeight / height;
27794                     }
27795                 }
27796                 
27797                 context.scale(scale, scale);
27798                 
27799                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27800                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27801
27802                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27803                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27804                 
27805                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27806                 
27807                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27808                 
27809                 break;
27810             default : 
27811                 break;
27812         }
27813         
27814         this.cropData = canvas.toDataURL(this.cropType);
27815         
27816         if(this.fireEvent('crop', this, this.cropData) !== false){
27817             this.process(this.file, this.cropData);
27818         }
27819         
27820         return;
27821         
27822     },
27823     
27824     setThumbBoxSize : function()
27825     {
27826         var width, height;
27827         
27828         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27829             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27830             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27831             
27832             this.minWidth = width;
27833             this.minHeight = height;
27834             
27835             if(this.rotate == 90 || this.rotate == 270){
27836                 this.minWidth = height;
27837                 this.minHeight = width;
27838             }
27839         }
27840         
27841         height = 300;
27842         width = Math.ceil(this.minWidth * height / this.minHeight);
27843         
27844         if(this.minWidth > this.minHeight){
27845             width = 300;
27846             height = Math.ceil(this.minHeight * width / this.minWidth);
27847         }
27848         
27849         this.thumbEl.setStyle({
27850             width : width + 'px',
27851             height : height + 'px'
27852         });
27853
27854         return;
27855             
27856     },
27857     
27858     setThumbBoxPosition : function()
27859     {
27860         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27861         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27862         
27863         this.thumbEl.setLeft(x);
27864         this.thumbEl.setTop(y);
27865         
27866     },
27867     
27868     baseRotateLevel : function()
27869     {
27870         this.baseRotate = 1;
27871         
27872         if(
27873                 typeof(this.exif) != 'undefined' &&
27874                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27875                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27876         ){
27877             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27878         }
27879         
27880         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27881         
27882     },
27883     
27884     baseScaleLevel : function()
27885     {
27886         var width, height;
27887         
27888         if(this.isDocument){
27889             
27890             if(this.baseRotate == 6 || this.baseRotate == 8){
27891             
27892                 height = this.thumbEl.getHeight();
27893                 this.baseScale = height / this.imageEl.OriginWidth;
27894
27895                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27896                     width = this.thumbEl.getWidth();
27897                     this.baseScale = width / this.imageEl.OriginHeight;
27898                 }
27899
27900                 return;
27901             }
27902
27903             height = this.thumbEl.getHeight();
27904             this.baseScale = height / this.imageEl.OriginHeight;
27905
27906             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27907                 width = this.thumbEl.getWidth();
27908                 this.baseScale = width / this.imageEl.OriginWidth;
27909             }
27910
27911             return;
27912         }
27913         
27914         if(this.baseRotate == 6 || this.baseRotate == 8){
27915             
27916             width = this.thumbEl.getHeight();
27917             this.baseScale = width / this.imageEl.OriginHeight;
27918             
27919             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27920                 height = this.thumbEl.getWidth();
27921                 this.baseScale = height / this.imageEl.OriginHeight;
27922             }
27923             
27924             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27925                 height = this.thumbEl.getWidth();
27926                 this.baseScale = height / this.imageEl.OriginHeight;
27927                 
27928                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27929                     width = this.thumbEl.getHeight();
27930                     this.baseScale = width / this.imageEl.OriginWidth;
27931                 }
27932             }
27933             
27934             return;
27935         }
27936         
27937         width = this.thumbEl.getWidth();
27938         this.baseScale = width / this.imageEl.OriginWidth;
27939         
27940         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27941             height = this.thumbEl.getHeight();
27942             this.baseScale = height / this.imageEl.OriginHeight;
27943         }
27944         
27945         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27946             
27947             height = this.thumbEl.getHeight();
27948             this.baseScale = height / this.imageEl.OriginHeight;
27949             
27950             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27951                 width = this.thumbEl.getWidth();
27952                 this.baseScale = width / this.imageEl.OriginWidth;
27953             }
27954             
27955         }
27956         
27957         return;
27958     },
27959     
27960     getScaleLevel : function()
27961     {
27962         return this.baseScale * Math.pow(1.1, this.scale);
27963     },
27964     
27965     onTouchStart : function(e)
27966     {
27967         if(!this.canvasLoaded){
27968             this.beforeSelectFile(e);
27969             return;
27970         }
27971         
27972         var touches = e.browserEvent.touches;
27973         
27974         if(!touches){
27975             return;
27976         }
27977         
27978         if(touches.length == 1){
27979             this.onMouseDown(e);
27980             return;
27981         }
27982         
27983         if(touches.length != 2){
27984             return;
27985         }
27986         
27987         var coords = [];
27988         
27989         for(var i = 0, finger; finger = touches[i]; i++){
27990             coords.push(finger.pageX, finger.pageY);
27991         }
27992         
27993         var x = Math.pow(coords[0] - coords[2], 2);
27994         var y = Math.pow(coords[1] - coords[3], 2);
27995         
27996         this.startDistance = Math.sqrt(x + y);
27997         
27998         this.startScale = this.scale;
27999         
28000         this.pinching = true;
28001         this.dragable = false;
28002         
28003     },
28004     
28005     onTouchMove : function(e)
28006     {
28007         if(!this.pinching && !this.dragable){
28008             return;
28009         }
28010         
28011         var touches = e.browserEvent.touches;
28012         
28013         if(!touches){
28014             return;
28015         }
28016         
28017         if(this.dragable){
28018             this.onMouseMove(e);
28019             return;
28020         }
28021         
28022         var coords = [];
28023         
28024         for(var i = 0, finger; finger = touches[i]; i++){
28025             coords.push(finger.pageX, finger.pageY);
28026         }
28027         
28028         var x = Math.pow(coords[0] - coords[2], 2);
28029         var y = Math.pow(coords[1] - coords[3], 2);
28030         
28031         this.endDistance = Math.sqrt(x + y);
28032         
28033         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28034         
28035         if(!this.zoomable()){
28036             this.scale = this.startScale;
28037             return;
28038         }
28039         
28040         this.draw();
28041         
28042     },
28043     
28044     onTouchEnd : function(e)
28045     {
28046         this.pinching = false;
28047         this.dragable = false;
28048         
28049     },
28050     
28051     process : function(file, crop)
28052     {
28053         if(this.loadMask){
28054             this.maskEl.mask(this.loadingText);
28055         }
28056         
28057         this.xhr = new XMLHttpRequest();
28058         
28059         file.xhr = this.xhr;
28060
28061         this.xhr.open(this.method, this.url, true);
28062         
28063         var headers = {
28064             "Accept": "application/json",
28065             "Cache-Control": "no-cache",
28066             "X-Requested-With": "XMLHttpRequest"
28067         };
28068         
28069         for (var headerName in headers) {
28070             var headerValue = headers[headerName];
28071             if (headerValue) {
28072                 this.xhr.setRequestHeader(headerName, headerValue);
28073             }
28074         }
28075         
28076         var _this = this;
28077         
28078         this.xhr.onload = function()
28079         {
28080             _this.xhrOnLoad(_this.xhr);
28081         }
28082         
28083         this.xhr.onerror = function()
28084         {
28085             _this.xhrOnError(_this.xhr);
28086         }
28087         
28088         var formData = new FormData();
28089
28090         formData.append('returnHTML', 'NO');
28091         
28092         if(crop){
28093             formData.append('crop', crop);
28094         }
28095         
28096         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28097             formData.append(this.paramName, file, file.name);
28098         }
28099         
28100         if(typeof(file.filename) != 'undefined'){
28101             formData.append('filename', file.filename);
28102         }
28103         
28104         if(typeof(file.mimetype) != 'undefined'){
28105             formData.append('mimetype', file.mimetype);
28106         }
28107         
28108         if(this.fireEvent('arrange', this, formData) != false){
28109             this.xhr.send(formData);
28110         };
28111     },
28112     
28113     xhrOnLoad : function(xhr)
28114     {
28115         if(this.loadMask){
28116             this.maskEl.unmask();
28117         }
28118         
28119         if (xhr.readyState !== 4) {
28120             this.fireEvent('exception', this, xhr);
28121             return;
28122         }
28123
28124         var response = Roo.decode(xhr.responseText);
28125         
28126         if(!response.success){
28127             this.fireEvent('exception', this, xhr);
28128             return;
28129         }
28130         
28131         var response = Roo.decode(xhr.responseText);
28132         
28133         this.fireEvent('upload', this, response);
28134         
28135     },
28136     
28137     xhrOnError : function()
28138     {
28139         if(this.loadMask){
28140             this.maskEl.unmask();
28141         }
28142         
28143         Roo.log('xhr on error');
28144         
28145         var response = Roo.decode(xhr.responseText);
28146           
28147         Roo.log(response);
28148         
28149     },
28150     
28151     prepare : function(file)
28152     {   
28153         if(this.loadMask){
28154             this.maskEl.mask(this.loadingText);
28155         }
28156         
28157         this.file = false;
28158         this.exif = {};
28159         
28160         if(typeof(file) === 'string'){
28161             this.loadCanvas(file);
28162             return;
28163         }
28164         
28165         if(!file || !this.urlAPI){
28166             return;
28167         }
28168         
28169         this.file = file;
28170         this.cropType = file.type;
28171         
28172         var _this = this;
28173         
28174         if(this.fireEvent('prepare', this, this.file) != false){
28175             
28176             var reader = new FileReader();
28177             
28178             reader.onload = function (e) {
28179                 if (e.target.error) {
28180                     Roo.log(e.target.error);
28181                     return;
28182                 }
28183                 
28184                 var buffer = e.target.result,
28185                     dataView = new DataView(buffer),
28186                     offset = 2,
28187                     maxOffset = dataView.byteLength - 4,
28188                     markerBytes,
28189                     markerLength;
28190                 
28191                 if (dataView.getUint16(0) === 0xffd8) {
28192                     while (offset < maxOffset) {
28193                         markerBytes = dataView.getUint16(offset);
28194                         
28195                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28196                             markerLength = dataView.getUint16(offset + 2) + 2;
28197                             if (offset + markerLength > dataView.byteLength) {
28198                                 Roo.log('Invalid meta data: Invalid segment size.');
28199                                 break;
28200                             }
28201                             
28202                             if(markerBytes == 0xffe1){
28203                                 _this.parseExifData(
28204                                     dataView,
28205                                     offset,
28206                                     markerLength
28207                                 );
28208                             }
28209                             
28210                             offset += markerLength;
28211                             
28212                             continue;
28213                         }
28214                         
28215                         break;
28216                     }
28217                     
28218                 }
28219                 
28220                 var url = _this.urlAPI.createObjectURL(_this.file);
28221                 
28222                 _this.loadCanvas(url);
28223                 
28224                 return;
28225             }
28226             
28227             reader.readAsArrayBuffer(this.file);
28228             
28229         }
28230         
28231     },
28232     
28233     parseExifData : function(dataView, offset, length)
28234     {
28235         var tiffOffset = offset + 10,
28236             littleEndian,
28237             dirOffset;
28238     
28239         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28240             // No Exif data, might be XMP data instead
28241             return;
28242         }
28243         
28244         // Check for the ASCII code for "Exif" (0x45786966):
28245         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28246             // No Exif data, might be XMP data instead
28247             return;
28248         }
28249         if (tiffOffset + 8 > dataView.byteLength) {
28250             Roo.log('Invalid Exif data: Invalid segment size.');
28251             return;
28252         }
28253         // Check for the two null bytes:
28254         if (dataView.getUint16(offset + 8) !== 0x0000) {
28255             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28256             return;
28257         }
28258         // Check the byte alignment:
28259         switch (dataView.getUint16(tiffOffset)) {
28260         case 0x4949:
28261             littleEndian = true;
28262             break;
28263         case 0x4D4D:
28264             littleEndian = false;
28265             break;
28266         default:
28267             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28268             return;
28269         }
28270         // Check for the TIFF tag marker (0x002A):
28271         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28272             Roo.log('Invalid Exif data: Missing TIFF marker.');
28273             return;
28274         }
28275         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28276         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28277         
28278         this.parseExifTags(
28279             dataView,
28280             tiffOffset,
28281             tiffOffset + dirOffset,
28282             littleEndian
28283         );
28284     },
28285     
28286     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28287     {
28288         var tagsNumber,
28289             dirEndOffset,
28290             i;
28291         if (dirOffset + 6 > dataView.byteLength) {
28292             Roo.log('Invalid Exif data: Invalid directory offset.');
28293             return;
28294         }
28295         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28296         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28297         if (dirEndOffset + 4 > dataView.byteLength) {
28298             Roo.log('Invalid Exif data: Invalid directory size.');
28299             return;
28300         }
28301         for (i = 0; i < tagsNumber; i += 1) {
28302             this.parseExifTag(
28303                 dataView,
28304                 tiffOffset,
28305                 dirOffset + 2 + 12 * i, // tag offset
28306                 littleEndian
28307             );
28308         }
28309         // Return the offset to the next directory:
28310         return dataView.getUint32(dirEndOffset, littleEndian);
28311     },
28312     
28313     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28314     {
28315         var tag = dataView.getUint16(offset, littleEndian);
28316         
28317         this.exif[tag] = this.getExifValue(
28318             dataView,
28319             tiffOffset,
28320             offset,
28321             dataView.getUint16(offset + 2, littleEndian), // tag type
28322             dataView.getUint32(offset + 4, littleEndian), // tag length
28323             littleEndian
28324         );
28325     },
28326     
28327     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28328     {
28329         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28330             tagSize,
28331             dataOffset,
28332             values,
28333             i,
28334             str,
28335             c;
28336     
28337         if (!tagType) {
28338             Roo.log('Invalid Exif data: Invalid tag type.');
28339             return;
28340         }
28341         
28342         tagSize = tagType.size * length;
28343         // Determine if the value is contained in the dataOffset bytes,
28344         // or if the value at the dataOffset is a pointer to the actual data:
28345         dataOffset = tagSize > 4 ?
28346                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28347         if (dataOffset + tagSize > dataView.byteLength) {
28348             Roo.log('Invalid Exif data: Invalid data offset.');
28349             return;
28350         }
28351         if (length === 1) {
28352             return tagType.getValue(dataView, dataOffset, littleEndian);
28353         }
28354         values = [];
28355         for (i = 0; i < length; i += 1) {
28356             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28357         }
28358         
28359         if (tagType.ascii) {
28360             str = '';
28361             // Concatenate the chars:
28362             for (i = 0; i < values.length; i += 1) {
28363                 c = values[i];
28364                 // Ignore the terminating NULL byte(s):
28365                 if (c === '\u0000') {
28366                     break;
28367                 }
28368                 str += c;
28369             }
28370             return str;
28371         }
28372         return values;
28373     }
28374     
28375 });
28376
28377 Roo.apply(Roo.bootstrap.UploadCropbox, {
28378     tags : {
28379         'Orientation': 0x0112
28380     },
28381     
28382     Orientation: {
28383             1: 0, //'top-left',
28384 //            2: 'top-right',
28385             3: 180, //'bottom-right',
28386 //            4: 'bottom-left',
28387 //            5: 'left-top',
28388             6: 90, //'right-top',
28389 //            7: 'right-bottom',
28390             8: 270 //'left-bottom'
28391     },
28392     
28393     exifTagTypes : {
28394         // byte, 8-bit unsigned int:
28395         1: {
28396             getValue: function (dataView, dataOffset) {
28397                 return dataView.getUint8(dataOffset);
28398             },
28399             size: 1
28400         },
28401         // ascii, 8-bit byte:
28402         2: {
28403             getValue: function (dataView, dataOffset) {
28404                 return String.fromCharCode(dataView.getUint8(dataOffset));
28405             },
28406             size: 1,
28407             ascii: true
28408         },
28409         // short, 16 bit int:
28410         3: {
28411             getValue: function (dataView, dataOffset, littleEndian) {
28412                 return dataView.getUint16(dataOffset, littleEndian);
28413             },
28414             size: 2
28415         },
28416         // long, 32 bit int:
28417         4: {
28418             getValue: function (dataView, dataOffset, littleEndian) {
28419                 return dataView.getUint32(dataOffset, littleEndian);
28420             },
28421             size: 4
28422         },
28423         // rational = two long values, first is numerator, second is denominator:
28424         5: {
28425             getValue: function (dataView, dataOffset, littleEndian) {
28426                 return dataView.getUint32(dataOffset, littleEndian) /
28427                     dataView.getUint32(dataOffset + 4, littleEndian);
28428             },
28429             size: 8
28430         },
28431         // slong, 32 bit signed int:
28432         9: {
28433             getValue: function (dataView, dataOffset, littleEndian) {
28434                 return dataView.getInt32(dataOffset, littleEndian);
28435             },
28436             size: 4
28437         },
28438         // srational, two slongs, first is numerator, second is denominator:
28439         10: {
28440             getValue: function (dataView, dataOffset, littleEndian) {
28441                 return dataView.getInt32(dataOffset, littleEndian) /
28442                     dataView.getInt32(dataOffset + 4, littleEndian);
28443             },
28444             size: 8
28445         }
28446     },
28447     
28448     footer : {
28449         STANDARD : [
28450             {
28451                 tag : 'div',
28452                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28453                 action : 'rotate-left',
28454                 cn : [
28455                     {
28456                         tag : 'button',
28457                         cls : 'btn btn-default',
28458                         html : '<i class="fa fa-undo"></i>'
28459                     }
28460                 ]
28461             },
28462             {
28463                 tag : 'div',
28464                 cls : 'btn-group roo-upload-cropbox-picture',
28465                 action : 'picture',
28466                 cn : [
28467                     {
28468                         tag : 'button',
28469                         cls : 'btn btn-default',
28470                         html : '<i class="fa fa-picture-o"></i>'
28471                     }
28472                 ]
28473             },
28474             {
28475                 tag : 'div',
28476                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28477                 action : 'rotate-right',
28478                 cn : [
28479                     {
28480                         tag : 'button',
28481                         cls : 'btn btn-default',
28482                         html : '<i class="fa fa-repeat"></i>'
28483                     }
28484                 ]
28485             }
28486         ],
28487         DOCUMENT : [
28488             {
28489                 tag : 'div',
28490                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28491                 action : 'rotate-left',
28492                 cn : [
28493                     {
28494                         tag : 'button',
28495                         cls : 'btn btn-default',
28496                         html : '<i class="fa fa-undo"></i>'
28497                     }
28498                 ]
28499             },
28500             {
28501                 tag : 'div',
28502                 cls : 'btn-group roo-upload-cropbox-download',
28503                 action : 'download',
28504                 cn : [
28505                     {
28506                         tag : 'button',
28507                         cls : 'btn btn-default',
28508                         html : '<i class="fa fa-download"></i>'
28509                     }
28510                 ]
28511             },
28512             {
28513                 tag : 'div',
28514                 cls : 'btn-group roo-upload-cropbox-crop',
28515                 action : 'crop',
28516                 cn : [
28517                     {
28518                         tag : 'button',
28519                         cls : 'btn btn-default',
28520                         html : '<i class="fa fa-crop"></i>'
28521                     }
28522                 ]
28523             },
28524             {
28525                 tag : 'div',
28526                 cls : 'btn-group roo-upload-cropbox-trash',
28527                 action : 'trash',
28528                 cn : [
28529                     {
28530                         tag : 'button',
28531                         cls : 'btn btn-default',
28532                         html : '<i class="fa fa-trash"></i>'
28533                     }
28534                 ]
28535             },
28536             {
28537                 tag : 'div',
28538                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28539                 action : 'rotate-right',
28540                 cn : [
28541                     {
28542                         tag : 'button',
28543                         cls : 'btn btn-default',
28544                         html : '<i class="fa fa-repeat"></i>'
28545                     }
28546                 ]
28547             }
28548         ],
28549         ROTATOR : [
28550             {
28551                 tag : 'div',
28552                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28553                 action : 'rotate-left',
28554                 cn : [
28555                     {
28556                         tag : 'button',
28557                         cls : 'btn btn-default',
28558                         html : '<i class="fa fa-undo"></i>'
28559                     }
28560                 ]
28561             },
28562             {
28563                 tag : 'div',
28564                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28565                 action : 'rotate-right',
28566                 cn : [
28567                     {
28568                         tag : 'button',
28569                         cls : 'btn btn-default',
28570                         html : '<i class="fa fa-repeat"></i>'
28571                     }
28572                 ]
28573             }
28574         ]
28575     }
28576 });
28577
28578 /*
28579 * Licence: LGPL
28580 */
28581
28582 /**
28583  * @class Roo.bootstrap.DocumentManager
28584  * @extends Roo.bootstrap.Component
28585  * Bootstrap DocumentManager class
28586  * @cfg {String} paramName default 'imageUpload'
28587  * @cfg {String} toolTipName default 'filename'
28588  * @cfg {String} method default POST
28589  * @cfg {String} url action url
28590  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28591  * @cfg {Boolean} multiple multiple upload default true
28592  * @cfg {Number} thumbSize default 300
28593  * @cfg {String} fieldLabel
28594  * @cfg {Number} labelWidth default 4
28595  * @cfg {String} labelAlign (left|top) default left
28596  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28597 * @cfg {Number} labellg set the width of label (1-12)
28598  * @cfg {Number} labelmd set the width of label (1-12)
28599  * @cfg {Number} labelsm set the width of label (1-12)
28600  * @cfg {Number} labelxs set the width of label (1-12)
28601  * 
28602  * @constructor
28603  * Create a new DocumentManager
28604  * @param {Object} config The config object
28605  */
28606
28607 Roo.bootstrap.DocumentManager = function(config){
28608     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28609     
28610     this.files = [];
28611     this.delegates = [];
28612     
28613     this.addEvents({
28614         /**
28615          * @event initial
28616          * Fire when initial the DocumentManager
28617          * @param {Roo.bootstrap.DocumentManager} this
28618          */
28619         "initial" : true,
28620         /**
28621          * @event inspect
28622          * inspect selected file
28623          * @param {Roo.bootstrap.DocumentManager} this
28624          * @param {File} file
28625          */
28626         "inspect" : true,
28627         /**
28628          * @event exception
28629          * Fire when xhr load exception
28630          * @param {Roo.bootstrap.DocumentManager} this
28631          * @param {XMLHttpRequest} xhr
28632          */
28633         "exception" : true,
28634         /**
28635          * @event afterupload
28636          * Fire when xhr load exception
28637          * @param {Roo.bootstrap.DocumentManager} this
28638          * @param {XMLHttpRequest} xhr
28639          */
28640         "afterupload" : true,
28641         /**
28642          * @event prepare
28643          * prepare the form data
28644          * @param {Roo.bootstrap.DocumentManager} this
28645          * @param {Object} formData
28646          */
28647         "prepare" : true,
28648         /**
28649          * @event remove
28650          * Fire when remove the file
28651          * @param {Roo.bootstrap.DocumentManager} this
28652          * @param {Object} file
28653          */
28654         "remove" : true,
28655         /**
28656          * @event refresh
28657          * Fire after refresh the file
28658          * @param {Roo.bootstrap.DocumentManager} this
28659          */
28660         "refresh" : true,
28661         /**
28662          * @event click
28663          * Fire after click the image
28664          * @param {Roo.bootstrap.DocumentManager} this
28665          * @param {Object} file
28666          */
28667         "click" : true,
28668         /**
28669          * @event edit
28670          * Fire when upload a image and editable set to true
28671          * @param {Roo.bootstrap.DocumentManager} this
28672          * @param {Object} file
28673          */
28674         "edit" : true,
28675         /**
28676          * @event beforeselectfile
28677          * Fire before select file
28678          * @param {Roo.bootstrap.DocumentManager} this
28679          */
28680         "beforeselectfile" : true,
28681         /**
28682          * @event process
28683          * Fire before process file
28684          * @param {Roo.bootstrap.DocumentManager} this
28685          * @param {Object} file
28686          */
28687         "process" : true,
28688         /**
28689          * @event previewrendered
28690          * Fire when preview rendered
28691          * @param {Roo.bootstrap.DocumentManager} this
28692          * @param {Object} file
28693          */
28694         "previewrendered" : true
28695         
28696     });
28697 };
28698
28699 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28700     
28701     boxes : 0,
28702     inputName : '',
28703     thumbSize : 300,
28704     multiple : true,
28705     files : false,
28706     method : 'POST',
28707     url : '',
28708     paramName : 'imageUpload',
28709     toolTipName : 'filename',
28710     fieldLabel : '',
28711     labelWidth : 4,
28712     labelAlign : 'left',
28713     editable : true,
28714     delegates : false,
28715     xhr : false, 
28716     
28717     labellg : 0,
28718     labelmd : 0,
28719     labelsm : 0,
28720     labelxs : 0,
28721     
28722     getAutoCreate : function()
28723     {   
28724         var managerWidget = {
28725             tag : 'div',
28726             cls : 'roo-document-manager',
28727             cn : [
28728                 {
28729                     tag : 'input',
28730                     cls : 'roo-document-manager-selector',
28731                     type : 'file'
28732                 },
28733                 {
28734                     tag : 'div',
28735                     cls : 'roo-document-manager-uploader',
28736                     cn : [
28737                         {
28738                             tag : 'div',
28739                             cls : 'roo-document-manager-upload-btn',
28740                             html : '<i class="fa fa-plus"></i>'
28741                         }
28742                     ]
28743                     
28744                 }
28745             ]
28746         };
28747         
28748         var content = [
28749             {
28750                 tag : 'div',
28751                 cls : 'column col-md-12',
28752                 cn : managerWidget
28753             }
28754         ];
28755         
28756         if(this.fieldLabel.length){
28757             
28758             content = [
28759                 {
28760                     tag : 'div',
28761                     cls : 'column col-md-12',
28762                     html : this.fieldLabel
28763                 },
28764                 {
28765                     tag : 'div',
28766                     cls : 'column col-md-12',
28767                     cn : managerWidget
28768                 }
28769             ];
28770
28771             if(this.labelAlign == 'left'){
28772                 content = [
28773                     {
28774                         tag : 'div',
28775                         cls : 'column',
28776                         html : this.fieldLabel
28777                     },
28778                     {
28779                         tag : 'div',
28780                         cls : 'column',
28781                         cn : managerWidget
28782                     }
28783                 ];
28784                 
28785                 if(this.labelWidth > 12){
28786                     content[0].style = "width: " + this.labelWidth + 'px';
28787                 }
28788
28789                 if(this.labelWidth < 13 && this.labelmd == 0){
28790                     this.labelmd = this.labelWidth;
28791                 }
28792
28793                 if(this.labellg > 0){
28794                     content[0].cls += ' col-lg-' + this.labellg;
28795                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28796                 }
28797
28798                 if(this.labelmd > 0){
28799                     content[0].cls += ' col-md-' + this.labelmd;
28800                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28801                 }
28802
28803                 if(this.labelsm > 0){
28804                     content[0].cls += ' col-sm-' + this.labelsm;
28805                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28806                 }
28807
28808                 if(this.labelxs > 0){
28809                     content[0].cls += ' col-xs-' + this.labelxs;
28810                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28811                 }
28812                 
28813             }
28814         }
28815         
28816         var cfg = {
28817             tag : 'div',
28818             cls : 'row clearfix',
28819             cn : content
28820         };
28821         
28822         return cfg;
28823         
28824     },
28825     
28826     initEvents : function()
28827     {
28828         this.managerEl = this.el.select('.roo-document-manager', true).first();
28829         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28830         
28831         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28832         this.selectorEl.hide();
28833         
28834         if(this.multiple){
28835             this.selectorEl.attr('multiple', 'multiple');
28836         }
28837         
28838         this.selectorEl.on('change', this.onFileSelected, this);
28839         
28840         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28841         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28842         
28843         this.uploader.on('click', this.onUploaderClick, this);
28844         
28845         this.renderProgressDialog();
28846         
28847         var _this = this;
28848         
28849         window.addEventListener("resize", function() { _this.refresh(); } );
28850         
28851         this.fireEvent('initial', this);
28852     },
28853     
28854     renderProgressDialog : function()
28855     {
28856         var _this = this;
28857         
28858         this.progressDialog = new Roo.bootstrap.Modal({
28859             cls : 'roo-document-manager-progress-dialog',
28860             allow_close : false,
28861             title : '',
28862             buttons : [
28863                 {
28864                     name  :'cancel',
28865                     weight : 'danger',
28866                     html : 'Cancel'
28867                 }
28868             ], 
28869             listeners : { 
28870                 btnclick : function() {
28871                     _this.uploadCancel();
28872                     this.hide();
28873                 }
28874             }
28875         });
28876          
28877         this.progressDialog.render(Roo.get(document.body));
28878          
28879         this.progress = new Roo.bootstrap.Progress({
28880             cls : 'roo-document-manager-progress',
28881             active : true,
28882             striped : true
28883         });
28884         
28885         this.progress.render(this.progressDialog.getChildContainer());
28886         
28887         this.progressBar = new Roo.bootstrap.ProgressBar({
28888             cls : 'roo-document-manager-progress-bar',
28889             aria_valuenow : 0,
28890             aria_valuemin : 0,
28891             aria_valuemax : 12,
28892             panel : 'success'
28893         });
28894         
28895         this.progressBar.render(this.progress.getChildContainer());
28896     },
28897     
28898     onUploaderClick : function(e)
28899     {
28900         e.preventDefault();
28901      
28902         if(this.fireEvent('beforeselectfile', this) != false){
28903             this.selectorEl.dom.click();
28904         }
28905         
28906     },
28907     
28908     onFileSelected : function(e)
28909     {
28910         e.preventDefault();
28911         
28912         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28913             return;
28914         }
28915         
28916         Roo.each(this.selectorEl.dom.files, function(file){
28917             if(this.fireEvent('inspect', this, file) != false){
28918                 this.files.push(file);
28919             }
28920         }, this);
28921         
28922         this.queue();
28923         
28924     },
28925     
28926     queue : function()
28927     {
28928         this.selectorEl.dom.value = '';
28929         
28930         if(!this.files || !this.files.length){
28931             return;
28932         }
28933         
28934         if(this.boxes > 0 && this.files.length > this.boxes){
28935             this.files = this.files.slice(0, this.boxes);
28936         }
28937         
28938         this.uploader.show();
28939         
28940         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28941             this.uploader.hide();
28942         }
28943         
28944         var _this = this;
28945         
28946         var files = [];
28947         
28948         var docs = [];
28949         
28950         Roo.each(this.files, function(file){
28951             
28952             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28953                 var f = this.renderPreview(file);
28954                 files.push(f);
28955                 return;
28956             }
28957             
28958             if(file.type.indexOf('image') != -1){
28959                 this.delegates.push(
28960                     (function(){
28961                         _this.process(file);
28962                     }).createDelegate(this)
28963                 );
28964         
28965                 return;
28966             }
28967             
28968             docs.push(
28969                 (function(){
28970                     _this.process(file);
28971                 }).createDelegate(this)
28972             );
28973             
28974         }, this);
28975         
28976         this.files = files;
28977         
28978         this.delegates = this.delegates.concat(docs);
28979         
28980         if(!this.delegates.length){
28981             this.refresh();
28982             return;
28983         }
28984         
28985         this.progressBar.aria_valuemax = this.delegates.length;
28986         
28987         this.arrange();
28988         
28989         return;
28990     },
28991     
28992     arrange : function()
28993     {
28994         if(!this.delegates.length){
28995             this.progressDialog.hide();
28996             this.refresh();
28997             return;
28998         }
28999         
29000         var delegate = this.delegates.shift();
29001         
29002         this.progressDialog.show();
29003         
29004         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29005         
29006         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29007         
29008         delegate();
29009     },
29010     
29011     refresh : function()
29012     {
29013         this.uploader.show();
29014         
29015         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29016             this.uploader.hide();
29017         }
29018         
29019         Roo.isTouch ? this.closable(false) : this.closable(true);
29020         
29021         this.fireEvent('refresh', this);
29022     },
29023     
29024     onRemove : function(e, el, o)
29025     {
29026         e.preventDefault();
29027         
29028         this.fireEvent('remove', this, o);
29029         
29030     },
29031     
29032     remove : function(o)
29033     {
29034         var files = [];
29035         
29036         Roo.each(this.files, function(file){
29037             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29038                 files.push(file);
29039                 return;
29040             }
29041
29042             o.target.remove();
29043
29044         }, this);
29045         
29046         this.files = files;
29047         
29048         this.refresh();
29049     },
29050     
29051     clear : function()
29052     {
29053         Roo.each(this.files, function(file){
29054             if(!file.target){
29055                 return;
29056             }
29057             
29058             file.target.remove();
29059
29060         }, this);
29061         
29062         this.files = [];
29063         
29064         this.refresh();
29065     },
29066     
29067     onClick : function(e, el, o)
29068     {
29069         e.preventDefault();
29070         
29071         this.fireEvent('click', this, o);
29072         
29073     },
29074     
29075     closable : function(closable)
29076     {
29077         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29078             
29079             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29080             
29081             if(closable){
29082                 el.show();
29083                 return;
29084             }
29085             
29086             el.hide();
29087             
29088         }, this);
29089     },
29090     
29091     xhrOnLoad : function(xhr)
29092     {
29093         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29094             el.remove();
29095         }, this);
29096         
29097         if (xhr.readyState !== 4) {
29098             this.arrange();
29099             this.fireEvent('exception', this, xhr);
29100             return;
29101         }
29102
29103         var response = Roo.decode(xhr.responseText);
29104         
29105         if(!response.success){
29106             this.arrange();
29107             this.fireEvent('exception', this, xhr);
29108             return;
29109         }
29110         
29111         var file = this.renderPreview(response.data);
29112         
29113         this.files.push(file);
29114         
29115         this.arrange();
29116         
29117         this.fireEvent('afterupload', this, xhr);
29118         
29119     },
29120     
29121     xhrOnError : function(xhr)
29122     {
29123         Roo.log('xhr on error');
29124         
29125         var response = Roo.decode(xhr.responseText);
29126           
29127         Roo.log(response);
29128         
29129         this.arrange();
29130     },
29131     
29132     process : function(file)
29133     {
29134         if(this.fireEvent('process', this, file) !== false){
29135             if(this.editable && file.type.indexOf('image') != -1){
29136                 this.fireEvent('edit', this, file);
29137                 return;
29138             }
29139
29140             this.uploadStart(file, false);
29141
29142             return;
29143         }
29144         
29145     },
29146     
29147     uploadStart : function(file, crop)
29148     {
29149         this.xhr = new XMLHttpRequest();
29150         
29151         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29152             this.arrange();
29153             return;
29154         }
29155         
29156         file.xhr = this.xhr;
29157             
29158         this.managerEl.createChild({
29159             tag : 'div',
29160             cls : 'roo-document-manager-loading',
29161             cn : [
29162                 {
29163                     tag : 'div',
29164                     tooltip : file.name,
29165                     cls : 'roo-document-manager-thumb',
29166                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29167                 }
29168             ]
29169
29170         });
29171
29172         this.xhr.open(this.method, this.url, true);
29173         
29174         var headers = {
29175             "Accept": "application/json",
29176             "Cache-Control": "no-cache",
29177             "X-Requested-With": "XMLHttpRequest"
29178         };
29179         
29180         for (var headerName in headers) {
29181             var headerValue = headers[headerName];
29182             if (headerValue) {
29183                 this.xhr.setRequestHeader(headerName, headerValue);
29184             }
29185         }
29186         
29187         var _this = this;
29188         
29189         this.xhr.onload = function()
29190         {
29191             _this.xhrOnLoad(_this.xhr);
29192         }
29193         
29194         this.xhr.onerror = function()
29195         {
29196             _this.xhrOnError(_this.xhr);
29197         }
29198         
29199         var formData = new FormData();
29200
29201         formData.append('returnHTML', 'NO');
29202         
29203         if(crop){
29204             formData.append('crop', crop);
29205         }
29206         
29207         formData.append(this.paramName, file, file.name);
29208         
29209         var options = {
29210             file : file, 
29211             manually : false
29212         };
29213         
29214         if(this.fireEvent('prepare', this, formData, options) != false){
29215             
29216             if(options.manually){
29217                 return;
29218             }
29219             
29220             this.xhr.send(formData);
29221             return;
29222         };
29223         
29224         this.uploadCancel();
29225     },
29226     
29227     uploadCancel : function()
29228     {
29229         if (this.xhr) {
29230             this.xhr.abort();
29231         }
29232         
29233         this.delegates = [];
29234         
29235         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29236             el.remove();
29237         }, this);
29238         
29239         this.arrange();
29240     },
29241     
29242     renderPreview : function(file)
29243     {
29244         if(typeof(file.target) != 'undefined' && file.target){
29245             return file;
29246         }
29247         
29248         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29249         
29250         var previewEl = this.managerEl.createChild({
29251             tag : 'div',
29252             cls : 'roo-document-manager-preview',
29253             cn : [
29254                 {
29255                     tag : 'div',
29256                     tooltip : file[this.toolTipName],
29257                     cls : 'roo-document-manager-thumb',
29258                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29259                 },
29260                 {
29261                     tag : 'button',
29262                     cls : 'close',
29263                     html : '<i class="fa fa-times-circle"></i>'
29264                 }
29265             ]
29266         });
29267
29268         var close = previewEl.select('button.close', true).first();
29269
29270         close.on('click', this.onRemove, this, file);
29271
29272         file.target = previewEl;
29273
29274         var image = previewEl.select('img', true).first();
29275         
29276         var _this = this;
29277         
29278         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29279         
29280         image.on('click', this.onClick, this, file);
29281         
29282         this.fireEvent('previewrendered', this, file);
29283         
29284         return file;
29285         
29286     },
29287     
29288     onPreviewLoad : function(file, image)
29289     {
29290         if(typeof(file.target) == 'undefined' || !file.target){
29291             return;
29292         }
29293         
29294         var width = image.dom.naturalWidth || image.dom.width;
29295         var height = image.dom.naturalHeight || image.dom.height;
29296         
29297         if(width > height){
29298             file.target.addClass('wide');
29299             return;
29300         }
29301         
29302         file.target.addClass('tall');
29303         return;
29304         
29305     },
29306     
29307     uploadFromSource : function(file, crop)
29308     {
29309         this.xhr = new XMLHttpRequest();
29310         
29311         this.managerEl.createChild({
29312             tag : 'div',
29313             cls : 'roo-document-manager-loading',
29314             cn : [
29315                 {
29316                     tag : 'div',
29317                     tooltip : file.name,
29318                     cls : 'roo-document-manager-thumb',
29319                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29320                 }
29321             ]
29322
29323         });
29324
29325         this.xhr.open(this.method, this.url, true);
29326         
29327         var headers = {
29328             "Accept": "application/json",
29329             "Cache-Control": "no-cache",
29330             "X-Requested-With": "XMLHttpRequest"
29331         };
29332         
29333         for (var headerName in headers) {
29334             var headerValue = headers[headerName];
29335             if (headerValue) {
29336                 this.xhr.setRequestHeader(headerName, headerValue);
29337             }
29338         }
29339         
29340         var _this = this;
29341         
29342         this.xhr.onload = function()
29343         {
29344             _this.xhrOnLoad(_this.xhr);
29345         }
29346         
29347         this.xhr.onerror = function()
29348         {
29349             _this.xhrOnError(_this.xhr);
29350         }
29351         
29352         var formData = new FormData();
29353
29354         formData.append('returnHTML', 'NO');
29355         
29356         formData.append('crop', crop);
29357         
29358         if(typeof(file.filename) != 'undefined'){
29359             formData.append('filename', file.filename);
29360         }
29361         
29362         if(typeof(file.mimetype) != 'undefined'){
29363             formData.append('mimetype', file.mimetype);
29364         }
29365         
29366         Roo.log(formData);
29367         
29368         if(this.fireEvent('prepare', this, formData) != false){
29369             this.xhr.send(formData);
29370         };
29371     }
29372 });
29373
29374 /*
29375 * Licence: LGPL
29376 */
29377
29378 /**
29379  * @class Roo.bootstrap.DocumentViewer
29380  * @extends Roo.bootstrap.Component
29381  * Bootstrap DocumentViewer class
29382  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29383  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29384  * 
29385  * @constructor
29386  * Create a new DocumentViewer
29387  * @param {Object} config The config object
29388  */
29389
29390 Roo.bootstrap.DocumentViewer = function(config){
29391     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29392     
29393     this.addEvents({
29394         /**
29395          * @event initial
29396          * Fire after initEvent
29397          * @param {Roo.bootstrap.DocumentViewer} this
29398          */
29399         "initial" : true,
29400         /**
29401          * @event click
29402          * Fire after click
29403          * @param {Roo.bootstrap.DocumentViewer} this
29404          */
29405         "click" : true,
29406         /**
29407          * @event download
29408          * Fire after download button
29409          * @param {Roo.bootstrap.DocumentViewer} this
29410          */
29411         "download" : true,
29412         /**
29413          * @event trash
29414          * Fire after trash button
29415          * @param {Roo.bootstrap.DocumentViewer} this
29416          */
29417         "trash" : true
29418         
29419     });
29420 };
29421
29422 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29423     
29424     showDownload : true,
29425     
29426     showTrash : true,
29427     
29428     getAutoCreate : function()
29429     {
29430         var cfg = {
29431             tag : 'div',
29432             cls : 'roo-document-viewer',
29433             cn : [
29434                 {
29435                     tag : 'div',
29436                     cls : 'roo-document-viewer-body',
29437                     cn : [
29438                         {
29439                             tag : 'div',
29440                             cls : 'roo-document-viewer-thumb',
29441                             cn : [
29442                                 {
29443                                     tag : 'img',
29444                                     cls : 'roo-document-viewer-image'
29445                                 }
29446                             ]
29447                         }
29448                     ]
29449                 },
29450                 {
29451                     tag : 'div',
29452                     cls : 'roo-document-viewer-footer',
29453                     cn : {
29454                         tag : 'div',
29455                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29456                         cn : [
29457                             {
29458                                 tag : 'div',
29459                                 cls : 'btn-group roo-document-viewer-download',
29460                                 cn : [
29461                                     {
29462                                         tag : 'button',
29463                                         cls : 'btn btn-default',
29464                                         html : '<i class="fa fa-download"></i>'
29465                                     }
29466                                 ]
29467                             },
29468                             {
29469                                 tag : 'div',
29470                                 cls : 'btn-group roo-document-viewer-trash',
29471                                 cn : [
29472                                     {
29473                                         tag : 'button',
29474                                         cls : 'btn btn-default',
29475                                         html : '<i class="fa fa-trash"></i>'
29476                                     }
29477                                 ]
29478                             }
29479                         ]
29480                     }
29481                 }
29482             ]
29483         };
29484         
29485         return cfg;
29486     },
29487     
29488     initEvents : function()
29489     {
29490         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29491         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29492         
29493         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29494         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29495         
29496         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29497         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29498         
29499         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29500         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29501         
29502         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29503         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29504         
29505         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29506         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29507         
29508         this.bodyEl.on('click', this.onClick, this);
29509         this.downloadBtn.on('click', this.onDownload, this);
29510         this.trashBtn.on('click', this.onTrash, this);
29511         
29512         this.downloadBtn.hide();
29513         this.trashBtn.hide();
29514         
29515         if(this.showDownload){
29516             this.downloadBtn.show();
29517         }
29518         
29519         if(this.showTrash){
29520             this.trashBtn.show();
29521         }
29522         
29523         if(!this.showDownload && !this.showTrash) {
29524             this.footerEl.hide();
29525         }
29526         
29527     },
29528     
29529     initial : function()
29530     {
29531         this.fireEvent('initial', this);
29532         
29533     },
29534     
29535     onClick : function(e)
29536     {
29537         e.preventDefault();
29538         
29539         this.fireEvent('click', this);
29540     },
29541     
29542     onDownload : function(e)
29543     {
29544         e.preventDefault();
29545         
29546         this.fireEvent('download', this);
29547     },
29548     
29549     onTrash : function(e)
29550     {
29551         e.preventDefault();
29552         
29553         this.fireEvent('trash', this);
29554     }
29555     
29556 });
29557 /*
29558  * - LGPL
29559  *
29560  * nav progress bar
29561  * 
29562  */
29563
29564 /**
29565  * @class Roo.bootstrap.NavProgressBar
29566  * @extends Roo.bootstrap.Component
29567  * Bootstrap NavProgressBar class
29568  * 
29569  * @constructor
29570  * Create a new nav progress bar
29571  * @param {Object} config The config object
29572  */
29573
29574 Roo.bootstrap.NavProgressBar = function(config){
29575     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29576
29577     this.bullets = this.bullets || [];
29578    
29579 //    Roo.bootstrap.NavProgressBar.register(this);
29580      this.addEvents({
29581         /**
29582              * @event changed
29583              * Fires when the active item changes
29584              * @param {Roo.bootstrap.NavProgressBar} this
29585              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29586              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29587          */
29588         'changed': true
29589      });
29590     
29591 };
29592
29593 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29594     
29595     bullets : [],
29596     barItems : [],
29597     
29598     getAutoCreate : function()
29599     {
29600         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29601         
29602         cfg = {
29603             tag : 'div',
29604             cls : 'roo-navigation-bar-group',
29605             cn : [
29606                 {
29607                     tag : 'div',
29608                     cls : 'roo-navigation-top-bar'
29609                 },
29610                 {
29611                     tag : 'div',
29612                     cls : 'roo-navigation-bullets-bar',
29613                     cn : [
29614                         {
29615                             tag : 'ul',
29616                             cls : 'roo-navigation-bar'
29617                         }
29618                     ]
29619                 },
29620                 
29621                 {
29622                     tag : 'div',
29623                     cls : 'roo-navigation-bottom-bar'
29624                 }
29625             ]
29626             
29627         };
29628         
29629         return cfg;
29630         
29631     },
29632     
29633     initEvents: function() 
29634     {
29635         
29636     },
29637     
29638     onRender : function(ct, position) 
29639     {
29640         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29641         
29642         if(this.bullets.length){
29643             Roo.each(this.bullets, function(b){
29644                this.addItem(b);
29645             }, this);
29646         }
29647         
29648         this.format();
29649         
29650     },
29651     
29652     addItem : function(cfg)
29653     {
29654         var item = new Roo.bootstrap.NavProgressItem(cfg);
29655         
29656         item.parentId = this.id;
29657         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29658         
29659         if(cfg.html){
29660             var top = new Roo.bootstrap.Element({
29661                 tag : 'div',
29662                 cls : 'roo-navigation-bar-text'
29663             });
29664             
29665             var bottom = new Roo.bootstrap.Element({
29666                 tag : 'div',
29667                 cls : 'roo-navigation-bar-text'
29668             });
29669             
29670             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29671             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29672             
29673             var topText = new Roo.bootstrap.Element({
29674                 tag : 'span',
29675                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29676             });
29677             
29678             var bottomText = new Roo.bootstrap.Element({
29679                 tag : 'span',
29680                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29681             });
29682             
29683             topText.onRender(top.el, null);
29684             bottomText.onRender(bottom.el, null);
29685             
29686             item.topEl = top;
29687             item.bottomEl = bottom;
29688         }
29689         
29690         this.barItems.push(item);
29691         
29692         return item;
29693     },
29694     
29695     getActive : function()
29696     {
29697         var active = false;
29698         
29699         Roo.each(this.barItems, function(v){
29700             
29701             if (!v.isActive()) {
29702                 return;
29703             }
29704             
29705             active = v;
29706             return false;
29707             
29708         });
29709         
29710         return active;
29711     },
29712     
29713     setActiveItem : function(item)
29714     {
29715         var prev = false;
29716         
29717         Roo.each(this.barItems, function(v){
29718             if (v.rid == item.rid) {
29719                 return ;
29720             }
29721             
29722             if (v.isActive()) {
29723                 v.setActive(false);
29724                 prev = v;
29725             }
29726         });
29727
29728         item.setActive(true);
29729         
29730         this.fireEvent('changed', this, item, prev);
29731     },
29732     
29733     getBarItem: function(rid)
29734     {
29735         var ret = false;
29736         
29737         Roo.each(this.barItems, function(e) {
29738             if (e.rid != rid) {
29739                 return;
29740             }
29741             
29742             ret =  e;
29743             return false;
29744         });
29745         
29746         return ret;
29747     },
29748     
29749     indexOfItem : function(item)
29750     {
29751         var index = false;
29752         
29753         Roo.each(this.barItems, function(v, i){
29754             
29755             if (v.rid != item.rid) {
29756                 return;
29757             }
29758             
29759             index = i;
29760             return false
29761         });
29762         
29763         return index;
29764     },
29765     
29766     setActiveNext : function()
29767     {
29768         var i = this.indexOfItem(this.getActive());
29769         
29770         if (i > this.barItems.length) {
29771             return;
29772         }
29773         
29774         this.setActiveItem(this.barItems[i+1]);
29775     },
29776     
29777     setActivePrev : function()
29778     {
29779         var i = this.indexOfItem(this.getActive());
29780         
29781         if (i  < 1) {
29782             return;
29783         }
29784         
29785         this.setActiveItem(this.barItems[i-1]);
29786     },
29787     
29788     format : function()
29789     {
29790         if(!this.barItems.length){
29791             return;
29792         }
29793      
29794         var width = 100 / this.barItems.length;
29795         
29796         Roo.each(this.barItems, function(i){
29797             i.el.setStyle('width', width + '%');
29798             i.topEl.el.setStyle('width', width + '%');
29799             i.bottomEl.el.setStyle('width', width + '%');
29800         }, this);
29801         
29802     }
29803     
29804 });
29805 /*
29806  * - LGPL
29807  *
29808  * Nav Progress Item
29809  * 
29810  */
29811
29812 /**
29813  * @class Roo.bootstrap.NavProgressItem
29814  * @extends Roo.bootstrap.Component
29815  * Bootstrap NavProgressItem class
29816  * @cfg {String} rid the reference id
29817  * @cfg {Boolean} active (true|false) Is item active default false
29818  * @cfg {Boolean} disabled (true|false) Is item active default false
29819  * @cfg {String} html
29820  * @cfg {String} position (top|bottom) text position default bottom
29821  * @cfg {String} icon show icon instead of number
29822  * 
29823  * @constructor
29824  * Create a new NavProgressItem
29825  * @param {Object} config The config object
29826  */
29827 Roo.bootstrap.NavProgressItem = function(config){
29828     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29829     this.addEvents({
29830         // raw events
29831         /**
29832          * @event click
29833          * The raw click event for the entire grid.
29834          * @param {Roo.bootstrap.NavProgressItem} this
29835          * @param {Roo.EventObject} e
29836          */
29837         "click" : true
29838     });
29839    
29840 };
29841
29842 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29843     
29844     rid : '',
29845     active : false,
29846     disabled : false,
29847     html : '',
29848     position : 'bottom',
29849     icon : false,
29850     
29851     getAutoCreate : function()
29852     {
29853         var iconCls = 'roo-navigation-bar-item-icon';
29854         
29855         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29856         
29857         var cfg = {
29858             tag: 'li',
29859             cls: 'roo-navigation-bar-item',
29860             cn : [
29861                 {
29862                     tag : 'i',
29863                     cls : iconCls
29864                 }
29865             ]
29866         };
29867         
29868         if(this.active){
29869             cfg.cls += ' active';
29870         }
29871         if(this.disabled){
29872             cfg.cls += ' disabled';
29873         }
29874         
29875         return cfg;
29876     },
29877     
29878     disable : function()
29879     {
29880         this.setDisabled(true);
29881     },
29882     
29883     enable : function()
29884     {
29885         this.setDisabled(false);
29886     },
29887     
29888     initEvents: function() 
29889     {
29890         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29891         
29892         this.iconEl.on('click', this.onClick, this);
29893     },
29894     
29895     onClick : function(e)
29896     {
29897         e.preventDefault();
29898         
29899         if(this.disabled){
29900             return;
29901         }
29902         
29903         if(this.fireEvent('click', this, e) === false){
29904             return;
29905         };
29906         
29907         this.parent().setActiveItem(this);
29908     },
29909     
29910     isActive: function () 
29911     {
29912         return this.active;
29913     },
29914     
29915     setActive : function(state)
29916     {
29917         if(this.active == state){
29918             return;
29919         }
29920         
29921         this.active = state;
29922         
29923         if (state) {
29924             this.el.addClass('active');
29925             return;
29926         }
29927         
29928         this.el.removeClass('active');
29929         
29930         return;
29931     },
29932     
29933     setDisabled : function(state)
29934     {
29935         if(this.disabled == state){
29936             return;
29937         }
29938         
29939         this.disabled = state;
29940         
29941         if (state) {
29942             this.el.addClass('disabled');
29943             return;
29944         }
29945         
29946         this.el.removeClass('disabled');
29947     },
29948     
29949     tooltipEl : function()
29950     {
29951         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29952     }
29953 });
29954  
29955
29956  /*
29957  * - LGPL
29958  *
29959  * FieldLabel
29960  * 
29961  */
29962
29963 /**
29964  * @class Roo.bootstrap.FieldLabel
29965  * @extends Roo.bootstrap.Component
29966  * Bootstrap FieldLabel class
29967  * @cfg {String} html contents of the element
29968  * @cfg {String} tag tag of the element default label
29969  * @cfg {String} cls class of the element
29970  * @cfg {String} target label target 
29971  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29972  * @cfg {String} invalidClass default "text-warning"
29973  * @cfg {String} validClass default "text-success"
29974  * @cfg {String} iconTooltip default "This field is required"
29975  * @cfg {String} indicatorpos (left|right) default left
29976  * 
29977  * @constructor
29978  * Create a new FieldLabel
29979  * @param {Object} config The config object
29980  */
29981
29982 Roo.bootstrap.FieldLabel = function(config){
29983     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29984     
29985     this.addEvents({
29986             /**
29987              * @event invalid
29988              * Fires after the field has been marked as invalid.
29989              * @param {Roo.form.FieldLabel} this
29990              * @param {String} msg The validation message
29991              */
29992             invalid : true,
29993             /**
29994              * @event valid
29995              * Fires after the field has been validated with no errors.
29996              * @param {Roo.form.FieldLabel} this
29997              */
29998             valid : true
29999         });
30000 };
30001
30002 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30003     
30004     tag: 'label',
30005     cls: '',
30006     html: '',
30007     target: '',
30008     allowBlank : true,
30009     invalidClass : 'has-warning',
30010     validClass : 'has-success',
30011     iconTooltip : 'This field is required',
30012     indicatorpos : 'left',
30013     
30014     getAutoCreate : function(){
30015         
30016         var cfg = {
30017             tag : this.tag,
30018             cls : 'roo-bootstrap-field-label ' + this.cls,
30019             for : this.target,
30020             cn : [
30021                 {
30022                     tag : 'i',
30023                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
30024                     tooltip : this.iconTooltip
30025                 },
30026                 {
30027                     tag : 'span',
30028                     html : this.html
30029                 }
30030             ] 
30031         };
30032         
30033         if(this.indicatorpos == 'right'){
30034             var cfg = {
30035                 tag : this.tag,
30036                 cls : 'roo-bootstrap-field-label ' + this.cls,
30037                 for : this.target,
30038                 cn : [
30039                     {
30040                         tag : 'span',
30041                         html : this.html
30042                     },
30043                     {
30044                         tag : 'i',
30045                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30046                         tooltip : this.iconTooltip
30047                     }
30048                 ] 
30049             };
30050         }
30051         
30052         return cfg;
30053     },
30054     
30055     initEvents: function() 
30056     {
30057         Roo.bootstrap.Element.superclass.initEvents.call(this);
30058         
30059         this.indicator = this.indicatorEl();
30060         
30061         if(this.indicator){
30062             this.indicator.removeClass('visible');
30063             this.indicator.addClass('invisible');
30064         }
30065         
30066         Roo.bootstrap.FieldLabel.register(this);
30067     },
30068     
30069     indicatorEl : function()
30070     {
30071         var indicator = this.el.select('i.roo-required-indicator',true).first();
30072         
30073         if(!indicator){
30074             return false;
30075         }
30076         
30077         return indicator;
30078         
30079     },
30080     
30081     /**
30082      * Mark this field as valid
30083      */
30084     markValid : function()
30085     {
30086         if(this.indicator){
30087             this.indicator.removeClass('visible');
30088             this.indicator.addClass('invisible');
30089         }
30090         
30091         this.el.removeClass(this.invalidClass);
30092         
30093         this.el.addClass(this.validClass);
30094         
30095         this.fireEvent('valid', this);
30096     },
30097     
30098     /**
30099      * Mark this field as invalid
30100      * @param {String} msg The validation message
30101      */
30102     markInvalid : function(msg)
30103     {
30104         if(this.indicator){
30105             this.indicator.removeClass('invisible');
30106             this.indicator.addClass('visible');
30107         }
30108         
30109         this.el.removeClass(this.validClass);
30110         
30111         this.el.addClass(this.invalidClass);
30112         
30113         this.fireEvent('invalid', this, msg);
30114     }
30115     
30116    
30117 });
30118
30119 Roo.apply(Roo.bootstrap.FieldLabel, {
30120     
30121     groups: {},
30122     
30123      /**
30124     * register a FieldLabel Group
30125     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30126     */
30127     register : function(label)
30128     {
30129         if(this.groups.hasOwnProperty(label.target)){
30130             return;
30131         }
30132      
30133         this.groups[label.target] = label;
30134         
30135     },
30136     /**
30137     * fetch a FieldLabel Group based on the target
30138     * @param {string} target
30139     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30140     */
30141     get: function(target) {
30142         if (typeof(this.groups[target]) == 'undefined') {
30143             return false;
30144         }
30145         
30146         return this.groups[target] ;
30147     }
30148 });
30149
30150  
30151
30152  /*
30153  * - LGPL
30154  *
30155  * page DateSplitField.
30156  * 
30157  */
30158
30159
30160 /**
30161  * @class Roo.bootstrap.DateSplitField
30162  * @extends Roo.bootstrap.Component
30163  * Bootstrap DateSplitField class
30164  * @cfg {string} fieldLabel - the label associated
30165  * @cfg {Number} labelWidth set the width of label (0-12)
30166  * @cfg {String} labelAlign (top|left)
30167  * @cfg {Boolean} dayAllowBlank (true|false) default false
30168  * @cfg {Boolean} monthAllowBlank (true|false) default false
30169  * @cfg {Boolean} yearAllowBlank (true|false) default false
30170  * @cfg {string} dayPlaceholder 
30171  * @cfg {string} monthPlaceholder
30172  * @cfg {string} yearPlaceholder
30173  * @cfg {string} dayFormat default 'd'
30174  * @cfg {string} monthFormat default 'm'
30175  * @cfg {string} yearFormat default 'Y'
30176  * @cfg {Number} labellg set the width of label (1-12)
30177  * @cfg {Number} labelmd set the width of label (1-12)
30178  * @cfg {Number} labelsm set the width of label (1-12)
30179  * @cfg {Number} labelxs set the width of label (1-12)
30180
30181  *     
30182  * @constructor
30183  * Create a new DateSplitField
30184  * @param {Object} config The config object
30185  */
30186
30187 Roo.bootstrap.DateSplitField = function(config){
30188     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30189     
30190     this.addEvents({
30191         // raw events
30192          /**
30193          * @event years
30194          * getting the data of years
30195          * @param {Roo.bootstrap.DateSplitField} this
30196          * @param {Object} years
30197          */
30198         "years" : true,
30199         /**
30200          * @event days
30201          * getting the data of days
30202          * @param {Roo.bootstrap.DateSplitField} this
30203          * @param {Object} days
30204          */
30205         "days" : true,
30206         /**
30207          * @event invalid
30208          * Fires after the field has been marked as invalid.
30209          * @param {Roo.form.Field} this
30210          * @param {String} msg The validation message
30211          */
30212         invalid : true,
30213        /**
30214          * @event valid
30215          * Fires after the field has been validated with no errors.
30216          * @param {Roo.form.Field} this
30217          */
30218         valid : true
30219     });
30220 };
30221
30222 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30223     
30224     fieldLabel : '',
30225     labelAlign : 'top',
30226     labelWidth : 3,
30227     dayAllowBlank : false,
30228     monthAllowBlank : false,
30229     yearAllowBlank : false,
30230     dayPlaceholder : '',
30231     monthPlaceholder : '',
30232     yearPlaceholder : '',
30233     dayFormat : 'd',
30234     monthFormat : 'm',
30235     yearFormat : 'Y',
30236     isFormField : true,
30237     labellg : 0,
30238     labelmd : 0,
30239     labelsm : 0,
30240     labelxs : 0,
30241     
30242     getAutoCreate : function()
30243     {
30244         var cfg = {
30245             tag : 'div',
30246             cls : 'row roo-date-split-field-group',
30247             cn : [
30248                 {
30249                     tag : 'input',
30250                     type : 'hidden',
30251                     cls : 'form-hidden-field roo-date-split-field-group-value',
30252                     name : this.name
30253                 }
30254             ]
30255         };
30256         
30257         var labelCls = 'col-md-12';
30258         var contentCls = 'col-md-4';
30259         
30260         if(this.fieldLabel){
30261             
30262             var label = {
30263                 tag : 'div',
30264                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30265                 cn : [
30266                     {
30267                         tag : 'label',
30268                         html : this.fieldLabel
30269                     }
30270                 ]
30271             };
30272             
30273             if(this.labelAlign == 'left'){
30274             
30275                 if(this.labelWidth > 12){
30276                     label.style = "width: " + this.labelWidth + 'px';
30277                 }
30278
30279                 if(this.labelWidth < 13 && this.labelmd == 0){
30280                     this.labelmd = this.labelWidth;
30281                 }
30282
30283                 if(this.labellg > 0){
30284                     labelCls = ' col-lg-' + this.labellg;
30285                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30286                 }
30287
30288                 if(this.labelmd > 0){
30289                     labelCls = ' col-md-' + this.labelmd;
30290                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30291                 }
30292
30293                 if(this.labelsm > 0){
30294                     labelCls = ' col-sm-' + this.labelsm;
30295                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30296                 }
30297
30298                 if(this.labelxs > 0){
30299                     labelCls = ' col-xs-' + this.labelxs;
30300                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30301                 }
30302             }
30303             
30304             label.cls += ' ' + labelCls;
30305             
30306             cfg.cn.push(label);
30307         }
30308         
30309         Roo.each(['day', 'month', 'year'], function(t){
30310             cfg.cn.push({
30311                 tag : 'div',
30312                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30313             });
30314         }, this);
30315         
30316         return cfg;
30317     },
30318     
30319     inputEl: function ()
30320     {
30321         return this.el.select('.roo-date-split-field-group-value', true).first();
30322     },
30323     
30324     onRender : function(ct, position) 
30325     {
30326         var _this = this;
30327         
30328         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30329         
30330         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30331         
30332         this.dayField = new Roo.bootstrap.ComboBox({
30333             allowBlank : this.dayAllowBlank,
30334             alwaysQuery : true,
30335             displayField : 'value',
30336             editable : false,
30337             fieldLabel : '',
30338             forceSelection : true,
30339             mode : 'local',
30340             placeholder : this.dayPlaceholder,
30341             selectOnFocus : true,
30342             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30343             triggerAction : 'all',
30344             typeAhead : true,
30345             valueField : 'value',
30346             store : new Roo.data.SimpleStore({
30347                 data : (function() {    
30348                     var days = [];
30349                     _this.fireEvent('days', _this, days);
30350                     return days;
30351                 })(),
30352                 fields : [ 'value' ]
30353             }),
30354             listeners : {
30355                 select : function (_self, record, index)
30356                 {
30357                     _this.setValue(_this.getValue());
30358                 }
30359             }
30360         });
30361
30362         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30363         
30364         this.monthField = new Roo.bootstrap.MonthField({
30365             after : '<i class=\"fa fa-calendar\"></i>',
30366             allowBlank : this.monthAllowBlank,
30367             placeholder : this.monthPlaceholder,
30368             readOnly : true,
30369             listeners : {
30370                 render : function (_self)
30371                 {
30372                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30373                         e.preventDefault();
30374                         _self.focus();
30375                     });
30376                 },
30377                 select : function (_self, oldvalue, newvalue)
30378                 {
30379                     _this.setValue(_this.getValue());
30380                 }
30381             }
30382         });
30383         
30384         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30385         
30386         this.yearField = new Roo.bootstrap.ComboBox({
30387             allowBlank : this.yearAllowBlank,
30388             alwaysQuery : true,
30389             displayField : 'value',
30390             editable : false,
30391             fieldLabel : '',
30392             forceSelection : true,
30393             mode : 'local',
30394             placeholder : this.yearPlaceholder,
30395             selectOnFocus : true,
30396             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30397             triggerAction : 'all',
30398             typeAhead : true,
30399             valueField : 'value',
30400             store : new Roo.data.SimpleStore({
30401                 data : (function() {
30402                     var years = [];
30403                     _this.fireEvent('years', _this, years);
30404                     return years;
30405                 })(),
30406                 fields : [ 'value' ]
30407             }),
30408             listeners : {
30409                 select : function (_self, record, index)
30410                 {
30411                     _this.setValue(_this.getValue());
30412                 }
30413             }
30414         });
30415
30416         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30417     },
30418     
30419     setValue : function(v, format)
30420     {
30421         this.inputEl.dom.value = v;
30422         
30423         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30424         
30425         var d = Date.parseDate(v, f);
30426         
30427         if(!d){
30428             this.validate();
30429             return;
30430         }
30431         
30432         this.setDay(d.format(this.dayFormat));
30433         this.setMonth(d.format(this.monthFormat));
30434         this.setYear(d.format(this.yearFormat));
30435         
30436         this.validate();
30437         
30438         return;
30439     },
30440     
30441     setDay : function(v)
30442     {
30443         this.dayField.setValue(v);
30444         this.inputEl.dom.value = this.getValue();
30445         this.validate();
30446         return;
30447     },
30448     
30449     setMonth : function(v)
30450     {
30451         this.monthField.setValue(v, true);
30452         this.inputEl.dom.value = this.getValue();
30453         this.validate();
30454         return;
30455     },
30456     
30457     setYear : function(v)
30458     {
30459         this.yearField.setValue(v);
30460         this.inputEl.dom.value = this.getValue();
30461         this.validate();
30462         return;
30463     },
30464     
30465     getDay : function()
30466     {
30467         return this.dayField.getValue();
30468     },
30469     
30470     getMonth : function()
30471     {
30472         return this.monthField.getValue();
30473     },
30474     
30475     getYear : function()
30476     {
30477         return this.yearField.getValue();
30478     },
30479     
30480     getValue : function()
30481     {
30482         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30483         
30484         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30485         
30486         return date;
30487     },
30488     
30489     reset : function()
30490     {
30491         this.setDay('');
30492         this.setMonth('');
30493         this.setYear('');
30494         this.inputEl.dom.value = '';
30495         this.validate();
30496         return;
30497     },
30498     
30499     validate : function()
30500     {
30501         var d = this.dayField.validate();
30502         var m = this.monthField.validate();
30503         var y = this.yearField.validate();
30504         
30505         var valid = true;
30506         
30507         if(
30508                 (!this.dayAllowBlank && !d) ||
30509                 (!this.monthAllowBlank && !m) ||
30510                 (!this.yearAllowBlank && !y)
30511         ){
30512             valid = false;
30513         }
30514         
30515         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30516             return valid;
30517         }
30518         
30519         if(valid){
30520             this.markValid();
30521             return valid;
30522         }
30523         
30524         this.markInvalid();
30525         
30526         return valid;
30527     },
30528     
30529     markValid : function()
30530     {
30531         
30532         var label = this.el.select('label', true).first();
30533         var icon = this.el.select('i.fa-star', true).first();
30534
30535         if(label && icon){
30536             icon.remove();
30537         }
30538         
30539         this.fireEvent('valid', this);
30540     },
30541     
30542      /**
30543      * Mark this field as invalid
30544      * @param {String} msg The validation message
30545      */
30546     markInvalid : function(msg)
30547     {
30548         
30549         var label = this.el.select('label', true).first();
30550         var icon = this.el.select('i.fa-star', true).first();
30551
30552         if(label && !icon){
30553             this.el.select('.roo-date-split-field-label', true).createChild({
30554                 tag : 'i',
30555                 cls : 'text-danger fa fa-lg fa-star',
30556                 tooltip : 'This field is required',
30557                 style : 'margin-right:5px;'
30558             }, label, true);
30559         }
30560         
30561         this.fireEvent('invalid', this, msg);
30562     },
30563     
30564     clearInvalid : function()
30565     {
30566         var label = this.el.select('label', true).first();
30567         var icon = this.el.select('i.fa-star', true).first();
30568
30569         if(label && icon){
30570             icon.remove();
30571         }
30572         
30573         this.fireEvent('valid', this);
30574     },
30575     
30576     getName: function()
30577     {
30578         return this.name;
30579     }
30580     
30581 });
30582
30583  /**
30584  *
30585  * This is based on 
30586  * http://masonry.desandro.com
30587  *
30588  * The idea is to render all the bricks based on vertical width...
30589  *
30590  * The original code extends 'outlayer' - we might need to use that....
30591  * 
30592  */
30593
30594
30595 /**
30596  * @class Roo.bootstrap.LayoutMasonry
30597  * @extends Roo.bootstrap.Component
30598  * Bootstrap Layout Masonry class
30599  * 
30600  * @constructor
30601  * Create a new Element
30602  * @param {Object} config The config object
30603  */
30604
30605 Roo.bootstrap.LayoutMasonry = function(config){
30606     
30607     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30608     
30609     this.bricks = [];
30610     
30611     Roo.bootstrap.LayoutMasonry.register(this);
30612     
30613     this.addEvents({
30614         // raw events
30615         /**
30616          * @event layout
30617          * Fire after layout the items
30618          * @param {Roo.bootstrap.LayoutMasonry} this
30619          * @param {Roo.EventObject} e
30620          */
30621         "layout" : true
30622     });
30623     
30624 };
30625
30626 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30627     
30628     /**
30629      * @cfg {Boolean} isLayoutInstant = no animation?
30630      */   
30631     isLayoutInstant : false, // needed?
30632    
30633     /**
30634      * @cfg {Number} boxWidth  width of the columns
30635      */   
30636     boxWidth : 450,
30637     
30638       /**
30639      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30640      */   
30641     boxHeight : 0,
30642     
30643     /**
30644      * @cfg {Number} padWidth padding below box..
30645      */   
30646     padWidth : 10, 
30647     
30648     /**
30649      * @cfg {Number} gutter gutter width..
30650      */   
30651     gutter : 10,
30652     
30653      /**
30654      * @cfg {Number} maxCols maximum number of columns
30655      */   
30656     
30657     maxCols: 0,
30658     
30659     /**
30660      * @cfg {Boolean} isAutoInitial defalut true
30661      */   
30662     isAutoInitial : true, 
30663     
30664     containerWidth: 0,
30665     
30666     /**
30667      * @cfg {Boolean} isHorizontal defalut false
30668      */   
30669     isHorizontal : false, 
30670
30671     currentSize : null,
30672     
30673     tag: 'div',
30674     
30675     cls: '',
30676     
30677     bricks: null, //CompositeElement
30678     
30679     cols : 1,
30680     
30681     _isLayoutInited : false,
30682     
30683 //    isAlternative : false, // only use for vertical layout...
30684     
30685     /**
30686      * @cfg {Number} alternativePadWidth padding below box..
30687      */   
30688     alternativePadWidth : 50,
30689     
30690     selectedBrick : [],
30691     
30692     getAutoCreate : function(){
30693         
30694         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30695         
30696         var cfg = {
30697             tag: this.tag,
30698             cls: 'blog-masonary-wrapper ' + this.cls,
30699             cn : {
30700                 cls : 'mas-boxes masonary'
30701             }
30702         };
30703         
30704         return cfg;
30705     },
30706     
30707     getChildContainer: function( )
30708     {
30709         if (this.boxesEl) {
30710             return this.boxesEl;
30711         }
30712         
30713         this.boxesEl = this.el.select('.mas-boxes').first();
30714         
30715         return this.boxesEl;
30716     },
30717     
30718     
30719     initEvents : function()
30720     {
30721         var _this = this;
30722         
30723         if(this.isAutoInitial){
30724             Roo.log('hook children rendered');
30725             this.on('childrenrendered', function() {
30726                 Roo.log('children rendered');
30727                 _this.initial();
30728             } ,this);
30729         }
30730     },
30731     
30732     initial : function()
30733     {
30734         this.selectedBrick = [];
30735         
30736         this.currentSize = this.el.getBox(true);
30737         
30738         Roo.EventManager.onWindowResize(this.resize, this); 
30739
30740         if(!this.isAutoInitial){
30741             this.layout();
30742             return;
30743         }
30744         
30745         this.layout();
30746         
30747         return;
30748         //this.layout.defer(500,this);
30749         
30750     },
30751     
30752     resize : function()
30753     {
30754         var cs = this.el.getBox(true);
30755         
30756         if (
30757                 this.currentSize.width == cs.width && 
30758                 this.currentSize.x == cs.x && 
30759                 this.currentSize.height == cs.height && 
30760                 this.currentSize.y == cs.y 
30761         ) {
30762             Roo.log("no change in with or X or Y");
30763             return;
30764         }
30765         
30766         this.currentSize = cs;
30767         
30768         this.layout();
30769         
30770     },
30771     
30772     layout : function()
30773     {   
30774         this._resetLayout();
30775         
30776         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30777         
30778         this.layoutItems( isInstant );
30779       
30780         this._isLayoutInited = true;
30781         
30782         this.fireEvent('layout', this);
30783         
30784     },
30785     
30786     _resetLayout : function()
30787     {
30788         if(this.isHorizontal){
30789             this.horizontalMeasureColumns();
30790             return;
30791         }
30792         
30793         this.verticalMeasureColumns();
30794         
30795     },
30796     
30797     verticalMeasureColumns : function()
30798     {
30799         this.getContainerWidth();
30800         
30801 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30802 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30803 //            return;
30804 //        }
30805         
30806         var boxWidth = this.boxWidth + this.padWidth;
30807         
30808         if(this.containerWidth < this.boxWidth){
30809             boxWidth = this.containerWidth
30810         }
30811         
30812         var containerWidth = this.containerWidth;
30813         
30814         var cols = Math.floor(containerWidth / boxWidth);
30815         
30816         this.cols = Math.max( cols, 1 );
30817         
30818         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30819         
30820         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30821         
30822         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30823         
30824         this.colWidth = boxWidth + avail - this.padWidth;
30825         
30826         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30827         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30828     },
30829     
30830     horizontalMeasureColumns : function()
30831     {
30832         this.getContainerWidth();
30833         
30834         var boxWidth = this.boxWidth;
30835         
30836         if(this.containerWidth < boxWidth){
30837             boxWidth = this.containerWidth;
30838         }
30839         
30840         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30841         
30842         this.el.setHeight(boxWidth);
30843         
30844     },
30845     
30846     getContainerWidth : function()
30847     {
30848         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30849     },
30850     
30851     layoutItems : function( isInstant )
30852     {
30853         Roo.log(this.bricks);
30854         
30855         var items = Roo.apply([], this.bricks);
30856         
30857         if(this.isHorizontal){
30858             this._horizontalLayoutItems( items , isInstant );
30859             return;
30860         }
30861         
30862 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30863 //            this._verticalAlternativeLayoutItems( items , isInstant );
30864 //            return;
30865 //        }
30866         
30867         this._verticalLayoutItems( items , isInstant );
30868         
30869     },
30870     
30871     _verticalLayoutItems : function ( items , isInstant)
30872     {
30873         if ( !items || !items.length ) {
30874             return;
30875         }
30876         
30877         var standard = [
30878             ['xs', 'xs', 'xs', 'tall'],
30879             ['xs', 'xs', 'tall'],
30880             ['xs', 'xs', 'sm'],
30881             ['xs', 'xs', 'xs'],
30882             ['xs', 'tall'],
30883             ['xs', 'sm'],
30884             ['xs', 'xs'],
30885             ['xs'],
30886             
30887             ['sm', 'xs', 'xs'],
30888             ['sm', 'xs'],
30889             ['sm'],
30890             
30891             ['tall', 'xs', 'xs', 'xs'],
30892             ['tall', 'xs', 'xs'],
30893             ['tall', 'xs'],
30894             ['tall']
30895             
30896         ];
30897         
30898         var queue = [];
30899         
30900         var boxes = [];
30901         
30902         var box = [];
30903         
30904         Roo.each(items, function(item, k){
30905             
30906             switch (item.size) {
30907                 // these layouts take up a full box,
30908                 case 'md' :
30909                 case 'md-left' :
30910                 case 'md-right' :
30911                 case 'wide' :
30912                     
30913                     if(box.length){
30914                         boxes.push(box);
30915                         box = [];
30916                     }
30917                     
30918                     boxes.push([item]);
30919                     
30920                     break;
30921                     
30922                 case 'xs' :
30923                 case 'sm' :
30924                 case 'tall' :
30925                     
30926                     box.push(item);
30927                     
30928                     break;
30929                 default :
30930                     break;
30931                     
30932             }
30933             
30934         }, this);
30935         
30936         if(box.length){
30937             boxes.push(box);
30938             box = [];
30939         }
30940         
30941         var filterPattern = function(box, length)
30942         {
30943             if(!box.length){
30944                 return;
30945             }
30946             
30947             var match = false;
30948             
30949             var pattern = box.slice(0, length);
30950             
30951             var format = [];
30952             
30953             Roo.each(pattern, function(i){
30954                 format.push(i.size);
30955             }, this);
30956             
30957             Roo.each(standard, function(s){
30958                 
30959                 if(String(s) != String(format)){
30960                     return;
30961                 }
30962                 
30963                 match = true;
30964                 return false;
30965                 
30966             }, this);
30967             
30968             if(!match && length == 1){
30969                 return;
30970             }
30971             
30972             if(!match){
30973                 filterPattern(box, length - 1);
30974                 return;
30975             }
30976                 
30977             queue.push(pattern);
30978
30979             box = box.slice(length, box.length);
30980
30981             filterPattern(box, 4);
30982
30983             return;
30984             
30985         }
30986         
30987         Roo.each(boxes, function(box, k){
30988             
30989             if(!box.length){
30990                 return;
30991             }
30992             
30993             if(box.length == 1){
30994                 queue.push(box);
30995                 return;
30996             }
30997             
30998             filterPattern(box, 4);
30999             
31000         }, this);
31001         
31002         this._processVerticalLayoutQueue( queue, isInstant );
31003         
31004     },
31005     
31006 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31007 //    {
31008 //        if ( !items || !items.length ) {
31009 //            return;
31010 //        }
31011 //
31012 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31013 //        
31014 //    },
31015     
31016     _horizontalLayoutItems : function ( items , isInstant)
31017     {
31018         if ( !items || !items.length || items.length < 3) {
31019             return;
31020         }
31021         
31022         items.reverse();
31023         
31024         var eItems = items.slice(0, 3);
31025         
31026         items = items.slice(3, items.length);
31027         
31028         var standard = [
31029             ['xs', 'xs', 'xs', 'wide'],
31030             ['xs', 'xs', 'wide'],
31031             ['xs', 'xs', 'sm'],
31032             ['xs', 'xs', 'xs'],
31033             ['xs', 'wide'],
31034             ['xs', 'sm'],
31035             ['xs', 'xs'],
31036             ['xs'],
31037             
31038             ['sm', 'xs', 'xs'],
31039             ['sm', 'xs'],
31040             ['sm'],
31041             
31042             ['wide', 'xs', 'xs', 'xs'],
31043             ['wide', 'xs', 'xs'],
31044             ['wide', 'xs'],
31045             ['wide'],
31046             
31047             ['wide-thin']
31048         ];
31049         
31050         var queue = [];
31051         
31052         var boxes = [];
31053         
31054         var box = [];
31055         
31056         Roo.each(items, function(item, k){
31057             
31058             switch (item.size) {
31059                 case 'md' :
31060                 case 'md-left' :
31061                 case 'md-right' :
31062                 case 'tall' :
31063                     
31064                     if(box.length){
31065                         boxes.push(box);
31066                         box = [];
31067                     }
31068                     
31069                     boxes.push([item]);
31070                     
31071                     break;
31072                     
31073                 case 'xs' :
31074                 case 'sm' :
31075                 case 'wide' :
31076                 case 'wide-thin' :
31077                     
31078                     box.push(item);
31079                     
31080                     break;
31081                 default :
31082                     break;
31083                     
31084             }
31085             
31086         }, this);
31087         
31088         if(box.length){
31089             boxes.push(box);
31090             box = [];
31091         }
31092         
31093         var filterPattern = function(box, length)
31094         {
31095             if(!box.length){
31096                 return;
31097             }
31098             
31099             var match = false;
31100             
31101             var pattern = box.slice(0, length);
31102             
31103             var format = [];
31104             
31105             Roo.each(pattern, function(i){
31106                 format.push(i.size);
31107             }, this);
31108             
31109             Roo.each(standard, function(s){
31110                 
31111                 if(String(s) != String(format)){
31112                     return;
31113                 }
31114                 
31115                 match = true;
31116                 return false;
31117                 
31118             }, this);
31119             
31120             if(!match && length == 1){
31121                 return;
31122             }
31123             
31124             if(!match){
31125                 filterPattern(box, length - 1);
31126                 return;
31127             }
31128                 
31129             queue.push(pattern);
31130
31131             box = box.slice(length, box.length);
31132
31133             filterPattern(box, 4);
31134
31135             return;
31136             
31137         }
31138         
31139         Roo.each(boxes, function(box, k){
31140             
31141             if(!box.length){
31142                 return;
31143             }
31144             
31145             if(box.length == 1){
31146                 queue.push(box);
31147                 return;
31148             }
31149             
31150             filterPattern(box, 4);
31151             
31152         }, this);
31153         
31154         
31155         var prune = [];
31156         
31157         var pos = this.el.getBox(true);
31158         
31159         var minX = pos.x;
31160         
31161         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31162         
31163         var hit_end = false;
31164         
31165         Roo.each(queue, function(box){
31166             
31167             if(hit_end){
31168                 
31169                 Roo.each(box, function(b){
31170                 
31171                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31172                     b.el.hide();
31173
31174                 }, this);
31175
31176                 return;
31177             }
31178             
31179             var mx = 0;
31180             
31181             Roo.each(box, function(b){
31182                 
31183                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31184                 b.el.show();
31185
31186                 mx = Math.max(mx, b.x);
31187                 
31188             }, this);
31189             
31190             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31191             
31192             if(maxX < minX){
31193                 
31194                 Roo.each(box, function(b){
31195                 
31196                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31197                     b.el.hide();
31198                     
31199                 }, this);
31200                 
31201                 hit_end = true;
31202                 
31203                 return;
31204             }
31205             
31206             prune.push(box);
31207             
31208         }, this);
31209         
31210         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31211     },
31212     
31213     /** Sets position of item in DOM
31214     * @param {Element} item
31215     * @param {Number} x - horizontal position
31216     * @param {Number} y - vertical position
31217     * @param {Boolean} isInstant - disables transitions
31218     */
31219     _processVerticalLayoutQueue : function( queue, isInstant )
31220     {
31221         var pos = this.el.getBox(true);
31222         var x = pos.x;
31223         var y = pos.y;
31224         var maxY = [];
31225         
31226         for (var i = 0; i < this.cols; i++){
31227             maxY[i] = pos.y;
31228         }
31229         
31230         Roo.each(queue, function(box, k){
31231             
31232             var col = k % this.cols;
31233             
31234             Roo.each(box, function(b,kk){
31235                 
31236                 b.el.position('absolute');
31237                 
31238                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31239                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31240                 
31241                 if(b.size == 'md-left' || b.size == 'md-right'){
31242                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31243                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31244                 }
31245                 
31246                 b.el.setWidth(width);
31247                 b.el.setHeight(height);
31248                 // iframe?
31249                 b.el.select('iframe',true).setSize(width,height);
31250                 
31251             }, this);
31252             
31253             for (var i = 0; i < this.cols; i++){
31254                 
31255                 if(maxY[i] < maxY[col]){
31256                     col = i;
31257                     continue;
31258                 }
31259                 
31260                 col = Math.min(col, i);
31261                 
31262             }
31263             
31264             x = pos.x + col * (this.colWidth + this.padWidth);
31265             
31266             y = maxY[col];
31267             
31268             var positions = [];
31269             
31270             switch (box.length){
31271                 case 1 :
31272                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31273                     break;
31274                 case 2 :
31275                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31276                     break;
31277                 case 3 :
31278                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31279                     break;
31280                 case 4 :
31281                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31282                     break;
31283                 default :
31284                     break;
31285             }
31286             
31287             Roo.each(box, function(b,kk){
31288                 
31289                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31290                 
31291                 var sz = b.el.getSize();
31292                 
31293                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31294                 
31295             }, this);
31296             
31297         }, this);
31298         
31299         var mY = 0;
31300         
31301         for (var i = 0; i < this.cols; i++){
31302             mY = Math.max(mY, maxY[i]);
31303         }
31304         
31305         this.el.setHeight(mY - pos.y);
31306         
31307     },
31308     
31309 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31310 //    {
31311 //        var pos = this.el.getBox(true);
31312 //        var x = pos.x;
31313 //        var y = pos.y;
31314 //        var maxX = pos.right;
31315 //        
31316 //        var maxHeight = 0;
31317 //        
31318 //        Roo.each(items, function(item, k){
31319 //            
31320 //            var c = k % 2;
31321 //            
31322 //            item.el.position('absolute');
31323 //                
31324 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31325 //
31326 //            item.el.setWidth(width);
31327 //
31328 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31329 //
31330 //            item.el.setHeight(height);
31331 //            
31332 //            if(c == 0){
31333 //                item.el.setXY([x, y], isInstant ? false : true);
31334 //            } else {
31335 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31336 //            }
31337 //            
31338 //            y = y + height + this.alternativePadWidth;
31339 //            
31340 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31341 //            
31342 //        }, this);
31343 //        
31344 //        this.el.setHeight(maxHeight);
31345 //        
31346 //    },
31347     
31348     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31349     {
31350         var pos = this.el.getBox(true);
31351         
31352         var minX = pos.x;
31353         var minY = pos.y;
31354         
31355         var maxX = pos.right;
31356         
31357         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31358         
31359         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31360         
31361         Roo.each(queue, function(box, k){
31362             
31363             Roo.each(box, function(b, kk){
31364                 
31365                 b.el.position('absolute');
31366                 
31367                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31368                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31369                 
31370                 if(b.size == 'md-left' || b.size == 'md-right'){
31371                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31372                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31373                 }
31374                 
31375                 b.el.setWidth(width);
31376                 b.el.setHeight(height);
31377                 
31378             }, this);
31379             
31380             if(!box.length){
31381                 return;
31382             }
31383             
31384             var positions = [];
31385             
31386             switch (box.length){
31387                 case 1 :
31388                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31389                     break;
31390                 case 2 :
31391                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31392                     break;
31393                 case 3 :
31394                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31395                     break;
31396                 case 4 :
31397                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31398                     break;
31399                 default :
31400                     break;
31401             }
31402             
31403             Roo.each(box, function(b,kk){
31404                 
31405                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31406                 
31407                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31408                 
31409             }, this);
31410             
31411         }, this);
31412         
31413     },
31414     
31415     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31416     {
31417         Roo.each(eItems, function(b,k){
31418             
31419             b.size = (k == 0) ? 'sm' : 'xs';
31420             b.x = (k == 0) ? 2 : 1;
31421             b.y = (k == 0) ? 2 : 1;
31422             
31423             b.el.position('absolute');
31424             
31425             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31426                 
31427             b.el.setWidth(width);
31428             
31429             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31430             
31431             b.el.setHeight(height);
31432             
31433         }, this);
31434
31435         var positions = [];
31436         
31437         positions.push({
31438             x : maxX - this.unitWidth * 2 - this.gutter,
31439             y : minY
31440         });
31441         
31442         positions.push({
31443             x : maxX - this.unitWidth,
31444             y : minY + (this.unitWidth + this.gutter) * 2
31445         });
31446         
31447         positions.push({
31448             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31449             y : minY
31450         });
31451         
31452         Roo.each(eItems, function(b,k){
31453             
31454             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31455
31456         }, this);
31457         
31458     },
31459     
31460     getVerticalOneBoxColPositions : function(x, y, box)
31461     {
31462         var pos = [];
31463         
31464         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31465         
31466         if(box[0].size == 'md-left'){
31467             rand = 0;
31468         }
31469         
31470         if(box[0].size == 'md-right'){
31471             rand = 1;
31472         }
31473         
31474         pos.push({
31475             x : x + (this.unitWidth + this.gutter) * rand,
31476             y : y
31477         });
31478         
31479         return pos;
31480     },
31481     
31482     getVerticalTwoBoxColPositions : function(x, y, box)
31483     {
31484         var pos = [];
31485         
31486         if(box[0].size == 'xs'){
31487             
31488             pos.push({
31489                 x : x,
31490                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31491             });
31492
31493             pos.push({
31494                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31495                 y : y
31496             });
31497             
31498             return pos;
31499             
31500         }
31501         
31502         pos.push({
31503             x : x,
31504             y : y
31505         });
31506
31507         pos.push({
31508             x : x + (this.unitWidth + this.gutter) * 2,
31509             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31510         });
31511         
31512         return pos;
31513         
31514     },
31515     
31516     getVerticalThreeBoxColPositions : function(x, y, box)
31517     {
31518         var pos = [];
31519         
31520         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31521             
31522             pos.push({
31523                 x : x,
31524                 y : y
31525             });
31526
31527             pos.push({
31528                 x : x + (this.unitWidth + this.gutter) * 1,
31529                 y : y
31530             });
31531             
31532             pos.push({
31533                 x : x + (this.unitWidth + this.gutter) * 2,
31534                 y : y
31535             });
31536             
31537             return pos;
31538             
31539         }
31540         
31541         if(box[0].size == 'xs' && box[1].size == 'xs'){
31542             
31543             pos.push({
31544                 x : x,
31545                 y : y
31546             });
31547
31548             pos.push({
31549                 x : x,
31550                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31551             });
31552             
31553             pos.push({
31554                 x : x + (this.unitWidth + this.gutter) * 1,
31555                 y : y
31556             });
31557             
31558             return pos;
31559             
31560         }
31561         
31562         pos.push({
31563             x : x,
31564             y : y
31565         });
31566
31567         pos.push({
31568             x : x + (this.unitWidth + this.gutter) * 2,
31569             y : y
31570         });
31571
31572         pos.push({
31573             x : x + (this.unitWidth + this.gutter) * 2,
31574             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31575         });
31576             
31577         return pos;
31578         
31579     },
31580     
31581     getVerticalFourBoxColPositions : function(x, y, box)
31582     {
31583         var pos = [];
31584         
31585         if(box[0].size == 'xs'){
31586             
31587             pos.push({
31588                 x : x,
31589                 y : y
31590             });
31591
31592             pos.push({
31593                 x : x,
31594                 y : y + (this.unitHeight + this.gutter) * 1
31595             });
31596             
31597             pos.push({
31598                 x : x,
31599                 y : y + (this.unitHeight + this.gutter) * 2
31600             });
31601             
31602             pos.push({
31603                 x : x + (this.unitWidth + this.gutter) * 1,
31604                 y : y
31605             });
31606             
31607             return pos;
31608             
31609         }
31610         
31611         pos.push({
31612             x : x,
31613             y : y
31614         });
31615
31616         pos.push({
31617             x : x + (this.unitWidth + this.gutter) * 2,
31618             y : y
31619         });
31620
31621         pos.push({
31622             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31623             y : y + (this.unitHeight + this.gutter) * 1
31624         });
31625
31626         pos.push({
31627             x : x + (this.unitWidth + this.gutter) * 2,
31628             y : y + (this.unitWidth + this.gutter) * 2
31629         });
31630
31631         return pos;
31632         
31633     },
31634     
31635     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31636     {
31637         var pos = [];
31638         
31639         if(box[0].size == 'md-left'){
31640             pos.push({
31641                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31642                 y : minY
31643             });
31644             
31645             return pos;
31646         }
31647         
31648         if(box[0].size == 'md-right'){
31649             pos.push({
31650                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31651                 y : minY + (this.unitWidth + this.gutter) * 1
31652             });
31653             
31654             return pos;
31655         }
31656         
31657         var rand = Math.floor(Math.random() * (4 - box[0].y));
31658         
31659         pos.push({
31660             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31661             y : minY + (this.unitWidth + this.gutter) * rand
31662         });
31663         
31664         return pos;
31665         
31666     },
31667     
31668     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31669     {
31670         var pos = [];
31671         
31672         if(box[0].size == 'xs'){
31673             
31674             pos.push({
31675                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31676                 y : minY
31677             });
31678
31679             pos.push({
31680                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31681                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31682             });
31683             
31684             return pos;
31685             
31686         }
31687         
31688         pos.push({
31689             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31690             y : minY
31691         });
31692
31693         pos.push({
31694             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31695             y : minY + (this.unitWidth + this.gutter) * 2
31696         });
31697         
31698         return pos;
31699         
31700     },
31701     
31702     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31703     {
31704         var pos = [];
31705         
31706         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31707             
31708             pos.push({
31709                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31710                 y : minY
31711             });
31712
31713             pos.push({
31714                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31715                 y : minY + (this.unitWidth + this.gutter) * 1
31716             });
31717             
31718             pos.push({
31719                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31720                 y : minY + (this.unitWidth + this.gutter) * 2
31721             });
31722             
31723             return pos;
31724             
31725         }
31726         
31727         if(box[0].size == 'xs' && box[1].size == 'xs'){
31728             
31729             pos.push({
31730                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31731                 y : minY
31732             });
31733
31734             pos.push({
31735                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31736                 y : minY
31737             });
31738             
31739             pos.push({
31740                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31741                 y : minY + (this.unitWidth + this.gutter) * 1
31742             });
31743             
31744             return pos;
31745             
31746         }
31747         
31748         pos.push({
31749             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31750             y : minY
31751         });
31752
31753         pos.push({
31754             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31755             y : minY + (this.unitWidth + this.gutter) * 2
31756         });
31757
31758         pos.push({
31759             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31760             y : minY + (this.unitWidth + this.gutter) * 2
31761         });
31762             
31763         return pos;
31764         
31765     },
31766     
31767     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31768     {
31769         var pos = [];
31770         
31771         if(box[0].size == 'xs'){
31772             
31773             pos.push({
31774                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31775                 y : minY
31776             });
31777
31778             pos.push({
31779                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31780                 y : minY
31781             });
31782             
31783             pos.push({
31784                 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),
31785                 y : minY
31786             });
31787             
31788             pos.push({
31789                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31790                 y : minY + (this.unitWidth + this.gutter) * 1
31791             });
31792             
31793             return pos;
31794             
31795         }
31796         
31797         pos.push({
31798             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31799             y : minY
31800         });
31801         
31802         pos.push({
31803             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31804             y : minY + (this.unitWidth + this.gutter) * 2
31805         });
31806         
31807         pos.push({
31808             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31809             y : minY + (this.unitWidth + this.gutter) * 2
31810         });
31811         
31812         pos.push({
31813             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),
31814             y : minY + (this.unitWidth + this.gutter) * 2
31815         });
31816
31817         return pos;
31818         
31819     },
31820     
31821     /**
31822     * remove a Masonry Brick
31823     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31824     */
31825     removeBrick : function(brick_id)
31826     {
31827         if (!brick_id) {
31828             return;
31829         }
31830         
31831         for (var i = 0; i<this.bricks.length; i++) {
31832             if (this.bricks[i].id == brick_id) {
31833                 this.bricks.splice(i,1);
31834                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31835                 this.initial();
31836             }
31837         }
31838     },
31839     
31840     /**
31841     * adds a Masonry Brick
31842     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31843     */
31844     addBrick : function(cfg)
31845     {
31846         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31847         //this.register(cn);
31848         cn.parentId = this.id;
31849         cn.onRender(this.el, null);
31850         return cn;
31851     },
31852     
31853     /**
31854     * register a Masonry Brick
31855     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31856     */
31857     
31858     register : function(brick)
31859     {
31860         this.bricks.push(brick);
31861         brick.masonryId = this.id;
31862     },
31863     
31864     /**
31865     * clear all the Masonry Brick
31866     */
31867     clearAll : function()
31868     {
31869         this.bricks = [];
31870         //this.getChildContainer().dom.innerHTML = "";
31871         this.el.dom.innerHTML = '';
31872     },
31873     
31874     getSelected : function()
31875     {
31876         if (!this.selectedBrick) {
31877             return false;
31878         }
31879         
31880         return this.selectedBrick;
31881     }
31882 });
31883
31884 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31885     
31886     groups: {},
31887      /**
31888     * register a Masonry Layout
31889     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31890     */
31891     
31892     register : function(layout)
31893     {
31894         this.groups[layout.id] = layout;
31895     },
31896     /**
31897     * fetch a  Masonry Layout based on the masonry layout ID
31898     * @param {string} the masonry layout to add
31899     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31900     */
31901     
31902     get: function(layout_id) {
31903         if (typeof(this.groups[layout_id]) == 'undefined') {
31904             return false;
31905         }
31906         return this.groups[layout_id] ;
31907     }
31908     
31909     
31910     
31911 });
31912
31913  
31914
31915  /**
31916  *
31917  * This is based on 
31918  * http://masonry.desandro.com
31919  *
31920  * The idea is to render all the bricks based on vertical width...
31921  *
31922  * The original code extends 'outlayer' - we might need to use that....
31923  * 
31924  */
31925
31926
31927 /**
31928  * @class Roo.bootstrap.LayoutMasonryAuto
31929  * @extends Roo.bootstrap.Component
31930  * Bootstrap Layout Masonry class
31931  * 
31932  * @constructor
31933  * Create a new Element
31934  * @param {Object} config The config object
31935  */
31936
31937 Roo.bootstrap.LayoutMasonryAuto = function(config){
31938     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31939 };
31940
31941 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31942     
31943       /**
31944      * @cfg {Boolean} isFitWidth  - resize the width..
31945      */   
31946     isFitWidth : false,  // options..
31947     /**
31948      * @cfg {Boolean} isOriginLeft = left align?
31949      */   
31950     isOriginLeft : true,
31951     /**
31952      * @cfg {Boolean} isOriginTop = top align?
31953      */   
31954     isOriginTop : false,
31955     /**
31956      * @cfg {Boolean} isLayoutInstant = no animation?
31957      */   
31958     isLayoutInstant : false, // needed?
31959     /**
31960      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31961      */   
31962     isResizingContainer : true,
31963     /**
31964      * @cfg {Number} columnWidth  width of the columns 
31965      */   
31966     
31967     columnWidth : 0,
31968     
31969     /**
31970      * @cfg {Number} maxCols maximum number of columns
31971      */   
31972     
31973     maxCols: 0,
31974     /**
31975      * @cfg {Number} padHeight padding below box..
31976      */   
31977     
31978     padHeight : 10, 
31979     
31980     /**
31981      * @cfg {Boolean} isAutoInitial defalut true
31982      */   
31983     
31984     isAutoInitial : true, 
31985     
31986     // private?
31987     gutter : 0,
31988     
31989     containerWidth: 0,
31990     initialColumnWidth : 0,
31991     currentSize : null,
31992     
31993     colYs : null, // array.
31994     maxY : 0,
31995     padWidth: 10,
31996     
31997     
31998     tag: 'div',
31999     cls: '',
32000     bricks: null, //CompositeElement
32001     cols : 0, // array?
32002     // element : null, // wrapped now this.el
32003     _isLayoutInited : null, 
32004     
32005     
32006     getAutoCreate : function(){
32007         
32008         var cfg = {
32009             tag: this.tag,
32010             cls: 'blog-masonary-wrapper ' + this.cls,
32011             cn : {
32012                 cls : 'mas-boxes masonary'
32013             }
32014         };
32015         
32016         return cfg;
32017     },
32018     
32019     getChildContainer: function( )
32020     {
32021         if (this.boxesEl) {
32022             return this.boxesEl;
32023         }
32024         
32025         this.boxesEl = this.el.select('.mas-boxes').first();
32026         
32027         return this.boxesEl;
32028     },
32029     
32030     
32031     initEvents : function()
32032     {
32033         var _this = this;
32034         
32035         if(this.isAutoInitial){
32036             Roo.log('hook children rendered');
32037             this.on('childrenrendered', function() {
32038                 Roo.log('children rendered');
32039                 _this.initial();
32040             } ,this);
32041         }
32042         
32043     },
32044     
32045     initial : function()
32046     {
32047         this.reloadItems();
32048
32049         this.currentSize = this.el.getBox(true);
32050
32051         /// was window resize... - let's see if this works..
32052         Roo.EventManager.onWindowResize(this.resize, this); 
32053
32054         if(!this.isAutoInitial){
32055             this.layout();
32056             return;
32057         }
32058         
32059         this.layout.defer(500,this);
32060     },
32061     
32062     reloadItems: function()
32063     {
32064         this.bricks = this.el.select('.masonry-brick', true);
32065         
32066         this.bricks.each(function(b) {
32067             //Roo.log(b.getSize());
32068             if (!b.attr('originalwidth')) {
32069                 b.attr('originalwidth',  b.getSize().width);
32070             }
32071             
32072         });
32073         
32074         Roo.log(this.bricks.elements.length);
32075     },
32076     
32077     resize : function()
32078     {
32079         Roo.log('resize');
32080         var cs = this.el.getBox(true);
32081         
32082         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32083             Roo.log("no change in with or X");
32084             return;
32085         }
32086         this.currentSize = cs;
32087         this.layout();
32088     },
32089     
32090     layout : function()
32091     {
32092          Roo.log('layout');
32093         this._resetLayout();
32094         //this._manageStamps();
32095       
32096         // don't animate first layout
32097         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32098         this.layoutItems( isInstant );
32099       
32100         // flag for initalized
32101         this._isLayoutInited = true;
32102     },
32103     
32104     layoutItems : function( isInstant )
32105     {
32106         //var items = this._getItemsForLayout( this.items );
32107         // original code supports filtering layout items.. we just ignore it..
32108         
32109         this._layoutItems( this.bricks , isInstant );
32110       
32111         this._postLayout();
32112     },
32113     _layoutItems : function ( items , isInstant)
32114     {
32115        //this.fireEvent( 'layout', this, items );
32116     
32117
32118         if ( !items || !items.elements.length ) {
32119           // no items, emit event with empty array
32120             return;
32121         }
32122
32123         var queue = [];
32124         items.each(function(item) {
32125             Roo.log("layout item");
32126             Roo.log(item);
32127             // get x/y object from method
32128             var position = this._getItemLayoutPosition( item );
32129             // enqueue
32130             position.item = item;
32131             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32132             queue.push( position );
32133         }, this);
32134       
32135         this._processLayoutQueue( queue );
32136     },
32137     /** Sets position of item in DOM
32138     * @param {Element} item
32139     * @param {Number} x - horizontal position
32140     * @param {Number} y - vertical position
32141     * @param {Boolean} isInstant - disables transitions
32142     */
32143     _processLayoutQueue : function( queue )
32144     {
32145         for ( var i=0, len = queue.length; i < len; i++ ) {
32146             var obj = queue[i];
32147             obj.item.position('absolute');
32148             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32149         }
32150     },
32151       
32152     
32153     /**
32154     * Any logic you want to do after each layout,
32155     * i.e. size the container
32156     */
32157     _postLayout : function()
32158     {
32159         this.resizeContainer();
32160     },
32161     
32162     resizeContainer : function()
32163     {
32164         if ( !this.isResizingContainer ) {
32165             return;
32166         }
32167         var size = this._getContainerSize();
32168         if ( size ) {
32169             this.el.setSize(size.width,size.height);
32170             this.boxesEl.setSize(size.width,size.height);
32171         }
32172     },
32173     
32174     
32175     
32176     _resetLayout : function()
32177     {
32178         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32179         this.colWidth = this.el.getWidth();
32180         //this.gutter = this.el.getWidth(); 
32181         
32182         this.measureColumns();
32183
32184         // reset column Y
32185         var i = this.cols;
32186         this.colYs = [];
32187         while (i--) {
32188             this.colYs.push( 0 );
32189         }
32190     
32191         this.maxY = 0;
32192     },
32193
32194     measureColumns : function()
32195     {
32196         this.getContainerWidth();
32197       // if columnWidth is 0, default to outerWidth of first item
32198         if ( !this.columnWidth ) {
32199             var firstItem = this.bricks.first();
32200             Roo.log(firstItem);
32201             this.columnWidth  = this.containerWidth;
32202             if (firstItem && firstItem.attr('originalwidth') ) {
32203                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32204             }
32205             // columnWidth fall back to item of first element
32206             Roo.log("set column width?");
32207                         this.initialColumnWidth = this.columnWidth  ;
32208
32209             // if first elem has no width, default to size of container
32210             
32211         }
32212         
32213         
32214         if (this.initialColumnWidth) {
32215             this.columnWidth = this.initialColumnWidth;
32216         }
32217         
32218         
32219             
32220         // column width is fixed at the top - however if container width get's smaller we should
32221         // reduce it...
32222         
32223         // this bit calcs how man columns..
32224             
32225         var columnWidth = this.columnWidth += this.gutter;
32226       
32227         // calculate columns
32228         var containerWidth = this.containerWidth + this.gutter;
32229         
32230         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32231         // fix rounding errors, typically with gutters
32232         var excess = columnWidth - containerWidth % columnWidth;
32233         
32234         
32235         // if overshoot is less than a pixel, round up, otherwise floor it
32236         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32237         cols = Math[ mathMethod ]( cols );
32238         this.cols = Math.max( cols, 1 );
32239         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32240         
32241          // padding positioning..
32242         var totalColWidth = this.cols * this.columnWidth;
32243         var padavail = this.containerWidth - totalColWidth;
32244         // so for 2 columns - we need 3 'pads'
32245         
32246         var padNeeded = (1+this.cols) * this.padWidth;
32247         
32248         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32249         
32250         this.columnWidth += padExtra
32251         //this.padWidth = Math.floor(padavail /  ( this.cols));
32252         
32253         // adjust colum width so that padding is fixed??
32254         
32255         // we have 3 columns ... total = width * 3
32256         // we have X left over... that should be used by 
32257         
32258         //if (this.expandC) {
32259             
32260         //}
32261         
32262         
32263         
32264     },
32265     
32266     getContainerWidth : function()
32267     {
32268        /* // container is parent if fit width
32269         var container = this.isFitWidth ? this.element.parentNode : this.element;
32270         // check that this.size and size are there
32271         // IE8 triggers resize on body size change, so they might not be
32272         
32273         var size = getSize( container );  //FIXME
32274         this.containerWidth = size && size.innerWidth; //FIXME
32275         */
32276          
32277         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32278         
32279     },
32280     
32281     _getItemLayoutPosition : function( item )  // what is item?
32282     {
32283         // we resize the item to our columnWidth..
32284       
32285         item.setWidth(this.columnWidth);
32286         item.autoBoxAdjust  = false;
32287         
32288         var sz = item.getSize();
32289  
32290         // how many columns does this brick span
32291         var remainder = this.containerWidth % this.columnWidth;
32292         
32293         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32294         // round if off by 1 pixel, otherwise use ceil
32295         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32296         colSpan = Math.min( colSpan, this.cols );
32297         
32298         // normally this should be '1' as we dont' currently allow multi width columns..
32299         
32300         var colGroup = this._getColGroup( colSpan );
32301         // get the minimum Y value from the columns
32302         var minimumY = Math.min.apply( Math, colGroup );
32303         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32304         
32305         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32306          
32307         // position the brick
32308         var position = {
32309             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32310             y: this.currentSize.y + minimumY + this.padHeight
32311         };
32312         
32313         Roo.log(position);
32314         // apply setHeight to necessary columns
32315         var setHeight = minimumY + sz.height + this.padHeight;
32316         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32317         
32318         var setSpan = this.cols + 1 - colGroup.length;
32319         for ( var i = 0; i < setSpan; i++ ) {
32320           this.colYs[ shortColIndex + i ] = setHeight ;
32321         }
32322       
32323         return position;
32324     },
32325     
32326     /**
32327      * @param {Number} colSpan - number of columns the element spans
32328      * @returns {Array} colGroup
32329      */
32330     _getColGroup : function( colSpan )
32331     {
32332         if ( colSpan < 2 ) {
32333           // if brick spans only one column, use all the column Ys
32334           return this.colYs;
32335         }
32336       
32337         var colGroup = [];
32338         // how many different places could this brick fit horizontally
32339         var groupCount = this.cols + 1 - colSpan;
32340         // for each group potential horizontal position
32341         for ( var i = 0; i < groupCount; i++ ) {
32342           // make an array of colY values for that one group
32343           var groupColYs = this.colYs.slice( i, i + colSpan );
32344           // and get the max value of the array
32345           colGroup[i] = Math.max.apply( Math, groupColYs );
32346         }
32347         return colGroup;
32348     },
32349     /*
32350     _manageStamp : function( stamp )
32351     {
32352         var stampSize =  stamp.getSize();
32353         var offset = stamp.getBox();
32354         // get the columns that this stamp affects
32355         var firstX = this.isOriginLeft ? offset.x : offset.right;
32356         var lastX = firstX + stampSize.width;
32357         var firstCol = Math.floor( firstX / this.columnWidth );
32358         firstCol = Math.max( 0, firstCol );
32359         
32360         var lastCol = Math.floor( lastX / this.columnWidth );
32361         // lastCol should not go over if multiple of columnWidth #425
32362         lastCol -= lastX % this.columnWidth ? 0 : 1;
32363         lastCol = Math.min( this.cols - 1, lastCol );
32364         
32365         // set colYs to bottom of the stamp
32366         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32367             stampSize.height;
32368             
32369         for ( var i = firstCol; i <= lastCol; i++ ) {
32370           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32371         }
32372     },
32373     */
32374     
32375     _getContainerSize : function()
32376     {
32377         this.maxY = Math.max.apply( Math, this.colYs );
32378         var size = {
32379             height: this.maxY
32380         };
32381       
32382         if ( this.isFitWidth ) {
32383             size.width = this._getContainerFitWidth();
32384         }
32385       
32386         return size;
32387     },
32388     
32389     _getContainerFitWidth : function()
32390     {
32391         var unusedCols = 0;
32392         // count unused columns
32393         var i = this.cols;
32394         while ( --i ) {
32395           if ( this.colYs[i] !== 0 ) {
32396             break;
32397           }
32398           unusedCols++;
32399         }
32400         // fit container to columns that have been used
32401         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32402     },
32403     
32404     needsResizeLayout : function()
32405     {
32406         var previousWidth = this.containerWidth;
32407         this.getContainerWidth();
32408         return previousWidth !== this.containerWidth;
32409     }
32410  
32411 });
32412
32413  
32414
32415  /*
32416  * - LGPL
32417  *
32418  * element
32419  * 
32420  */
32421
32422 /**
32423  * @class Roo.bootstrap.MasonryBrick
32424  * @extends Roo.bootstrap.Component
32425  * Bootstrap MasonryBrick class
32426  * 
32427  * @constructor
32428  * Create a new MasonryBrick
32429  * @param {Object} config The config object
32430  */
32431
32432 Roo.bootstrap.MasonryBrick = function(config){
32433     
32434     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32435     
32436     Roo.bootstrap.MasonryBrick.register(this);
32437     
32438     this.addEvents({
32439         // raw events
32440         /**
32441          * @event click
32442          * When a MasonryBrick is clcik
32443          * @param {Roo.bootstrap.MasonryBrick} this
32444          * @param {Roo.EventObject} e
32445          */
32446         "click" : true
32447     });
32448 };
32449
32450 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32451     
32452     /**
32453      * @cfg {String} title
32454      */   
32455     title : '',
32456     /**
32457      * @cfg {String} html
32458      */   
32459     html : '',
32460     /**
32461      * @cfg {String} bgimage
32462      */   
32463     bgimage : '',
32464     /**
32465      * @cfg {String} videourl
32466      */   
32467     videourl : '',
32468     /**
32469      * @cfg {String} cls
32470      */   
32471     cls : '',
32472     /**
32473      * @cfg {String} href
32474      */   
32475     href : '',
32476     /**
32477      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32478      */   
32479     size : 'xs',
32480     
32481     /**
32482      * @cfg {String} placetitle (center|bottom)
32483      */   
32484     placetitle : '',
32485     
32486     /**
32487      * @cfg {Boolean} isFitContainer defalut true
32488      */   
32489     isFitContainer : true, 
32490     
32491     /**
32492      * @cfg {Boolean} preventDefault defalut false
32493      */   
32494     preventDefault : false, 
32495     
32496     /**
32497      * @cfg {Boolean} inverse defalut false
32498      */   
32499     maskInverse : false, 
32500     
32501     getAutoCreate : function()
32502     {
32503         if(!this.isFitContainer){
32504             return this.getSplitAutoCreate();
32505         }
32506         
32507         var cls = 'masonry-brick masonry-brick-full';
32508         
32509         if(this.href.length){
32510             cls += ' masonry-brick-link';
32511         }
32512         
32513         if(this.bgimage.length){
32514             cls += ' masonry-brick-image';
32515         }
32516         
32517         if(this.maskInverse){
32518             cls += ' mask-inverse';
32519         }
32520         
32521         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32522             cls += ' enable-mask';
32523         }
32524         
32525         if(this.size){
32526             cls += ' masonry-' + this.size + '-brick';
32527         }
32528         
32529         if(this.placetitle.length){
32530             
32531             switch (this.placetitle) {
32532                 case 'center' :
32533                     cls += ' masonry-center-title';
32534                     break;
32535                 case 'bottom' :
32536                     cls += ' masonry-bottom-title';
32537                     break;
32538                 default:
32539                     break;
32540             }
32541             
32542         } else {
32543             if(!this.html.length && !this.bgimage.length){
32544                 cls += ' masonry-center-title';
32545             }
32546
32547             if(!this.html.length && this.bgimage.length){
32548                 cls += ' masonry-bottom-title';
32549             }
32550         }
32551         
32552         if(this.cls){
32553             cls += ' ' + this.cls;
32554         }
32555         
32556         var cfg = {
32557             tag: (this.href.length) ? 'a' : 'div',
32558             cls: cls,
32559             cn: [
32560                 {
32561                     tag: 'div',
32562                     cls: 'masonry-brick-mask'
32563                 },
32564                 {
32565                     tag: 'div',
32566                     cls: 'masonry-brick-paragraph',
32567                     cn: []
32568                 }
32569             ]
32570         };
32571         
32572         if(this.href.length){
32573             cfg.href = this.href;
32574         }
32575         
32576         var cn = cfg.cn[1].cn;
32577         
32578         if(this.title.length){
32579             cn.push({
32580                 tag: 'h4',
32581                 cls: 'masonry-brick-title',
32582                 html: this.title
32583             });
32584         }
32585         
32586         if(this.html.length){
32587             cn.push({
32588                 tag: 'p',
32589                 cls: 'masonry-brick-text',
32590                 html: this.html
32591             });
32592         }
32593         
32594         if (!this.title.length && !this.html.length) {
32595             cfg.cn[1].cls += ' hide';
32596         }
32597         
32598         if(this.bgimage.length){
32599             cfg.cn.push({
32600                 tag: 'img',
32601                 cls: 'masonry-brick-image-view',
32602                 src: this.bgimage
32603             });
32604         }
32605         
32606         if(this.videourl.length){
32607             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32608             // youtube support only?
32609             cfg.cn.push({
32610                 tag: 'iframe',
32611                 cls: 'masonry-brick-image-view',
32612                 src: vurl,
32613                 frameborder : 0,
32614                 allowfullscreen : true
32615             });
32616         }
32617         
32618         return cfg;
32619         
32620     },
32621     
32622     getSplitAutoCreate : function()
32623     {
32624         var cls = 'masonry-brick masonry-brick-split';
32625         
32626         if(this.href.length){
32627             cls += ' masonry-brick-link';
32628         }
32629         
32630         if(this.bgimage.length){
32631             cls += ' masonry-brick-image';
32632         }
32633         
32634         if(this.size){
32635             cls += ' masonry-' + this.size + '-brick';
32636         }
32637         
32638         switch (this.placetitle) {
32639             case 'center' :
32640                 cls += ' masonry-center-title';
32641                 break;
32642             case 'bottom' :
32643                 cls += ' masonry-bottom-title';
32644                 break;
32645             default:
32646                 if(!this.bgimage.length){
32647                     cls += ' masonry-center-title';
32648                 }
32649
32650                 if(this.bgimage.length){
32651                     cls += ' masonry-bottom-title';
32652                 }
32653                 break;
32654         }
32655         
32656         if(this.cls){
32657             cls += ' ' + this.cls;
32658         }
32659         
32660         var cfg = {
32661             tag: (this.href.length) ? 'a' : 'div',
32662             cls: cls,
32663             cn: [
32664                 {
32665                     tag: 'div',
32666                     cls: 'masonry-brick-split-head',
32667                     cn: [
32668                         {
32669                             tag: 'div',
32670                             cls: 'masonry-brick-paragraph',
32671                             cn: []
32672                         }
32673                     ]
32674                 },
32675                 {
32676                     tag: 'div',
32677                     cls: 'masonry-brick-split-body',
32678                     cn: []
32679                 }
32680             ]
32681         };
32682         
32683         if(this.href.length){
32684             cfg.href = this.href;
32685         }
32686         
32687         if(this.title.length){
32688             cfg.cn[0].cn[0].cn.push({
32689                 tag: 'h4',
32690                 cls: 'masonry-brick-title',
32691                 html: this.title
32692             });
32693         }
32694         
32695         if(this.html.length){
32696             cfg.cn[1].cn.push({
32697                 tag: 'p',
32698                 cls: 'masonry-brick-text',
32699                 html: this.html
32700             });
32701         }
32702
32703         if(this.bgimage.length){
32704             cfg.cn[0].cn.push({
32705                 tag: 'img',
32706                 cls: 'masonry-brick-image-view',
32707                 src: this.bgimage
32708             });
32709         }
32710         
32711         if(this.videourl.length){
32712             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32713             // youtube support only?
32714             cfg.cn[0].cn.cn.push({
32715                 tag: 'iframe',
32716                 cls: 'masonry-brick-image-view',
32717                 src: vurl,
32718                 frameborder : 0,
32719                 allowfullscreen : true
32720             });
32721         }
32722         
32723         return cfg;
32724     },
32725     
32726     initEvents: function() 
32727     {
32728         switch (this.size) {
32729             case 'xs' :
32730                 this.x = 1;
32731                 this.y = 1;
32732                 break;
32733             case 'sm' :
32734                 this.x = 2;
32735                 this.y = 2;
32736                 break;
32737             case 'md' :
32738             case 'md-left' :
32739             case 'md-right' :
32740                 this.x = 3;
32741                 this.y = 3;
32742                 break;
32743             case 'tall' :
32744                 this.x = 2;
32745                 this.y = 3;
32746                 break;
32747             case 'wide' :
32748                 this.x = 3;
32749                 this.y = 2;
32750                 break;
32751             case 'wide-thin' :
32752                 this.x = 3;
32753                 this.y = 1;
32754                 break;
32755                         
32756             default :
32757                 break;
32758         }
32759         
32760         if(Roo.isTouch){
32761             this.el.on('touchstart', this.onTouchStart, this);
32762             this.el.on('touchmove', this.onTouchMove, this);
32763             this.el.on('touchend', this.onTouchEnd, this);
32764             this.el.on('contextmenu', this.onContextMenu, this);
32765         } else {
32766             this.el.on('mouseenter'  ,this.enter, this);
32767             this.el.on('mouseleave', this.leave, this);
32768             this.el.on('click', this.onClick, this);
32769         }
32770         
32771         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32772             this.parent().bricks.push(this);   
32773         }
32774         
32775     },
32776     
32777     onClick: function(e, el)
32778     {
32779         var time = this.endTimer - this.startTimer;
32780         // Roo.log(e.preventDefault());
32781         if(Roo.isTouch){
32782             if(time > 1000){
32783                 e.preventDefault();
32784                 return;
32785             }
32786         }
32787         
32788         if(!this.preventDefault){
32789             return;
32790         }
32791         
32792         e.preventDefault();
32793         
32794         if (this.activeClass != '') {
32795             this.selectBrick();
32796         }
32797         
32798         this.fireEvent('click', this, e);
32799     },
32800     
32801     enter: function(e, el)
32802     {
32803         e.preventDefault();
32804         
32805         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32806             return;
32807         }
32808         
32809         if(this.bgimage.length && this.html.length){
32810             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32811         }
32812     },
32813     
32814     leave: function(e, el)
32815     {
32816         e.preventDefault();
32817         
32818         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32819             return;
32820         }
32821         
32822         if(this.bgimage.length && this.html.length){
32823             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32824         }
32825     },
32826     
32827     onTouchStart: function(e, el)
32828     {
32829 //        e.preventDefault();
32830         
32831         this.touchmoved = false;
32832         
32833         if(!this.isFitContainer){
32834             return;
32835         }
32836         
32837         if(!this.bgimage.length || !this.html.length){
32838             return;
32839         }
32840         
32841         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32842         
32843         this.timer = new Date().getTime();
32844         
32845     },
32846     
32847     onTouchMove: function(e, el)
32848     {
32849         this.touchmoved = true;
32850     },
32851     
32852     onContextMenu : function(e,el)
32853     {
32854         e.preventDefault();
32855         e.stopPropagation();
32856         return false;
32857     },
32858     
32859     onTouchEnd: function(e, el)
32860     {
32861 //        e.preventDefault();
32862         
32863         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32864         
32865             this.leave(e,el);
32866             
32867             return;
32868         }
32869         
32870         if(!this.bgimage.length || !this.html.length){
32871             
32872             if(this.href.length){
32873                 window.location.href = this.href;
32874             }
32875             
32876             return;
32877         }
32878         
32879         if(!this.isFitContainer){
32880             return;
32881         }
32882         
32883         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32884         
32885         window.location.href = this.href;
32886     },
32887     
32888     //selection on single brick only
32889     selectBrick : function() {
32890         
32891         if (!this.parentId) {
32892             return;
32893         }
32894         
32895         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32896         var index = m.selectedBrick.indexOf(this.id);
32897         
32898         if ( index > -1) {
32899             m.selectedBrick.splice(index,1);
32900             this.el.removeClass(this.activeClass);
32901             return;
32902         }
32903         
32904         for(var i = 0; i < m.selectedBrick.length; i++) {
32905             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32906             b.el.removeClass(b.activeClass);
32907         }
32908         
32909         m.selectedBrick = [];
32910         
32911         m.selectedBrick.push(this.id);
32912         this.el.addClass(this.activeClass);
32913         return;
32914     },
32915     
32916     isSelected : function(){
32917         return this.el.hasClass(this.activeClass);
32918         
32919     }
32920 });
32921
32922 Roo.apply(Roo.bootstrap.MasonryBrick, {
32923     
32924     //groups: {},
32925     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32926      /**
32927     * register a Masonry Brick
32928     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32929     */
32930     
32931     register : function(brick)
32932     {
32933         //this.groups[brick.id] = brick;
32934         this.groups.add(brick.id, brick);
32935     },
32936     /**
32937     * fetch a  masonry brick based on the masonry brick ID
32938     * @param {string} the masonry brick to add
32939     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32940     */
32941     
32942     get: function(brick_id) 
32943     {
32944         // if (typeof(this.groups[brick_id]) == 'undefined') {
32945         //     return false;
32946         // }
32947         // return this.groups[brick_id] ;
32948         
32949         if(this.groups.key(brick_id)) {
32950             return this.groups.key(brick_id);
32951         }
32952         
32953         return false;
32954     }
32955     
32956     
32957     
32958 });
32959
32960  /*
32961  * - LGPL
32962  *
32963  * element
32964  * 
32965  */
32966
32967 /**
32968  * @class Roo.bootstrap.Brick
32969  * @extends Roo.bootstrap.Component
32970  * Bootstrap Brick class
32971  * 
32972  * @constructor
32973  * Create a new Brick
32974  * @param {Object} config The config object
32975  */
32976
32977 Roo.bootstrap.Brick = function(config){
32978     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32979     
32980     this.addEvents({
32981         // raw events
32982         /**
32983          * @event click
32984          * When a Brick is click
32985          * @param {Roo.bootstrap.Brick} this
32986          * @param {Roo.EventObject} e
32987          */
32988         "click" : true
32989     });
32990 };
32991
32992 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32993     
32994     /**
32995      * @cfg {String} title
32996      */   
32997     title : '',
32998     /**
32999      * @cfg {String} html
33000      */   
33001     html : '',
33002     /**
33003      * @cfg {String} bgimage
33004      */   
33005     bgimage : '',
33006     /**
33007      * @cfg {String} cls
33008      */   
33009     cls : '',
33010     /**
33011      * @cfg {String} href
33012      */   
33013     href : '',
33014     /**
33015      * @cfg {String} video
33016      */   
33017     video : '',
33018     /**
33019      * @cfg {Boolean} square
33020      */   
33021     square : true,
33022     
33023     getAutoCreate : function()
33024     {
33025         var cls = 'roo-brick';
33026         
33027         if(this.href.length){
33028             cls += ' roo-brick-link';
33029         }
33030         
33031         if(this.bgimage.length){
33032             cls += ' roo-brick-image';
33033         }
33034         
33035         if(!this.html.length && !this.bgimage.length){
33036             cls += ' roo-brick-center-title';
33037         }
33038         
33039         if(!this.html.length && this.bgimage.length){
33040             cls += ' roo-brick-bottom-title';
33041         }
33042         
33043         if(this.cls){
33044             cls += ' ' + this.cls;
33045         }
33046         
33047         var cfg = {
33048             tag: (this.href.length) ? 'a' : 'div',
33049             cls: cls,
33050             cn: [
33051                 {
33052                     tag: 'div',
33053                     cls: 'roo-brick-paragraph',
33054                     cn: []
33055                 }
33056             ]
33057         };
33058         
33059         if(this.href.length){
33060             cfg.href = this.href;
33061         }
33062         
33063         var cn = cfg.cn[0].cn;
33064         
33065         if(this.title.length){
33066             cn.push({
33067                 tag: 'h4',
33068                 cls: 'roo-brick-title',
33069                 html: this.title
33070             });
33071         }
33072         
33073         if(this.html.length){
33074             cn.push({
33075                 tag: 'p',
33076                 cls: 'roo-brick-text',
33077                 html: this.html
33078             });
33079         } else {
33080             cn.cls += ' hide';
33081         }
33082         
33083         if(this.bgimage.length){
33084             cfg.cn.push({
33085                 tag: 'img',
33086                 cls: 'roo-brick-image-view',
33087                 src: this.bgimage
33088             });
33089         }
33090         
33091         return cfg;
33092     },
33093     
33094     initEvents: function() 
33095     {
33096         if(this.title.length || this.html.length){
33097             this.el.on('mouseenter'  ,this.enter, this);
33098             this.el.on('mouseleave', this.leave, this);
33099         }
33100         
33101         Roo.EventManager.onWindowResize(this.resize, this); 
33102         
33103         if(this.bgimage.length){
33104             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33105             this.imageEl.on('load', this.onImageLoad, this);
33106             return;
33107         }
33108         
33109         this.resize();
33110     },
33111     
33112     onImageLoad : function()
33113     {
33114         this.resize();
33115     },
33116     
33117     resize : function()
33118     {
33119         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33120         
33121         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33122         
33123         if(this.bgimage.length){
33124             var image = this.el.select('.roo-brick-image-view', true).first();
33125             
33126             image.setWidth(paragraph.getWidth());
33127             
33128             if(this.square){
33129                 image.setHeight(paragraph.getWidth());
33130             }
33131             
33132             this.el.setHeight(image.getHeight());
33133             paragraph.setHeight(image.getHeight());
33134             
33135         }
33136         
33137     },
33138     
33139     enter: function(e, el)
33140     {
33141         e.preventDefault();
33142         
33143         if(this.bgimage.length){
33144             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33145             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33146         }
33147     },
33148     
33149     leave: function(e, el)
33150     {
33151         e.preventDefault();
33152         
33153         if(this.bgimage.length){
33154             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33155             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33156         }
33157     }
33158     
33159 });
33160
33161  
33162
33163  /*
33164  * - LGPL
33165  *
33166  * Number field 
33167  */
33168
33169 /**
33170  * @class Roo.bootstrap.NumberField
33171  * @extends Roo.bootstrap.Input
33172  * Bootstrap NumberField class
33173  * 
33174  * 
33175  * 
33176  * 
33177  * @constructor
33178  * Create a new NumberField
33179  * @param {Object} config The config object
33180  */
33181
33182 Roo.bootstrap.NumberField = function(config){
33183     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33184 };
33185
33186 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33187     
33188     /**
33189      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33190      */
33191     allowDecimals : true,
33192     /**
33193      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33194      */
33195     decimalSeparator : ".",
33196     /**
33197      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33198      */
33199     decimalPrecision : 2,
33200     /**
33201      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33202      */
33203     allowNegative : true,
33204     
33205     /**
33206      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33207      */
33208     allowZero: true,
33209     /**
33210      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33211      */
33212     minValue : Number.NEGATIVE_INFINITY,
33213     /**
33214      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33215      */
33216     maxValue : Number.MAX_VALUE,
33217     /**
33218      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33219      */
33220     minText : "The minimum value for this field is {0}",
33221     /**
33222      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33223      */
33224     maxText : "The maximum value for this field is {0}",
33225     /**
33226      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33227      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33228      */
33229     nanText : "{0} is not a valid number",
33230     /**
33231      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33232      */
33233     castInt : true,
33234     /**
33235      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33236      */
33237     thousandsDelimiter : false,
33238     /**
33239      * @cfg {String} valueAlign alignment of value
33240      */
33241     valueAlign : "left",
33242
33243     getAutoCreate : function()
33244     {
33245         var hiddenInput = {
33246             tag: 'input',
33247             type: 'hidden',
33248             id: Roo.id(),
33249             cls: 'hidden-number-input'
33250         };
33251         
33252         if (this.name) {
33253             hiddenInput.name = this.name;
33254         }
33255         
33256         this.name = '';
33257         
33258         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33259         
33260         this.name = hiddenInput.name;
33261         
33262         if(cfg.cn.length > 0) {
33263             cfg.cn.push(hiddenInput);
33264         }
33265         
33266         return cfg;
33267     },
33268
33269     // private
33270     initEvents : function()
33271     {   
33272         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33273         
33274         var allowed = "0123456789";
33275         
33276         if(this.allowDecimals){
33277             allowed += this.decimalSeparator;
33278         }
33279         
33280         if(this.allowNegative){
33281             allowed += "-";
33282         }
33283         
33284         if(this.thousandsDelimiter) {
33285             allowed += ",";
33286         }
33287         
33288         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33289         
33290         var keyPress = function(e){
33291             
33292             var k = e.getKey();
33293             
33294             var c = e.getCharCode();
33295             
33296             if(
33297                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33298                     allowed.indexOf(String.fromCharCode(c)) === -1
33299             ){
33300                 e.stopEvent();
33301                 return;
33302             }
33303             
33304             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33305                 return;
33306             }
33307             
33308             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33309                 e.stopEvent();
33310             }
33311         };
33312         
33313         this.el.on("keypress", keyPress, this);
33314     },
33315     
33316     validateValue : function(value)
33317     {
33318         
33319         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33320             return false;
33321         }
33322         
33323         var num = this.parseValue(value);
33324         
33325         if(isNaN(num)){
33326             this.markInvalid(String.format(this.nanText, value));
33327             return false;
33328         }
33329         
33330         if(num < this.minValue){
33331             this.markInvalid(String.format(this.minText, this.minValue));
33332             return false;
33333         }
33334         
33335         if(num > this.maxValue){
33336             this.markInvalid(String.format(this.maxText, this.maxValue));
33337             return false;
33338         }
33339         
33340         return true;
33341     },
33342
33343     getValue : function()
33344     {
33345         var v = this.hiddenEl().getValue();
33346         
33347         return this.fixPrecision(this.parseValue(v));
33348     },
33349
33350     parseValue : function(value)
33351     {
33352         if(this.thousandsDelimiter) {
33353             value += "";
33354             r = new RegExp(",", "g");
33355             value = value.replace(r, "");
33356         }
33357         
33358         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33359         return isNaN(value) ? '' : value;
33360     },
33361
33362     fixPrecision : function(value)
33363     {
33364         if(this.thousandsDelimiter) {
33365             value += "";
33366             r = new RegExp(",", "g");
33367             value = value.replace(r, "");
33368         }
33369         
33370         var nan = isNaN(value);
33371         
33372         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33373             return nan ? '' : value;
33374         }
33375         return parseFloat(value).toFixed(this.decimalPrecision);
33376     },
33377
33378     setValue : function(v)
33379     {
33380         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33381         
33382         this.value = v;
33383         
33384         if(this.rendered){
33385             
33386             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33387             
33388             this.inputEl().dom.value = (v == '') ? '' :
33389                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33390             
33391             if(!this.allowZero && v === '0') {
33392                 this.hiddenEl().dom.value = '';
33393                 this.inputEl().dom.value = '';
33394             }
33395             
33396             this.validate();
33397         }
33398     },
33399
33400     decimalPrecisionFcn : function(v)
33401     {
33402         return Math.floor(v);
33403     },
33404
33405     beforeBlur : function()
33406     {
33407         if(!this.castInt){
33408             return;
33409         }
33410         
33411         var v = this.parseValue(this.getRawValue());
33412         
33413         if(v || v === 0){
33414             this.setValue(v);
33415         }
33416     },
33417     
33418     hiddenEl : function()
33419     {
33420         return this.el.select('input.hidden-number-input',true).first();
33421     }
33422     
33423 });
33424
33425  
33426
33427 /*
33428 * Licence: LGPL
33429 */
33430
33431 /**
33432  * @class Roo.bootstrap.DocumentSlider
33433  * @extends Roo.bootstrap.Component
33434  * Bootstrap DocumentSlider class
33435  * 
33436  * @constructor
33437  * Create a new DocumentViewer
33438  * @param {Object} config The config object
33439  */
33440
33441 Roo.bootstrap.DocumentSlider = function(config){
33442     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33443     
33444     this.files = [];
33445     
33446     this.addEvents({
33447         /**
33448          * @event initial
33449          * Fire after initEvent
33450          * @param {Roo.bootstrap.DocumentSlider} this
33451          */
33452         "initial" : true,
33453         /**
33454          * @event update
33455          * Fire after update
33456          * @param {Roo.bootstrap.DocumentSlider} this
33457          */
33458         "update" : true,
33459         /**
33460          * @event click
33461          * Fire after click
33462          * @param {Roo.bootstrap.DocumentSlider} this
33463          */
33464         "click" : true
33465     });
33466 };
33467
33468 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33469     
33470     files : false,
33471     
33472     indicator : 0,
33473     
33474     getAutoCreate : function()
33475     {
33476         var cfg = {
33477             tag : 'div',
33478             cls : 'roo-document-slider',
33479             cn : [
33480                 {
33481                     tag : 'div',
33482                     cls : 'roo-document-slider-header',
33483                     cn : [
33484                         {
33485                             tag : 'div',
33486                             cls : 'roo-document-slider-header-title'
33487                         }
33488                     ]
33489                 },
33490                 {
33491                     tag : 'div',
33492                     cls : 'roo-document-slider-body',
33493                     cn : [
33494                         {
33495                             tag : 'div',
33496                             cls : 'roo-document-slider-prev',
33497                             cn : [
33498                                 {
33499                                     tag : 'i',
33500                                     cls : 'fa fa-chevron-left'
33501                                 }
33502                             ]
33503                         },
33504                         {
33505                             tag : 'div',
33506                             cls : 'roo-document-slider-thumb',
33507                             cn : [
33508                                 {
33509                                     tag : 'img',
33510                                     cls : 'roo-document-slider-image'
33511                                 }
33512                             ]
33513                         },
33514                         {
33515                             tag : 'div',
33516                             cls : 'roo-document-slider-next',
33517                             cn : [
33518                                 {
33519                                     tag : 'i',
33520                                     cls : 'fa fa-chevron-right'
33521                                 }
33522                             ]
33523                         }
33524                     ]
33525                 }
33526             ]
33527         };
33528         
33529         return cfg;
33530     },
33531     
33532     initEvents : function()
33533     {
33534         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33535         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33536         
33537         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33538         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33539         
33540         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33541         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33542         
33543         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33544         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33545         
33546         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33547         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33548         
33549         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33550         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33551         
33552         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33553         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33554         
33555         this.thumbEl.on('click', this.onClick, this);
33556         
33557         this.prevIndicator.on('click', this.prev, this);
33558         
33559         this.nextIndicator.on('click', this.next, this);
33560         
33561     },
33562     
33563     initial : function()
33564     {
33565         if(this.files.length){
33566             this.indicator = 1;
33567             this.update()
33568         }
33569         
33570         this.fireEvent('initial', this);
33571     },
33572     
33573     update : function()
33574     {
33575         this.imageEl.attr('src', this.files[this.indicator - 1]);
33576         
33577         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33578         
33579         this.prevIndicator.show();
33580         
33581         if(this.indicator == 1){
33582             this.prevIndicator.hide();
33583         }
33584         
33585         this.nextIndicator.show();
33586         
33587         if(this.indicator == this.files.length){
33588             this.nextIndicator.hide();
33589         }
33590         
33591         this.thumbEl.scrollTo('top');
33592         
33593         this.fireEvent('update', this);
33594     },
33595     
33596     onClick : function(e)
33597     {
33598         e.preventDefault();
33599         
33600         this.fireEvent('click', this);
33601     },
33602     
33603     prev : function(e)
33604     {
33605         e.preventDefault();
33606         
33607         this.indicator = Math.max(1, this.indicator - 1);
33608         
33609         this.update();
33610     },
33611     
33612     next : function(e)
33613     {
33614         e.preventDefault();
33615         
33616         this.indicator = Math.min(this.files.length, this.indicator + 1);
33617         
33618         this.update();
33619     }
33620 });
33621 /*
33622  * - LGPL
33623  *
33624  * RadioSet
33625  *
33626  *
33627  */
33628
33629 /**
33630  * @class Roo.bootstrap.RadioSet
33631  * @extends Roo.bootstrap.Input
33632  * Bootstrap RadioSet class
33633  * @cfg {String} indicatorpos (left|right) default left
33634  * @cfg {Boolean} inline (true|false) inline the element (default true)
33635  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33636  * @constructor
33637  * Create a new RadioSet
33638  * @param {Object} config The config object
33639  */
33640
33641 Roo.bootstrap.RadioSet = function(config){
33642     
33643     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33644     
33645     this.radioes = [];
33646     
33647     Roo.bootstrap.RadioSet.register(this);
33648     
33649     this.addEvents({
33650         /**
33651         * @event check
33652         * Fires when the element is checked or unchecked.
33653         * @param {Roo.bootstrap.RadioSet} this This radio
33654         * @param {Roo.bootstrap.Radio} item The checked item
33655         */
33656        check : true,
33657        /**
33658         * @event click
33659         * Fires when the element is click.
33660         * @param {Roo.bootstrap.RadioSet} this This radio set
33661         * @param {Roo.bootstrap.Radio} item The checked item
33662         * @param {Roo.EventObject} e The event object
33663         */
33664        click : true
33665     });
33666     
33667 };
33668
33669 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33670
33671     radioes : false,
33672     
33673     inline : true,
33674     
33675     weight : '',
33676     
33677     indicatorpos : 'left',
33678     
33679     getAutoCreate : function()
33680     {
33681         var label = {
33682             tag : 'label',
33683             cls : 'roo-radio-set-label',
33684             cn : [
33685                 {
33686                     tag : 'span',
33687                     html : this.fieldLabel
33688                 }
33689             ]
33690         };
33691         
33692         if(this.indicatorpos == 'left'){
33693             label.cn.unshift({
33694                 tag : 'i',
33695                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33696                 tooltip : 'This field is required'
33697             });
33698         } else {
33699             label.cn.push({
33700                 tag : 'i',
33701                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33702                 tooltip : 'This field is required'
33703             });
33704         }
33705         
33706         var items = {
33707             tag : 'div',
33708             cls : 'roo-radio-set-items'
33709         };
33710         
33711         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33712         
33713         if (align === 'left' && this.fieldLabel.length) {
33714             
33715             items = {
33716                 cls : "roo-radio-set-right", 
33717                 cn: [
33718                     items
33719                 ]
33720             };
33721             
33722             if(this.labelWidth > 12){
33723                 label.style = "width: " + this.labelWidth + 'px';
33724             }
33725             
33726             if(this.labelWidth < 13 && this.labelmd == 0){
33727                 this.labelmd = this.labelWidth;
33728             }
33729             
33730             if(this.labellg > 0){
33731                 label.cls += ' col-lg-' + this.labellg;
33732                 items.cls += ' col-lg-' + (12 - this.labellg);
33733             }
33734             
33735             if(this.labelmd > 0){
33736                 label.cls += ' col-md-' + this.labelmd;
33737                 items.cls += ' col-md-' + (12 - this.labelmd);
33738             }
33739             
33740             if(this.labelsm > 0){
33741                 label.cls += ' col-sm-' + this.labelsm;
33742                 items.cls += ' col-sm-' + (12 - this.labelsm);
33743             }
33744             
33745             if(this.labelxs > 0){
33746                 label.cls += ' col-xs-' + this.labelxs;
33747                 items.cls += ' col-xs-' + (12 - this.labelxs);
33748             }
33749         }
33750         
33751         var cfg = {
33752             tag : 'div',
33753             cls : 'roo-radio-set',
33754             cn : [
33755                 {
33756                     tag : 'input',
33757                     cls : 'roo-radio-set-input',
33758                     type : 'hidden',
33759                     name : this.name,
33760                     value : this.value ? this.value :  ''
33761                 },
33762                 label,
33763                 items
33764             ]
33765         };
33766         
33767         if(this.weight.length){
33768             cfg.cls += ' roo-radio-' + this.weight;
33769         }
33770         
33771         if(this.inline) {
33772             cfg.cls += ' roo-radio-set-inline';
33773         }
33774         
33775         var settings=this;
33776         ['xs','sm','md','lg'].map(function(size){
33777             if (settings[size]) {
33778                 cfg.cls += ' col-' + size + '-' + settings[size];
33779             }
33780         });
33781         
33782         return cfg;
33783         
33784     },
33785
33786     initEvents : function()
33787     {
33788         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33789         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33790         
33791         if(!this.fieldLabel.length){
33792             this.labelEl.hide();
33793         }
33794         
33795         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33796         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33797         
33798         this.indicator = this.indicatorEl();
33799         
33800         if(this.indicator){
33801             this.indicator.addClass('invisible');
33802         }
33803         
33804         this.originalValue = this.getValue();
33805         
33806     },
33807     
33808     inputEl: function ()
33809     {
33810         return this.el.select('.roo-radio-set-input', true).first();
33811     },
33812     
33813     getChildContainer : function()
33814     {
33815         return this.itemsEl;
33816     },
33817     
33818     register : function(item)
33819     {
33820         this.radioes.push(item);
33821         
33822     },
33823     
33824     validate : function()
33825     {   
33826         if(this.getVisibilityEl().hasClass('hidden')){
33827             return true;
33828         }
33829         
33830         var valid = false;
33831         
33832         Roo.each(this.radioes, function(i){
33833             if(!i.checked){
33834                 return;
33835             }
33836             
33837             valid = true;
33838             return false;
33839         });
33840         
33841         if(this.allowBlank) {
33842             return true;
33843         }
33844         
33845         if(this.disabled || valid){
33846             this.markValid();
33847             return true;
33848         }
33849         
33850         this.markInvalid();
33851         return false;
33852         
33853     },
33854     
33855     markValid : function()
33856     {
33857         if(this.labelEl.isVisible(true)){
33858             this.indicatorEl().removeClass('visible');
33859             this.indicatorEl().addClass('invisible');
33860         }
33861         
33862         this.el.removeClass([this.invalidClass, this.validClass]);
33863         this.el.addClass(this.validClass);
33864         
33865         this.fireEvent('valid', this);
33866     },
33867     
33868     markInvalid : function(msg)
33869     {
33870         if(this.allowBlank || this.disabled){
33871             return;
33872         }
33873         
33874         if(this.labelEl.isVisible(true)){
33875             this.indicatorEl().removeClass('invisible');
33876             this.indicatorEl().addClass('visible');
33877         }
33878         
33879         this.el.removeClass([this.invalidClass, this.validClass]);
33880         this.el.addClass(this.invalidClass);
33881         
33882         this.fireEvent('invalid', this, msg);
33883         
33884     },
33885     
33886     setValue : function(v, suppressEvent)
33887     {   
33888         if(this.value === v){
33889             return;
33890         }
33891         
33892         this.value = v;
33893         
33894         if(this.rendered){
33895             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33896         }
33897         
33898         Roo.each(this.radioes, function(i){
33899             i.checked = false;
33900             i.el.removeClass('checked');
33901         });
33902         
33903         Roo.each(this.radioes, function(i){
33904             
33905             if(i.value === v || i.value.toString() === v.toString()){
33906                 i.checked = true;
33907                 i.el.addClass('checked');
33908                 
33909                 if(suppressEvent !== true){
33910                     this.fireEvent('check', this, i);
33911                 }
33912                 
33913                 return false;
33914             }
33915             
33916         }, this);
33917         
33918         this.validate();
33919     },
33920     
33921     clearInvalid : function(){
33922         
33923         if(!this.el || this.preventMark){
33924             return;
33925         }
33926         
33927         this.el.removeClass([this.invalidClass]);
33928         
33929         this.fireEvent('valid', this);
33930     }
33931     
33932 });
33933
33934 Roo.apply(Roo.bootstrap.RadioSet, {
33935     
33936     groups: {},
33937     
33938     register : function(set)
33939     {
33940         this.groups[set.name] = set;
33941     },
33942     
33943     get: function(name) 
33944     {
33945         if (typeof(this.groups[name]) == 'undefined') {
33946             return false;
33947         }
33948         
33949         return this.groups[name] ;
33950     }
33951     
33952 });
33953 /*
33954  * Based on:
33955  * Ext JS Library 1.1.1
33956  * Copyright(c) 2006-2007, Ext JS, LLC.
33957  *
33958  * Originally Released Under LGPL - original licence link has changed is not relivant.
33959  *
33960  * Fork - LGPL
33961  * <script type="text/javascript">
33962  */
33963
33964
33965 /**
33966  * @class Roo.bootstrap.SplitBar
33967  * @extends Roo.util.Observable
33968  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33969  * <br><br>
33970  * Usage:
33971  * <pre><code>
33972 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33973                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33974 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33975 split.minSize = 100;
33976 split.maxSize = 600;
33977 split.animate = true;
33978 split.on('moved', splitterMoved);
33979 </code></pre>
33980  * @constructor
33981  * Create a new SplitBar
33982  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33983  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33984  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33985  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33986                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33987                         position of the SplitBar).
33988  */
33989 Roo.bootstrap.SplitBar = function(cfg){
33990     
33991     /** @private */
33992     
33993     //{
33994     //  dragElement : elm
33995     //  resizingElement: el,
33996         // optional..
33997     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33998     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33999         // existingProxy ???
34000     //}
34001     
34002     this.el = Roo.get(cfg.dragElement, true);
34003     this.el.dom.unselectable = "on";
34004     /** @private */
34005     this.resizingEl = Roo.get(cfg.resizingElement, true);
34006
34007     /**
34008      * @private
34009      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34010      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34011      * @type Number
34012      */
34013     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34014     
34015     /**
34016      * The minimum size of the resizing element. (Defaults to 0)
34017      * @type Number
34018      */
34019     this.minSize = 0;
34020     
34021     /**
34022      * The maximum size of the resizing element. (Defaults to 2000)
34023      * @type Number
34024      */
34025     this.maxSize = 2000;
34026     
34027     /**
34028      * Whether to animate the transition to the new size
34029      * @type Boolean
34030      */
34031     this.animate = false;
34032     
34033     /**
34034      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34035      * @type Boolean
34036      */
34037     this.useShim = false;
34038     
34039     /** @private */
34040     this.shim = null;
34041     
34042     if(!cfg.existingProxy){
34043         /** @private */
34044         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34045     }else{
34046         this.proxy = Roo.get(cfg.existingProxy).dom;
34047     }
34048     /** @private */
34049     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34050     
34051     /** @private */
34052     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34053     
34054     /** @private */
34055     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34056     
34057     /** @private */
34058     this.dragSpecs = {};
34059     
34060     /**
34061      * @private The adapter to use to positon and resize elements
34062      */
34063     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34064     this.adapter.init(this);
34065     
34066     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34067         /** @private */
34068         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34069         this.el.addClass("roo-splitbar-h");
34070     }else{
34071         /** @private */
34072         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34073         this.el.addClass("roo-splitbar-v");
34074     }
34075     
34076     this.addEvents({
34077         /**
34078          * @event resize
34079          * Fires when the splitter is moved (alias for {@link #event-moved})
34080          * @param {Roo.bootstrap.SplitBar} this
34081          * @param {Number} newSize the new width or height
34082          */
34083         "resize" : true,
34084         /**
34085          * @event moved
34086          * Fires when the splitter is moved
34087          * @param {Roo.bootstrap.SplitBar} this
34088          * @param {Number} newSize the new width or height
34089          */
34090         "moved" : true,
34091         /**
34092          * @event beforeresize
34093          * Fires before the splitter is dragged
34094          * @param {Roo.bootstrap.SplitBar} this
34095          */
34096         "beforeresize" : true,
34097
34098         "beforeapply" : true
34099     });
34100
34101     Roo.util.Observable.call(this);
34102 };
34103
34104 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34105     onStartProxyDrag : function(x, y){
34106         this.fireEvent("beforeresize", this);
34107         if(!this.overlay){
34108             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34109             o.unselectable();
34110             o.enableDisplayMode("block");
34111             // all splitbars share the same overlay
34112             Roo.bootstrap.SplitBar.prototype.overlay = o;
34113         }
34114         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34115         this.overlay.show();
34116         Roo.get(this.proxy).setDisplayed("block");
34117         var size = this.adapter.getElementSize(this);
34118         this.activeMinSize = this.getMinimumSize();;
34119         this.activeMaxSize = this.getMaximumSize();;
34120         var c1 = size - this.activeMinSize;
34121         var c2 = Math.max(this.activeMaxSize - size, 0);
34122         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34123             this.dd.resetConstraints();
34124             this.dd.setXConstraint(
34125                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34126                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34127             );
34128             this.dd.setYConstraint(0, 0);
34129         }else{
34130             this.dd.resetConstraints();
34131             this.dd.setXConstraint(0, 0);
34132             this.dd.setYConstraint(
34133                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34134                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34135             );
34136          }
34137         this.dragSpecs.startSize = size;
34138         this.dragSpecs.startPoint = [x, y];
34139         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34140     },
34141     
34142     /** 
34143      * @private Called after the drag operation by the DDProxy
34144      */
34145     onEndProxyDrag : function(e){
34146         Roo.get(this.proxy).setDisplayed(false);
34147         var endPoint = Roo.lib.Event.getXY(e);
34148         if(this.overlay){
34149             this.overlay.hide();
34150         }
34151         var newSize;
34152         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34153             newSize = this.dragSpecs.startSize + 
34154                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34155                     endPoint[0] - this.dragSpecs.startPoint[0] :
34156                     this.dragSpecs.startPoint[0] - endPoint[0]
34157                 );
34158         }else{
34159             newSize = this.dragSpecs.startSize + 
34160                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34161                     endPoint[1] - this.dragSpecs.startPoint[1] :
34162                     this.dragSpecs.startPoint[1] - endPoint[1]
34163                 );
34164         }
34165         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34166         if(newSize != this.dragSpecs.startSize){
34167             if(this.fireEvent('beforeapply', this, newSize) !== false){
34168                 this.adapter.setElementSize(this, newSize);
34169                 this.fireEvent("moved", this, newSize);
34170                 this.fireEvent("resize", this, newSize);
34171             }
34172         }
34173     },
34174     
34175     /**
34176      * Get the adapter this SplitBar uses
34177      * @return The adapter object
34178      */
34179     getAdapter : function(){
34180         return this.adapter;
34181     },
34182     
34183     /**
34184      * Set the adapter this SplitBar uses
34185      * @param {Object} adapter A SplitBar adapter object
34186      */
34187     setAdapter : function(adapter){
34188         this.adapter = adapter;
34189         this.adapter.init(this);
34190     },
34191     
34192     /**
34193      * Gets the minimum size for the resizing element
34194      * @return {Number} The minimum size
34195      */
34196     getMinimumSize : function(){
34197         return this.minSize;
34198     },
34199     
34200     /**
34201      * Sets the minimum size for the resizing element
34202      * @param {Number} minSize The minimum size
34203      */
34204     setMinimumSize : function(minSize){
34205         this.minSize = minSize;
34206     },
34207     
34208     /**
34209      * Gets the maximum size for the resizing element
34210      * @return {Number} The maximum size
34211      */
34212     getMaximumSize : function(){
34213         return this.maxSize;
34214     },
34215     
34216     /**
34217      * Sets the maximum size for the resizing element
34218      * @param {Number} maxSize The maximum size
34219      */
34220     setMaximumSize : function(maxSize){
34221         this.maxSize = maxSize;
34222     },
34223     
34224     /**
34225      * Sets the initialize size for the resizing element
34226      * @param {Number} size The initial size
34227      */
34228     setCurrentSize : function(size){
34229         var oldAnimate = this.animate;
34230         this.animate = false;
34231         this.adapter.setElementSize(this, size);
34232         this.animate = oldAnimate;
34233     },
34234     
34235     /**
34236      * Destroy this splitbar. 
34237      * @param {Boolean} removeEl True to remove the element
34238      */
34239     destroy : function(removeEl){
34240         if(this.shim){
34241             this.shim.remove();
34242         }
34243         this.dd.unreg();
34244         this.proxy.parentNode.removeChild(this.proxy);
34245         if(removeEl){
34246             this.el.remove();
34247         }
34248     }
34249 });
34250
34251 /**
34252  * @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.
34253  */
34254 Roo.bootstrap.SplitBar.createProxy = function(dir){
34255     var proxy = new Roo.Element(document.createElement("div"));
34256     proxy.unselectable();
34257     var cls = 'roo-splitbar-proxy';
34258     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34259     document.body.appendChild(proxy.dom);
34260     return proxy.dom;
34261 };
34262
34263 /** 
34264  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34265  * Default Adapter. It assumes the splitter and resizing element are not positioned
34266  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34267  */
34268 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34269 };
34270
34271 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34272     // do nothing for now
34273     init : function(s){
34274     
34275     },
34276     /**
34277      * Called before drag operations to get the current size of the resizing element. 
34278      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34279      */
34280      getElementSize : function(s){
34281         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34282             return s.resizingEl.getWidth();
34283         }else{
34284             return s.resizingEl.getHeight();
34285         }
34286     },
34287     
34288     /**
34289      * Called after drag operations to set the size of the resizing element.
34290      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34291      * @param {Number} newSize The new size to set
34292      * @param {Function} onComplete A function to be invoked when resizing is complete
34293      */
34294     setElementSize : function(s, newSize, onComplete){
34295         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34296             if(!s.animate){
34297                 s.resizingEl.setWidth(newSize);
34298                 if(onComplete){
34299                     onComplete(s, newSize);
34300                 }
34301             }else{
34302                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34303             }
34304         }else{
34305             
34306             if(!s.animate){
34307                 s.resizingEl.setHeight(newSize);
34308                 if(onComplete){
34309                     onComplete(s, newSize);
34310                 }
34311             }else{
34312                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34313             }
34314         }
34315     }
34316 };
34317
34318 /** 
34319  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34320  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34321  * Adapter that  moves the splitter element to align with the resized sizing element. 
34322  * Used with an absolute positioned SplitBar.
34323  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34324  * document.body, make sure you assign an id to the body element.
34325  */
34326 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34327     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34328     this.container = Roo.get(container);
34329 };
34330
34331 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34332     init : function(s){
34333         this.basic.init(s);
34334     },
34335     
34336     getElementSize : function(s){
34337         return this.basic.getElementSize(s);
34338     },
34339     
34340     setElementSize : function(s, newSize, onComplete){
34341         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34342     },
34343     
34344     moveSplitter : function(s){
34345         var yes = Roo.bootstrap.SplitBar;
34346         switch(s.placement){
34347             case yes.LEFT:
34348                 s.el.setX(s.resizingEl.getRight());
34349                 break;
34350             case yes.RIGHT:
34351                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34352                 break;
34353             case yes.TOP:
34354                 s.el.setY(s.resizingEl.getBottom());
34355                 break;
34356             case yes.BOTTOM:
34357                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34358                 break;
34359         }
34360     }
34361 };
34362
34363 /**
34364  * Orientation constant - Create a vertical SplitBar
34365  * @static
34366  * @type Number
34367  */
34368 Roo.bootstrap.SplitBar.VERTICAL = 1;
34369
34370 /**
34371  * Orientation constant - Create a horizontal SplitBar
34372  * @static
34373  * @type Number
34374  */
34375 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34376
34377 /**
34378  * Placement constant - The resizing element is to the left of the splitter element
34379  * @static
34380  * @type Number
34381  */
34382 Roo.bootstrap.SplitBar.LEFT = 1;
34383
34384 /**
34385  * Placement constant - The resizing element is to the right of the splitter element
34386  * @static
34387  * @type Number
34388  */
34389 Roo.bootstrap.SplitBar.RIGHT = 2;
34390
34391 /**
34392  * Placement constant - The resizing element is positioned above the splitter element
34393  * @static
34394  * @type Number
34395  */
34396 Roo.bootstrap.SplitBar.TOP = 3;
34397
34398 /**
34399  * Placement constant - The resizing element is positioned under splitter element
34400  * @static
34401  * @type Number
34402  */
34403 Roo.bootstrap.SplitBar.BOTTOM = 4;
34404 Roo.namespace("Roo.bootstrap.layout");/*
34405  * Based on:
34406  * Ext JS Library 1.1.1
34407  * Copyright(c) 2006-2007, Ext JS, LLC.
34408  *
34409  * Originally Released Under LGPL - original licence link has changed is not relivant.
34410  *
34411  * Fork - LGPL
34412  * <script type="text/javascript">
34413  */
34414
34415 /**
34416  * @class Roo.bootstrap.layout.Manager
34417  * @extends Roo.bootstrap.Component
34418  * Base class for layout managers.
34419  */
34420 Roo.bootstrap.layout.Manager = function(config)
34421 {
34422     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34423
34424
34425
34426
34427
34428     /** false to disable window resize monitoring @type Boolean */
34429     this.monitorWindowResize = true;
34430     this.regions = {};
34431     this.addEvents({
34432         /**
34433          * @event layout
34434          * Fires when a layout is performed.
34435          * @param {Roo.LayoutManager} this
34436          */
34437         "layout" : true,
34438         /**
34439          * @event regionresized
34440          * Fires when the user resizes a region.
34441          * @param {Roo.LayoutRegion} region The resized region
34442          * @param {Number} newSize The new size (width for east/west, height for north/south)
34443          */
34444         "regionresized" : true,
34445         /**
34446          * @event regioncollapsed
34447          * Fires when a region is collapsed.
34448          * @param {Roo.LayoutRegion} region The collapsed region
34449          */
34450         "regioncollapsed" : true,
34451         /**
34452          * @event regionexpanded
34453          * Fires when a region is expanded.
34454          * @param {Roo.LayoutRegion} region The expanded region
34455          */
34456         "regionexpanded" : true
34457     });
34458     this.updating = false;
34459
34460     if (config.el) {
34461         this.el = Roo.get(config.el);
34462         this.initEvents();
34463     }
34464
34465 };
34466
34467 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34468
34469
34470     regions : null,
34471
34472     monitorWindowResize : true,
34473
34474
34475     updating : false,
34476
34477
34478     onRender : function(ct, position)
34479     {
34480         if(!this.el){
34481             this.el = Roo.get(ct);
34482             this.initEvents();
34483         }
34484         //this.fireEvent('render',this);
34485     },
34486
34487
34488     initEvents: function()
34489     {
34490
34491
34492         // ie scrollbar fix
34493         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34494             document.body.scroll = "no";
34495         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34496             this.el.position('relative');
34497         }
34498         this.id = this.el.id;
34499         this.el.addClass("roo-layout-container");
34500         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34501         if(this.el.dom != document.body ) {
34502             this.el.on('resize', this.layout,this);
34503             this.el.on('show', this.layout,this);
34504         }
34505
34506     },
34507
34508     /**
34509      * Returns true if this layout is currently being updated
34510      * @return {Boolean}
34511      */
34512     isUpdating : function(){
34513         return this.updating;
34514     },
34515
34516     /**
34517      * Suspend the LayoutManager from doing auto-layouts while
34518      * making multiple add or remove calls
34519      */
34520     beginUpdate : function(){
34521         this.updating = true;
34522     },
34523
34524     /**
34525      * Restore auto-layouts and optionally disable the manager from performing a layout
34526      * @param {Boolean} noLayout true to disable a layout update
34527      */
34528     endUpdate : function(noLayout){
34529         this.updating = false;
34530         if(!noLayout){
34531             this.layout();
34532         }
34533     },
34534
34535     layout: function(){
34536         // abstract...
34537     },
34538
34539     onRegionResized : function(region, newSize){
34540         this.fireEvent("regionresized", region, newSize);
34541         this.layout();
34542     },
34543
34544     onRegionCollapsed : function(region){
34545         this.fireEvent("regioncollapsed", region);
34546     },
34547
34548     onRegionExpanded : function(region){
34549         this.fireEvent("regionexpanded", region);
34550     },
34551
34552     /**
34553      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34554      * performs box-model adjustments.
34555      * @return {Object} The size as an object {width: (the width), height: (the height)}
34556      */
34557     getViewSize : function()
34558     {
34559         var size;
34560         if(this.el.dom != document.body){
34561             size = this.el.getSize();
34562         }else{
34563             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34564         }
34565         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34566         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34567         return size;
34568     },
34569
34570     /**
34571      * Returns the Element this layout is bound to.
34572      * @return {Roo.Element}
34573      */
34574     getEl : function(){
34575         return this.el;
34576     },
34577
34578     /**
34579      * Returns the specified region.
34580      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34581      * @return {Roo.LayoutRegion}
34582      */
34583     getRegion : function(target){
34584         return this.regions[target.toLowerCase()];
34585     },
34586
34587     onWindowResize : function(){
34588         if(this.monitorWindowResize){
34589             this.layout();
34590         }
34591     }
34592 });
34593 /*
34594  * Based on:
34595  * Ext JS Library 1.1.1
34596  * Copyright(c) 2006-2007, Ext JS, LLC.
34597  *
34598  * Originally Released Under LGPL - original licence link has changed is not relivant.
34599  *
34600  * Fork - LGPL
34601  * <script type="text/javascript">
34602  */
34603 /**
34604  * @class Roo.bootstrap.layout.Border
34605  * @extends Roo.bootstrap.layout.Manager
34606  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34607  * please see: examples/bootstrap/nested.html<br><br>
34608  
34609 <b>The container the layout is rendered into can be either the body element or any other element.
34610 If it is not the body element, the container needs to either be an absolute positioned element,
34611 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34612 the container size if it is not the body element.</b>
34613
34614 * @constructor
34615 * Create a new Border
34616 * @param {Object} config Configuration options
34617  */
34618 Roo.bootstrap.layout.Border = function(config){
34619     config = config || {};
34620     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34621     
34622     
34623     
34624     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34625         if(config[region]){
34626             config[region].region = region;
34627             this.addRegion(config[region]);
34628         }
34629     },this);
34630     
34631 };
34632
34633 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34634
34635 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34636     /**
34637      * Creates and adds a new region if it doesn't already exist.
34638      * @param {String} target The target region key (north, south, east, west or center).
34639      * @param {Object} config The regions config object
34640      * @return {BorderLayoutRegion} The new region
34641      */
34642     addRegion : function(config)
34643     {
34644         if(!this.regions[config.region]){
34645             var r = this.factory(config);
34646             this.bindRegion(r);
34647         }
34648         return this.regions[config.region];
34649     },
34650
34651     // private (kinda)
34652     bindRegion : function(r){
34653         this.regions[r.config.region] = r;
34654         
34655         r.on("visibilitychange",    this.layout, this);
34656         r.on("paneladded",          this.layout, this);
34657         r.on("panelremoved",        this.layout, this);
34658         r.on("invalidated",         this.layout, this);
34659         r.on("resized",             this.onRegionResized, this);
34660         r.on("collapsed",           this.onRegionCollapsed, this);
34661         r.on("expanded",            this.onRegionExpanded, this);
34662     },
34663
34664     /**
34665      * Performs a layout update.
34666      */
34667     layout : function()
34668     {
34669         if(this.updating) {
34670             return;
34671         }
34672         
34673         // render all the rebions if they have not been done alreayd?
34674         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34675             if(this.regions[region] && !this.regions[region].bodyEl){
34676                 this.regions[region].onRender(this.el)
34677             }
34678         },this);
34679         
34680         var size = this.getViewSize();
34681         var w = size.width;
34682         var h = size.height;
34683         var centerW = w;
34684         var centerH = h;
34685         var centerY = 0;
34686         var centerX = 0;
34687         //var x = 0, y = 0;
34688
34689         var rs = this.regions;
34690         var north = rs["north"];
34691         var south = rs["south"]; 
34692         var west = rs["west"];
34693         var east = rs["east"];
34694         var center = rs["center"];
34695         //if(this.hideOnLayout){ // not supported anymore
34696             //c.el.setStyle("display", "none");
34697         //}
34698         if(north && north.isVisible()){
34699             var b = north.getBox();
34700             var m = north.getMargins();
34701             b.width = w - (m.left+m.right);
34702             b.x = m.left;
34703             b.y = m.top;
34704             centerY = b.height + b.y + m.bottom;
34705             centerH -= centerY;
34706             north.updateBox(this.safeBox(b));
34707         }
34708         if(south && south.isVisible()){
34709             var b = south.getBox();
34710             var m = south.getMargins();
34711             b.width = w - (m.left+m.right);
34712             b.x = m.left;
34713             var totalHeight = (b.height + m.top + m.bottom);
34714             b.y = h - totalHeight + m.top;
34715             centerH -= totalHeight;
34716             south.updateBox(this.safeBox(b));
34717         }
34718         if(west && west.isVisible()){
34719             var b = west.getBox();
34720             var m = west.getMargins();
34721             b.height = centerH - (m.top+m.bottom);
34722             b.x = m.left;
34723             b.y = centerY + m.top;
34724             var totalWidth = (b.width + m.left + m.right);
34725             centerX += totalWidth;
34726             centerW -= totalWidth;
34727             west.updateBox(this.safeBox(b));
34728         }
34729         if(east && east.isVisible()){
34730             var b = east.getBox();
34731             var m = east.getMargins();
34732             b.height = centerH - (m.top+m.bottom);
34733             var totalWidth = (b.width + m.left + m.right);
34734             b.x = w - totalWidth + m.left;
34735             b.y = centerY + m.top;
34736             centerW -= totalWidth;
34737             east.updateBox(this.safeBox(b));
34738         }
34739         if(center){
34740             var m = center.getMargins();
34741             var centerBox = {
34742                 x: centerX + m.left,
34743                 y: centerY + m.top,
34744                 width: centerW - (m.left+m.right),
34745                 height: centerH - (m.top+m.bottom)
34746             };
34747             //if(this.hideOnLayout){
34748                 //center.el.setStyle("display", "block");
34749             //}
34750             center.updateBox(this.safeBox(centerBox));
34751         }
34752         this.el.repaint();
34753         this.fireEvent("layout", this);
34754     },
34755
34756     // private
34757     safeBox : function(box){
34758         box.width = Math.max(0, box.width);
34759         box.height = Math.max(0, box.height);
34760         return box;
34761     },
34762
34763     /**
34764      * Adds a ContentPanel (or subclass) to this layout.
34765      * @param {String} target The target region key (north, south, east, west or center).
34766      * @param {Roo.ContentPanel} panel The panel to add
34767      * @return {Roo.ContentPanel} The added panel
34768      */
34769     add : function(target, panel){
34770          
34771         target = target.toLowerCase();
34772         return this.regions[target].add(panel);
34773     },
34774
34775     /**
34776      * Remove a ContentPanel (or subclass) to this layout.
34777      * @param {String} target The target region key (north, south, east, west or center).
34778      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34779      * @return {Roo.ContentPanel} The removed panel
34780      */
34781     remove : function(target, panel){
34782         target = target.toLowerCase();
34783         return this.regions[target].remove(panel);
34784     },
34785
34786     /**
34787      * Searches all regions for a panel with the specified id
34788      * @param {String} panelId
34789      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34790      */
34791     findPanel : function(panelId){
34792         var rs = this.regions;
34793         for(var target in rs){
34794             if(typeof rs[target] != "function"){
34795                 var p = rs[target].getPanel(panelId);
34796                 if(p){
34797                     return p;
34798                 }
34799             }
34800         }
34801         return null;
34802     },
34803
34804     /**
34805      * Searches all regions for a panel with the specified id and activates (shows) it.
34806      * @param {String/ContentPanel} panelId The panels id or the panel itself
34807      * @return {Roo.ContentPanel} The shown panel or null
34808      */
34809     showPanel : function(panelId) {
34810       var rs = this.regions;
34811       for(var target in rs){
34812          var r = rs[target];
34813          if(typeof r != "function"){
34814             if(r.hasPanel(panelId)){
34815                return r.showPanel(panelId);
34816             }
34817          }
34818       }
34819       return null;
34820    },
34821
34822    /**
34823      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34824      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34825      */
34826    /*
34827     restoreState : function(provider){
34828         if(!provider){
34829             provider = Roo.state.Manager;
34830         }
34831         var sm = new Roo.LayoutStateManager();
34832         sm.init(this, provider);
34833     },
34834 */
34835  
34836  
34837     /**
34838      * Adds a xtype elements to the layout.
34839      * <pre><code>
34840
34841 layout.addxtype({
34842        xtype : 'ContentPanel',
34843        region: 'west',
34844        items: [ .... ]
34845    }
34846 );
34847
34848 layout.addxtype({
34849         xtype : 'NestedLayoutPanel',
34850         region: 'west',
34851         layout: {
34852            center: { },
34853            west: { }   
34854         },
34855         items : [ ... list of content panels or nested layout panels.. ]
34856    }
34857 );
34858 </code></pre>
34859      * @param {Object} cfg Xtype definition of item to add.
34860      */
34861     addxtype : function(cfg)
34862     {
34863         // basically accepts a pannel...
34864         // can accept a layout region..!?!?
34865         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34866         
34867         
34868         // theory?  children can only be panels??
34869         
34870         //if (!cfg.xtype.match(/Panel$/)) {
34871         //    return false;
34872         //}
34873         var ret = false;
34874         
34875         if (typeof(cfg.region) == 'undefined') {
34876             Roo.log("Failed to add Panel, region was not set");
34877             Roo.log(cfg);
34878             return false;
34879         }
34880         var region = cfg.region;
34881         delete cfg.region;
34882         
34883           
34884         var xitems = [];
34885         if (cfg.items) {
34886             xitems = cfg.items;
34887             delete cfg.items;
34888         }
34889         var nb = false;
34890         
34891         switch(cfg.xtype) 
34892         {
34893             case 'Content':  // ContentPanel (el, cfg)
34894             case 'Scroll':  // ContentPanel (el, cfg)
34895             case 'View': 
34896                 cfg.autoCreate = true;
34897                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34898                 //} else {
34899                 //    var el = this.el.createChild();
34900                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34901                 //}
34902                 
34903                 this.add(region, ret);
34904                 break;
34905             
34906             /*
34907             case 'TreePanel': // our new panel!
34908                 cfg.el = this.el.createChild();
34909                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34910                 this.add(region, ret);
34911                 break;
34912             */
34913             
34914             case 'Nest': 
34915                 // create a new Layout (which is  a Border Layout...
34916                 
34917                 var clayout = cfg.layout;
34918                 clayout.el  = this.el.createChild();
34919                 clayout.items   = clayout.items  || [];
34920                 
34921                 delete cfg.layout;
34922                 
34923                 // replace this exitems with the clayout ones..
34924                 xitems = clayout.items;
34925                  
34926                 // force background off if it's in center...
34927                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34928                     cfg.background = false;
34929                 }
34930                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34931                 
34932                 
34933                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34934                 //console.log('adding nested layout panel '  + cfg.toSource());
34935                 this.add(region, ret);
34936                 nb = {}; /// find first...
34937                 break;
34938             
34939             case 'Grid':
34940                 
34941                 // needs grid and region
34942                 
34943                 //var el = this.getRegion(region).el.createChild();
34944                 /*
34945                  *var el = this.el.createChild();
34946                 // create the grid first...
34947                 cfg.grid.container = el;
34948                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34949                 */
34950                 
34951                 if (region == 'center' && this.active ) {
34952                     cfg.background = false;
34953                 }
34954                 
34955                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34956                 
34957                 this.add(region, ret);
34958                 /*
34959                 if (cfg.background) {
34960                     // render grid on panel activation (if panel background)
34961                     ret.on('activate', function(gp) {
34962                         if (!gp.grid.rendered) {
34963                     //        gp.grid.render(el);
34964                         }
34965                     });
34966                 } else {
34967                   //  cfg.grid.render(el);
34968                 }
34969                 */
34970                 break;
34971            
34972            
34973             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34974                 // it was the old xcomponent building that caused this before.
34975                 // espeically if border is the top element in the tree.
34976                 ret = this;
34977                 break; 
34978                 
34979                     
34980                 
34981                 
34982                 
34983             default:
34984                 /*
34985                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34986                     
34987                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34988                     this.add(region, ret);
34989                 } else {
34990                 */
34991                     Roo.log(cfg);
34992                     throw "Can not add '" + cfg.xtype + "' to Border";
34993                     return null;
34994              
34995                                 
34996              
34997         }
34998         this.beginUpdate();
34999         // add children..
35000         var region = '';
35001         var abn = {};
35002         Roo.each(xitems, function(i)  {
35003             region = nb && i.region ? i.region : false;
35004             
35005             var add = ret.addxtype(i);
35006            
35007             if (region) {
35008                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35009                 if (!i.background) {
35010                     abn[region] = nb[region] ;
35011                 }
35012             }
35013             
35014         });
35015         this.endUpdate();
35016
35017         // make the last non-background panel active..
35018         //if (nb) { Roo.log(abn); }
35019         if (nb) {
35020             
35021             for(var r in abn) {
35022                 region = this.getRegion(r);
35023                 if (region) {
35024                     // tried using nb[r], but it does not work..
35025                      
35026                     region.showPanel(abn[r]);
35027                    
35028                 }
35029             }
35030         }
35031         return ret;
35032         
35033     },
35034     
35035     
35036 // private
35037     factory : function(cfg)
35038     {
35039         
35040         var validRegions = Roo.bootstrap.layout.Border.regions;
35041
35042         var target = cfg.region;
35043         cfg.mgr = this;
35044         
35045         var r = Roo.bootstrap.layout;
35046         Roo.log(target);
35047         switch(target){
35048             case "north":
35049                 return new r.North(cfg);
35050             case "south":
35051                 return new r.South(cfg);
35052             case "east":
35053                 return new r.East(cfg);
35054             case "west":
35055                 return new r.West(cfg);
35056             case "center":
35057                 return new r.Center(cfg);
35058         }
35059         throw 'Layout region "'+target+'" not supported.';
35060     }
35061     
35062     
35063 });
35064  /*
35065  * Based on:
35066  * Ext JS Library 1.1.1
35067  * Copyright(c) 2006-2007, Ext JS, LLC.
35068  *
35069  * Originally Released Under LGPL - original licence link has changed is not relivant.
35070  *
35071  * Fork - LGPL
35072  * <script type="text/javascript">
35073  */
35074  
35075 /**
35076  * @class Roo.bootstrap.layout.Basic
35077  * @extends Roo.util.Observable
35078  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35079  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35080  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35081  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35082  * @cfg {string}   region  the region that it inhabits..
35083  * @cfg {bool}   skipConfig skip config?
35084  * 
35085
35086  */
35087 Roo.bootstrap.layout.Basic = function(config){
35088     
35089     this.mgr = config.mgr;
35090     
35091     this.position = config.region;
35092     
35093     var skipConfig = config.skipConfig;
35094     
35095     this.events = {
35096         /**
35097          * @scope Roo.BasicLayoutRegion
35098          */
35099         
35100         /**
35101          * @event beforeremove
35102          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35103          * @param {Roo.LayoutRegion} this
35104          * @param {Roo.ContentPanel} panel The panel
35105          * @param {Object} e The cancel event object
35106          */
35107         "beforeremove" : true,
35108         /**
35109          * @event invalidated
35110          * Fires when the layout for this region is changed.
35111          * @param {Roo.LayoutRegion} this
35112          */
35113         "invalidated" : true,
35114         /**
35115          * @event visibilitychange
35116          * Fires when this region is shown or hidden 
35117          * @param {Roo.LayoutRegion} this
35118          * @param {Boolean} visibility true or false
35119          */
35120         "visibilitychange" : true,
35121         /**
35122          * @event paneladded
35123          * Fires when a panel is added. 
35124          * @param {Roo.LayoutRegion} this
35125          * @param {Roo.ContentPanel} panel The panel
35126          */
35127         "paneladded" : true,
35128         /**
35129          * @event panelremoved
35130          * Fires when a panel is removed. 
35131          * @param {Roo.LayoutRegion} this
35132          * @param {Roo.ContentPanel} panel The panel
35133          */
35134         "panelremoved" : true,
35135         /**
35136          * @event beforecollapse
35137          * Fires when this region before collapse.
35138          * @param {Roo.LayoutRegion} this
35139          */
35140         "beforecollapse" : true,
35141         /**
35142          * @event collapsed
35143          * Fires when this region is collapsed.
35144          * @param {Roo.LayoutRegion} this
35145          */
35146         "collapsed" : true,
35147         /**
35148          * @event expanded
35149          * Fires when this region is expanded.
35150          * @param {Roo.LayoutRegion} this
35151          */
35152         "expanded" : true,
35153         /**
35154          * @event slideshow
35155          * Fires when this region is slid into view.
35156          * @param {Roo.LayoutRegion} this
35157          */
35158         "slideshow" : true,
35159         /**
35160          * @event slidehide
35161          * Fires when this region slides out of view. 
35162          * @param {Roo.LayoutRegion} this
35163          */
35164         "slidehide" : true,
35165         /**
35166          * @event panelactivated
35167          * Fires when a panel is activated. 
35168          * @param {Roo.LayoutRegion} this
35169          * @param {Roo.ContentPanel} panel The activated panel
35170          */
35171         "panelactivated" : true,
35172         /**
35173          * @event resized
35174          * Fires when the user resizes this region. 
35175          * @param {Roo.LayoutRegion} this
35176          * @param {Number} newSize The new size (width for east/west, height for north/south)
35177          */
35178         "resized" : true
35179     };
35180     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35181     this.panels = new Roo.util.MixedCollection();
35182     this.panels.getKey = this.getPanelId.createDelegate(this);
35183     this.box = null;
35184     this.activePanel = null;
35185     // ensure listeners are added...
35186     
35187     if (config.listeners || config.events) {
35188         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35189             listeners : config.listeners || {},
35190             events : config.events || {}
35191         });
35192     }
35193     
35194     if(skipConfig !== true){
35195         this.applyConfig(config);
35196     }
35197 };
35198
35199 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35200 {
35201     getPanelId : function(p){
35202         return p.getId();
35203     },
35204     
35205     applyConfig : function(config){
35206         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35207         this.config = config;
35208         
35209     },
35210     
35211     /**
35212      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35213      * the width, for horizontal (north, south) the height.
35214      * @param {Number} newSize The new width or height
35215      */
35216     resizeTo : function(newSize){
35217         var el = this.el ? this.el :
35218                  (this.activePanel ? this.activePanel.getEl() : null);
35219         if(el){
35220             switch(this.position){
35221                 case "east":
35222                 case "west":
35223                     el.setWidth(newSize);
35224                     this.fireEvent("resized", this, newSize);
35225                 break;
35226                 case "north":
35227                 case "south":
35228                     el.setHeight(newSize);
35229                     this.fireEvent("resized", this, newSize);
35230                 break;                
35231             }
35232         }
35233     },
35234     
35235     getBox : function(){
35236         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35237     },
35238     
35239     getMargins : function(){
35240         return this.margins;
35241     },
35242     
35243     updateBox : function(box){
35244         this.box = box;
35245         var el = this.activePanel.getEl();
35246         el.dom.style.left = box.x + "px";
35247         el.dom.style.top = box.y + "px";
35248         this.activePanel.setSize(box.width, box.height);
35249     },
35250     
35251     /**
35252      * Returns the container element for this region.
35253      * @return {Roo.Element}
35254      */
35255     getEl : function(){
35256         return this.activePanel;
35257     },
35258     
35259     /**
35260      * Returns true if this region is currently visible.
35261      * @return {Boolean}
35262      */
35263     isVisible : function(){
35264         return this.activePanel ? true : false;
35265     },
35266     
35267     setActivePanel : function(panel){
35268         panel = this.getPanel(panel);
35269         if(this.activePanel && this.activePanel != panel){
35270             this.activePanel.setActiveState(false);
35271             this.activePanel.getEl().setLeftTop(-10000,-10000);
35272         }
35273         this.activePanel = panel;
35274         panel.setActiveState(true);
35275         if(this.box){
35276             panel.setSize(this.box.width, this.box.height);
35277         }
35278         this.fireEvent("panelactivated", this, panel);
35279         this.fireEvent("invalidated");
35280     },
35281     
35282     /**
35283      * Show the specified panel.
35284      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35285      * @return {Roo.ContentPanel} The shown panel or null
35286      */
35287     showPanel : function(panel){
35288         panel = this.getPanel(panel);
35289         if(panel){
35290             this.setActivePanel(panel);
35291         }
35292         return panel;
35293     },
35294     
35295     /**
35296      * Get the active panel for this region.
35297      * @return {Roo.ContentPanel} The active panel or null
35298      */
35299     getActivePanel : function(){
35300         return this.activePanel;
35301     },
35302     
35303     /**
35304      * Add the passed ContentPanel(s)
35305      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35306      * @return {Roo.ContentPanel} The panel added (if only one was added)
35307      */
35308     add : function(panel){
35309         if(arguments.length > 1){
35310             for(var i = 0, len = arguments.length; i < len; i++) {
35311                 this.add(arguments[i]);
35312             }
35313             return null;
35314         }
35315         if(this.hasPanel(panel)){
35316             this.showPanel(panel);
35317             return panel;
35318         }
35319         var el = panel.getEl();
35320         if(el.dom.parentNode != this.mgr.el.dom){
35321             this.mgr.el.dom.appendChild(el.dom);
35322         }
35323         if(panel.setRegion){
35324             panel.setRegion(this);
35325         }
35326         this.panels.add(panel);
35327         el.setStyle("position", "absolute");
35328         if(!panel.background){
35329             this.setActivePanel(panel);
35330             if(this.config.initialSize && this.panels.getCount()==1){
35331                 this.resizeTo(this.config.initialSize);
35332             }
35333         }
35334         this.fireEvent("paneladded", this, panel);
35335         return panel;
35336     },
35337     
35338     /**
35339      * Returns true if the panel is in this region.
35340      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35341      * @return {Boolean}
35342      */
35343     hasPanel : function(panel){
35344         if(typeof panel == "object"){ // must be panel obj
35345             panel = panel.getId();
35346         }
35347         return this.getPanel(panel) ? true : false;
35348     },
35349     
35350     /**
35351      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35352      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35353      * @param {Boolean} preservePanel Overrides the config preservePanel option
35354      * @return {Roo.ContentPanel} The panel that was removed
35355      */
35356     remove : function(panel, preservePanel){
35357         panel = this.getPanel(panel);
35358         if(!panel){
35359             return null;
35360         }
35361         var e = {};
35362         this.fireEvent("beforeremove", this, panel, e);
35363         if(e.cancel === true){
35364             return null;
35365         }
35366         var panelId = panel.getId();
35367         this.panels.removeKey(panelId);
35368         return panel;
35369     },
35370     
35371     /**
35372      * Returns the panel specified or null if it's not in this region.
35373      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35374      * @return {Roo.ContentPanel}
35375      */
35376     getPanel : function(id){
35377         if(typeof id == "object"){ // must be panel obj
35378             return id;
35379         }
35380         return this.panels.get(id);
35381     },
35382     
35383     /**
35384      * Returns this regions position (north/south/east/west/center).
35385      * @return {String} 
35386      */
35387     getPosition: function(){
35388         return this.position;    
35389     }
35390 });/*
35391  * Based on:
35392  * Ext JS Library 1.1.1
35393  * Copyright(c) 2006-2007, Ext JS, LLC.
35394  *
35395  * Originally Released Under LGPL - original licence link has changed is not relivant.
35396  *
35397  * Fork - LGPL
35398  * <script type="text/javascript">
35399  */
35400  
35401 /**
35402  * @class Roo.bootstrap.layout.Region
35403  * @extends Roo.bootstrap.layout.Basic
35404  * This class represents a region in a layout manager.
35405  
35406  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35407  * @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})
35408  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35409  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35410  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35411  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35412  * @cfg {String}    title           The title for the region (overrides panel titles)
35413  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35414  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35415  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35416  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35417  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35418  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35419  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35420  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35421  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35422  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35423
35424  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35425  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35426  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35427  * @cfg {Number}    width           For East/West panels
35428  * @cfg {Number}    height          For North/South panels
35429  * @cfg {Boolean}   split           To show the splitter
35430  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35431  * 
35432  * @cfg {string}   cls             Extra CSS classes to add to region
35433  * 
35434  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35435  * @cfg {string}   region  the region that it inhabits..
35436  *
35437
35438  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35439  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35440
35441  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35442  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35443  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35444  */
35445 Roo.bootstrap.layout.Region = function(config)
35446 {
35447     this.applyConfig(config);
35448
35449     var mgr = config.mgr;
35450     var pos = config.region;
35451     config.skipConfig = true;
35452     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35453     
35454     if (mgr.el) {
35455         this.onRender(mgr.el);   
35456     }
35457      
35458     this.visible = true;
35459     this.collapsed = false;
35460     this.unrendered_panels = [];
35461 };
35462
35463 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35464
35465     position: '', // set by wrapper (eg. north/south etc..)
35466     unrendered_panels : null,  // unrendered panels.
35467     createBody : function(){
35468         /** This region's body element 
35469         * @type Roo.Element */
35470         this.bodyEl = this.el.createChild({
35471                 tag: "div",
35472                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35473         });
35474     },
35475
35476     onRender: function(ctr, pos)
35477     {
35478         var dh = Roo.DomHelper;
35479         /** This region's container element 
35480         * @type Roo.Element */
35481         this.el = dh.append(ctr.dom, {
35482                 tag: "div",
35483                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35484             }, true);
35485         /** This region's title element 
35486         * @type Roo.Element */
35487     
35488         this.titleEl = dh.append(this.el.dom,
35489             {
35490                     tag: "div",
35491                     unselectable: "on",
35492                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35493                     children:[
35494                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35495                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35496                     ]}, true);
35497         
35498         this.titleEl.enableDisplayMode();
35499         /** This region's title text element 
35500         * @type HTMLElement */
35501         this.titleTextEl = this.titleEl.dom.firstChild;
35502         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35503         /*
35504         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35505         this.closeBtn.enableDisplayMode();
35506         this.closeBtn.on("click", this.closeClicked, this);
35507         this.closeBtn.hide();
35508     */
35509         this.createBody(this.config);
35510         if(this.config.hideWhenEmpty){
35511             this.hide();
35512             this.on("paneladded", this.validateVisibility, this);
35513             this.on("panelremoved", this.validateVisibility, this);
35514         }
35515         if(this.autoScroll){
35516             this.bodyEl.setStyle("overflow", "auto");
35517         }else{
35518             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35519         }
35520         //if(c.titlebar !== false){
35521             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35522                 this.titleEl.hide();
35523             }else{
35524                 this.titleEl.show();
35525                 if(this.config.title){
35526                     this.titleTextEl.innerHTML = this.config.title;
35527                 }
35528             }
35529         //}
35530         if(this.config.collapsed){
35531             this.collapse(true);
35532         }
35533         if(this.config.hidden){
35534             this.hide();
35535         }
35536         
35537         if (this.unrendered_panels && this.unrendered_panels.length) {
35538             for (var i =0;i< this.unrendered_panels.length; i++) {
35539                 this.add(this.unrendered_panels[i]);
35540             }
35541             this.unrendered_panels = null;
35542             
35543         }
35544         
35545     },
35546     
35547     applyConfig : function(c)
35548     {
35549         /*
35550          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35551             var dh = Roo.DomHelper;
35552             if(c.titlebar !== false){
35553                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35554                 this.collapseBtn.on("click", this.collapse, this);
35555                 this.collapseBtn.enableDisplayMode();
35556                 /*
35557                 if(c.showPin === true || this.showPin){
35558                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35559                     this.stickBtn.enableDisplayMode();
35560                     this.stickBtn.on("click", this.expand, this);
35561                     this.stickBtn.hide();
35562                 }
35563                 
35564             }
35565             */
35566             /** This region's collapsed element
35567             * @type Roo.Element */
35568             /*
35569              *
35570             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35571                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35572             ]}, true);
35573             
35574             if(c.floatable !== false){
35575                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35576                this.collapsedEl.on("click", this.collapseClick, this);
35577             }
35578
35579             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35580                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35581                    id: "message", unselectable: "on", style:{"float":"left"}});
35582                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35583              }
35584             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35585             this.expandBtn.on("click", this.expand, this);
35586             
35587         }
35588         
35589         if(this.collapseBtn){
35590             this.collapseBtn.setVisible(c.collapsible == true);
35591         }
35592         
35593         this.cmargins = c.cmargins || this.cmargins ||
35594                          (this.position == "west" || this.position == "east" ?
35595                              {top: 0, left: 2, right:2, bottom: 0} :
35596                              {top: 2, left: 0, right:0, bottom: 2});
35597         */
35598         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35599         
35600         
35601         this.bottomTabs = c.tabPosition != "top";
35602         
35603         this.autoScroll = c.autoScroll || false;
35604         
35605         
35606        
35607         
35608         this.duration = c.duration || .30;
35609         this.slideDuration = c.slideDuration || .45;
35610         this.config = c;
35611        
35612     },
35613     /**
35614      * Returns true if this region is currently visible.
35615      * @return {Boolean}
35616      */
35617     isVisible : function(){
35618         return this.visible;
35619     },
35620
35621     /**
35622      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35623      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35624      */
35625     //setCollapsedTitle : function(title){
35626     //    title = title || "&#160;";
35627      //   if(this.collapsedTitleTextEl){
35628       //      this.collapsedTitleTextEl.innerHTML = title;
35629        // }
35630     //},
35631
35632     getBox : function(){
35633         var b;
35634       //  if(!this.collapsed){
35635             b = this.el.getBox(false, true);
35636        // }else{
35637           //  b = this.collapsedEl.getBox(false, true);
35638         //}
35639         return b;
35640     },
35641
35642     getMargins : function(){
35643         return this.margins;
35644         //return this.collapsed ? this.cmargins : this.margins;
35645     },
35646 /*
35647     highlight : function(){
35648         this.el.addClass("x-layout-panel-dragover");
35649     },
35650
35651     unhighlight : function(){
35652         this.el.removeClass("x-layout-panel-dragover");
35653     },
35654 */
35655     updateBox : function(box)
35656     {
35657         if (!this.bodyEl) {
35658             return; // not rendered yet..
35659         }
35660         
35661         this.box = box;
35662         if(!this.collapsed){
35663             this.el.dom.style.left = box.x + "px";
35664             this.el.dom.style.top = box.y + "px";
35665             this.updateBody(box.width, box.height);
35666         }else{
35667             this.collapsedEl.dom.style.left = box.x + "px";
35668             this.collapsedEl.dom.style.top = box.y + "px";
35669             this.collapsedEl.setSize(box.width, box.height);
35670         }
35671         if(this.tabs){
35672             this.tabs.autoSizeTabs();
35673         }
35674     },
35675
35676     updateBody : function(w, h)
35677     {
35678         if(w !== null){
35679             this.el.setWidth(w);
35680             w -= this.el.getBorderWidth("rl");
35681             if(this.config.adjustments){
35682                 w += this.config.adjustments[0];
35683             }
35684         }
35685         if(h !== null && h > 0){
35686             this.el.setHeight(h);
35687             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35688             h -= this.el.getBorderWidth("tb");
35689             if(this.config.adjustments){
35690                 h += this.config.adjustments[1];
35691             }
35692             this.bodyEl.setHeight(h);
35693             if(this.tabs){
35694                 h = this.tabs.syncHeight(h);
35695             }
35696         }
35697         if(this.panelSize){
35698             w = w !== null ? w : this.panelSize.width;
35699             h = h !== null ? h : this.panelSize.height;
35700         }
35701         if(this.activePanel){
35702             var el = this.activePanel.getEl();
35703             w = w !== null ? w : el.getWidth();
35704             h = h !== null ? h : el.getHeight();
35705             this.panelSize = {width: w, height: h};
35706             this.activePanel.setSize(w, h);
35707         }
35708         if(Roo.isIE && this.tabs){
35709             this.tabs.el.repaint();
35710         }
35711     },
35712
35713     /**
35714      * Returns the container element for this region.
35715      * @return {Roo.Element}
35716      */
35717     getEl : function(){
35718         return this.el;
35719     },
35720
35721     /**
35722      * Hides this region.
35723      */
35724     hide : function(){
35725         //if(!this.collapsed){
35726             this.el.dom.style.left = "-2000px";
35727             this.el.hide();
35728         //}else{
35729          //   this.collapsedEl.dom.style.left = "-2000px";
35730          //   this.collapsedEl.hide();
35731        // }
35732         this.visible = false;
35733         this.fireEvent("visibilitychange", this, false);
35734     },
35735
35736     /**
35737      * Shows this region if it was previously hidden.
35738      */
35739     show : function(){
35740         //if(!this.collapsed){
35741             this.el.show();
35742         //}else{
35743         //    this.collapsedEl.show();
35744        // }
35745         this.visible = true;
35746         this.fireEvent("visibilitychange", this, true);
35747     },
35748 /*
35749     closeClicked : function(){
35750         if(this.activePanel){
35751             this.remove(this.activePanel);
35752         }
35753     },
35754
35755     collapseClick : function(e){
35756         if(this.isSlid){
35757            e.stopPropagation();
35758            this.slideIn();
35759         }else{
35760            e.stopPropagation();
35761            this.slideOut();
35762         }
35763     },
35764 */
35765     /**
35766      * Collapses this region.
35767      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35768      */
35769     /*
35770     collapse : function(skipAnim, skipCheck = false){
35771         if(this.collapsed) {
35772             return;
35773         }
35774         
35775         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35776             
35777             this.collapsed = true;
35778             if(this.split){
35779                 this.split.el.hide();
35780             }
35781             if(this.config.animate && skipAnim !== true){
35782                 this.fireEvent("invalidated", this);
35783                 this.animateCollapse();
35784             }else{
35785                 this.el.setLocation(-20000,-20000);
35786                 this.el.hide();
35787                 this.collapsedEl.show();
35788                 this.fireEvent("collapsed", this);
35789                 this.fireEvent("invalidated", this);
35790             }
35791         }
35792         
35793     },
35794 */
35795     animateCollapse : function(){
35796         // overridden
35797     },
35798
35799     /**
35800      * Expands this region if it was previously collapsed.
35801      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35802      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35803      */
35804     /*
35805     expand : function(e, skipAnim){
35806         if(e) {
35807             e.stopPropagation();
35808         }
35809         if(!this.collapsed || this.el.hasActiveFx()) {
35810             return;
35811         }
35812         if(this.isSlid){
35813             this.afterSlideIn();
35814             skipAnim = true;
35815         }
35816         this.collapsed = false;
35817         if(this.config.animate && skipAnim !== true){
35818             this.animateExpand();
35819         }else{
35820             this.el.show();
35821             if(this.split){
35822                 this.split.el.show();
35823             }
35824             this.collapsedEl.setLocation(-2000,-2000);
35825             this.collapsedEl.hide();
35826             this.fireEvent("invalidated", this);
35827             this.fireEvent("expanded", this);
35828         }
35829     },
35830 */
35831     animateExpand : function(){
35832         // overridden
35833     },
35834
35835     initTabs : function()
35836     {
35837         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35838         
35839         var ts = new Roo.bootstrap.panel.Tabs({
35840                 el: this.bodyEl.dom,
35841                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35842                 disableTooltips: this.config.disableTabTips,
35843                 toolbar : this.config.toolbar
35844             });
35845         
35846         if(this.config.hideTabs){
35847             ts.stripWrap.setDisplayed(false);
35848         }
35849         this.tabs = ts;
35850         ts.resizeTabs = this.config.resizeTabs === true;
35851         ts.minTabWidth = this.config.minTabWidth || 40;
35852         ts.maxTabWidth = this.config.maxTabWidth || 250;
35853         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35854         ts.monitorResize = false;
35855         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35856         ts.bodyEl.addClass('roo-layout-tabs-body');
35857         this.panels.each(this.initPanelAsTab, this);
35858     },
35859
35860     initPanelAsTab : function(panel){
35861         var ti = this.tabs.addTab(
35862             panel.getEl().id,
35863             panel.getTitle(),
35864             null,
35865             this.config.closeOnTab && panel.isClosable(),
35866             panel.tpl
35867         );
35868         if(panel.tabTip !== undefined){
35869             ti.setTooltip(panel.tabTip);
35870         }
35871         ti.on("activate", function(){
35872               this.setActivePanel(panel);
35873         }, this);
35874         
35875         if(this.config.closeOnTab){
35876             ti.on("beforeclose", function(t, e){
35877                 e.cancel = true;
35878                 this.remove(panel);
35879             }, this);
35880         }
35881         
35882         panel.tabItem = ti;
35883         
35884         return ti;
35885     },
35886
35887     updatePanelTitle : function(panel, title)
35888     {
35889         if(this.activePanel == panel){
35890             this.updateTitle(title);
35891         }
35892         if(this.tabs){
35893             var ti = this.tabs.getTab(panel.getEl().id);
35894             ti.setText(title);
35895             if(panel.tabTip !== undefined){
35896                 ti.setTooltip(panel.tabTip);
35897             }
35898         }
35899     },
35900
35901     updateTitle : function(title){
35902         if(this.titleTextEl && !this.config.title){
35903             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35904         }
35905     },
35906
35907     setActivePanel : function(panel)
35908     {
35909         panel = this.getPanel(panel);
35910         if(this.activePanel && this.activePanel != panel){
35911             if(this.activePanel.setActiveState(false) === false){
35912                 return;
35913             }
35914         }
35915         this.activePanel = panel;
35916         panel.setActiveState(true);
35917         if(this.panelSize){
35918             panel.setSize(this.panelSize.width, this.panelSize.height);
35919         }
35920         if(this.closeBtn){
35921             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35922         }
35923         this.updateTitle(panel.getTitle());
35924         if(this.tabs){
35925             this.fireEvent("invalidated", this);
35926         }
35927         this.fireEvent("panelactivated", this, panel);
35928     },
35929
35930     /**
35931      * Shows the specified panel.
35932      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35933      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35934      */
35935     showPanel : function(panel)
35936     {
35937         panel = this.getPanel(panel);
35938         if(panel){
35939             if(this.tabs){
35940                 var tab = this.tabs.getTab(panel.getEl().id);
35941                 if(tab.isHidden()){
35942                     this.tabs.unhideTab(tab.id);
35943                 }
35944                 tab.activate();
35945             }else{
35946                 this.setActivePanel(panel);
35947             }
35948         }
35949         return panel;
35950     },
35951
35952     /**
35953      * Get the active panel for this region.
35954      * @return {Roo.ContentPanel} The active panel or null
35955      */
35956     getActivePanel : function(){
35957         return this.activePanel;
35958     },
35959
35960     validateVisibility : function(){
35961         if(this.panels.getCount() < 1){
35962             this.updateTitle("&#160;");
35963             this.closeBtn.hide();
35964             this.hide();
35965         }else{
35966             if(!this.isVisible()){
35967                 this.show();
35968             }
35969         }
35970     },
35971
35972     /**
35973      * Adds the passed ContentPanel(s) to this region.
35974      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35975      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35976      */
35977     add : function(panel)
35978     {
35979         if(arguments.length > 1){
35980             for(var i = 0, len = arguments.length; i < len; i++) {
35981                 this.add(arguments[i]);
35982             }
35983             return null;
35984         }
35985         
35986         // if we have not been rendered yet, then we can not really do much of this..
35987         if (!this.bodyEl) {
35988             this.unrendered_panels.push(panel);
35989             return panel;
35990         }
35991         
35992         
35993         
35994         
35995         if(this.hasPanel(panel)){
35996             this.showPanel(panel);
35997             return panel;
35998         }
35999         panel.setRegion(this);
36000         this.panels.add(panel);
36001        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36002             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36003             // and hide them... ???
36004             this.bodyEl.dom.appendChild(panel.getEl().dom);
36005             if(panel.background !== true){
36006                 this.setActivePanel(panel);
36007             }
36008             this.fireEvent("paneladded", this, panel);
36009             return panel;
36010         }
36011         */
36012         if(!this.tabs){
36013             this.initTabs();
36014         }else{
36015             this.initPanelAsTab(panel);
36016         }
36017         
36018         
36019         if(panel.background !== true){
36020             this.tabs.activate(panel.getEl().id);
36021         }
36022         this.fireEvent("paneladded", this, panel);
36023         return panel;
36024     },
36025
36026     /**
36027      * Hides the tab for the specified panel.
36028      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36029      */
36030     hidePanel : function(panel){
36031         if(this.tabs && (panel = this.getPanel(panel))){
36032             this.tabs.hideTab(panel.getEl().id);
36033         }
36034     },
36035
36036     /**
36037      * Unhides the tab for a previously hidden panel.
36038      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36039      */
36040     unhidePanel : function(panel){
36041         if(this.tabs && (panel = this.getPanel(panel))){
36042             this.tabs.unhideTab(panel.getEl().id);
36043         }
36044     },
36045
36046     clearPanels : function(){
36047         while(this.panels.getCount() > 0){
36048              this.remove(this.panels.first());
36049         }
36050     },
36051
36052     /**
36053      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36054      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36055      * @param {Boolean} preservePanel Overrides the config preservePanel option
36056      * @return {Roo.ContentPanel} The panel that was removed
36057      */
36058     remove : function(panel, preservePanel)
36059     {
36060         panel = this.getPanel(panel);
36061         if(!panel){
36062             return null;
36063         }
36064         var e = {};
36065         this.fireEvent("beforeremove", this, panel, e);
36066         if(e.cancel === true){
36067             return null;
36068         }
36069         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36070         var panelId = panel.getId();
36071         this.panels.removeKey(panelId);
36072         if(preservePanel){
36073             document.body.appendChild(panel.getEl().dom);
36074         }
36075         if(this.tabs){
36076             this.tabs.removeTab(panel.getEl().id);
36077         }else if (!preservePanel){
36078             this.bodyEl.dom.removeChild(panel.getEl().dom);
36079         }
36080         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36081             var p = this.panels.first();
36082             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36083             tempEl.appendChild(p.getEl().dom);
36084             this.bodyEl.update("");
36085             this.bodyEl.dom.appendChild(p.getEl().dom);
36086             tempEl = null;
36087             this.updateTitle(p.getTitle());
36088             this.tabs = null;
36089             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36090             this.setActivePanel(p);
36091         }
36092         panel.setRegion(null);
36093         if(this.activePanel == panel){
36094             this.activePanel = null;
36095         }
36096         if(this.config.autoDestroy !== false && preservePanel !== true){
36097             try{panel.destroy();}catch(e){}
36098         }
36099         this.fireEvent("panelremoved", this, panel);
36100         return panel;
36101     },
36102
36103     /**
36104      * Returns the TabPanel component used by this region
36105      * @return {Roo.TabPanel}
36106      */
36107     getTabs : function(){
36108         return this.tabs;
36109     },
36110
36111     createTool : function(parentEl, className){
36112         var btn = Roo.DomHelper.append(parentEl, {
36113             tag: "div",
36114             cls: "x-layout-tools-button",
36115             children: [ {
36116                 tag: "div",
36117                 cls: "roo-layout-tools-button-inner " + className,
36118                 html: "&#160;"
36119             }]
36120         }, true);
36121         btn.addClassOnOver("roo-layout-tools-button-over");
36122         return btn;
36123     }
36124 });/*
36125  * Based on:
36126  * Ext JS Library 1.1.1
36127  * Copyright(c) 2006-2007, Ext JS, LLC.
36128  *
36129  * Originally Released Under LGPL - original licence link has changed is not relivant.
36130  *
36131  * Fork - LGPL
36132  * <script type="text/javascript">
36133  */
36134  
36135
36136
36137 /**
36138  * @class Roo.SplitLayoutRegion
36139  * @extends Roo.LayoutRegion
36140  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36141  */
36142 Roo.bootstrap.layout.Split = function(config){
36143     this.cursor = config.cursor;
36144     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36145 };
36146
36147 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36148 {
36149     splitTip : "Drag to resize.",
36150     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36151     useSplitTips : false,
36152
36153     applyConfig : function(config){
36154         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36155     },
36156     
36157     onRender : function(ctr,pos) {
36158         
36159         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36160         if(!this.config.split){
36161             return;
36162         }
36163         if(!this.split){
36164             
36165             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36166                             tag: "div",
36167                             id: this.el.id + "-split",
36168                             cls: "roo-layout-split roo-layout-split-"+this.position,
36169                             html: "&#160;"
36170             });
36171             /** The SplitBar for this region 
36172             * @type Roo.SplitBar */
36173             // does not exist yet...
36174             Roo.log([this.position, this.orientation]);
36175             
36176             this.split = new Roo.bootstrap.SplitBar({
36177                 dragElement : splitEl,
36178                 resizingElement: this.el,
36179                 orientation : this.orientation
36180             });
36181             
36182             this.split.on("moved", this.onSplitMove, this);
36183             this.split.useShim = this.config.useShim === true;
36184             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36185             if(this.useSplitTips){
36186                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36187             }
36188             //if(config.collapsible){
36189             //    this.split.el.on("dblclick", this.collapse,  this);
36190             //}
36191         }
36192         if(typeof this.config.minSize != "undefined"){
36193             this.split.minSize = this.config.minSize;
36194         }
36195         if(typeof this.config.maxSize != "undefined"){
36196             this.split.maxSize = this.config.maxSize;
36197         }
36198         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36199             this.hideSplitter();
36200         }
36201         
36202     },
36203
36204     getHMaxSize : function(){
36205          var cmax = this.config.maxSize || 10000;
36206          var center = this.mgr.getRegion("center");
36207          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36208     },
36209
36210     getVMaxSize : function(){
36211          var cmax = this.config.maxSize || 10000;
36212          var center = this.mgr.getRegion("center");
36213          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36214     },
36215
36216     onSplitMove : function(split, newSize){
36217         this.fireEvent("resized", this, newSize);
36218     },
36219     
36220     /** 
36221      * Returns the {@link Roo.SplitBar} for this region.
36222      * @return {Roo.SplitBar}
36223      */
36224     getSplitBar : function(){
36225         return this.split;
36226     },
36227     
36228     hide : function(){
36229         this.hideSplitter();
36230         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36231     },
36232
36233     hideSplitter : function(){
36234         if(this.split){
36235             this.split.el.setLocation(-2000,-2000);
36236             this.split.el.hide();
36237         }
36238     },
36239
36240     show : function(){
36241         if(this.split){
36242             this.split.el.show();
36243         }
36244         Roo.bootstrap.layout.Split.superclass.show.call(this);
36245     },
36246     
36247     beforeSlide: function(){
36248         if(Roo.isGecko){// firefox overflow auto bug workaround
36249             this.bodyEl.clip();
36250             if(this.tabs) {
36251                 this.tabs.bodyEl.clip();
36252             }
36253             if(this.activePanel){
36254                 this.activePanel.getEl().clip();
36255                 
36256                 if(this.activePanel.beforeSlide){
36257                     this.activePanel.beforeSlide();
36258                 }
36259             }
36260         }
36261     },
36262     
36263     afterSlide : function(){
36264         if(Roo.isGecko){// firefox overflow auto bug workaround
36265             this.bodyEl.unclip();
36266             if(this.tabs) {
36267                 this.tabs.bodyEl.unclip();
36268             }
36269             if(this.activePanel){
36270                 this.activePanel.getEl().unclip();
36271                 if(this.activePanel.afterSlide){
36272                     this.activePanel.afterSlide();
36273                 }
36274             }
36275         }
36276     },
36277
36278     initAutoHide : function(){
36279         if(this.autoHide !== false){
36280             if(!this.autoHideHd){
36281                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36282                 this.autoHideHd = {
36283                     "mouseout": function(e){
36284                         if(!e.within(this.el, true)){
36285                             st.delay(500);
36286                         }
36287                     },
36288                     "mouseover" : function(e){
36289                         st.cancel();
36290                     },
36291                     scope : this
36292                 };
36293             }
36294             this.el.on(this.autoHideHd);
36295         }
36296     },
36297
36298     clearAutoHide : function(){
36299         if(this.autoHide !== false){
36300             this.el.un("mouseout", this.autoHideHd.mouseout);
36301             this.el.un("mouseover", this.autoHideHd.mouseover);
36302         }
36303     },
36304
36305     clearMonitor : function(){
36306         Roo.get(document).un("click", this.slideInIf, this);
36307     },
36308
36309     // these names are backwards but not changed for compat
36310     slideOut : function(){
36311         if(this.isSlid || this.el.hasActiveFx()){
36312             return;
36313         }
36314         this.isSlid = true;
36315         if(this.collapseBtn){
36316             this.collapseBtn.hide();
36317         }
36318         this.closeBtnState = this.closeBtn.getStyle('display');
36319         this.closeBtn.hide();
36320         if(this.stickBtn){
36321             this.stickBtn.show();
36322         }
36323         this.el.show();
36324         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36325         this.beforeSlide();
36326         this.el.setStyle("z-index", 10001);
36327         this.el.slideIn(this.getSlideAnchor(), {
36328             callback: function(){
36329                 this.afterSlide();
36330                 this.initAutoHide();
36331                 Roo.get(document).on("click", this.slideInIf, this);
36332                 this.fireEvent("slideshow", this);
36333             },
36334             scope: this,
36335             block: true
36336         });
36337     },
36338
36339     afterSlideIn : function(){
36340         this.clearAutoHide();
36341         this.isSlid = false;
36342         this.clearMonitor();
36343         this.el.setStyle("z-index", "");
36344         if(this.collapseBtn){
36345             this.collapseBtn.show();
36346         }
36347         this.closeBtn.setStyle('display', this.closeBtnState);
36348         if(this.stickBtn){
36349             this.stickBtn.hide();
36350         }
36351         this.fireEvent("slidehide", this);
36352     },
36353
36354     slideIn : function(cb){
36355         if(!this.isSlid || this.el.hasActiveFx()){
36356             Roo.callback(cb);
36357             return;
36358         }
36359         this.isSlid = false;
36360         this.beforeSlide();
36361         this.el.slideOut(this.getSlideAnchor(), {
36362             callback: function(){
36363                 this.el.setLeftTop(-10000, -10000);
36364                 this.afterSlide();
36365                 this.afterSlideIn();
36366                 Roo.callback(cb);
36367             },
36368             scope: this,
36369             block: true
36370         });
36371     },
36372     
36373     slideInIf : function(e){
36374         if(!e.within(this.el)){
36375             this.slideIn();
36376         }
36377     },
36378
36379     animateCollapse : function(){
36380         this.beforeSlide();
36381         this.el.setStyle("z-index", 20000);
36382         var anchor = this.getSlideAnchor();
36383         this.el.slideOut(anchor, {
36384             callback : function(){
36385                 this.el.setStyle("z-index", "");
36386                 this.collapsedEl.slideIn(anchor, {duration:.3});
36387                 this.afterSlide();
36388                 this.el.setLocation(-10000,-10000);
36389                 this.el.hide();
36390                 this.fireEvent("collapsed", this);
36391             },
36392             scope: this,
36393             block: true
36394         });
36395     },
36396
36397     animateExpand : function(){
36398         this.beforeSlide();
36399         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36400         this.el.setStyle("z-index", 20000);
36401         this.collapsedEl.hide({
36402             duration:.1
36403         });
36404         this.el.slideIn(this.getSlideAnchor(), {
36405             callback : function(){
36406                 this.el.setStyle("z-index", "");
36407                 this.afterSlide();
36408                 if(this.split){
36409                     this.split.el.show();
36410                 }
36411                 this.fireEvent("invalidated", this);
36412                 this.fireEvent("expanded", this);
36413             },
36414             scope: this,
36415             block: true
36416         });
36417     },
36418
36419     anchors : {
36420         "west" : "left",
36421         "east" : "right",
36422         "north" : "top",
36423         "south" : "bottom"
36424     },
36425
36426     sanchors : {
36427         "west" : "l",
36428         "east" : "r",
36429         "north" : "t",
36430         "south" : "b"
36431     },
36432
36433     canchors : {
36434         "west" : "tl-tr",
36435         "east" : "tr-tl",
36436         "north" : "tl-bl",
36437         "south" : "bl-tl"
36438     },
36439
36440     getAnchor : function(){
36441         return this.anchors[this.position];
36442     },
36443
36444     getCollapseAnchor : function(){
36445         return this.canchors[this.position];
36446     },
36447
36448     getSlideAnchor : function(){
36449         return this.sanchors[this.position];
36450     },
36451
36452     getAlignAdj : function(){
36453         var cm = this.cmargins;
36454         switch(this.position){
36455             case "west":
36456                 return [0, 0];
36457             break;
36458             case "east":
36459                 return [0, 0];
36460             break;
36461             case "north":
36462                 return [0, 0];
36463             break;
36464             case "south":
36465                 return [0, 0];
36466             break;
36467         }
36468     },
36469
36470     getExpandAdj : function(){
36471         var c = this.collapsedEl, cm = this.cmargins;
36472         switch(this.position){
36473             case "west":
36474                 return [-(cm.right+c.getWidth()+cm.left), 0];
36475             break;
36476             case "east":
36477                 return [cm.right+c.getWidth()+cm.left, 0];
36478             break;
36479             case "north":
36480                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36481             break;
36482             case "south":
36483                 return [0, cm.top+cm.bottom+c.getHeight()];
36484             break;
36485         }
36486     }
36487 });/*
36488  * Based on:
36489  * Ext JS Library 1.1.1
36490  * Copyright(c) 2006-2007, Ext JS, LLC.
36491  *
36492  * Originally Released Under LGPL - original licence link has changed is not relivant.
36493  *
36494  * Fork - LGPL
36495  * <script type="text/javascript">
36496  */
36497 /*
36498  * These classes are private internal classes
36499  */
36500 Roo.bootstrap.layout.Center = function(config){
36501     config.region = "center";
36502     Roo.bootstrap.layout.Region.call(this, config);
36503     this.visible = true;
36504     this.minWidth = config.minWidth || 20;
36505     this.minHeight = config.minHeight || 20;
36506 };
36507
36508 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36509     hide : function(){
36510         // center panel can't be hidden
36511     },
36512     
36513     show : function(){
36514         // center panel can't be hidden
36515     },
36516     
36517     getMinWidth: function(){
36518         return this.minWidth;
36519     },
36520     
36521     getMinHeight: function(){
36522         return this.minHeight;
36523     }
36524 });
36525
36526
36527
36528
36529  
36530
36531
36532
36533
36534
36535 Roo.bootstrap.layout.North = function(config)
36536 {
36537     config.region = 'north';
36538     config.cursor = 'n-resize';
36539     
36540     Roo.bootstrap.layout.Split.call(this, config);
36541     
36542     
36543     if(this.split){
36544         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36545         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36546         this.split.el.addClass("roo-layout-split-v");
36547     }
36548     var size = config.initialSize || config.height;
36549     if(typeof size != "undefined"){
36550         this.el.setHeight(size);
36551     }
36552 };
36553 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36554 {
36555     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36556     
36557     
36558     
36559     getBox : function(){
36560         if(this.collapsed){
36561             return this.collapsedEl.getBox();
36562         }
36563         var box = this.el.getBox();
36564         if(this.split){
36565             box.height += this.split.el.getHeight();
36566         }
36567         return box;
36568     },
36569     
36570     updateBox : function(box){
36571         if(this.split && !this.collapsed){
36572             box.height -= this.split.el.getHeight();
36573             this.split.el.setLeft(box.x);
36574             this.split.el.setTop(box.y+box.height);
36575             this.split.el.setWidth(box.width);
36576         }
36577         if(this.collapsed){
36578             this.updateBody(box.width, null);
36579         }
36580         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36581     }
36582 });
36583
36584
36585
36586
36587
36588 Roo.bootstrap.layout.South = function(config){
36589     config.region = 'south';
36590     config.cursor = 's-resize';
36591     Roo.bootstrap.layout.Split.call(this, config);
36592     if(this.split){
36593         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36594         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36595         this.split.el.addClass("roo-layout-split-v");
36596     }
36597     var size = config.initialSize || config.height;
36598     if(typeof size != "undefined"){
36599         this.el.setHeight(size);
36600     }
36601 };
36602
36603 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36604     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36605     getBox : function(){
36606         if(this.collapsed){
36607             return this.collapsedEl.getBox();
36608         }
36609         var box = this.el.getBox();
36610         if(this.split){
36611             var sh = this.split.el.getHeight();
36612             box.height += sh;
36613             box.y -= sh;
36614         }
36615         return box;
36616     },
36617     
36618     updateBox : function(box){
36619         if(this.split && !this.collapsed){
36620             var sh = this.split.el.getHeight();
36621             box.height -= sh;
36622             box.y += sh;
36623             this.split.el.setLeft(box.x);
36624             this.split.el.setTop(box.y-sh);
36625             this.split.el.setWidth(box.width);
36626         }
36627         if(this.collapsed){
36628             this.updateBody(box.width, null);
36629         }
36630         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36631     }
36632 });
36633
36634 Roo.bootstrap.layout.East = function(config){
36635     config.region = "east";
36636     config.cursor = "e-resize";
36637     Roo.bootstrap.layout.Split.call(this, config);
36638     if(this.split){
36639         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36640         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36641         this.split.el.addClass("roo-layout-split-h");
36642     }
36643     var size = config.initialSize || config.width;
36644     if(typeof size != "undefined"){
36645         this.el.setWidth(size);
36646     }
36647 };
36648 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36649     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36650     getBox : function(){
36651         if(this.collapsed){
36652             return this.collapsedEl.getBox();
36653         }
36654         var box = this.el.getBox();
36655         if(this.split){
36656             var sw = this.split.el.getWidth();
36657             box.width += sw;
36658             box.x -= sw;
36659         }
36660         return box;
36661     },
36662
36663     updateBox : function(box){
36664         if(this.split && !this.collapsed){
36665             var sw = this.split.el.getWidth();
36666             box.width -= sw;
36667             this.split.el.setLeft(box.x);
36668             this.split.el.setTop(box.y);
36669             this.split.el.setHeight(box.height);
36670             box.x += sw;
36671         }
36672         if(this.collapsed){
36673             this.updateBody(null, box.height);
36674         }
36675         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36676     }
36677 });
36678
36679 Roo.bootstrap.layout.West = function(config){
36680     config.region = "west";
36681     config.cursor = "w-resize";
36682     
36683     Roo.bootstrap.layout.Split.call(this, config);
36684     if(this.split){
36685         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36686         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36687         this.split.el.addClass("roo-layout-split-h");
36688     }
36689     
36690 };
36691 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36692     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36693     
36694     onRender: function(ctr, pos)
36695     {
36696         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36697         var size = this.config.initialSize || this.config.width;
36698         if(typeof size != "undefined"){
36699             this.el.setWidth(size);
36700         }
36701     },
36702     
36703     getBox : function(){
36704         if(this.collapsed){
36705             return this.collapsedEl.getBox();
36706         }
36707         var box = this.el.getBox();
36708         if(this.split){
36709             box.width += this.split.el.getWidth();
36710         }
36711         return box;
36712     },
36713     
36714     updateBox : function(box){
36715         if(this.split && !this.collapsed){
36716             var sw = this.split.el.getWidth();
36717             box.width -= sw;
36718             this.split.el.setLeft(box.x+box.width);
36719             this.split.el.setTop(box.y);
36720             this.split.el.setHeight(box.height);
36721         }
36722         if(this.collapsed){
36723             this.updateBody(null, box.height);
36724         }
36725         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36726     }
36727 });
36728 Roo.namespace("Roo.bootstrap.panel");/*
36729  * Based on:
36730  * Ext JS Library 1.1.1
36731  * Copyright(c) 2006-2007, Ext JS, LLC.
36732  *
36733  * Originally Released Under LGPL - original licence link has changed is not relivant.
36734  *
36735  * Fork - LGPL
36736  * <script type="text/javascript">
36737  */
36738 /**
36739  * @class Roo.ContentPanel
36740  * @extends Roo.util.Observable
36741  * A basic ContentPanel element.
36742  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36743  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36744  * @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
36745  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36746  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36747  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36748  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36749  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36750  * @cfg {String} title          The title for this panel
36751  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36752  * @cfg {String} url            Calls {@link #setUrl} with this value
36753  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36754  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36755  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36756  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36757  * @cfg {Boolean} badges render the badges
36758
36759  * @constructor
36760  * Create a new ContentPanel.
36761  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36762  * @param {String/Object} config A string to set only the title or a config object
36763  * @param {String} content (optional) Set the HTML content for this panel
36764  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36765  */
36766 Roo.bootstrap.panel.Content = function( config){
36767     
36768     this.tpl = config.tpl || false;
36769     
36770     var el = config.el;
36771     var content = config.content;
36772
36773     if(config.autoCreate){ // xtype is available if this is called from factory
36774         el = Roo.id();
36775     }
36776     this.el = Roo.get(el);
36777     if(!this.el && config && config.autoCreate){
36778         if(typeof config.autoCreate == "object"){
36779             if(!config.autoCreate.id){
36780                 config.autoCreate.id = config.id||el;
36781             }
36782             this.el = Roo.DomHelper.append(document.body,
36783                         config.autoCreate, true);
36784         }else{
36785             var elcfg =  {   tag: "div",
36786                             cls: "roo-layout-inactive-content",
36787                             id: config.id||el
36788                             };
36789             if (config.html) {
36790                 elcfg.html = config.html;
36791                 
36792             }
36793                         
36794             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36795         }
36796     } 
36797     this.closable = false;
36798     this.loaded = false;
36799     this.active = false;
36800    
36801       
36802     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36803         
36804         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36805         
36806         this.wrapEl = this.el; //this.el.wrap();
36807         var ti = [];
36808         if (config.toolbar.items) {
36809             ti = config.toolbar.items ;
36810             delete config.toolbar.items ;
36811         }
36812         
36813         var nitems = [];
36814         this.toolbar.render(this.wrapEl, 'before');
36815         for(var i =0;i < ti.length;i++) {
36816           //  Roo.log(['add child', items[i]]);
36817             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36818         }
36819         this.toolbar.items = nitems;
36820         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36821         delete config.toolbar;
36822         
36823     }
36824     /*
36825     // xtype created footer. - not sure if will work as we normally have to render first..
36826     if (this.footer && !this.footer.el && this.footer.xtype) {
36827         if (!this.wrapEl) {
36828             this.wrapEl = this.el.wrap();
36829         }
36830     
36831         this.footer.container = this.wrapEl.createChild();
36832          
36833         this.footer = Roo.factory(this.footer, Roo);
36834         
36835     }
36836     */
36837     
36838      if(typeof config == "string"){
36839         this.title = config;
36840     }else{
36841         Roo.apply(this, config);
36842     }
36843     
36844     if(this.resizeEl){
36845         this.resizeEl = Roo.get(this.resizeEl, true);
36846     }else{
36847         this.resizeEl = this.el;
36848     }
36849     // handle view.xtype
36850     
36851  
36852     
36853     
36854     this.addEvents({
36855         /**
36856          * @event activate
36857          * Fires when this panel is activated. 
36858          * @param {Roo.ContentPanel} this
36859          */
36860         "activate" : true,
36861         /**
36862          * @event deactivate
36863          * Fires when this panel is activated. 
36864          * @param {Roo.ContentPanel} this
36865          */
36866         "deactivate" : true,
36867
36868         /**
36869          * @event resize
36870          * Fires when this panel is resized if fitToFrame is true.
36871          * @param {Roo.ContentPanel} this
36872          * @param {Number} width The width after any component adjustments
36873          * @param {Number} height The height after any component adjustments
36874          */
36875         "resize" : true,
36876         
36877          /**
36878          * @event render
36879          * Fires when this tab is created
36880          * @param {Roo.ContentPanel} this
36881          */
36882         "render" : true
36883         
36884         
36885         
36886     });
36887     
36888
36889     
36890     
36891     if(this.autoScroll){
36892         this.resizeEl.setStyle("overflow", "auto");
36893     } else {
36894         // fix randome scrolling
36895         //this.el.on('scroll', function() {
36896         //    Roo.log('fix random scolling');
36897         //    this.scrollTo('top',0); 
36898         //});
36899     }
36900     content = content || this.content;
36901     if(content){
36902         this.setContent(content);
36903     }
36904     if(config && config.url){
36905         this.setUrl(this.url, this.params, this.loadOnce);
36906     }
36907     
36908     
36909     
36910     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36911     
36912     if (this.view && typeof(this.view.xtype) != 'undefined') {
36913         this.view.el = this.el.appendChild(document.createElement("div"));
36914         this.view = Roo.factory(this.view); 
36915         this.view.render  &&  this.view.render(false, '');  
36916     }
36917     
36918     
36919     this.fireEvent('render', this);
36920 };
36921
36922 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36923     
36924     tabTip : '',
36925     
36926     setRegion : function(region){
36927         this.region = region;
36928         this.setActiveClass(region && !this.background);
36929     },
36930     
36931     
36932     setActiveClass: function(state)
36933     {
36934         if(state){
36935            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36936            this.el.setStyle('position','relative');
36937         }else{
36938            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36939            this.el.setStyle('position', 'absolute');
36940         } 
36941     },
36942     
36943     /**
36944      * Returns the toolbar for this Panel if one was configured. 
36945      * @return {Roo.Toolbar} 
36946      */
36947     getToolbar : function(){
36948         return this.toolbar;
36949     },
36950     
36951     setActiveState : function(active)
36952     {
36953         this.active = active;
36954         this.setActiveClass(active);
36955         if(!active){
36956             if(this.fireEvent("deactivate", this) === false){
36957                 return false;
36958             }
36959             return true;
36960         }
36961         this.fireEvent("activate", this);
36962         return true;
36963     },
36964     /**
36965      * Updates this panel's element
36966      * @param {String} content The new content
36967      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36968     */
36969     setContent : function(content, loadScripts){
36970         this.el.update(content, loadScripts);
36971     },
36972
36973     ignoreResize : function(w, h){
36974         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36975             return true;
36976         }else{
36977             this.lastSize = {width: w, height: h};
36978             return false;
36979         }
36980     },
36981     /**
36982      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36983      * @return {Roo.UpdateManager} The UpdateManager
36984      */
36985     getUpdateManager : function(){
36986         return this.el.getUpdateManager();
36987     },
36988      /**
36989      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36990      * @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:
36991 <pre><code>
36992 panel.load({
36993     url: "your-url.php",
36994     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36995     callback: yourFunction,
36996     scope: yourObject, //(optional scope)
36997     discardUrl: false,
36998     nocache: false,
36999     text: "Loading...",
37000     timeout: 30,
37001     scripts: false
37002 });
37003 </code></pre>
37004      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37005      * 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.
37006      * @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}
37007      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37008      * @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.
37009      * @return {Roo.ContentPanel} this
37010      */
37011     load : function(){
37012         var um = this.el.getUpdateManager();
37013         um.update.apply(um, arguments);
37014         return this;
37015     },
37016
37017
37018     /**
37019      * 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.
37020      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37021      * @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)
37022      * @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)
37023      * @return {Roo.UpdateManager} The UpdateManager
37024      */
37025     setUrl : function(url, params, loadOnce){
37026         if(this.refreshDelegate){
37027             this.removeListener("activate", this.refreshDelegate);
37028         }
37029         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37030         this.on("activate", this.refreshDelegate);
37031         return this.el.getUpdateManager();
37032     },
37033     
37034     _handleRefresh : function(url, params, loadOnce){
37035         if(!loadOnce || !this.loaded){
37036             var updater = this.el.getUpdateManager();
37037             updater.update(url, params, this._setLoaded.createDelegate(this));
37038         }
37039     },
37040     
37041     _setLoaded : function(){
37042         this.loaded = true;
37043     }, 
37044     
37045     /**
37046      * Returns this panel's id
37047      * @return {String} 
37048      */
37049     getId : function(){
37050         return this.el.id;
37051     },
37052     
37053     /** 
37054      * Returns this panel's element - used by regiosn to add.
37055      * @return {Roo.Element} 
37056      */
37057     getEl : function(){
37058         return this.wrapEl || this.el;
37059     },
37060     
37061    
37062     
37063     adjustForComponents : function(width, height)
37064     {
37065         //Roo.log('adjustForComponents ');
37066         if(this.resizeEl != this.el){
37067             width -= this.el.getFrameWidth('lr');
37068             height -= this.el.getFrameWidth('tb');
37069         }
37070         if(this.toolbar){
37071             var te = this.toolbar.getEl();
37072             te.setWidth(width);
37073             height -= te.getHeight();
37074         }
37075         if(this.footer){
37076             var te = this.footer.getEl();
37077             te.setWidth(width);
37078             height -= te.getHeight();
37079         }
37080         
37081         
37082         if(this.adjustments){
37083             width += this.adjustments[0];
37084             height += this.adjustments[1];
37085         }
37086         return {"width": width, "height": height};
37087     },
37088     
37089     setSize : function(width, height){
37090         if(this.fitToFrame && !this.ignoreResize(width, height)){
37091             if(this.fitContainer && this.resizeEl != this.el){
37092                 this.el.setSize(width, height);
37093             }
37094             var size = this.adjustForComponents(width, height);
37095             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37096             this.fireEvent('resize', this, size.width, size.height);
37097         }
37098     },
37099     
37100     /**
37101      * Returns this panel's title
37102      * @return {String} 
37103      */
37104     getTitle : function(){
37105         
37106         if (typeof(this.title) != 'object') {
37107             return this.title;
37108         }
37109         
37110         var t = '';
37111         for (var k in this.title) {
37112             if (!this.title.hasOwnProperty(k)) {
37113                 continue;
37114             }
37115             
37116             if (k.indexOf('-') >= 0) {
37117                 var s = k.split('-');
37118                 for (var i = 0; i<s.length; i++) {
37119                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37120                 }
37121             } else {
37122                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37123             }
37124         }
37125         return t;
37126     },
37127     
37128     /**
37129      * Set this panel's title
37130      * @param {String} title
37131      */
37132     setTitle : function(title){
37133         this.title = title;
37134         if(this.region){
37135             this.region.updatePanelTitle(this, title);
37136         }
37137     },
37138     
37139     /**
37140      * Returns true is this panel was configured to be closable
37141      * @return {Boolean} 
37142      */
37143     isClosable : function(){
37144         return this.closable;
37145     },
37146     
37147     beforeSlide : function(){
37148         this.el.clip();
37149         this.resizeEl.clip();
37150     },
37151     
37152     afterSlide : function(){
37153         this.el.unclip();
37154         this.resizeEl.unclip();
37155     },
37156     
37157     /**
37158      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37159      *   Will fail silently if the {@link #setUrl} method has not been called.
37160      *   This does not activate the panel, just updates its content.
37161      */
37162     refresh : function(){
37163         if(this.refreshDelegate){
37164            this.loaded = false;
37165            this.refreshDelegate();
37166         }
37167     },
37168     
37169     /**
37170      * Destroys this panel
37171      */
37172     destroy : function(){
37173         this.el.removeAllListeners();
37174         var tempEl = document.createElement("span");
37175         tempEl.appendChild(this.el.dom);
37176         tempEl.innerHTML = "";
37177         this.el.remove();
37178         this.el = null;
37179     },
37180     
37181     /**
37182      * form - if the content panel contains a form - this is a reference to it.
37183      * @type {Roo.form.Form}
37184      */
37185     form : false,
37186     /**
37187      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37188      *    This contains a reference to it.
37189      * @type {Roo.View}
37190      */
37191     view : false,
37192     
37193       /**
37194      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37195      * <pre><code>
37196
37197 layout.addxtype({
37198        xtype : 'Form',
37199        items: [ .... ]
37200    }
37201 );
37202
37203 </code></pre>
37204      * @param {Object} cfg Xtype definition of item to add.
37205      */
37206     
37207     
37208     getChildContainer: function () {
37209         return this.getEl();
37210     }
37211     
37212     
37213     /*
37214         var  ret = new Roo.factory(cfg);
37215         return ret;
37216         
37217         
37218         // add form..
37219         if (cfg.xtype.match(/^Form$/)) {
37220             
37221             var el;
37222             //if (this.footer) {
37223             //    el = this.footer.container.insertSibling(false, 'before');
37224             //} else {
37225                 el = this.el.createChild();
37226             //}
37227
37228             this.form = new  Roo.form.Form(cfg);
37229             
37230             
37231             if ( this.form.allItems.length) {
37232                 this.form.render(el.dom);
37233             }
37234             return this.form;
37235         }
37236         // should only have one of theses..
37237         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37238             // views.. should not be just added - used named prop 'view''
37239             
37240             cfg.el = this.el.appendChild(document.createElement("div"));
37241             // factory?
37242             
37243             var ret = new Roo.factory(cfg);
37244              
37245              ret.render && ret.render(false, ''); // render blank..
37246             this.view = ret;
37247             return ret;
37248         }
37249         return false;
37250     }
37251     \*/
37252 });
37253  
37254 /**
37255  * @class Roo.bootstrap.panel.Grid
37256  * @extends Roo.bootstrap.panel.Content
37257  * @constructor
37258  * Create a new GridPanel.
37259  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37260  * @param {Object} config A the config object
37261   
37262  */
37263
37264
37265
37266 Roo.bootstrap.panel.Grid = function(config)
37267 {
37268     
37269       
37270     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37271         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37272
37273     config.el = this.wrapper;
37274     //this.el = this.wrapper;
37275     
37276       if (config.container) {
37277         // ctor'ed from a Border/panel.grid
37278         
37279         
37280         this.wrapper.setStyle("overflow", "hidden");
37281         this.wrapper.addClass('roo-grid-container');
37282
37283     }
37284     
37285     
37286     if(config.toolbar){
37287         var tool_el = this.wrapper.createChild();    
37288         this.toolbar = Roo.factory(config.toolbar);
37289         var ti = [];
37290         if (config.toolbar.items) {
37291             ti = config.toolbar.items ;
37292             delete config.toolbar.items ;
37293         }
37294         
37295         var nitems = [];
37296         this.toolbar.render(tool_el);
37297         for(var i =0;i < ti.length;i++) {
37298           //  Roo.log(['add child', items[i]]);
37299             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37300         }
37301         this.toolbar.items = nitems;
37302         
37303         delete config.toolbar;
37304     }
37305     
37306     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37307     config.grid.scrollBody = true;;
37308     config.grid.monitorWindowResize = false; // turn off autosizing
37309     config.grid.autoHeight = false;
37310     config.grid.autoWidth = false;
37311     
37312     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37313     
37314     if (config.background) {
37315         // render grid on panel activation (if panel background)
37316         this.on('activate', function(gp) {
37317             if (!gp.grid.rendered) {
37318                 gp.grid.render(this.wrapper);
37319                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37320             }
37321         });
37322             
37323     } else {
37324         this.grid.render(this.wrapper);
37325         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37326
37327     }
37328     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37329     // ??? needed ??? config.el = this.wrapper;
37330     
37331     
37332     
37333   
37334     // xtype created footer. - not sure if will work as we normally have to render first..
37335     if (this.footer && !this.footer.el && this.footer.xtype) {
37336         
37337         var ctr = this.grid.getView().getFooterPanel(true);
37338         this.footer.dataSource = this.grid.dataSource;
37339         this.footer = Roo.factory(this.footer, Roo);
37340         this.footer.render(ctr);
37341         
37342     }
37343     
37344     
37345     
37346     
37347      
37348 };
37349
37350 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37351     getId : function(){
37352         return this.grid.id;
37353     },
37354     
37355     /**
37356      * Returns the grid for this panel
37357      * @return {Roo.bootstrap.Table} 
37358      */
37359     getGrid : function(){
37360         return this.grid;    
37361     },
37362     
37363     setSize : function(width, height){
37364         if(!this.ignoreResize(width, height)){
37365             var grid = this.grid;
37366             var size = this.adjustForComponents(width, height);
37367             var gridel = grid.getGridEl();
37368             gridel.setSize(size.width, size.height);
37369             /*
37370             var thd = grid.getGridEl().select('thead',true).first();
37371             var tbd = grid.getGridEl().select('tbody', true).first();
37372             if (tbd) {
37373                 tbd.setSize(width, height - thd.getHeight());
37374             }
37375             */
37376             grid.autoSize();
37377         }
37378     },
37379      
37380     
37381     
37382     beforeSlide : function(){
37383         this.grid.getView().scroller.clip();
37384     },
37385     
37386     afterSlide : function(){
37387         this.grid.getView().scroller.unclip();
37388     },
37389     
37390     destroy : function(){
37391         this.grid.destroy();
37392         delete this.grid;
37393         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37394     }
37395 });
37396
37397 /**
37398  * @class Roo.bootstrap.panel.Nest
37399  * @extends Roo.bootstrap.panel.Content
37400  * @constructor
37401  * Create a new Panel, that can contain a layout.Border.
37402  * 
37403  * 
37404  * @param {Roo.BorderLayout} layout The layout for this panel
37405  * @param {String/Object} config A string to set only the title or a config object
37406  */
37407 Roo.bootstrap.panel.Nest = function(config)
37408 {
37409     // construct with only one argument..
37410     /* FIXME - implement nicer consturctors
37411     if (layout.layout) {
37412         config = layout;
37413         layout = config.layout;
37414         delete config.layout;
37415     }
37416     if (layout.xtype && !layout.getEl) {
37417         // then layout needs constructing..
37418         layout = Roo.factory(layout, Roo);
37419     }
37420     */
37421     
37422     config.el =  config.layout.getEl();
37423     
37424     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37425     
37426     config.layout.monitorWindowResize = false; // turn off autosizing
37427     this.layout = config.layout;
37428     this.layout.getEl().addClass("roo-layout-nested-layout");
37429     
37430     
37431     
37432     
37433 };
37434
37435 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37436
37437     setSize : function(width, height){
37438         if(!this.ignoreResize(width, height)){
37439             var size = this.adjustForComponents(width, height);
37440             var el = this.layout.getEl();
37441             if (size.height < 1) {
37442                 el.setWidth(size.width);   
37443             } else {
37444                 el.setSize(size.width, size.height);
37445             }
37446             var touch = el.dom.offsetWidth;
37447             this.layout.layout();
37448             // ie requires a double layout on the first pass
37449             if(Roo.isIE && !this.initialized){
37450                 this.initialized = true;
37451                 this.layout.layout();
37452             }
37453         }
37454     },
37455     
37456     // activate all subpanels if not currently active..
37457     
37458     setActiveState : function(active){
37459         this.active = active;
37460         this.setActiveClass(active);
37461         
37462         if(!active){
37463             this.fireEvent("deactivate", this);
37464             return;
37465         }
37466         
37467         this.fireEvent("activate", this);
37468         // not sure if this should happen before or after..
37469         if (!this.layout) {
37470             return; // should not happen..
37471         }
37472         var reg = false;
37473         for (var r in this.layout.regions) {
37474             reg = this.layout.getRegion(r);
37475             if (reg.getActivePanel()) {
37476                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37477                 reg.setActivePanel(reg.getActivePanel());
37478                 continue;
37479             }
37480             if (!reg.panels.length) {
37481                 continue;
37482             }
37483             reg.showPanel(reg.getPanel(0));
37484         }
37485         
37486         
37487         
37488         
37489     },
37490     
37491     /**
37492      * Returns the nested BorderLayout for this panel
37493      * @return {Roo.BorderLayout} 
37494      */
37495     getLayout : function(){
37496         return this.layout;
37497     },
37498     
37499      /**
37500      * Adds a xtype elements to the layout of the nested panel
37501      * <pre><code>
37502
37503 panel.addxtype({
37504        xtype : 'ContentPanel',
37505        region: 'west',
37506        items: [ .... ]
37507    }
37508 );
37509
37510 panel.addxtype({
37511         xtype : 'NestedLayoutPanel',
37512         region: 'west',
37513         layout: {
37514            center: { },
37515            west: { }   
37516         },
37517         items : [ ... list of content panels or nested layout panels.. ]
37518    }
37519 );
37520 </code></pre>
37521      * @param {Object} cfg Xtype definition of item to add.
37522      */
37523     addxtype : function(cfg) {
37524         return this.layout.addxtype(cfg);
37525     
37526     }
37527 });        /*
37528  * Based on:
37529  * Ext JS Library 1.1.1
37530  * Copyright(c) 2006-2007, Ext JS, LLC.
37531  *
37532  * Originally Released Under LGPL - original licence link has changed is not relivant.
37533  *
37534  * Fork - LGPL
37535  * <script type="text/javascript">
37536  */
37537 /**
37538  * @class Roo.TabPanel
37539  * @extends Roo.util.Observable
37540  * A lightweight tab container.
37541  * <br><br>
37542  * Usage:
37543  * <pre><code>
37544 // basic tabs 1, built from existing content
37545 var tabs = new Roo.TabPanel("tabs1");
37546 tabs.addTab("script", "View Script");
37547 tabs.addTab("markup", "View Markup");
37548 tabs.activate("script");
37549
37550 // more advanced tabs, built from javascript
37551 var jtabs = new Roo.TabPanel("jtabs");
37552 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37553
37554 // set up the UpdateManager
37555 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37556 var updater = tab2.getUpdateManager();
37557 updater.setDefaultUrl("ajax1.htm");
37558 tab2.on('activate', updater.refresh, updater, true);
37559
37560 // Use setUrl for Ajax loading
37561 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37562 tab3.setUrl("ajax2.htm", null, true);
37563
37564 // Disabled tab
37565 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37566 tab4.disable();
37567
37568 jtabs.activate("jtabs-1");
37569  * </code></pre>
37570  * @constructor
37571  * Create a new TabPanel.
37572  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37573  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37574  */
37575 Roo.bootstrap.panel.Tabs = function(config){
37576     /**
37577     * The container element for this TabPanel.
37578     * @type Roo.Element
37579     */
37580     this.el = Roo.get(config.el);
37581     delete config.el;
37582     if(config){
37583         if(typeof config == "boolean"){
37584             this.tabPosition = config ? "bottom" : "top";
37585         }else{
37586             Roo.apply(this, config);
37587         }
37588     }
37589     
37590     if(this.tabPosition == "bottom"){
37591         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37592         this.el.addClass("roo-tabs-bottom");
37593     }
37594     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37595     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37596     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37597     if(Roo.isIE){
37598         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37599     }
37600     if(this.tabPosition != "bottom"){
37601         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37602          * @type Roo.Element
37603          */
37604         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37605         this.el.addClass("roo-tabs-top");
37606     }
37607     this.items = [];
37608
37609     this.bodyEl.setStyle("position", "relative");
37610
37611     this.active = null;
37612     this.activateDelegate = this.activate.createDelegate(this);
37613
37614     this.addEvents({
37615         /**
37616          * @event tabchange
37617          * Fires when the active tab changes
37618          * @param {Roo.TabPanel} this
37619          * @param {Roo.TabPanelItem} activePanel The new active tab
37620          */
37621         "tabchange": true,
37622         /**
37623          * @event beforetabchange
37624          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37625          * @param {Roo.TabPanel} this
37626          * @param {Object} e Set cancel to true on this object to cancel the tab change
37627          * @param {Roo.TabPanelItem} tab The tab being changed to
37628          */
37629         "beforetabchange" : true
37630     });
37631
37632     Roo.EventManager.onWindowResize(this.onResize, this);
37633     this.cpad = this.el.getPadding("lr");
37634     this.hiddenCount = 0;
37635
37636
37637     // toolbar on the tabbar support...
37638     if (this.toolbar) {
37639         alert("no toolbar support yet");
37640         this.toolbar  = false;
37641         /*
37642         var tcfg = this.toolbar;
37643         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37644         this.toolbar = new Roo.Toolbar(tcfg);
37645         if (Roo.isSafari) {
37646             var tbl = tcfg.container.child('table', true);
37647             tbl.setAttribute('width', '100%');
37648         }
37649         */
37650         
37651     }
37652    
37653
37654
37655     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37656 };
37657
37658 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37659     /*
37660      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37661      */
37662     tabPosition : "top",
37663     /*
37664      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37665      */
37666     currentTabWidth : 0,
37667     /*
37668      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37669      */
37670     minTabWidth : 40,
37671     /*
37672      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37673      */
37674     maxTabWidth : 250,
37675     /*
37676      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37677      */
37678     preferredTabWidth : 175,
37679     /*
37680      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37681      */
37682     resizeTabs : false,
37683     /*
37684      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37685      */
37686     monitorResize : true,
37687     /*
37688      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37689      */
37690     toolbar : false,
37691
37692     /**
37693      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37694      * @param {String} id The id of the div to use <b>or create</b>
37695      * @param {String} text The text for the tab
37696      * @param {String} content (optional) Content to put in the TabPanelItem body
37697      * @param {Boolean} closable (optional) True to create a close icon on the tab
37698      * @return {Roo.TabPanelItem} The created TabPanelItem
37699      */
37700     addTab : function(id, text, content, closable, tpl)
37701     {
37702         var item = new Roo.bootstrap.panel.TabItem({
37703             panel: this,
37704             id : id,
37705             text : text,
37706             closable : closable,
37707             tpl : tpl
37708         });
37709         this.addTabItem(item);
37710         if(content){
37711             item.setContent(content);
37712         }
37713         return item;
37714     },
37715
37716     /**
37717      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37718      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37719      * @return {Roo.TabPanelItem}
37720      */
37721     getTab : function(id){
37722         return this.items[id];
37723     },
37724
37725     /**
37726      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37727      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37728      */
37729     hideTab : function(id){
37730         var t = this.items[id];
37731         if(!t.isHidden()){
37732            t.setHidden(true);
37733            this.hiddenCount++;
37734            this.autoSizeTabs();
37735         }
37736     },
37737
37738     /**
37739      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37740      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37741      */
37742     unhideTab : function(id){
37743         var t = this.items[id];
37744         if(t.isHidden()){
37745            t.setHidden(false);
37746            this.hiddenCount--;
37747            this.autoSizeTabs();
37748         }
37749     },
37750
37751     /**
37752      * Adds an existing {@link Roo.TabPanelItem}.
37753      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37754      */
37755     addTabItem : function(item){
37756         this.items[item.id] = item;
37757         this.items.push(item);
37758       //  if(this.resizeTabs){
37759     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37760   //         this.autoSizeTabs();
37761 //        }else{
37762 //            item.autoSize();
37763        // }
37764     },
37765
37766     /**
37767      * Removes a {@link Roo.TabPanelItem}.
37768      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37769      */
37770     removeTab : function(id){
37771         var items = this.items;
37772         var tab = items[id];
37773         if(!tab) { return; }
37774         var index = items.indexOf(tab);
37775         if(this.active == tab && items.length > 1){
37776             var newTab = this.getNextAvailable(index);
37777             if(newTab) {
37778                 newTab.activate();
37779             }
37780         }
37781         this.stripEl.dom.removeChild(tab.pnode.dom);
37782         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37783             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37784         }
37785         items.splice(index, 1);
37786         delete this.items[tab.id];
37787         tab.fireEvent("close", tab);
37788         tab.purgeListeners();
37789         this.autoSizeTabs();
37790     },
37791
37792     getNextAvailable : function(start){
37793         var items = this.items;
37794         var index = start;
37795         // look for a next tab that will slide over to
37796         // replace the one being removed
37797         while(index < items.length){
37798             var item = items[++index];
37799             if(item && !item.isHidden()){
37800                 return item;
37801             }
37802         }
37803         // if one isn't found select the previous tab (on the left)
37804         index = start;
37805         while(index >= 0){
37806             var item = items[--index];
37807             if(item && !item.isHidden()){
37808                 return item;
37809             }
37810         }
37811         return null;
37812     },
37813
37814     /**
37815      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37816      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37817      */
37818     disableTab : function(id){
37819         var tab = this.items[id];
37820         if(tab && this.active != tab){
37821             tab.disable();
37822         }
37823     },
37824
37825     /**
37826      * Enables a {@link Roo.TabPanelItem} that is disabled.
37827      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37828      */
37829     enableTab : function(id){
37830         var tab = this.items[id];
37831         tab.enable();
37832     },
37833
37834     /**
37835      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37836      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37837      * @return {Roo.TabPanelItem} The TabPanelItem.
37838      */
37839     activate : function(id){
37840         var tab = this.items[id];
37841         if(!tab){
37842             return null;
37843         }
37844         if(tab == this.active || tab.disabled){
37845             return tab;
37846         }
37847         var e = {};
37848         this.fireEvent("beforetabchange", this, e, tab);
37849         if(e.cancel !== true && !tab.disabled){
37850             if(this.active){
37851                 this.active.hide();
37852             }
37853             this.active = this.items[id];
37854             this.active.show();
37855             this.fireEvent("tabchange", this, this.active);
37856         }
37857         return tab;
37858     },
37859
37860     /**
37861      * Gets the active {@link Roo.TabPanelItem}.
37862      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37863      */
37864     getActiveTab : function(){
37865         return this.active;
37866     },
37867
37868     /**
37869      * Updates the tab body element to fit the height of the container element
37870      * for overflow scrolling
37871      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37872      */
37873     syncHeight : function(targetHeight){
37874         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37875         var bm = this.bodyEl.getMargins();
37876         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37877         this.bodyEl.setHeight(newHeight);
37878         return newHeight;
37879     },
37880
37881     onResize : function(){
37882         if(this.monitorResize){
37883             this.autoSizeTabs();
37884         }
37885     },
37886
37887     /**
37888      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37889      */
37890     beginUpdate : function(){
37891         this.updating = true;
37892     },
37893
37894     /**
37895      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37896      */
37897     endUpdate : function(){
37898         this.updating = false;
37899         this.autoSizeTabs();
37900     },
37901
37902     /**
37903      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37904      */
37905     autoSizeTabs : function(){
37906         var count = this.items.length;
37907         var vcount = count - this.hiddenCount;
37908         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37909             return;
37910         }
37911         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37912         var availWidth = Math.floor(w / vcount);
37913         var b = this.stripBody;
37914         if(b.getWidth() > w){
37915             var tabs = this.items;
37916             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37917             if(availWidth < this.minTabWidth){
37918                 /*if(!this.sleft){    // incomplete scrolling code
37919                     this.createScrollButtons();
37920                 }
37921                 this.showScroll();
37922                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37923             }
37924         }else{
37925             if(this.currentTabWidth < this.preferredTabWidth){
37926                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37927             }
37928         }
37929     },
37930
37931     /**
37932      * Returns the number of tabs in this TabPanel.
37933      * @return {Number}
37934      */
37935      getCount : function(){
37936          return this.items.length;
37937      },
37938
37939     /**
37940      * Resizes all the tabs to the passed width
37941      * @param {Number} The new width
37942      */
37943     setTabWidth : function(width){
37944         this.currentTabWidth = width;
37945         for(var i = 0, len = this.items.length; i < len; i++) {
37946                 if(!this.items[i].isHidden()) {
37947                 this.items[i].setWidth(width);
37948             }
37949         }
37950     },
37951
37952     /**
37953      * Destroys this TabPanel
37954      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37955      */
37956     destroy : function(removeEl){
37957         Roo.EventManager.removeResizeListener(this.onResize, this);
37958         for(var i = 0, len = this.items.length; i < len; i++){
37959             this.items[i].purgeListeners();
37960         }
37961         if(removeEl === true){
37962             this.el.update("");
37963             this.el.remove();
37964         }
37965     },
37966     
37967     createStrip : function(container)
37968     {
37969         var strip = document.createElement("nav");
37970         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37971         container.appendChild(strip);
37972         return strip;
37973     },
37974     
37975     createStripList : function(strip)
37976     {
37977         // div wrapper for retard IE
37978         // returns the "tr" element.
37979         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37980         //'<div class="x-tabs-strip-wrap">'+
37981           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37982           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37983         return strip.firstChild; //.firstChild.firstChild.firstChild;
37984     },
37985     createBody : function(container)
37986     {
37987         var body = document.createElement("div");
37988         Roo.id(body, "tab-body");
37989         //Roo.fly(body).addClass("x-tabs-body");
37990         Roo.fly(body).addClass("tab-content");
37991         container.appendChild(body);
37992         return body;
37993     },
37994     createItemBody :function(bodyEl, id){
37995         var body = Roo.getDom(id);
37996         if(!body){
37997             body = document.createElement("div");
37998             body.id = id;
37999         }
38000         //Roo.fly(body).addClass("x-tabs-item-body");
38001         Roo.fly(body).addClass("tab-pane");
38002          bodyEl.insertBefore(body, bodyEl.firstChild);
38003         return body;
38004     },
38005     /** @private */
38006     createStripElements :  function(stripEl, text, closable, tpl)
38007     {
38008         var td = document.createElement("li"); // was td..
38009         
38010         
38011         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38012         
38013         
38014         stripEl.appendChild(td);
38015         /*if(closable){
38016             td.className = "x-tabs-closable";
38017             if(!this.closeTpl){
38018                 this.closeTpl = new Roo.Template(
38019                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38020                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38021                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38022                 );
38023             }
38024             var el = this.closeTpl.overwrite(td, {"text": text});
38025             var close = el.getElementsByTagName("div")[0];
38026             var inner = el.getElementsByTagName("em")[0];
38027             return {"el": el, "close": close, "inner": inner};
38028         } else {
38029         */
38030         // not sure what this is..
38031 //            if(!this.tabTpl){
38032                 //this.tabTpl = new Roo.Template(
38033                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38034                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38035                 //);
38036 //                this.tabTpl = new Roo.Template(
38037 //                   '<a href="#">' +
38038 //                   '<span unselectable="on"' +
38039 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38040 //                            ' >{text}</span></a>'
38041 //                );
38042 //                
38043 //            }
38044
38045
38046             var template = tpl || this.tabTpl || false;
38047             
38048             if(!template){
38049                 
38050                 template = new Roo.Template(
38051                    '<a href="#">' +
38052                    '<span unselectable="on"' +
38053                             (this.disableTooltips ? '' : ' title="{text}"') +
38054                             ' >{text}</span></a>'
38055                 );
38056             }
38057             
38058             switch (typeof(template)) {
38059                 case 'object' :
38060                     break;
38061                 case 'string' :
38062                     template = new Roo.Template(template);
38063                     break;
38064                 default :
38065                     break;
38066             }
38067             
38068             var el = template.overwrite(td, {"text": text});
38069             
38070             var inner = el.getElementsByTagName("span")[0];
38071             
38072             return {"el": el, "inner": inner};
38073             
38074     }
38075         
38076     
38077 });
38078
38079 /**
38080  * @class Roo.TabPanelItem
38081  * @extends Roo.util.Observable
38082  * Represents an individual item (tab plus body) in a TabPanel.
38083  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38084  * @param {String} id The id of this TabPanelItem
38085  * @param {String} text The text for the tab of this TabPanelItem
38086  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38087  */
38088 Roo.bootstrap.panel.TabItem = function(config){
38089     /**
38090      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38091      * @type Roo.TabPanel
38092      */
38093     this.tabPanel = config.panel;
38094     /**
38095      * The id for this TabPanelItem
38096      * @type String
38097      */
38098     this.id = config.id;
38099     /** @private */
38100     this.disabled = false;
38101     /** @private */
38102     this.text = config.text;
38103     /** @private */
38104     this.loaded = false;
38105     this.closable = config.closable;
38106
38107     /**
38108      * The body element for this TabPanelItem.
38109      * @type Roo.Element
38110      */
38111     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38112     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38113     this.bodyEl.setStyle("display", "block");
38114     this.bodyEl.setStyle("zoom", "1");
38115     //this.hideAction();
38116
38117     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38118     /** @private */
38119     this.el = Roo.get(els.el);
38120     this.inner = Roo.get(els.inner, true);
38121     this.textEl = Roo.get(this.el.dom.firstChild, true);
38122     this.pnode = Roo.get(els.el.parentNode, true);
38123 //    this.el.on("mousedown", this.onTabMouseDown, this);
38124     this.el.on("click", this.onTabClick, this);
38125     /** @private */
38126     if(config.closable){
38127         var c = Roo.get(els.close, true);
38128         c.dom.title = this.closeText;
38129         c.addClassOnOver("close-over");
38130         c.on("click", this.closeClick, this);
38131      }
38132
38133     this.addEvents({
38134          /**
38135          * @event activate
38136          * Fires when this tab becomes the active tab.
38137          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38138          * @param {Roo.TabPanelItem} this
38139          */
38140         "activate": true,
38141         /**
38142          * @event beforeclose
38143          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38144          * @param {Roo.TabPanelItem} this
38145          * @param {Object} e Set cancel to true on this object to cancel the close.
38146          */
38147         "beforeclose": true,
38148         /**
38149          * @event close
38150          * Fires when this tab is closed.
38151          * @param {Roo.TabPanelItem} this
38152          */
38153          "close": true,
38154         /**
38155          * @event deactivate
38156          * Fires when this tab is no longer the active tab.
38157          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38158          * @param {Roo.TabPanelItem} this
38159          */
38160          "deactivate" : true
38161     });
38162     this.hidden = false;
38163
38164     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38165 };
38166
38167 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38168            {
38169     purgeListeners : function(){
38170        Roo.util.Observable.prototype.purgeListeners.call(this);
38171        this.el.removeAllListeners();
38172     },
38173     /**
38174      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38175      */
38176     show : function(){
38177         this.pnode.addClass("active");
38178         this.showAction();
38179         if(Roo.isOpera){
38180             this.tabPanel.stripWrap.repaint();
38181         }
38182         this.fireEvent("activate", this.tabPanel, this);
38183     },
38184
38185     /**
38186      * Returns true if this tab is the active tab.
38187      * @return {Boolean}
38188      */
38189     isActive : function(){
38190         return this.tabPanel.getActiveTab() == this;
38191     },
38192
38193     /**
38194      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38195      */
38196     hide : function(){
38197         this.pnode.removeClass("active");
38198         this.hideAction();
38199         this.fireEvent("deactivate", this.tabPanel, this);
38200     },
38201
38202     hideAction : function(){
38203         this.bodyEl.hide();
38204         this.bodyEl.setStyle("position", "absolute");
38205         this.bodyEl.setLeft("-20000px");
38206         this.bodyEl.setTop("-20000px");
38207     },
38208
38209     showAction : function(){
38210         this.bodyEl.setStyle("position", "relative");
38211         this.bodyEl.setTop("");
38212         this.bodyEl.setLeft("");
38213         this.bodyEl.show();
38214     },
38215
38216     /**
38217      * Set the tooltip for the tab.
38218      * @param {String} tooltip The tab's tooltip
38219      */
38220     setTooltip : function(text){
38221         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38222             this.textEl.dom.qtip = text;
38223             this.textEl.dom.removeAttribute('title');
38224         }else{
38225             this.textEl.dom.title = text;
38226         }
38227     },
38228
38229     onTabClick : function(e){
38230         e.preventDefault();
38231         this.tabPanel.activate(this.id);
38232     },
38233
38234     onTabMouseDown : function(e){
38235         e.preventDefault();
38236         this.tabPanel.activate(this.id);
38237     },
38238 /*
38239     getWidth : function(){
38240         return this.inner.getWidth();
38241     },
38242
38243     setWidth : function(width){
38244         var iwidth = width - this.pnode.getPadding("lr");
38245         this.inner.setWidth(iwidth);
38246         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38247         this.pnode.setWidth(width);
38248     },
38249 */
38250     /**
38251      * Show or hide the tab
38252      * @param {Boolean} hidden True to hide or false to show.
38253      */
38254     setHidden : function(hidden){
38255         this.hidden = hidden;
38256         this.pnode.setStyle("display", hidden ? "none" : "");
38257     },
38258
38259     /**
38260      * Returns true if this tab is "hidden"
38261      * @return {Boolean}
38262      */
38263     isHidden : function(){
38264         return this.hidden;
38265     },
38266
38267     /**
38268      * Returns the text for this tab
38269      * @return {String}
38270      */
38271     getText : function(){
38272         return this.text;
38273     },
38274     /*
38275     autoSize : function(){
38276         //this.el.beginMeasure();
38277         this.textEl.setWidth(1);
38278         /*
38279          *  #2804 [new] Tabs in Roojs
38280          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38281          */
38282         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38283         //this.el.endMeasure();
38284     //},
38285
38286     /**
38287      * Sets the text for the tab (Note: this also sets the tooltip text)
38288      * @param {String} text The tab's text and tooltip
38289      */
38290     setText : function(text){
38291         this.text = text;
38292         this.textEl.update(text);
38293         this.setTooltip(text);
38294         //if(!this.tabPanel.resizeTabs){
38295         //    this.autoSize();
38296         //}
38297     },
38298     /**
38299      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38300      */
38301     activate : function(){
38302         this.tabPanel.activate(this.id);
38303     },
38304
38305     /**
38306      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38307      */
38308     disable : function(){
38309         if(this.tabPanel.active != this){
38310             this.disabled = true;
38311             this.pnode.addClass("disabled");
38312         }
38313     },
38314
38315     /**
38316      * Enables this TabPanelItem if it was previously disabled.
38317      */
38318     enable : function(){
38319         this.disabled = false;
38320         this.pnode.removeClass("disabled");
38321     },
38322
38323     /**
38324      * Sets the content for this TabPanelItem.
38325      * @param {String} content The content
38326      * @param {Boolean} loadScripts true to look for and load scripts
38327      */
38328     setContent : function(content, loadScripts){
38329         this.bodyEl.update(content, loadScripts);
38330     },
38331
38332     /**
38333      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38334      * @return {Roo.UpdateManager} The UpdateManager
38335      */
38336     getUpdateManager : function(){
38337         return this.bodyEl.getUpdateManager();
38338     },
38339
38340     /**
38341      * Set a URL to be used to load the content for this TabPanelItem.
38342      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38343      * @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)
38344      * @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)
38345      * @return {Roo.UpdateManager} The UpdateManager
38346      */
38347     setUrl : function(url, params, loadOnce){
38348         if(this.refreshDelegate){
38349             this.un('activate', this.refreshDelegate);
38350         }
38351         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38352         this.on("activate", this.refreshDelegate);
38353         return this.bodyEl.getUpdateManager();
38354     },
38355
38356     /** @private */
38357     _handleRefresh : function(url, params, loadOnce){
38358         if(!loadOnce || !this.loaded){
38359             var updater = this.bodyEl.getUpdateManager();
38360             updater.update(url, params, this._setLoaded.createDelegate(this));
38361         }
38362     },
38363
38364     /**
38365      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38366      *   Will fail silently if the setUrl method has not been called.
38367      *   This does not activate the panel, just updates its content.
38368      */
38369     refresh : function(){
38370         if(this.refreshDelegate){
38371            this.loaded = false;
38372            this.refreshDelegate();
38373         }
38374     },
38375
38376     /** @private */
38377     _setLoaded : function(){
38378         this.loaded = true;
38379     },
38380
38381     /** @private */
38382     closeClick : function(e){
38383         var o = {};
38384         e.stopEvent();
38385         this.fireEvent("beforeclose", this, o);
38386         if(o.cancel !== true){
38387             this.tabPanel.removeTab(this.id);
38388         }
38389     },
38390     /**
38391      * The text displayed in the tooltip for the close icon.
38392      * @type String
38393      */
38394     closeText : "Close this tab"
38395 });
38396 /**
38397 *    This script refer to:
38398 *    Title: International Telephone Input
38399 *    Author: Jack O'Connor
38400 *    Code version:  v12.1.12
38401 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38402 **/
38403
38404 Roo.bootstrap.PhoneInputData = function() {
38405     var d = [
38406       [
38407         "Afghanistan (‫افغانستان‬‎)",
38408         "af",
38409         "93"
38410       ],
38411       [
38412         "Albania (Shqipëri)",
38413         "al",
38414         "355"
38415       ],
38416       [
38417         "Algeria (‫الجزائر‬‎)",
38418         "dz",
38419         "213"
38420       ],
38421       [
38422         "American Samoa",
38423         "as",
38424         "1684"
38425       ],
38426       [
38427         "Andorra",
38428         "ad",
38429         "376"
38430       ],
38431       [
38432         "Angola",
38433         "ao",
38434         "244"
38435       ],
38436       [
38437         "Anguilla",
38438         "ai",
38439         "1264"
38440       ],
38441       [
38442         "Antigua and Barbuda",
38443         "ag",
38444         "1268"
38445       ],
38446       [
38447         "Argentina",
38448         "ar",
38449         "54"
38450       ],
38451       [
38452         "Armenia (Հայաստան)",
38453         "am",
38454         "374"
38455       ],
38456       [
38457         "Aruba",
38458         "aw",
38459         "297"
38460       ],
38461       [
38462         "Australia",
38463         "au",
38464         "61",
38465         0
38466       ],
38467       [
38468         "Austria (Österreich)",
38469         "at",
38470         "43"
38471       ],
38472       [
38473         "Azerbaijan (Azərbaycan)",
38474         "az",
38475         "994"
38476       ],
38477       [
38478         "Bahamas",
38479         "bs",
38480         "1242"
38481       ],
38482       [
38483         "Bahrain (‫البحرين‬‎)",
38484         "bh",
38485         "973"
38486       ],
38487       [
38488         "Bangladesh (বাংলাদেশ)",
38489         "bd",
38490         "880"
38491       ],
38492       [
38493         "Barbados",
38494         "bb",
38495         "1246"
38496       ],
38497       [
38498         "Belarus (Беларусь)",
38499         "by",
38500         "375"
38501       ],
38502       [
38503         "Belgium (België)",
38504         "be",
38505         "32"
38506       ],
38507       [
38508         "Belize",
38509         "bz",
38510         "501"
38511       ],
38512       [
38513         "Benin (Bénin)",
38514         "bj",
38515         "229"
38516       ],
38517       [
38518         "Bermuda",
38519         "bm",
38520         "1441"
38521       ],
38522       [
38523         "Bhutan (འབྲུག)",
38524         "bt",
38525         "975"
38526       ],
38527       [
38528         "Bolivia",
38529         "bo",
38530         "591"
38531       ],
38532       [
38533         "Bosnia and Herzegovina (Босна и Херцеговина)",
38534         "ba",
38535         "387"
38536       ],
38537       [
38538         "Botswana",
38539         "bw",
38540         "267"
38541       ],
38542       [
38543         "Brazil (Brasil)",
38544         "br",
38545         "55"
38546       ],
38547       [
38548         "British Indian Ocean Territory",
38549         "io",
38550         "246"
38551       ],
38552       [
38553         "British Virgin Islands",
38554         "vg",
38555         "1284"
38556       ],
38557       [
38558         "Brunei",
38559         "bn",
38560         "673"
38561       ],
38562       [
38563         "Bulgaria (България)",
38564         "bg",
38565         "359"
38566       ],
38567       [
38568         "Burkina Faso",
38569         "bf",
38570         "226"
38571       ],
38572       [
38573         "Burundi (Uburundi)",
38574         "bi",
38575         "257"
38576       ],
38577       [
38578         "Cambodia (កម្ពុជា)",
38579         "kh",
38580         "855"
38581       ],
38582       [
38583         "Cameroon (Cameroun)",
38584         "cm",
38585         "237"
38586       ],
38587       [
38588         "Canada",
38589         "ca",
38590         "1",
38591         1,
38592         ["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"]
38593       ],
38594       [
38595         "Cape Verde (Kabu Verdi)",
38596         "cv",
38597         "238"
38598       ],
38599       [
38600         "Caribbean Netherlands",
38601         "bq",
38602         "599",
38603         1
38604       ],
38605       [
38606         "Cayman Islands",
38607         "ky",
38608         "1345"
38609       ],
38610       [
38611         "Central African Republic (République centrafricaine)",
38612         "cf",
38613         "236"
38614       ],
38615       [
38616         "Chad (Tchad)",
38617         "td",
38618         "235"
38619       ],
38620       [
38621         "Chile",
38622         "cl",
38623         "56"
38624       ],
38625       [
38626         "China (中国)",
38627         "cn",
38628         "86"
38629       ],
38630       [
38631         "Christmas Island",
38632         "cx",
38633         "61",
38634         2
38635       ],
38636       [
38637         "Cocos (Keeling) Islands",
38638         "cc",
38639         "61",
38640         1
38641       ],
38642       [
38643         "Colombia",
38644         "co",
38645         "57"
38646       ],
38647       [
38648         "Comoros (‫جزر القمر‬‎)",
38649         "km",
38650         "269"
38651       ],
38652       [
38653         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38654         "cd",
38655         "243"
38656       ],
38657       [
38658         "Congo (Republic) (Congo-Brazzaville)",
38659         "cg",
38660         "242"
38661       ],
38662       [
38663         "Cook Islands",
38664         "ck",
38665         "682"
38666       ],
38667       [
38668         "Costa Rica",
38669         "cr",
38670         "506"
38671       ],
38672       [
38673         "Côte d’Ivoire",
38674         "ci",
38675         "225"
38676       ],
38677       [
38678         "Croatia (Hrvatska)",
38679         "hr",
38680         "385"
38681       ],
38682       [
38683         "Cuba",
38684         "cu",
38685         "53"
38686       ],
38687       [
38688         "Curaçao",
38689         "cw",
38690         "599",
38691         0
38692       ],
38693       [
38694         "Cyprus (Κύπρος)",
38695         "cy",
38696         "357"
38697       ],
38698       [
38699         "Czech Republic (Česká republika)",
38700         "cz",
38701         "420"
38702       ],
38703       [
38704         "Denmark (Danmark)",
38705         "dk",
38706         "45"
38707       ],
38708       [
38709         "Djibouti",
38710         "dj",
38711         "253"
38712       ],
38713       [
38714         "Dominica",
38715         "dm",
38716         "1767"
38717       ],
38718       [
38719         "Dominican Republic (República Dominicana)",
38720         "do",
38721         "1",
38722         2,
38723         ["809", "829", "849"]
38724       ],
38725       [
38726         "Ecuador",
38727         "ec",
38728         "593"
38729       ],
38730       [
38731         "Egypt (‫مصر‬‎)",
38732         "eg",
38733         "20"
38734       ],
38735       [
38736         "El Salvador",
38737         "sv",
38738         "503"
38739       ],
38740       [
38741         "Equatorial Guinea (Guinea Ecuatorial)",
38742         "gq",
38743         "240"
38744       ],
38745       [
38746         "Eritrea",
38747         "er",
38748         "291"
38749       ],
38750       [
38751         "Estonia (Eesti)",
38752         "ee",
38753         "372"
38754       ],
38755       [
38756         "Ethiopia",
38757         "et",
38758         "251"
38759       ],
38760       [
38761         "Falkland Islands (Islas Malvinas)",
38762         "fk",
38763         "500"
38764       ],
38765       [
38766         "Faroe Islands (Føroyar)",
38767         "fo",
38768         "298"
38769       ],
38770       [
38771         "Fiji",
38772         "fj",
38773         "679"
38774       ],
38775       [
38776         "Finland (Suomi)",
38777         "fi",
38778         "358",
38779         0
38780       ],
38781       [
38782         "France",
38783         "fr",
38784         "33"
38785       ],
38786       [
38787         "French Guiana (Guyane française)",
38788         "gf",
38789         "594"
38790       ],
38791       [
38792         "French Polynesia (Polynésie française)",
38793         "pf",
38794         "689"
38795       ],
38796       [
38797         "Gabon",
38798         "ga",
38799         "241"
38800       ],
38801       [
38802         "Gambia",
38803         "gm",
38804         "220"
38805       ],
38806       [
38807         "Georgia (საქართველო)",
38808         "ge",
38809         "995"
38810       ],
38811       [
38812         "Germany (Deutschland)",
38813         "de",
38814         "49"
38815       ],
38816       [
38817         "Ghana (Gaana)",
38818         "gh",
38819         "233"
38820       ],
38821       [
38822         "Gibraltar",
38823         "gi",
38824         "350"
38825       ],
38826       [
38827         "Greece (Ελλάδα)",
38828         "gr",
38829         "30"
38830       ],
38831       [
38832         "Greenland (Kalaallit Nunaat)",
38833         "gl",
38834         "299"
38835       ],
38836       [
38837         "Grenada",
38838         "gd",
38839         "1473"
38840       ],
38841       [
38842         "Guadeloupe",
38843         "gp",
38844         "590",
38845         0
38846       ],
38847       [
38848         "Guam",
38849         "gu",
38850         "1671"
38851       ],
38852       [
38853         "Guatemala",
38854         "gt",
38855         "502"
38856       ],
38857       [
38858         "Guernsey",
38859         "gg",
38860         "44",
38861         1
38862       ],
38863       [
38864         "Guinea (Guinée)",
38865         "gn",
38866         "224"
38867       ],
38868       [
38869         "Guinea-Bissau (Guiné Bissau)",
38870         "gw",
38871         "245"
38872       ],
38873       [
38874         "Guyana",
38875         "gy",
38876         "592"
38877       ],
38878       [
38879         "Haiti",
38880         "ht",
38881         "509"
38882       ],
38883       [
38884         "Honduras",
38885         "hn",
38886         "504"
38887       ],
38888       [
38889         "Hong Kong (香港)",
38890         "hk",
38891         "852"
38892       ],
38893       [
38894         "Hungary (Magyarország)",
38895         "hu",
38896         "36"
38897       ],
38898       [
38899         "Iceland (Ísland)",
38900         "is",
38901         "354"
38902       ],
38903       [
38904         "India (भारत)",
38905         "in",
38906         "91"
38907       ],
38908       [
38909         "Indonesia",
38910         "id",
38911         "62"
38912       ],
38913       [
38914         "Iran (‫ایران‬‎)",
38915         "ir",
38916         "98"
38917       ],
38918       [
38919         "Iraq (‫العراق‬‎)",
38920         "iq",
38921         "964"
38922       ],
38923       [
38924         "Ireland",
38925         "ie",
38926         "353"
38927       ],
38928       [
38929         "Isle of Man",
38930         "im",
38931         "44",
38932         2
38933       ],
38934       [
38935         "Israel (‫ישראל‬‎)",
38936         "il",
38937         "972"
38938       ],
38939       [
38940         "Italy (Italia)",
38941         "it",
38942         "39",
38943         0
38944       ],
38945       [
38946         "Jamaica",
38947         "jm",
38948         "1876"
38949       ],
38950       [
38951         "Japan (日本)",
38952         "jp",
38953         "81"
38954       ],
38955       [
38956         "Jersey",
38957         "je",
38958         "44",
38959         3
38960       ],
38961       [
38962         "Jordan (‫الأردن‬‎)",
38963         "jo",
38964         "962"
38965       ],
38966       [
38967         "Kazakhstan (Казахстан)",
38968         "kz",
38969         "7",
38970         1
38971       ],
38972       [
38973         "Kenya",
38974         "ke",
38975         "254"
38976       ],
38977       [
38978         "Kiribati",
38979         "ki",
38980         "686"
38981       ],
38982       [
38983         "Kosovo",
38984         "xk",
38985         "383"
38986       ],
38987       [
38988         "Kuwait (‫الكويت‬‎)",
38989         "kw",
38990         "965"
38991       ],
38992       [
38993         "Kyrgyzstan (Кыргызстан)",
38994         "kg",
38995         "996"
38996       ],
38997       [
38998         "Laos (ລາວ)",
38999         "la",
39000         "856"
39001       ],
39002       [
39003         "Latvia (Latvija)",
39004         "lv",
39005         "371"
39006       ],
39007       [
39008         "Lebanon (‫لبنان‬‎)",
39009         "lb",
39010         "961"
39011       ],
39012       [
39013         "Lesotho",
39014         "ls",
39015         "266"
39016       ],
39017       [
39018         "Liberia",
39019         "lr",
39020         "231"
39021       ],
39022       [
39023         "Libya (‫ليبيا‬‎)",
39024         "ly",
39025         "218"
39026       ],
39027       [
39028         "Liechtenstein",
39029         "li",
39030         "423"
39031       ],
39032       [
39033         "Lithuania (Lietuva)",
39034         "lt",
39035         "370"
39036       ],
39037       [
39038         "Luxembourg",
39039         "lu",
39040         "352"
39041       ],
39042       [
39043         "Macau (澳門)",
39044         "mo",
39045         "853"
39046       ],
39047       [
39048         "Macedonia (FYROM) (Македонија)",
39049         "mk",
39050         "389"
39051       ],
39052       [
39053         "Madagascar (Madagasikara)",
39054         "mg",
39055         "261"
39056       ],
39057       [
39058         "Malawi",
39059         "mw",
39060         "265"
39061       ],
39062       [
39063         "Malaysia",
39064         "my",
39065         "60"
39066       ],
39067       [
39068         "Maldives",
39069         "mv",
39070         "960"
39071       ],
39072       [
39073         "Mali",
39074         "ml",
39075         "223"
39076       ],
39077       [
39078         "Malta",
39079         "mt",
39080         "356"
39081       ],
39082       [
39083         "Marshall Islands",
39084         "mh",
39085         "692"
39086       ],
39087       [
39088         "Martinique",
39089         "mq",
39090         "596"
39091       ],
39092       [
39093         "Mauritania (‫موريتانيا‬‎)",
39094         "mr",
39095         "222"
39096       ],
39097       [
39098         "Mauritius (Moris)",
39099         "mu",
39100         "230"
39101       ],
39102       [
39103         "Mayotte",
39104         "yt",
39105         "262",
39106         1
39107       ],
39108       [
39109         "Mexico (México)",
39110         "mx",
39111         "52"
39112       ],
39113       [
39114         "Micronesia",
39115         "fm",
39116         "691"
39117       ],
39118       [
39119         "Moldova (Republica Moldova)",
39120         "md",
39121         "373"
39122       ],
39123       [
39124         "Monaco",
39125         "mc",
39126         "377"
39127       ],
39128       [
39129         "Mongolia (Монгол)",
39130         "mn",
39131         "976"
39132       ],
39133       [
39134         "Montenegro (Crna Gora)",
39135         "me",
39136         "382"
39137       ],
39138       [
39139         "Montserrat",
39140         "ms",
39141         "1664"
39142       ],
39143       [
39144         "Morocco (‫المغرب‬‎)",
39145         "ma",
39146         "212",
39147         0
39148       ],
39149       [
39150         "Mozambique (Moçambique)",
39151         "mz",
39152         "258"
39153       ],
39154       [
39155         "Myanmar (Burma) (မြန်မာ)",
39156         "mm",
39157         "95"
39158       ],
39159       [
39160         "Namibia (Namibië)",
39161         "na",
39162         "264"
39163       ],
39164       [
39165         "Nauru",
39166         "nr",
39167         "674"
39168       ],
39169       [
39170         "Nepal (नेपाल)",
39171         "np",
39172         "977"
39173       ],
39174       [
39175         "Netherlands (Nederland)",
39176         "nl",
39177         "31"
39178       ],
39179       [
39180         "New Caledonia (Nouvelle-Calédonie)",
39181         "nc",
39182         "687"
39183       ],
39184       [
39185         "New Zealand",
39186         "nz",
39187         "64"
39188       ],
39189       [
39190         "Nicaragua",
39191         "ni",
39192         "505"
39193       ],
39194       [
39195         "Niger (Nijar)",
39196         "ne",
39197         "227"
39198       ],
39199       [
39200         "Nigeria",
39201         "ng",
39202         "234"
39203       ],
39204       [
39205         "Niue",
39206         "nu",
39207         "683"
39208       ],
39209       [
39210         "Norfolk Island",
39211         "nf",
39212         "672"
39213       ],
39214       [
39215         "North Korea (조선 민주주의 인민 공화국)",
39216         "kp",
39217         "850"
39218       ],
39219       [
39220         "Northern Mariana Islands",
39221         "mp",
39222         "1670"
39223       ],
39224       [
39225         "Norway (Norge)",
39226         "no",
39227         "47",
39228         0
39229       ],
39230       [
39231         "Oman (‫عُمان‬‎)",
39232         "om",
39233         "968"
39234       ],
39235       [
39236         "Pakistan (‫پاکستان‬‎)",
39237         "pk",
39238         "92"
39239       ],
39240       [
39241         "Palau",
39242         "pw",
39243         "680"
39244       ],
39245       [
39246         "Palestine (‫فلسطين‬‎)",
39247         "ps",
39248         "970"
39249       ],
39250       [
39251         "Panama (Panamá)",
39252         "pa",
39253         "507"
39254       ],
39255       [
39256         "Papua New Guinea",
39257         "pg",
39258         "675"
39259       ],
39260       [
39261         "Paraguay",
39262         "py",
39263         "595"
39264       ],
39265       [
39266         "Peru (Perú)",
39267         "pe",
39268         "51"
39269       ],
39270       [
39271         "Philippines",
39272         "ph",
39273         "63"
39274       ],
39275       [
39276         "Poland (Polska)",
39277         "pl",
39278         "48"
39279       ],
39280       [
39281         "Portugal",
39282         "pt",
39283         "351"
39284       ],
39285       [
39286         "Puerto Rico",
39287         "pr",
39288         "1",
39289         3,
39290         ["787", "939"]
39291       ],
39292       [
39293         "Qatar (‫قطر‬‎)",
39294         "qa",
39295         "974"
39296       ],
39297       [
39298         "Réunion (La Réunion)",
39299         "re",
39300         "262",
39301         0
39302       ],
39303       [
39304         "Romania (România)",
39305         "ro",
39306         "40"
39307       ],
39308       [
39309         "Russia (Россия)",
39310         "ru",
39311         "7",
39312         0
39313       ],
39314       [
39315         "Rwanda",
39316         "rw",
39317         "250"
39318       ],
39319       [
39320         "Saint Barthélemy",
39321         "bl",
39322         "590",
39323         1
39324       ],
39325       [
39326         "Saint Helena",
39327         "sh",
39328         "290"
39329       ],
39330       [
39331         "Saint Kitts and Nevis",
39332         "kn",
39333         "1869"
39334       ],
39335       [
39336         "Saint Lucia",
39337         "lc",
39338         "1758"
39339       ],
39340       [
39341         "Saint Martin (Saint-Martin (partie française))",
39342         "mf",
39343         "590",
39344         2
39345       ],
39346       [
39347         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39348         "pm",
39349         "508"
39350       ],
39351       [
39352         "Saint Vincent and the Grenadines",
39353         "vc",
39354         "1784"
39355       ],
39356       [
39357         "Samoa",
39358         "ws",
39359         "685"
39360       ],
39361       [
39362         "San Marino",
39363         "sm",
39364         "378"
39365       ],
39366       [
39367         "São Tomé and Príncipe (São Tomé e Príncipe)",
39368         "st",
39369         "239"
39370       ],
39371       [
39372         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39373         "sa",
39374         "966"
39375       ],
39376       [
39377         "Senegal (Sénégal)",
39378         "sn",
39379         "221"
39380       ],
39381       [
39382         "Serbia (Србија)",
39383         "rs",
39384         "381"
39385       ],
39386       [
39387         "Seychelles",
39388         "sc",
39389         "248"
39390       ],
39391       [
39392         "Sierra Leone",
39393         "sl",
39394         "232"
39395       ],
39396       [
39397         "Singapore",
39398         "sg",
39399         "65"
39400       ],
39401       [
39402         "Sint Maarten",
39403         "sx",
39404         "1721"
39405       ],
39406       [
39407         "Slovakia (Slovensko)",
39408         "sk",
39409         "421"
39410       ],
39411       [
39412         "Slovenia (Slovenija)",
39413         "si",
39414         "386"
39415       ],
39416       [
39417         "Solomon Islands",
39418         "sb",
39419         "677"
39420       ],
39421       [
39422         "Somalia (Soomaaliya)",
39423         "so",
39424         "252"
39425       ],
39426       [
39427         "South Africa",
39428         "za",
39429         "27"
39430       ],
39431       [
39432         "South Korea (대한민국)",
39433         "kr",
39434         "82"
39435       ],
39436       [
39437         "South Sudan (‫جنوب السودان‬‎)",
39438         "ss",
39439         "211"
39440       ],
39441       [
39442         "Spain (España)",
39443         "es",
39444         "34"
39445       ],
39446       [
39447         "Sri Lanka (ශ්‍රී ලංකාව)",
39448         "lk",
39449         "94"
39450       ],
39451       [
39452         "Sudan (‫السودان‬‎)",
39453         "sd",
39454         "249"
39455       ],
39456       [
39457         "Suriname",
39458         "sr",
39459         "597"
39460       ],
39461       [
39462         "Svalbard and Jan Mayen",
39463         "sj",
39464         "47",
39465         1
39466       ],
39467       [
39468         "Swaziland",
39469         "sz",
39470         "268"
39471       ],
39472       [
39473         "Sweden (Sverige)",
39474         "se",
39475         "46"
39476       ],
39477       [
39478         "Switzerland (Schweiz)",
39479         "ch",
39480         "41"
39481       ],
39482       [
39483         "Syria (‫سوريا‬‎)",
39484         "sy",
39485         "963"
39486       ],
39487       [
39488         "Taiwan (台灣)",
39489         "tw",
39490         "886"
39491       ],
39492       [
39493         "Tajikistan",
39494         "tj",
39495         "992"
39496       ],
39497       [
39498         "Tanzania",
39499         "tz",
39500         "255"
39501       ],
39502       [
39503         "Thailand (ไทย)",
39504         "th",
39505         "66"
39506       ],
39507       [
39508         "Timor-Leste",
39509         "tl",
39510         "670"
39511       ],
39512       [
39513         "Togo",
39514         "tg",
39515         "228"
39516       ],
39517       [
39518         "Tokelau",
39519         "tk",
39520         "690"
39521       ],
39522       [
39523         "Tonga",
39524         "to",
39525         "676"
39526       ],
39527       [
39528         "Trinidad and Tobago",
39529         "tt",
39530         "1868"
39531       ],
39532       [
39533         "Tunisia (‫تونس‬‎)",
39534         "tn",
39535         "216"
39536       ],
39537       [
39538         "Turkey (Türkiye)",
39539         "tr",
39540         "90"
39541       ],
39542       [
39543         "Turkmenistan",
39544         "tm",
39545         "993"
39546       ],
39547       [
39548         "Turks and Caicos Islands",
39549         "tc",
39550         "1649"
39551       ],
39552       [
39553         "Tuvalu",
39554         "tv",
39555         "688"
39556       ],
39557       [
39558         "U.S. Virgin Islands",
39559         "vi",
39560         "1340"
39561       ],
39562       [
39563         "Uganda",
39564         "ug",
39565         "256"
39566       ],
39567       [
39568         "Ukraine (Україна)",
39569         "ua",
39570         "380"
39571       ],
39572       [
39573         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39574         "ae",
39575         "971"
39576       ],
39577       [
39578         "United Kingdom",
39579         "gb",
39580         "44",
39581         0
39582       ],
39583       [
39584         "United States",
39585         "us",
39586         "1",
39587         0
39588       ],
39589       [
39590         "Uruguay",
39591         "uy",
39592         "598"
39593       ],
39594       [
39595         "Uzbekistan (Oʻzbekiston)",
39596         "uz",
39597         "998"
39598       ],
39599       [
39600         "Vanuatu",
39601         "vu",
39602         "678"
39603       ],
39604       [
39605         "Vatican City (Città del Vaticano)",
39606         "va",
39607         "39",
39608         1
39609       ],
39610       [
39611         "Venezuela",
39612         "ve",
39613         "58"
39614       ],
39615       [
39616         "Vietnam (Việt Nam)",
39617         "vn",
39618         "84"
39619       ],
39620       [
39621         "Wallis and Futuna (Wallis-et-Futuna)",
39622         "wf",
39623         "681"
39624       ],
39625       [
39626         "Western Sahara (‫الصحراء الغربية‬‎)",
39627         "eh",
39628         "212",
39629         1
39630       ],
39631       [
39632         "Yemen (‫اليمن‬‎)",
39633         "ye",
39634         "967"
39635       ],
39636       [
39637         "Zambia",
39638         "zm",
39639         "260"
39640       ],
39641       [
39642         "Zimbabwe",
39643         "zw",
39644         "263"
39645       ],
39646       [
39647         "Åland Islands",
39648         "ax",
39649         "358",
39650         1
39651       ]
39652   ];
39653   
39654   return d;
39655 }/**
39656 *    This script refer to:
39657 *    Title: International Telephone Input
39658 *    Author: Jack O'Connor
39659 *    Code version:  v12.1.12
39660 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39661 **/
39662
39663 /**
39664  * @class Roo.bootstrap.PhoneInput
39665  * @extends Roo.bootstrap.TriggerField
39666  * An input with International dial-code selection
39667  
39668  * @cfg {String} defaultDialCode default '+852'
39669  * @cfg {Array} preferedCountries default []
39670   
39671  * @constructor
39672  * Create a new PhoneInput.
39673  * @param {Object} config Configuration options
39674  */
39675
39676 Roo.bootstrap.PhoneInput = function(config) {
39677     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39678 };
39679
39680 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39681         
39682         listWidth: undefined,
39683         
39684         selectedClass: 'active',
39685         
39686         invalidClass : "has-warning",
39687         
39688         validClass: 'has-success',
39689         
39690         allowed: '0123456789',
39691         
39692         /**
39693          * @cfg {String} defaultDialCode The default dial code when initializing the input
39694          */
39695         defaultDialCode: '+852',
39696         
39697         /**
39698          * @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
39699          */
39700         preferedCountries: false,
39701         
39702         getAutoCreate : function()
39703         {
39704             var data = Roo.bootstrap.PhoneInputData();
39705             var align = this.labelAlign || this.parentLabelAlign();
39706             var id = Roo.id();
39707             
39708             this.allCountries = [];
39709             this.dialCodeMapping = [];
39710             
39711             for (var i = 0; i < data.length; i++) {
39712               var c = data[i];
39713               this.allCountries[i] = {
39714                 name: c[0],
39715                 iso2: c[1],
39716                 dialCode: c[2],
39717                 priority: c[3] || 0,
39718                 areaCodes: c[4] || null
39719               };
39720               this.dialCodeMapping[c[2]] = {
39721                   name: c[0],
39722                   iso2: c[1],
39723                   priority: c[3] || 0,
39724                   areaCodes: c[4] || null
39725               };
39726             }
39727             
39728             var cfg = {
39729                 cls: 'form-group',
39730                 cn: []
39731             };
39732             
39733             var input =  {
39734                 tag: 'input',
39735                 id : id,
39736                 cls : 'form-control tel-input',
39737                 autocomplete: 'new-password'
39738             };
39739             
39740             var hiddenInput = {
39741                 tag: 'input',
39742                 type: 'hidden',
39743                 cls: 'hidden-tel-input'
39744             };
39745             
39746             if (this.name) {
39747                 hiddenInput.name = this.name;
39748             }
39749             
39750             if (this.disabled) {
39751                 input.disabled = true;
39752             }
39753             
39754             var flag_container = {
39755                 tag: 'div',
39756                 cls: 'flag-box',
39757                 cn: [
39758                     {
39759                         tag: 'div',
39760                         cls: 'flag'
39761                     },
39762                     {
39763                         tag: 'div',
39764                         cls: 'caret'
39765                     }
39766                 ]
39767             };
39768             
39769             var box = {
39770                 tag: 'div',
39771                 cls: this.hasFeedback ? 'has-feedback' : '',
39772                 cn: [
39773                     hiddenInput,
39774                     input,
39775                     {
39776                         tag: 'input',
39777                         cls: 'dial-code-holder',
39778                         disabled: true
39779                     }
39780                 ]
39781             };
39782             
39783             var container = {
39784                 cls: 'roo-select2-container input-group',
39785                 cn: [
39786                     flag_container,
39787                     box
39788                 ]
39789             };
39790             
39791             if (this.fieldLabel.length) {
39792                 var indicator = {
39793                     tag: 'i',
39794                     tooltip: 'This field is required'
39795                 };
39796                 
39797                 var label = {
39798                     tag: 'label',
39799                     'for':  id,
39800                     cls: 'control-label',
39801                     cn: []
39802                 };
39803                 
39804                 var label_text = {
39805                     tag: 'span',
39806                     html: this.fieldLabel
39807                 };
39808                 
39809                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39810                 label.cn = [
39811                     indicator,
39812                     label_text
39813                 ];
39814                 
39815                 if(this.indicatorpos == 'right') {
39816                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39817                     label.cn = [
39818                         label_text,
39819                         indicator
39820                     ];
39821                 }
39822                 
39823                 if(align == 'left') {
39824                     container = {
39825                         tag: 'div',
39826                         cn: [
39827                             container
39828                         ]
39829                     };
39830                     
39831                     if(this.labelWidth > 12){
39832                         label.style = "width: " + this.labelWidth + 'px';
39833                     }
39834                     if(this.labelWidth < 13 && this.labelmd == 0){
39835                         this.labelmd = this.labelWidth;
39836                     }
39837                     if(this.labellg > 0){
39838                         label.cls += ' col-lg-' + this.labellg;
39839                         input.cls += ' col-lg-' + (12 - this.labellg);
39840                     }
39841                     if(this.labelmd > 0){
39842                         label.cls += ' col-md-' + this.labelmd;
39843                         container.cls += ' col-md-' + (12 - this.labelmd);
39844                     }
39845                     if(this.labelsm > 0){
39846                         label.cls += ' col-sm-' + this.labelsm;
39847                         container.cls += ' col-sm-' + (12 - this.labelsm);
39848                     }
39849                     if(this.labelxs > 0){
39850                         label.cls += ' col-xs-' + this.labelxs;
39851                         container.cls += ' col-xs-' + (12 - this.labelxs);
39852                     }
39853                 }
39854             }
39855             
39856             cfg.cn = [
39857                 label,
39858                 container
39859             ];
39860             
39861             var settings = this;
39862             
39863             ['xs','sm','md','lg'].map(function(size){
39864                 if (settings[size]) {
39865                     cfg.cls += ' col-' + size + '-' + settings[size];
39866                 }
39867             });
39868             
39869             this.store = new Roo.data.Store({
39870                 proxy : new Roo.data.MemoryProxy({}),
39871                 reader : new Roo.data.JsonReader({
39872                     fields : [
39873                         {
39874                             'name' : 'name',
39875                             'type' : 'string'
39876                         },
39877                         {
39878                             'name' : 'iso2',
39879                             'type' : 'string'
39880                         },
39881                         {
39882                             'name' : 'dialCode',
39883                             'type' : 'string'
39884                         },
39885                         {
39886                             'name' : 'priority',
39887                             'type' : 'string'
39888                         },
39889                         {
39890                             'name' : 'areaCodes',
39891                             'type' : 'string'
39892                         }
39893                     ]
39894                 })
39895             });
39896             
39897             if(!this.preferedCountries) {
39898                 this.preferedCountries = [
39899                     'hk',
39900                     'gb',
39901                     'us'
39902                 ];
39903             }
39904             
39905             var p = this.preferedCountries.reverse();
39906             
39907             if(p) {
39908                 for (var i = 0; i < p.length; i++) {
39909                     for (var j = 0; j < this.allCountries.length; j++) {
39910                         if(this.allCountries[j].iso2 == p[i]) {
39911                             var t = this.allCountries[j];
39912                             this.allCountries.splice(j,1);
39913                             this.allCountries.unshift(t);
39914                         }
39915                     } 
39916                 }
39917             }
39918             
39919             this.store.proxy.data = {
39920                 success: true,
39921                 data: this.allCountries
39922             };
39923             
39924             return cfg;
39925         },
39926         
39927         initEvents : function()
39928         {
39929             this.createList();
39930             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39931             
39932             this.indicator = this.indicatorEl();
39933             this.flag = this.flagEl();
39934             this.dialCodeHolder = this.dialCodeHolderEl();
39935             
39936             this.trigger = this.el.select('div.flag-box',true).first();
39937             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39938             
39939             var _this = this;
39940             
39941             (function(){
39942                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39943                 _this.list.setWidth(lw);
39944             }).defer(100);
39945             
39946             this.list.on('mouseover', this.onViewOver, this);
39947             this.list.on('mousemove', this.onViewMove, this);
39948             this.inputEl().on("keyup", this.onKeyUp, this);
39949             
39950             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39951
39952             this.view = new Roo.View(this.list, this.tpl, {
39953                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39954             });
39955             
39956             this.view.on('click', this.onViewClick, this);
39957             this.setValue(this.defaultDialCode);
39958         },
39959         
39960         onTriggerClick : function(e)
39961         {
39962             Roo.log('trigger click');
39963             if(this.disabled){
39964                 return;
39965             }
39966             
39967             if(this.isExpanded()){
39968                 this.collapse();
39969                 this.hasFocus = false;
39970             }else {
39971                 this.store.load({});
39972                 this.hasFocus = true;
39973                 this.expand();
39974             }
39975         },
39976         
39977         isExpanded : function()
39978         {
39979             return this.list.isVisible();
39980         },
39981         
39982         collapse : function()
39983         {
39984             if(!this.isExpanded()){
39985                 return;
39986             }
39987             this.list.hide();
39988             Roo.get(document).un('mousedown', this.collapseIf, this);
39989             Roo.get(document).un('mousewheel', this.collapseIf, this);
39990             this.fireEvent('collapse', this);
39991             this.validate();
39992         },
39993         
39994         expand : function()
39995         {
39996             Roo.log('expand');
39997
39998             if(this.isExpanded() || !this.hasFocus){
39999                 return;
40000             }
40001             
40002             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40003             this.list.setWidth(lw);
40004             
40005             this.list.show();
40006             this.restrictHeight();
40007             
40008             Roo.get(document).on('mousedown', this.collapseIf, this);
40009             Roo.get(document).on('mousewheel', this.collapseIf, this);
40010             
40011             this.fireEvent('expand', this);
40012         },
40013         
40014         restrictHeight : function()
40015         {
40016             this.list.alignTo(this.inputEl(), this.listAlign);
40017             this.list.alignTo(this.inputEl(), this.listAlign);
40018         },
40019         
40020         onViewOver : function(e, t)
40021         {
40022             if(this.inKeyMode){
40023                 return;
40024             }
40025             var item = this.view.findItemFromChild(t);
40026             
40027             if(item){
40028                 var index = this.view.indexOf(item);
40029                 this.select(index, false);
40030             }
40031         },
40032
40033         // private
40034         onViewClick : function(view, doFocus, el, e)
40035         {
40036             var index = this.view.getSelectedIndexes()[0];
40037             
40038             var r = this.store.getAt(index);
40039             
40040             if(r){
40041                 this.onSelect(r, index);
40042             }
40043             if(doFocus !== false && !this.blockFocus){
40044                 this.inputEl().focus();
40045             }
40046         },
40047         
40048         onViewMove : function(e, t)
40049         {
40050             this.inKeyMode = false;
40051         },
40052         
40053         select : function(index, scrollIntoView)
40054         {
40055             this.selectedIndex = index;
40056             this.view.select(index);
40057             if(scrollIntoView !== false){
40058                 var el = this.view.getNode(index);
40059                 if(el){
40060                     this.list.scrollChildIntoView(el, false);
40061                 }
40062             }
40063         },
40064         
40065         createList : function()
40066         {
40067             this.list = Roo.get(document.body).createChild({
40068                 tag: 'ul',
40069                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40070                 style: 'display:none'
40071             });
40072             
40073             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40074         },
40075         
40076         collapseIf : function(e)
40077         {
40078             var in_combo  = e.within(this.el);
40079             var in_list =  e.within(this.list);
40080             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40081             
40082             if (in_combo || in_list || is_list) {
40083                 return;
40084             }
40085             this.collapse();
40086         },
40087         
40088         onSelect : function(record, index)
40089         {
40090             if(this.fireEvent('beforeselect', this, record, index) !== false){
40091                 
40092                 this.setFlagClass(record.data.iso2);
40093                 this.setDialCode(record.data.dialCode);
40094                 this.hasFocus = false;
40095                 this.collapse();
40096                 this.fireEvent('select', this, record, index);
40097             }
40098         },
40099         
40100         flagEl : function()
40101         {
40102             var flag = this.el.select('div.flag',true).first();
40103             if(!flag){
40104                 return false;
40105             }
40106             return flag;
40107         },
40108         
40109         dialCodeHolderEl : function()
40110         {
40111             var d = this.el.select('input.dial-code-holder',true).first();
40112             if(!d){
40113                 return false;
40114             }
40115             return d;
40116         },
40117         
40118         setDialCode : function(v)
40119         {
40120             this.dialCodeHolder.dom.value = '+'+v;
40121         },
40122         
40123         setFlagClass : function(n)
40124         {
40125             this.flag.dom.className = 'flag '+n;
40126         },
40127         
40128         getValue : function()
40129         {
40130             var v = this.inputEl().getValue();
40131             if(this.dialCodeHolder) {
40132                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40133             }
40134             return v;
40135         },
40136         
40137         setValue : function(v)
40138         {
40139             var d = this.getDialCode(v);
40140             
40141             //invalid dial code
40142             if(v.length == 0 || !d || d.length == 0) {
40143                 if(this.rendered){
40144                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40145                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40146                 }
40147                 return;
40148             }
40149             
40150             //valid dial code
40151             this.setFlagClass(this.dialCodeMapping[d].iso2);
40152             this.setDialCode(d);
40153             this.inputEl().dom.value = v.replace('+'+d,'');
40154             this.hiddenEl().dom.value = this.getValue();
40155             
40156             this.validate();
40157         },
40158         
40159         getDialCode : function(v)
40160         {
40161             v = v ||  '';
40162             
40163             if (v.length == 0) {
40164                 return this.dialCodeHolder.dom.value;
40165             }
40166             
40167             var dialCode = "";
40168             if (v.charAt(0) != "+") {
40169                 return false;
40170             }
40171             var numericChars = "";
40172             for (var i = 1; i < v.length; i++) {
40173               var c = v.charAt(i);
40174               if (!isNaN(c)) {
40175                 numericChars += c;
40176                 if (this.dialCodeMapping[numericChars]) {
40177                   dialCode = v.substr(1, i);
40178                 }
40179                 if (numericChars.length == 4) {
40180                   break;
40181                 }
40182               }
40183             }
40184             return dialCode;
40185         },
40186         
40187         reset : function()
40188         {
40189             this.setValue(this.defaultDialCode);
40190             this.validate();
40191         },
40192         
40193         hiddenEl : function()
40194         {
40195             return this.el.select('input.hidden-tel-input',true).first();
40196         },
40197         
40198         onKeyUp : function(e){
40199             
40200             var k = e.getKey();
40201             var c = e.getCharCode();
40202             
40203             if(
40204                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40205                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40206             ){
40207                 e.stopEvent();
40208             }
40209             
40210             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40211             //     return;
40212             // }
40213             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40214                 e.stopEvent();
40215             }
40216             
40217             this.setValue(this.getValue());
40218         }
40219         
40220 });
40221 /**
40222  * @class Roo.bootstrap.MoneyField
40223  * @extends Roo.bootstrap.ComboBox
40224  * Bootstrap MoneyField class
40225  * 
40226  * @constructor
40227  * Create a new MoneyField.
40228  * @param {Object} config Configuration options
40229  */
40230
40231 Roo.bootstrap.MoneyField = function(config) {
40232     
40233     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40234     
40235 };
40236
40237 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40238     
40239     /**
40240      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40241      */
40242     allowDecimals : true,
40243     /**
40244      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40245      */
40246     decimalSeparator : ".",
40247     /**
40248      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40249      */
40250     decimalPrecision : 0,
40251     /**
40252      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40253      */
40254     allowNegative : true,
40255     /**
40256      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40257      */
40258     allowZero: true,
40259     /**
40260      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40261      */
40262     minValue : Number.NEGATIVE_INFINITY,
40263     /**
40264      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40265      */
40266     maxValue : Number.MAX_VALUE,
40267     /**
40268      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40269      */
40270     minText : "The minimum value for this field is {0}",
40271     /**
40272      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40273      */
40274     maxText : "The maximum value for this field is {0}",
40275     /**
40276      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40277      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40278      */
40279     nanText : "{0} is not a valid number",
40280     /**
40281      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40282      */
40283     castInt : true,
40284     /**
40285      * @cfg {String} defaults currency of the MoneyField
40286      * value should be in lkey
40287      */
40288     defaultCurrency : false,
40289     /**
40290      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40291      */
40292     thousandsDelimiter : false,
40293     
40294     
40295     inputlg : 9,
40296     inputmd : 9,
40297     inputsm : 9,
40298     inputxs : 6,
40299     
40300     store : false,
40301     
40302     getAutoCreate : function()
40303     {
40304         var align = this.labelAlign || this.parentLabelAlign();
40305         
40306         var id = Roo.id();
40307
40308         var cfg = {
40309             cls: 'form-group',
40310             cn: []
40311         };
40312
40313         var input =  {
40314             tag: 'input',
40315             id : id,
40316             cls : 'form-control roo-money-amount-input',
40317             autocomplete: 'new-password'
40318         };
40319         
40320         var hiddenInput = {
40321             tag: 'input',
40322             type: 'hidden',
40323             id: Roo.id(),
40324             cls: 'hidden-number-input'
40325         };
40326         
40327         if (this.name) {
40328             hiddenInput.name = this.name;
40329         }
40330
40331         if (this.disabled) {
40332             input.disabled = true;
40333         }
40334
40335         var clg = 12 - this.inputlg;
40336         var cmd = 12 - this.inputmd;
40337         var csm = 12 - this.inputsm;
40338         var cxs = 12 - this.inputxs;
40339         
40340         var container = {
40341             tag : 'div',
40342             cls : 'row roo-money-field',
40343             cn : [
40344                 {
40345                     tag : 'div',
40346                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40347                     cn : [
40348                         {
40349                             tag : 'div',
40350                             cls: 'roo-select2-container input-group',
40351                             cn: [
40352                                 {
40353                                     tag : 'input',
40354                                     cls : 'form-control roo-money-currency-input',
40355                                     autocomplete: 'new-password',
40356                                     readOnly : 1,
40357                                     name : this.currencyName
40358                                 },
40359                                 {
40360                                     tag :'span',
40361                                     cls : 'input-group-addon',
40362                                     cn : [
40363                                         {
40364                                             tag: 'span',
40365                                             cls: 'caret'
40366                                         }
40367                                     ]
40368                                 }
40369                             ]
40370                         }
40371                     ]
40372                 },
40373                 {
40374                     tag : 'div',
40375                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40376                     cn : [
40377                         {
40378                             tag: 'div',
40379                             cls: this.hasFeedback ? 'has-feedback' : '',
40380                             cn: [
40381                                 input
40382                             ]
40383                         }
40384                     ]
40385                 }
40386             ]
40387             
40388         };
40389         
40390         if (this.fieldLabel.length) {
40391             var indicator = {
40392                 tag: 'i',
40393                 tooltip: 'This field is required'
40394             };
40395
40396             var label = {
40397                 tag: 'label',
40398                 'for':  id,
40399                 cls: 'control-label',
40400                 cn: []
40401             };
40402
40403             var label_text = {
40404                 tag: 'span',
40405                 html: this.fieldLabel
40406             };
40407
40408             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40409             label.cn = [
40410                 indicator,
40411                 label_text
40412             ];
40413
40414             if(this.indicatorpos == 'right') {
40415                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40416                 label.cn = [
40417                     label_text,
40418                     indicator
40419                 ];
40420             }
40421
40422             if(align == 'left') {
40423                 container = {
40424                     tag: 'div',
40425                     cn: [
40426                         container
40427                     ]
40428                 };
40429
40430                 if(this.labelWidth > 12){
40431                     label.style = "width: " + this.labelWidth + 'px';
40432                 }
40433                 if(this.labelWidth < 13 && this.labelmd == 0){
40434                     this.labelmd = this.labelWidth;
40435                 }
40436                 if(this.labellg > 0){
40437                     label.cls += ' col-lg-' + this.labellg;
40438                     input.cls += ' col-lg-' + (12 - this.labellg);
40439                 }
40440                 if(this.labelmd > 0){
40441                     label.cls += ' col-md-' + this.labelmd;
40442                     container.cls += ' col-md-' + (12 - this.labelmd);
40443                 }
40444                 if(this.labelsm > 0){
40445                     label.cls += ' col-sm-' + this.labelsm;
40446                     container.cls += ' col-sm-' + (12 - this.labelsm);
40447                 }
40448                 if(this.labelxs > 0){
40449                     label.cls += ' col-xs-' + this.labelxs;
40450                     container.cls += ' col-xs-' + (12 - this.labelxs);
40451                 }
40452             }
40453         }
40454
40455         cfg.cn = [
40456             label,
40457             container,
40458             hiddenInput
40459         ];
40460         
40461         var settings = this;
40462
40463         ['xs','sm','md','lg'].map(function(size){
40464             if (settings[size]) {
40465                 cfg.cls += ' col-' + size + '-' + settings[size];
40466             }
40467         });
40468         
40469         return cfg;
40470     },
40471     
40472     initEvents : function()
40473     {
40474         this.indicator = this.indicatorEl();
40475         
40476         this.initCurrencyEvent();
40477         
40478         this.initNumberEvent();
40479     },
40480     
40481     initCurrencyEvent : function()
40482     {
40483         if (!this.store) {
40484             throw "can not find store for combo";
40485         }
40486         
40487         this.store = Roo.factory(this.store, Roo.data);
40488         this.store.parent = this;
40489         
40490         this.createList();
40491         
40492         this.triggerEl = this.el.select('.input-group-addon', true).first();
40493         
40494         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40495         
40496         var _this = this;
40497         
40498         (function(){
40499             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40500             _this.list.setWidth(lw);
40501         }).defer(100);
40502         
40503         this.list.on('mouseover', this.onViewOver, this);
40504         this.list.on('mousemove', this.onViewMove, this);
40505         this.list.on('scroll', this.onViewScroll, this);
40506         
40507         if(!this.tpl){
40508             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40509         }
40510         
40511         this.view = new Roo.View(this.list, this.tpl, {
40512             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40513         });
40514         
40515         this.view.on('click', this.onViewClick, this);
40516         
40517         this.store.on('beforeload', this.onBeforeLoad, this);
40518         this.store.on('load', this.onLoad, this);
40519         this.store.on('loadexception', this.onLoadException, this);
40520         
40521         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40522             "up" : function(e){
40523                 this.inKeyMode = true;
40524                 this.selectPrev();
40525             },
40526
40527             "down" : function(e){
40528                 if(!this.isExpanded()){
40529                     this.onTriggerClick();
40530                 }else{
40531                     this.inKeyMode = true;
40532                     this.selectNext();
40533                 }
40534             },
40535
40536             "enter" : function(e){
40537                 this.collapse();
40538                 
40539                 if(this.fireEvent("specialkey", this, e)){
40540                     this.onViewClick(false);
40541                 }
40542                 
40543                 return true;
40544             },
40545
40546             "esc" : function(e){
40547                 this.collapse();
40548             },
40549
40550             "tab" : function(e){
40551                 this.collapse();
40552                 
40553                 if(this.fireEvent("specialkey", this, e)){
40554                     this.onViewClick(false);
40555                 }
40556                 
40557                 return true;
40558             },
40559
40560             scope : this,
40561
40562             doRelay : function(foo, bar, hname){
40563                 if(hname == 'down' || this.scope.isExpanded()){
40564                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40565                 }
40566                 return true;
40567             },
40568
40569             forceKeyDown: true
40570         });
40571         
40572         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40573         
40574     },
40575     
40576     initNumberEvent : function(e)
40577     {
40578         this.inputEl().on("keydown" , this.fireKey,  this);
40579         this.inputEl().on("focus", this.onFocus,  this);
40580         this.inputEl().on("blur", this.onBlur,  this);
40581         
40582         this.inputEl().relayEvent('keyup', this);
40583         
40584         if(this.indicator){
40585             this.indicator.addClass('invisible');
40586         }
40587  
40588         this.originalValue = this.getValue();
40589         
40590         if(this.validationEvent == 'keyup'){
40591             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40592             this.inputEl().on('keyup', this.filterValidation, this);
40593         }
40594         else if(this.validationEvent !== false){
40595             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40596         }
40597         
40598         if(this.selectOnFocus){
40599             this.on("focus", this.preFocus, this);
40600             
40601         }
40602         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40603             this.inputEl().on("keypress", this.filterKeys, this);
40604         } else {
40605             this.inputEl().relayEvent('keypress', this);
40606         }
40607         
40608         var allowed = "0123456789";
40609         
40610         if(this.allowDecimals){
40611             allowed += this.decimalSeparator;
40612         }
40613         
40614         if(this.allowNegative){
40615             allowed += "-";
40616         }
40617         
40618         if(this.thousandsDelimiter) {
40619             allowed += ",";
40620         }
40621         
40622         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40623         
40624         var keyPress = function(e){
40625             
40626             var k = e.getKey();
40627             
40628             var c = e.getCharCode();
40629             
40630             if(
40631                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40632                     allowed.indexOf(String.fromCharCode(c)) === -1
40633             ){
40634                 e.stopEvent();
40635                 return;
40636             }
40637             
40638             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40639                 return;
40640             }
40641             
40642             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40643                 e.stopEvent();
40644             }
40645         };
40646         
40647         this.inputEl().on("keypress", keyPress, this);
40648         
40649     },
40650     
40651     onTriggerClick : function(e)
40652     {   
40653         if(this.disabled){
40654             return;
40655         }
40656         
40657         this.page = 0;
40658         this.loadNext = false;
40659         
40660         if(this.isExpanded()){
40661             this.collapse();
40662             return;
40663         }
40664         
40665         this.hasFocus = true;
40666         
40667         if(this.triggerAction == 'all') {
40668             this.doQuery(this.allQuery, true);
40669             return;
40670         }
40671         
40672         this.doQuery(this.getRawValue());
40673     },
40674     
40675     getCurrency : function()
40676     {   
40677         var v = this.currencyEl().getValue();
40678         
40679         return v;
40680     },
40681     
40682     restrictHeight : function()
40683     {
40684         this.list.alignTo(this.currencyEl(), this.listAlign);
40685         this.list.alignTo(this.currencyEl(), this.listAlign);
40686     },
40687     
40688     onViewClick : function(view, doFocus, el, e)
40689     {
40690         var index = this.view.getSelectedIndexes()[0];
40691         
40692         var r = this.store.getAt(index);
40693         
40694         if(r){
40695             this.onSelect(r, index);
40696         }
40697     },
40698     
40699     onSelect : function(record, index){
40700         
40701         if(this.fireEvent('beforeselect', this, record, index) !== false){
40702         
40703             this.setFromCurrencyData(index > -1 ? record.data : false);
40704             
40705             this.collapse();
40706             
40707             this.fireEvent('select', this, record, index);
40708         }
40709     },
40710     
40711     setFromCurrencyData : function(o)
40712     {
40713         var currency = '';
40714         
40715         this.lastCurrency = o;
40716         
40717         if (this.currencyField) {
40718             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40719         } else {
40720             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40721         }
40722         
40723         this.lastSelectionText = currency;
40724         
40725         //setting default currency
40726         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40727             this.setCurrency(this.defaultCurrency);
40728             return;
40729         }
40730         
40731         this.setCurrency(currency);
40732     },
40733     
40734     setFromData : function(o)
40735     {
40736         var c = {};
40737         
40738         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40739         
40740         this.setFromCurrencyData(c);
40741         
40742         var value = '';
40743         
40744         if (this.name) {
40745             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40746         } else {
40747             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40748         }
40749         
40750         this.setValue(value);
40751         
40752     },
40753     
40754     setCurrency : function(v)
40755     {   
40756         this.currencyValue = v;
40757         
40758         if(this.rendered){
40759             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40760             this.validate();
40761         }
40762     },
40763     
40764     setValue : function(v)
40765     {
40766         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40767         
40768         this.value = v;
40769         
40770         if(this.rendered){
40771             
40772             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40773             
40774             this.inputEl().dom.value = (v == '') ? '' :
40775                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40776             
40777             if(!this.allowZero && v === '0') {
40778                 this.hiddenEl().dom.value = '';
40779                 this.inputEl().dom.value = '';
40780             }
40781             
40782             this.validate();
40783         }
40784     },
40785     
40786     getRawValue : function()
40787     {
40788         var v = this.inputEl().getValue();
40789         
40790         return v;
40791     },
40792     
40793     getValue : function()
40794     {
40795         return this.fixPrecision(this.parseValue(this.getRawValue()));
40796     },
40797     
40798     parseValue : function(value)
40799     {
40800         if(this.thousandsDelimiter) {
40801             value += "";
40802             r = new RegExp(",", "g");
40803             value = value.replace(r, "");
40804         }
40805         
40806         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40807         return isNaN(value) ? '' : value;
40808         
40809     },
40810     
40811     fixPrecision : function(value)
40812     {
40813         if(this.thousandsDelimiter) {
40814             value += "";
40815             r = new RegExp(",", "g");
40816             value = value.replace(r, "");
40817         }
40818         
40819         var nan = isNaN(value);
40820         
40821         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40822             return nan ? '' : value;
40823         }
40824         return parseFloat(value).toFixed(this.decimalPrecision);
40825     },
40826     
40827     decimalPrecisionFcn : function(v)
40828     {
40829         return Math.floor(v);
40830     },
40831     
40832     validateValue : function(value)
40833     {
40834         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40835             return false;
40836         }
40837         
40838         var num = this.parseValue(value);
40839         
40840         if(isNaN(num)){
40841             this.markInvalid(String.format(this.nanText, value));
40842             return false;
40843         }
40844         
40845         if(num < this.minValue){
40846             this.markInvalid(String.format(this.minText, this.minValue));
40847             return false;
40848         }
40849         
40850         if(num > this.maxValue){
40851             this.markInvalid(String.format(this.maxText, this.maxValue));
40852             return false;
40853         }
40854         
40855         return true;
40856     },
40857     
40858     validate : function()
40859     {
40860         if(this.disabled || this.allowBlank){
40861             this.markValid();
40862             return true;
40863         }
40864         
40865         var currency = this.getCurrency();
40866         
40867         if(this.validateValue(this.getRawValue()) && currency.length){
40868             this.markValid();
40869             return true;
40870         }
40871         
40872         this.markInvalid();
40873         return false;
40874     },
40875     
40876     getName: function()
40877     {
40878         return this.name;
40879     },
40880     
40881     beforeBlur : function()
40882     {
40883         if(!this.castInt){
40884             return;
40885         }
40886         
40887         var v = this.parseValue(this.getRawValue());
40888         
40889         if(v || v == 0){
40890             this.setValue(v);
40891         }
40892     },
40893     
40894     onBlur : function()
40895     {
40896         this.beforeBlur();
40897         
40898         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40899             //this.el.removeClass(this.focusClass);
40900         }
40901         
40902         this.hasFocus = false;
40903         
40904         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40905             this.validate();
40906         }
40907         
40908         var v = this.getValue();
40909         
40910         if(String(v) !== String(this.startValue)){
40911             this.fireEvent('change', this, v, this.startValue);
40912         }
40913         
40914         this.fireEvent("blur", this);
40915     },
40916     
40917     inputEl : function()
40918     {
40919         return this.el.select('.roo-money-amount-input', true).first();
40920     },
40921     
40922     currencyEl : function()
40923     {
40924         return this.el.select('.roo-money-currency-input', true).first();
40925     },
40926     
40927     hiddenEl : function()
40928     {
40929         return this.el.select('input.hidden-number-input',true).first();
40930     }
40931     
40932 });