bef0ab713ff3d9250d3da11fdea08506a2c1941e
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         
405     },
406     /**
407      * Hide a component - adds 'hidden' class
408      */
409     hide: function()
410     {
411         if(!this.getVisibilityEl()){
412             return;
413         }
414         
415         this.getVisibilityEl().addClass('hidden');
416         
417     }
418 });
419
420  /*
421  * - LGPL
422  *
423  * Body
424  *
425  */
426
427 /**
428  * @class Roo.bootstrap.Body
429  * @extends Roo.bootstrap.Component
430  * Bootstrap Body class
431  *
432  * @constructor
433  * Create a new body
434  * @param {Object} config The config object
435  */
436
437 Roo.bootstrap.Body = function(config){
438
439     config = config || {};
440
441     Roo.bootstrap.Body.superclass.constructor.call(this, config);
442     this.el = Roo.get(config.el ? config.el : document.body );
443     if (this.cls && this.cls.length) {
444         Roo.get(document.body).addClass(this.cls);
445     }
446 };
447
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
449
450     is_body : true,// just to make sure it's constructed?
451
452         autoCreate : {
453         cls: 'container'
454     },
455     onRender : function(ct, position)
456     {
457        /* Roo.log("Roo.bootstrap.Body - onRender");
458         if (this.cls && this.cls.length) {
459             Roo.get(document.body).addClass(this.cls);
460         }
461         // style??? xttr???
462         */
463     }
464
465
466
467
468 });
469 /*
470  * - LGPL
471  *
472  * button group
473  * 
474  */
475
476
477 /**
478  * @class Roo.bootstrap.ButtonGroup
479  * @extends Roo.bootstrap.Component
480  * Bootstrap ButtonGroup class
481  * @cfg {String} size lg | sm | xs (default empty normal)
482  * @cfg {String} align vertical | justified  (default none)
483  * @cfg {String} direction up | down (default down)
484  * @cfg {Boolean} toolbar false | true
485  * @cfg {Boolean} btn true | false
486  * 
487  * 
488  * @constructor
489  * Create a new Input
490  * @param {Object} config The config object
491  */
492
493 Roo.bootstrap.ButtonGroup = function(config){
494     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
495 };
496
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
498     
499     size: '',
500     align: '',
501     direction: '',
502     toolbar: false,
503     btn: true,
504
505     getAutoCreate : function(){
506         var cfg = {
507             cls: 'btn-group',
508             html : null
509         };
510         
511         cfg.html = this.html || cfg.html;
512         
513         if (this.toolbar) {
514             cfg = {
515                 cls: 'btn-toolbar',
516                 html: null
517             };
518             
519             return cfg;
520         }
521         
522         if (['vertical','justified'].indexOf(this.align)!==-1) {
523             cfg.cls = 'btn-group-' + this.align;
524             
525             if (this.align == 'justified') {
526                 console.log(this.items);
527             }
528         }
529         
530         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531             cfg.cls += ' btn-group-' + this.size;
532         }
533         
534         if (this.direction == 'up') {
535             cfg.cls += ' dropup' ;
536         }
537         
538         return cfg;
539     }
540    
541 });
542
543  /*
544  * - LGPL
545  *
546  * button
547  * 
548  */
549
550 /**
551  * @class Roo.bootstrap.Button
552  * @extends Roo.bootstrap.Component
553  * Bootstrap Button class
554  * @cfg {String} html The button content
555  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
556  * @cfg {String} size ( lg | sm | xs)
557  * @cfg {String} tag ( a | input | submit)
558  * @cfg {String} href empty or href
559  * @cfg {Boolean} disabled default false;
560  * @cfg {Boolean} isClose default false;
561  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
562  * @cfg {String} badge text for badge
563  * @cfg {String} theme (default|glow)  
564  * @cfg {Boolean} inverse dark themed version
565  * @cfg {Boolean} toggle is it a slidy toggle button
566  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567  * @cfg {String} ontext text for on slidy toggle state
568  * @cfg {String} offtext text for off slidy toggle state
569  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
570  * @cfg {Boolean} removeClass remove the standard class..
571  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
572  * 
573  * @constructor
574  * Create a new button
575  * @param {Object} config The config object
576  */
577
578
579 Roo.bootstrap.Button = function(config){
580     Roo.bootstrap.Button.superclass.constructor.call(this, config);
581     this.weightClass = ["btn-default", 
582                        "btn-primary", 
583                        "btn-success", 
584                        "btn-info", 
585                        "btn-warning",
586                        "btn-danger",
587                        "btn-link"
588                       ],  
589     this.addEvents({
590         // raw events
591         /**
592          * @event click
593          * When a butotn is pressed
594          * @param {Roo.bootstrap.Button} btn
595          * @param {Roo.EventObject} e
596          */
597         "click" : true,
598          /**
599          * @event toggle
600          * After the button has been toggles
601          * @param {Roo.bootstrap.Button} btn
602          * @param {Roo.EventObject} e
603          * @param {boolean} pressed (also available as button.pressed)
604          */
605         "toggle" : true
606     });
607 };
608
609 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
610     html: false,
611     active: false,
612     weight: '',
613     size: '',
614     tag: 'button',
615     href: '',
616     disabled: false,
617     isClose: false,
618     glyphicon: '',
619     badge: '',
620     theme: 'default',
621     inverse: false,
622     
623     toggle: false,
624     ontext: 'ON',
625     offtext: 'OFF',
626     defaulton: true,
627     preventDefault: true,
628     removeClass: false,
629     name: false,
630     target: false,
631      
632     pressed : null,
633      
634     
635     getAutoCreate : function(){
636         
637         var cfg = {
638             tag : 'button',
639             cls : 'roo-button',
640             html: ''
641         };
642         
643         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
645             this.tag = 'button';
646         } else {
647             cfg.tag = this.tag;
648         }
649         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
650         
651         if (this.toggle == true) {
652             cfg={
653                 tag: 'div',
654                 cls: 'slider-frame roo-button',
655                 cn: [
656                     {
657                         tag: 'span',
658                         'data-on-text':'ON',
659                         'data-off-text':'OFF',
660                         cls: 'slider-button',
661                         html: this.offtext
662                     }
663                 ]
664             };
665             
666             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667                 cfg.cls += ' '+this.weight;
668             }
669             
670             return cfg;
671         }
672         
673         if (this.isClose) {
674             cfg.cls += ' close';
675             
676             cfg["aria-hidden"] = true;
677             
678             cfg.html = "&times;";
679             
680             return cfg;
681         }
682         
683          
684         if (this.theme==='default') {
685             cfg.cls = 'btn roo-button';
686             
687             //if (this.parentType != 'Navbar') {
688             this.weight = this.weight.length ?  this.weight : 'default';
689             //}
690             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
691                 
692                 cfg.cls += ' btn-' + this.weight;
693             }
694         } else if (this.theme==='glow') {
695             
696             cfg.tag = 'a';
697             cfg.cls = 'btn-glow roo-button';
698             
699             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
700                 
701                 cfg.cls += ' ' + this.weight;
702             }
703         }
704    
705         
706         if (this.inverse) {
707             this.cls += ' inverse';
708         }
709         
710         
711         if (this.active || this.pressed === true) {
712             cfg.cls += ' active';
713         }
714         
715         if (this.disabled) {
716             cfg.disabled = 'disabled';
717         }
718         
719         if (this.items) {
720             Roo.log('changing to ul' );
721             cfg.tag = 'ul';
722             this.glyphicon = 'caret';
723         }
724         
725         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
726          
727         //gsRoo.log(this.parentType);
728         if (this.parentType === 'Navbar' && !this.parent().bar) {
729             Roo.log('changing to li?');
730             
731             cfg.tag = 'li';
732             
733             cfg.cls = '';
734             cfg.cn =  [{
735                 tag : 'a',
736                 cls : 'roo-button',
737                 html : this.html,
738                 href : this.href || '#'
739             }];
740             if (this.menu) {
741                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
742                 cfg.cls += ' dropdown';
743             }   
744             
745             delete cfg.html;
746             
747         }
748         
749        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
750         
751         if (this.glyphicon) {
752             cfg.html = ' ' + cfg.html;
753             
754             cfg.cn = [
755                 {
756                     tag: 'span',
757                     cls: 'glyphicon glyphicon-' + this.glyphicon
758                 }
759             ];
760         }
761         
762         if (this.badge) {
763             cfg.html += ' ';
764             
765             cfg.tag = 'a';
766             
767 //            cfg.cls='btn roo-button';
768             
769             cfg.href=this.href;
770             
771             var value = cfg.html;
772             
773             if(this.glyphicon){
774                 value = {
775                             tag: 'span',
776                             cls: 'glyphicon glyphicon-' + this.glyphicon,
777                             html: this.html
778                         };
779                 
780             }
781             
782             cfg.cn = [
783                 value,
784                 {
785                     tag: 'span',
786                     cls: 'badge',
787                     html: this.badge
788                 }
789             ];
790             
791             cfg.html='';
792         }
793         
794         if (this.menu) {
795             cfg.cls += ' dropdown';
796             cfg.html = typeof(cfg.html) != 'undefined' ?
797                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
798         }
799         
800         if (cfg.tag !== 'a' && this.href !== '') {
801             throw "Tag must be a to set href.";
802         } else if (this.href.length > 0) {
803             cfg.href = this.href;
804         }
805         
806         if(this.removeClass){
807             cfg.cls = '';
808         }
809         
810         if(this.target){
811             cfg.target = this.target;
812         }
813         
814         return cfg;
815     },
816     initEvents: function() {
817        // Roo.log('init events?');
818 //        Roo.log(this.el.dom);
819         // add the menu...
820         
821         if (typeof (this.menu) != 'undefined') {
822             this.menu.parentType = this.xtype;
823             this.menu.triggerEl = this.el;
824             this.addxtype(Roo.apply({}, this.menu));
825         }
826
827
828        if (this.el.hasClass('roo-button')) {
829             this.el.on('click', this.onClick, this);
830        } else {
831             this.el.select('.roo-button').on('click', this.onClick, this);
832        }
833        
834        if(this.removeClass){
835            this.el.on('click', this.onClick, this);
836        }
837        
838        this.el.enableDisplayMode();
839         
840     },
841     onClick : function(e)
842     {
843         if (this.disabled) {
844             return;
845         }
846         
847         Roo.log('button on click ');
848         if(this.preventDefault){
849             e.preventDefault();
850         }
851         
852         if (this.pressed === true || this.pressed === false) {
853             this.toggleActive(e);
854         }
855         
856         
857         this.fireEvent('click', this, e);
858     },
859     
860     /**
861      * Enables this button
862      */
863     enable : function()
864     {
865         this.disabled = false;
866         this.el.removeClass('disabled');
867     },
868     
869     /**
870      * Disable this button
871      */
872     disable : function()
873     {
874         this.disabled = true;
875         this.el.addClass('disabled');
876     },
877      /**
878      * sets the active state on/off, 
879      * @param {Boolean} state (optional) Force a particular state
880      */
881     setActive : function(v) {
882         
883         this.el[v ? 'addClass' : 'removeClass']('active');
884         this.pressed = v;
885     },
886      /**
887      * toggles the current active state 
888      */
889     toggleActive : function(e)
890     {
891         this.setActive(!this.pressed);
892         this.fireEvent('toggle', this, e, !this.pressed);
893     },
894      /**
895      * get the current active state
896      * @return {boolean} true if it's active
897      */
898     isActive : function()
899     {
900         return this.el.hasClass('active');
901     },
902     /**
903      * set the text of the first selected button
904      */
905     setText : function(str)
906     {
907         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
908     },
909     /**
910      * get the text of the first selected button
911      */
912     getText : function()
913     {
914         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
915     },
916     hide: function() {
917        
918      
919         this.el.hide();   
920     },
921     show: function() {
922        
923         this.el.show();   
924     },
925     setWeight : function(str)
926     {
927         this.el.removeClass(this.weightClass);
928         this.el.addClass('btn-' + str);        
929     }
930     
931     
932 });
933
934  /*
935  * - LGPL
936  *
937  * column
938  * 
939  */
940
941 /**
942  * @class Roo.bootstrap.Column
943  * @extends Roo.bootstrap.Component
944  * Bootstrap Column class
945  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
949  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
953  *
954  * 
955  * @cfg {Boolean} hidden (true|false) hide the element
956  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
957  * @cfg {String} fa (ban|check|...) font awesome icon
958  * @cfg {Number} fasize (1|2|....) font awsome size
959
960  * @cfg {String} icon (info-sign|check|...) glyphicon name
961
962  * @cfg {String} html content of column.
963  * 
964  * @constructor
965  * Create a new Column
966  * @param {Object} config The config object
967  */
968
969 Roo.bootstrap.Column = function(config){
970     Roo.bootstrap.Column.superclass.constructor.call(this, config);
971 };
972
973 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
974     
975     xs: false,
976     sm: false,
977     md: false,
978     lg: false,
979     xsoff: false,
980     smoff: false,
981     mdoff: false,
982     lgoff: false,
983     html: '',
984     offset: 0,
985     alert: false,
986     fa: false,
987     icon : false,
988     hidden : false,
989     fasize : 1,
990     
991     getAutoCreate : function(){
992         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
993         
994         cfg = {
995             tag: 'div',
996             cls: 'column'
997         };
998         
999         var settings=this;
1000         ['xs','sm','md','lg'].map(function(size){
1001             //Roo.log( size + ':' + settings[size]);
1002             
1003             if (settings[size+'off'] !== false) {
1004                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1005             }
1006             
1007             if (settings[size] === false) {
1008                 return;
1009             }
1010             
1011             if (!settings[size]) { // 0 = hidden
1012                 cfg.cls += ' hidden-' + size;
1013                 return;
1014             }
1015             cfg.cls += ' col-' + size + '-' + settings[size];
1016             
1017         });
1018         
1019         if (this.hidden) {
1020             cfg.cls += ' hidden';
1021         }
1022         
1023         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024             cfg.cls +=' alert alert-' + this.alert;
1025         }
1026         
1027         
1028         if (this.html.length) {
1029             cfg.html = this.html;
1030         }
1031         if (this.fa) {
1032             var fasize = '';
1033             if (this.fasize > 1) {
1034                 fasize = ' fa-' + this.fasize + 'x';
1035             }
1036             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1037             
1038             
1039         }
1040         if (this.icon) {
1041             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1042         }
1043         
1044         return cfg;
1045     }
1046    
1047 });
1048
1049  
1050
1051  /*
1052  * - LGPL
1053  *
1054  * page container.
1055  * 
1056  */
1057
1058
1059 /**
1060  * @class Roo.bootstrap.Container
1061  * @extends Roo.bootstrap.Component
1062  * Bootstrap Container class
1063  * @cfg {Boolean} jumbotron is it a jumbotron element
1064  * @cfg {String} html content of element
1065  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1066  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1067  * @cfg {String} header content of header (for panel)
1068  * @cfg {String} footer content of footer (for panel)
1069  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1070  * @cfg {String} tag (header|aside|section) type of HTML tag.
1071  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1072  * @cfg {String} fa font awesome icon
1073  * @cfg {String} icon (info-sign|check|...) glyphicon name
1074  * @cfg {Boolean} hidden (true|false) hide the element
1075  * @cfg {Boolean} expandable (true|false) default false
1076  * @cfg {Boolean} expanded (true|false) default true
1077  * @cfg {String} rheader contet on the right of header
1078  * @cfg {Boolean} clickable (true|false) default false
1079
1080  *     
1081  * @constructor
1082  * Create a new Container
1083  * @param {Object} config The config object
1084  */
1085
1086 Roo.bootstrap.Container = function(config){
1087     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1088     
1089     this.addEvents({
1090         // raw events
1091          /**
1092          * @event expand
1093          * After the panel has been expand
1094          * 
1095          * @param {Roo.bootstrap.Container} this
1096          */
1097         "expand" : true,
1098         /**
1099          * @event collapse
1100          * After the panel has been collapsed
1101          * 
1102          * @param {Roo.bootstrap.Container} this
1103          */
1104         "collapse" : true,
1105         /**
1106          * @event click
1107          * When a element is chick
1108          * @param {Roo.bootstrap.Container} this
1109          * @param {Roo.EventObject} e
1110          */
1111         "click" : true
1112     });
1113 };
1114
1115 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1116     
1117     jumbotron : false,
1118     well: '',
1119     panel : '',
1120     header: '',
1121     footer : '',
1122     sticky: '',
1123     tag : false,
1124     alert : false,
1125     fa: false,
1126     icon : false,
1127     expandable : false,
1128     rheader : '',
1129     expanded : true,
1130     clickable: false,
1131   
1132      
1133     getChildContainer : function() {
1134         
1135         if(!this.el){
1136             return false;
1137         }
1138         
1139         if (this.panel.length) {
1140             return this.el.select('.panel-body',true).first();
1141         }
1142         
1143         return this.el;
1144     },
1145     
1146     
1147     getAutoCreate : function(){
1148         
1149         var cfg = {
1150             tag : this.tag || 'div',
1151             html : '',
1152             cls : ''
1153         };
1154         if (this.jumbotron) {
1155             cfg.cls = 'jumbotron';
1156         }
1157         
1158         
1159         
1160         // - this is applied by the parent..
1161         //if (this.cls) {
1162         //    cfg.cls = this.cls + '';
1163         //}
1164         
1165         if (this.sticky.length) {
1166             
1167             var bd = Roo.get(document.body);
1168             if (!bd.hasClass('bootstrap-sticky')) {
1169                 bd.addClass('bootstrap-sticky');
1170                 Roo.select('html',true).setStyle('height', '100%');
1171             }
1172              
1173             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1174         }
1175         
1176         
1177         if (this.well.length) {
1178             switch (this.well) {
1179                 case 'lg':
1180                 case 'sm':
1181                     cfg.cls +=' well well-' +this.well;
1182                     break;
1183                 default:
1184                     cfg.cls +=' well';
1185                     break;
1186             }
1187         }
1188         
1189         if (this.hidden) {
1190             cfg.cls += ' hidden';
1191         }
1192         
1193         
1194         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1195             cfg.cls +=' alert alert-' + this.alert;
1196         }
1197         
1198         var body = cfg;
1199         
1200         if (this.panel.length) {
1201             cfg.cls += ' panel panel-' + this.panel;
1202             cfg.cn = [];
1203             if (this.header.length) {
1204                 
1205                 var h = [];
1206                 
1207                 if(this.expandable){
1208                     
1209                     cfg.cls = cfg.cls + ' expandable';
1210                     
1211                     h.push({
1212                         tag: 'i',
1213                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1214                     });
1215                     
1216                 }
1217                 
1218                 h.push(
1219                     {
1220                         tag: 'span',
1221                         cls : 'panel-title',
1222                         html : (this.expandable ? '&nbsp;' : '') + this.header
1223                     },
1224                     {
1225                         tag: 'span',
1226                         cls: 'panel-header-right',
1227                         html: this.rheader
1228                     }
1229                 );
1230                 
1231                 cfg.cn.push({
1232                     cls : 'panel-heading',
1233                     style : this.expandable ? 'cursor: pointer' : '',
1234                     cn : h
1235                 });
1236                 
1237             }
1238             
1239             body = false;
1240             cfg.cn.push({
1241                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1242                 html : this.html
1243             });
1244             
1245             
1246             if (this.footer.length) {
1247                 cfg.cn.push({
1248                     cls : 'panel-footer',
1249                     html : this.footer
1250                     
1251                 });
1252             }
1253             
1254         }
1255         
1256         if (body) {
1257             body.html = this.html || cfg.html;
1258             // prefix with the icons..
1259             if (this.fa) {
1260                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1261             }
1262             if (this.icon) {
1263                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1264             }
1265             
1266             
1267         }
1268         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1269             cfg.cls =  'container';
1270         }
1271         
1272         return cfg;
1273     },
1274     
1275     initEvents: function() 
1276     {
1277         if(this.expandable){
1278             var headerEl = this.headerEl();
1279         
1280             if(headerEl){
1281                 headerEl.on('click', this.onToggleClick, this);
1282             }
1283         }
1284         
1285         if(this.clickable){
1286             this.el.on('click', this.onClick, this);
1287         }
1288         
1289     },
1290     
1291     onToggleClick : function()
1292     {
1293         var headerEl = this.headerEl();
1294         
1295         if(!headerEl){
1296             return;
1297         }
1298         
1299         if(this.expanded){
1300             this.collapse();
1301             return;
1302         }
1303         
1304         this.expand();
1305     },
1306     
1307     expand : function()
1308     {
1309         if(this.fireEvent('expand', this)) {
1310             
1311             this.expanded = true;
1312             
1313             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1314             
1315             this.el.select('.panel-body',true).first().removeClass('hide');
1316             
1317             var toggleEl = this.toggleEl();
1318
1319             if(!toggleEl){
1320                 return;
1321             }
1322
1323             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1324         }
1325         
1326     },
1327     
1328     collapse : function()
1329     {
1330         if(this.fireEvent('collapse', this)) {
1331             
1332             this.expanded = false;
1333             
1334             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1335             this.el.select('.panel-body',true).first().addClass('hide');
1336         
1337             var toggleEl = this.toggleEl();
1338
1339             if(!toggleEl){
1340                 return;
1341             }
1342
1343             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1344         }
1345     },
1346     
1347     toggleEl : function()
1348     {
1349         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1350             return;
1351         }
1352         
1353         return this.el.select('.panel-heading .fa',true).first();
1354     },
1355     
1356     headerEl : function()
1357     {
1358         if(!this.el || !this.panel.length || !this.header.length){
1359             return;
1360         }
1361         
1362         return this.el.select('.panel-heading',true).first()
1363     },
1364     
1365     bodyEl : function()
1366     {
1367         if(!this.el || !this.panel.length){
1368             return;
1369         }
1370         
1371         return this.el.select('.panel-body',true).first()
1372     },
1373     
1374     titleEl : function()
1375     {
1376         if(!this.el || !this.panel.length || !this.header.length){
1377             return;
1378         }
1379         
1380         return this.el.select('.panel-title',true).first();
1381     },
1382     
1383     setTitle : function(v)
1384     {
1385         var titleEl = this.titleEl();
1386         
1387         if(!titleEl){
1388             return;
1389         }
1390         
1391         titleEl.dom.innerHTML = v;
1392     },
1393     
1394     getTitle : function()
1395     {
1396         
1397         var titleEl = this.titleEl();
1398         
1399         if(!titleEl){
1400             return '';
1401         }
1402         
1403         return titleEl.dom.innerHTML;
1404     },
1405     
1406     setRightTitle : function(v)
1407     {
1408         var t = this.el.select('.panel-header-right',true).first();
1409         
1410         if(!t){
1411             return;
1412         }
1413         
1414         t.dom.innerHTML = v;
1415     },
1416     
1417     onClick : function(e)
1418     {
1419         e.preventDefault();
1420         
1421         this.fireEvent('click', this, e);
1422     }
1423 });
1424
1425  /*
1426  * - LGPL
1427  *
1428  * image
1429  * 
1430  */
1431
1432
1433 /**
1434  * @class Roo.bootstrap.Img
1435  * @extends Roo.bootstrap.Component
1436  * Bootstrap Img class
1437  * @cfg {Boolean} imgResponsive false | true
1438  * @cfg {String} border rounded | circle | thumbnail
1439  * @cfg {String} src image source
1440  * @cfg {String} alt image alternative text
1441  * @cfg {String} href a tag href
1442  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1443  * @cfg {String} xsUrl xs image source
1444  * @cfg {String} smUrl sm image source
1445  * @cfg {String} mdUrl md image source
1446  * @cfg {String} lgUrl lg image source
1447  * 
1448  * @constructor
1449  * Create a new Input
1450  * @param {Object} config The config object
1451  */
1452
1453 Roo.bootstrap.Img = function(config){
1454     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1455     
1456     this.addEvents({
1457         // img events
1458         /**
1459          * @event click
1460          * The img click event for the img.
1461          * @param {Roo.EventObject} e
1462          */
1463         "click" : true
1464     });
1465 };
1466
1467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1468     
1469     imgResponsive: true,
1470     border: '',
1471     src: 'about:blank',
1472     href: false,
1473     target: false,
1474     xsUrl: '',
1475     smUrl: '',
1476     mdUrl: '',
1477     lgUrl: '',
1478
1479     getAutoCreate : function()
1480     {   
1481         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1482             return this.createSingleImg();
1483         }
1484         
1485         var cfg = {
1486             tag: 'div',
1487             cls: 'roo-image-responsive-group',
1488             cn: []
1489         };
1490         var _this = this;
1491         
1492         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1493             
1494             if(!_this[size + 'Url']){
1495                 return;
1496             }
1497             
1498             var img = {
1499                 tag: 'img',
1500                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1501                 html: _this.html || cfg.html,
1502                 src: _this[size + 'Url']
1503             };
1504             
1505             img.cls += ' roo-image-responsive-' + size;
1506             
1507             var s = ['xs', 'sm', 'md', 'lg'];
1508             
1509             s.splice(s.indexOf(size), 1);
1510             
1511             Roo.each(s, function(ss){
1512                 img.cls += ' hidden-' + ss;
1513             });
1514             
1515             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1516                 cfg.cls += ' img-' + _this.border;
1517             }
1518             
1519             if(_this.alt){
1520                 cfg.alt = _this.alt;
1521             }
1522             
1523             if(_this.href){
1524                 var a = {
1525                     tag: 'a',
1526                     href: _this.href,
1527                     cn: [
1528                         img
1529                     ]
1530                 };
1531
1532                 if(this.target){
1533                     a.target = _this.target;
1534                 }
1535             }
1536             
1537             cfg.cn.push((_this.href) ? a : img);
1538             
1539         });
1540         
1541         return cfg;
1542     },
1543     
1544     createSingleImg : function()
1545     {
1546         var cfg = {
1547             tag: 'img',
1548             cls: (this.imgResponsive) ? 'img-responsive' : '',
1549             html : null,
1550             src : 'about:blank'  // just incase src get's set to undefined?!?
1551         };
1552         
1553         cfg.html = this.html || cfg.html;
1554         
1555         cfg.src = this.src || cfg.src;
1556         
1557         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1558             cfg.cls += ' img-' + this.border;
1559         }
1560         
1561         if(this.alt){
1562             cfg.alt = this.alt;
1563         }
1564         
1565         if(this.href){
1566             var a = {
1567                 tag: 'a',
1568                 href: this.href,
1569                 cn: [
1570                     cfg
1571                 ]
1572             };
1573             
1574             if(this.target){
1575                 a.target = this.target;
1576             }
1577             
1578         }
1579         
1580         return (this.href) ? a : cfg;
1581     },
1582     
1583     initEvents: function() 
1584     {
1585         if(!this.href){
1586             this.el.on('click', this.onClick, this);
1587         }
1588         
1589     },
1590     
1591     onClick : function(e)
1592     {
1593         Roo.log('img onclick');
1594         this.fireEvent('click', this, e);
1595     },
1596     /**
1597      * Sets the url of the image - used to update it
1598      * @param {String} url the url of the image
1599      */
1600     
1601     setSrc : function(url)
1602     {
1603         this.src =  url;
1604         
1605         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1606             this.el.dom.src =  url;
1607             return;
1608         }
1609         
1610         this.el.select('img', true).first().dom.src =  url;
1611     }
1612     
1613     
1614    
1615 });
1616
1617  /*
1618  * - LGPL
1619  *
1620  * image
1621  * 
1622  */
1623
1624
1625 /**
1626  * @class Roo.bootstrap.Link
1627  * @extends Roo.bootstrap.Component
1628  * Bootstrap Link Class
1629  * @cfg {String} alt image alternative text
1630  * @cfg {String} href a tag href
1631  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1632  * @cfg {String} html the content of the link.
1633  * @cfg {String} anchor name for the anchor link
1634  * @cfg {String} fa - favicon
1635
1636  * @cfg {Boolean} preventDefault (true | false) default false
1637
1638  * 
1639  * @constructor
1640  * Create a new Input
1641  * @param {Object} config The config object
1642  */
1643
1644 Roo.bootstrap.Link = function(config){
1645     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1646     
1647     this.addEvents({
1648         // img events
1649         /**
1650          * @event click
1651          * The img click event for the img.
1652          * @param {Roo.EventObject} e
1653          */
1654         "click" : true
1655     });
1656 };
1657
1658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1659     
1660     href: false,
1661     target: false,
1662     preventDefault: false,
1663     anchor : false,
1664     alt : false,
1665     fa: false,
1666
1667
1668     getAutoCreate : function()
1669     {
1670         var html = this.html || '';
1671         
1672         if (this.fa !== false) {
1673             html = '<i class="fa fa-' + this.fa + '"></i>';
1674         }
1675         var cfg = {
1676             tag: 'a'
1677         };
1678         // anchor's do not require html/href...
1679         if (this.anchor === false) {
1680             cfg.html = html;
1681             cfg.href = this.href || '#';
1682         } else {
1683             cfg.name = this.anchor;
1684             if (this.html !== false || this.fa !== false) {
1685                 cfg.html = html;
1686             }
1687             if (this.href !== false) {
1688                 cfg.href = this.href;
1689             }
1690         }
1691         
1692         if(this.alt !== false){
1693             cfg.alt = this.alt;
1694         }
1695         
1696         
1697         if(this.target !== false) {
1698             cfg.target = this.target;
1699         }
1700         
1701         return cfg;
1702     },
1703     
1704     initEvents: function() {
1705         
1706         if(!this.href || this.preventDefault){
1707             this.el.on('click', this.onClick, this);
1708         }
1709     },
1710     
1711     onClick : function(e)
1712     {
1713         if(this.preventDefault){
1714             e.preventDefault();
1715         }
1716         //Roo.log('img onclick');
1717         this.fireEvent('click', this, e);
1718     }
1719    
1720 });
1721
1722  /*
1723  * - LGPL
1724  *
1725  * header
1726  * 
1727  */
1728
1729 /**
1730  * @class Roo.bootstrap.Header
1731  * @extends Roo.bootstrap.Component
1732  * Bootstrap Header class
1733  * @cfg {String} html content of header
1734  * @cfg {Number} level (1|2|3|4|5|6) default 1
1735  * 
1736  * @constructor
1737  * Create a new Header
1738  * @param {Object} config The config object
1739  */
1740
1741
1742 Roo.bootstrap.Header  = function(config){
1743     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1744 };
1745
1746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1747     
1748     //href : false,
1749     html : false,
1750     level : 1,
1751     
1752     
1753     
1754     getAutoCreate : function(){
1755         
1756         
1757         
1758         var cfg = {
1759             tag: 'h' + (1 *this.level),
1760             html: this.html || ''
1761         } ;
1762         
1763         return cfg;
1764     }
1765    
1766 });
1767
1768  
1769
1770  /*
1771  * Based on:
1772  * Ext JS Library 1.1.1
1773  * Copyright(c) 2006-2007, Ext JS, LLC.
1774  *
1775  * Originally Released Under LGPL - original licence link has changed is not relivant.
1776  *
1777  * Fork - LGPL
1778  * <script type="text/javascript">
1779  */
1780  
1781 /**
1782  * @class Roo.bootstrap.MenuMgr
1783  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1784  * @singleton
1785  */
1786 Roo.bootstrap.MenuMgr = function(){
1787    var menus, active, groups = {}, attached = false, lastShow = new Date();
1788
1789    // private - called when first menu is created
1790    function init(){
1791        menus = {};
1792        active = new Roo.util.MixedCollection();
1793        Roo.get(document).addKeyListener(27, function(){
1794            if(active.length > 0){
1795                hideAll();
1796            }
1797        });
1798    }
1799
1800    // private
1801    function hideAll(){
1802        if(active && active.length > 0){
1803            var c = active.clone();
1804            c.each(function(m){
1805                m.hide();
1806            });
1807        }
1808    }
1809
1810    // private
1811    function onHide(m){
1812        active.remove(m);
1813        if(active.length < 1){
1814            Roo.get(document).un("mouseup", onMouseDown);
1815             
1816            attached = false;
1817        }
1818    }
1819
1820    // private
1821    function onShow(m){
1822        var last = active.last();
1823        lastShow = new Date();
1824        active.add(m);
1825        if(!attached){
1826           Roo.get(document).on("mouseup", onMouseDown);
1827            
1828            attached = true;
1829        }
1830        if(m.parentMenu){
1831           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1832           m.parentMenu.activeChild = m;
1833        }else if(last && last.isVisible()){
1834           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1835        }
1836    }
1837
1838    // private
1839    function onBeforeHide(m){
1840        if(m.activeChild){
1841            m.activeChild.hide();
1842        }
1843        if(m.autoHideTimer){
1844            clearTimeout(m.autoHideTimer);
1845            delete m.autoHideTimer;
1846        }
1847    }
1848
1849    // private
1850    function onBeforeShow(m){
1851        var pm = m.parentMenu;
1852        if(!pm && !m.allowOtherMenus){
1853            hideAll();
1854        }else if(pm && pm.activeChild && active != m){
1855            pm.activeChild.hide();
1856        }
1857    }
1858
1859    // private this should really trigger on mouseup..
1860    function onMouseDown(e){
1861         Roo.log("on Mouse Up");
1862         
1863         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1864             Roo.log("MenuManager hideAll");
1865             hideAll();
1866             e.stopEvent();
1867         }
1868         
1869         
1870    }
1871
1872    // private
1873    function onBeforeCheck(mi, state){
1874        if(state){
1875            var g = groups[mi.group];
1876            for(var i = 0, l = g.length; i < l; i++){
1877                if(g[i] != mi){
1878                    g[i].setChecked(false);
1879                }
1880            }
1881        }
1882    }
1883
1884    return {
1885
1886        /**
1887         * Hides all menus that are currently visible
1888         */
1889        hideAll : function(){
1890             hideAll();  
1891        },
1892
1893        // private
1894        register : function(menu){
1895            if(!menus){
1896                init();
1897            }
1898            menus[menu.id] = menu;
1899            menu.on("beforehide", onBeforeHide);
1900            menu.on("hide", onHide);
1901            menu.on("beforeshow", onBeforeShow);
1902            menu.on("show", onShow);
1903            var g = menu.group;
1904            if(g && menu.events["checkchange"]){
1905                if(!groups[g]){
1906                    groups[g] = [];
1907                }
1908                groups[g].push(menu);
1909                menu.on("checkchange", onCheck);
1910            }
1911        },
1912
1913         /**
1914          * Returns a {@link Roo.menu.Menu} object
1915          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1916          * be used to generate and return a new Menu instance.
1917          */
1918        get : function(menu){
1919            if(typeof menu == "string"){ // menu id
1920                return menus[menu];
1921            }else if(menu.events){  // menu instance
1922                return menu;
1923            }
1924            /*else if(typeof menu.length == 'number'){ // array of menu items?
1925                return new Roo.bootstrap.Menu({items:menu});
1926            }else{ // otherwise, must be a config
1927                return new Roo.bootstrap.Menu(menu);
1928            }
1929            */
1930            return false;
1931        },
1932
1933        // private
1934        unregister : function(menu){
1935            delete menus[menu.id];
1936            menu.un("beforehide", onBeforeHide);
1937            menu.un("hide", onHide);
1938            menu.un("beforeshow", onBeforeShow);
1939            menu.un("show", onShow);
1940            var g = menu.group;
1941            if(g && menu.events["checkchange"]){
1942                groups[g].remove(menu);
1943                menu.un("checkchange", onCheck);
1944            }
1945        },
1946
1947        // private
1948        registerCheckable : function(menuItem){
1949            var g = menuItem.group;
1950            if(g){
1951                if(!groups[g]){
1952                    groups[g] = [];
1953                }
1954                groups[g].push(menuItem);
1955                menuItem.on("beforecheckchange", onBeforeCheck);
1956            }
1957        },
1958
1959        // private
1960        unregisterCheckable : function(menuItem){
1961            var g = menuItem.group;
1962            if(g){
1963                groups[g].remove(menuItem);
1964                menuItem.un("beforecheckchange", onBeforeCheck);
1965            }
1966        }
1967    };
1968 }();/*
1969  * - LGPL
1970  *
1971  * menu
1972  * 
1973  */
1974
1975 /**
1976  * @class Roo.bootstrap.Menu
1977  * @extends Roo.bootstrap.Component
1978  * Bootstrap Menu class - container for MenuItems
1979  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1980  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1981  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1982  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1983  * 
1984  * @constructor
1985  * Create a new Menu
1986  * @param {Object} config The config object
1987  */
1988
1989
1990 Roo.bootstrap.Menu = function(config){
1991     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1992     if (this.registerMenu && this.type != 'treeview')  {
1993         Roo.bootstrap.MenuMgr.register(this);
1994     }
1995     this.addEvents({
1996         /**
1997          * @event beforeshow
1998          * Fires before this menu is displayed
1999          * @param {Roo.menu.Menu} this
2000          */
2001         beforeshow : true,
2002         /**
2003          * @event beforehide
2004          * Fires before this menu is hidden
2005          * @param {Roo.menu.Menu} this
2006          */
2007         beforehide : true,
2008         /**
2009          * @event show
2010          * Fires after this menu is displayed
2011          * @param {Roo.menu.Menu} this
2012          */
2013         show : true,
2014         /**
2015          * @event hide
2016          * Fires after this menu is hidden
2017          * @param {Roo.menu.Menu} this
2018          */
2019         hide : true,
2020         /**
2021          * @event click
2022          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2023          * @param {Roo.menu.Menu} this
2024          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2025          * @param {Roo.EventObject} e
2026          */
2027         click : true,
2028         /**
2029          * @event mouseover
2030          * Fires when the mouse is hovering over this menu
2031          * @param {Roo.menu.Menu} this
2032          * @param {Roo.EventObject} e
2033          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034          */
2035         mouseover : true,
2036         /**
2037          * @event mouseout
2038          * Fires when the mouse exits this menu
2039          * @param {Roo.menu.Menu} this
2040          * @param {Roo.EventObject} e
2041          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042          */
2043         mouseout : true,
2044         /**
2045          * @event itemclick
2046          * Fires when a menu item contained in this menu is clicked
2047          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2048          * @param {Roo.EventObject} e
2049          */
2050         itemclick: true
2051     });
2052     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2053 };
2054
2055 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2056     
2057    /// html : false,
2058     //align : '',
2059     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2060     type: false,
2061     /**
2062      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2063      */
2064     registerMenu : true,
2065     
2066     menuItems :false, // stores the menu items..
2067     
2068     hidden:true,
2069         
2070     parentMenu : false,
2071     
2072     stopEvent : true,
2073     
2074     isLink : false,
2075     
2076     getChildContainer : function() {
2077         return this.el;  
2078     },
2079     
2080     getAutoCreate : function(){
2081          
2082         //if (['right'].indexOf(this.align)!==-1) {
2083         //    cfg.cn[1].cls += ' pull-right'
2084         //}
2085         
2086         
2087         var cfg = {
2088             tag : 'ul',
2089             cls : 'dropdown-menu' ,
2090             style : 'z-index:1000'
2091             
2092         };
2093         
2094         if (this.type === 'submenu') {
2095             cfg.cls = 'submenu active';
2096         }
2097         if (this.type === 'treeview') {
2098             cfg.cls = 'treeview-menu';
2099         }
2100         
2101         return cfg;
2102     },
2103     initEvents : function() {
2104         
2105        // Roo.log("ADD event");
2106        // Roo.log(this.triggerEl.dom);
2107         
2108         this.triggerEl.on('click', this.onTriggerClick, this);
2109         
2110         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2111         
2112         this.triggerEl.addClass('dropdown-toggle');
2113         
2114         if (Roo.isTouch) {
2115             this.el.on('touchstart'  , this.onTouch, this);
2116         }
2117         this.el.on('click' , this.onClick, this);
2118
2119         this.el.on("mouseover", this.onMouseOver, this);
2120         this.el.on("mouseout", this.onMouseOut, this);
2121         
2122     },
2123     
2124     findTargetItem : function(e)
2125     {
2126         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2127         if(!t){
2128             return false;
2129         }
2130         //Roo.log(t);         Roo.log(t.id);
2131         if(t && t.id){
2132             //Roo.log(this.menuitems);
2133             return this.menuitems.get(t.id);
2134             
2135             //return this.items.get(t.menuItemId);
2136         }
2137         
2138         return false;
2139     },
2140     
2141     onTouch : function(e) 
2142     {
2143         Roo.log("menu.onTouch");
2144         //e.stopEvent(); this make the user popdown broken
2145         this.onClick(e);
2146     },
2147     
2148     onClick : function(e)
2149     {
2150         Roo.log("menu.onClick");
2151         
2152         var t = this.findTargetItem(e);
2153         if(!t || t.isContainer){
2154             return;
2155         }
2156         Roo.log(e);
2157         /*
2158         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2159             if(t == this.activeItem && t.shouldDeactivate(e)){
2160                 this.activeItem.deactivate();
2161                 delete this.activeItem;
2162                 return;
2163             }
2164             if(t.canActivate){
2165                 this.setActiveItem(t, true);
2166             }
2167             return;
2168             
2169             
2170         }
2171         */
2172        
2173         Roo.log('pass click event');
2174         
2175         t.onClick(e);
2176         
2177         this.fireEvent("click", this, t, e);
2178         
2179         var _this = this;
2180         
2181         if(!t.href.length || t.href == '#'){
2182             (function() { _this.hide(); }).defer(100);
2183         }
2184         
2185     },
2186     
2187     onMouseOver : function(e){
2188         var t  = this.findTargetItem(e);
2189         //Roo.log(t);
2190         //if(t){
2191         //    if(t.canActivate && !t.disabled){
2192         //        this.setActiveItem(t, true);
2193         //    }
2194         //}
2195         
2196         this.fireEvent("mouseover", this, e, t);
2197     },
2198     isVisible : function(){
2199         return !this.hidden;
2200     },
2201      onMouseOut : function(e){
2202         var t  = this.findTargetItem(e);
2203         
2204         //if(t ){
2205         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2206         //        this.activeItem.deactivate();
2207         //        delete this.activeItem;
2208         //    }
2209         //}
2210         this.fireEvent("mouseout", this, e, t);
2211     },
2212     
2213     
2214     /**
2215      * Displays this menu relative to another element
2216      * @param {String/HTMLElement/Roo.Element} element The element to align to
2217      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2218      * the element (defaults to this.defaultAlign)
2219      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2220      */
2221     show : function(el, pos, parentMenu){
2222         this.parentMenu = parentMenu;
2223         if(!this.el){
2224             this.render();
2225         }
2226         this.fireEvent("beforeshow", this);
2227         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2228     },
2229      /**
2230      * Displays this menu at a specific xy position
2231      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2232      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2233      */
2234     showAt : function(xy, parentMenu, /* private: */_e){
2235         this.parentMenu = parentMenu;
2236         if(!this.el){
2237             this.render();
2238         }
2239         if(_e !== false){
2240             this.fireEvent("beforeshow", this);
2241             //xy = this.el.adjustForConstraints(xy);
2242         }
2243         
2244         //this.el.show();
2245         this.hideMenuItems();
2246         this.hidden = false;
2247         this.triggerEl.addClass('open');
2248         
2249         // reassign x when hitting right
2250         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2251             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2252         }
2253         
2254         // reassign y when hitting bottom
2255         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2256             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2257         }
2258         
2259         // but the list may align on trigger left or trigger top... should it be a properity?
2260         
2261         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2262             this.el.setXY(xy);
2263         }
2264         
2265         this.focus();
2266         this.fireEvent("show", this);
2267     },
2268     
2269     focus : function(){
2270         return;
2271         if(!this.hidden){
2272             this.doFocus.defer(50, this);
2273         }
2274     },
2275
2276     doFocus : function(){
2277         if(!this.hidden){
2278             this.focusEl.focus();
2279         }
2280     },
2281
2282     /**
2283      * Hides this menu and optionally all parent menus
2284      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2285      */
2286     hide : function(deep)
2287     {
2288         
2289         this.hideMenuItems();
2290         if(this.el && this.isVisible()){
2291             this.fireEvent("beforehide", this);
2292             if(this.activeItem){
2293                 this.activeItem.deactivate();
2294                 this.activeItem = null;
2295             }
2296             this.triggerEl.removeClass('open');;
2297             this.hidden = true;
2298             this.fireEvent("hide", this);
2299         }
2300         if(deep === true && this.parentMenu){
2301             this.parentMenu.hide(true);
2302         }
2303     },
2304     
2305     onTriggerClick : function(e)
2306     {
2307         Roo.log('trigger click');
2308         
2309         var target = e.getTarget();
2310         
2311         Roo.log(target.nodeName.toLowerCase());
2312         
2313         if(target.nodeName.toLowerCase() === 'i'){
2314             e.preventDefault();
2315         }
2316         
2317     },
2318     
2319     onTriggerPress  : function(e)
2320     {
2321         Roo.log('trigger press');
2322         //Roo.log(e.getTarget());
2323        // Roo.log(this.triggerEl.dom);
2324        
2325         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2326         var pel = Roo.get(e.getTarget());
2327         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2328             Roo.log('is treeview or dropdown?');
2329             return;
2330         }
2331         
2332         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2333             return;
2334         }
2335         
2336         if (this.isVisible()) {
2337             Roo.log('hide');
2338             this.hide();
2339         } else {
2340             Roo.log('show');
2341             this.show(this.triggerEl, false, false);
2342         }
2343         
2344         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2345             e.stopEvent();
2346         }
2347         
2348     },
2349        
2350     
2351     hideMenuItems : function()
2352     {
2353         Roo.log("hide Menu Items");
2354         if (!this.el) { 
2355             return;
2356         }
2357         //$(backdrop).remove()
2358         this.el.select('.open',true).each(function(aa) {
2359             
2360             aa.removeClass('open');
2361           //var parent = getParent($(this))
2362           //var relatedTarget = { relatedTarget: this }
2363           
2364            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2365           //if (e.isDefaultPrevented()) return
2366            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2367         });
2368     },
2369     addxtypeChild : function (tree, cntr) {
2370         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2371           
2372         this.menuitems.add(comp);
2373         return comp;
2374
2375     },
2376     getEl : function()
2377     {
2378         Roo.log(this.el);
2379         return this.el;
2380     },
2381     
2382     clear : function()
2383     {
2384         this.getEl().dom.innerHTML = '';
2385         this.menuitems.clear();
2386     }
2387 });
2388
2389  
2390  /*
2391  * - LGPL
2392  *
2393  * menu item
2394  * 
2395  */
2396
2397
2398 /**
2399  * @class Roo.bootstrap.MenuItem
2400  * @extends Roo.bootstrap.Component
2401  * Bootstrap MenuItem class
2402  * @cfg {String} html the menu label
2403  * @cfg {String} href the link
2404  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2405  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2406  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2407  * @cfg {String} fa favicon to show on left of menu item.
2408  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2409  * 
2410  * 
2411  * @constructor
2412  * Create a new MenuItem
2413  * @param {Object} config The config object
2414  */
2415
2416
2417 Roo.bootstrap.MenuItem = function(config){
2418     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2419     this.addEvents({
2420         // raw events
2421         /**
2422          * @event click
2423          * The raw click event for the entire grid.
2424          * @param {Roo.bootstrap.MenuItem} this
2425          * @param {Roo.EventObject} e
2426          */
2427         "click" : true
2428     });
2429 };
2430
2431 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2432     
2433     href : false,
2434     html : false,
2435     preventDefault: false,
2436     isContainer : false,
2437     active : false,
2438     fa: false,
2439     
2440     getAutoCreate : function(){
2441         
2442         if(this.isContainer){
2443             return {
2444                 tag: 'li',
2445                 cls: 'dropdown-menu-item'
2446             };
2447         }
2448         var ctag = {
2449             tag: 'span',
2450             html: 'Link'
2451         };
2452         
2453         var anc = {
2454             tag : 'a',
2455             href : '#',
2456             cn : [  ]
2457         };
2458         
2459         if (this.fa !== false) {
2460             anc.cn.push({
2461                 tag : 'i',
2462                 cls : 'fa fa-' + this.fa
2463             });
2464         }
2465         
2466         anc.cn.push(ctag);
2467         
2468         
2469         var cfg= {
2470             tag: 'li',
2471             cls: 'dropdown-menu-item',
2472             cn: [ anc ]
2473         };
2474         if (this.parent().type == 'treeview') {
2475             cfg.cls = 'treeview-menu';
2476         }
2477         if (this.active) {
2478             cfg.cls += ' active';
2479         }
2480         
2481         
2482         
2483         anc.href = this.href || cfg.cn[0].href ;
2484         ctag.html = this.html || cfg.cn[0].html ;
2485         return cfg;
2486     },
2487     
2488     initEvents: function()
2489     {
2490         if (this.parent().type == 'treeview') {
2491             this.el.select('a').on('click', this.onClick, this);
2492         }
2493         
2494         if (this.menu) {
2495             this.menu.parentType = this.xtype;
2496             this.menu.triggerEl = this.el;
2497             this.menu = this.addxtype(Roo.apply({}, this.menu));
2498         }
2499         
2500     },
2501     onClick : function(e)
2502     {
2503         Roo.log('item on click ');
2504         
2505         if(this.preventDefault){
2506             e.preventDefault();
2507         }
2508         //this.parent().hideMenuItems();
2509         
2510         this.fireEvent('click', this, e);
2511     },
2512     getEl : function()
2513     {
2514         return this.el;
2515     } 
2516 });
2517
2518  
2519
2520  /*
2521  * - LGPL
2522  *
2523  * menu separator
2524  * 
2525  */
2526
2527
2528 /**
2529  * @class Roo.bootstrap.MenuSeparator
2530  * @extends Roo.bootstrap.Component
2531  * Bootstrap MenuSeparator class
2532  * 
2533  * @constructor
2534  * Create a new MenuItem
2535  * @param {Object} config The config object
2536  */
2537
2538
2539 Roo.bootstrap.MenuSeparator = function(config){
2540     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2541 };
2542
2543 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2544     
2545     getAutoCreate : function(){
2546         var cfg = {
2547             cls: 'divider',
2548             tag : 'li'
2549         };
2550         
2551         return cfg;
2552     }
2553    
2554 });
2555
2556  
2557
2558  
2559 /*
2560 * Licence: LGPL
2561 */
2562
2563 /**
2564  * @class Roo.bootstrap.Modal
2565  * @extends Roo.bootstrap.Component
2566  * Bootstrap Modal class
2567  * @cfg {String} title Title of dialog
2568  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2569  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2570  * @cfg {Boolean} specificTitle default false
2571  * @cfg {Array} buttons Array of buttons or standard button set..
2572  * @cfg {String} buttonPosition (left|right|center) default right
2573  * @cfg {Boolean} animate default true
2574  * @cfg {Boolean} allow_close default true
2575  * @cfg {Boolean} fitwindow default false
2576  * @cfg {String} size (sm|lg) default empty
2577  *
2578  *
2579  * @constructor
2580  * Create a new Modal Dialog
2581  * @param {Object} config The config object
2582  */
2583
2584 Roo.bootstrap.Modal = function(config){
2585     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2586     this.addEvents({
2587         // raw events
2588         /**
2589          * @event btnclick
2590          * The raw btnclick event for the button
2591          * @param {Roo.EventObject} e
2592          */
2593         "btnclick" : true,
2594         /**
2595          * @event resize
2596          * Fire when dialog resize
2597          * @param {Roo.bootstrap.Modal} this
2598          * @param {Roo.EventObject} e
2599          */
2600         "resize" : true
2601     });
2602     this.buttons = this.buttons || [];
2603
2604     if (this.tmpl) {
2605         this.tmpl = Roo.factory(this.tmpl);
2606     }
2607
2608 };
2609
2610 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2611
2612     title : 'test dialog',
2613
2614     buttons : false,
2615
2616     // set on load...
2617
2618     html: false,
2619
2620     tmp: false,
2621
2622     specificTitle: false,
2623
2624     buttonPosition: 'right',
2625
2626     allow_close : true,
2627
2628     animate : true,
2629
2630     fitwindow: false,
2631
2632
2633      // private
2634     dialogEl: false,
2635     bodyEl:  false,
2636     footerEl:  false,
2637     titleEl:  false,
2638     closeEl:  false,
2639
2640     size: '',
2641
2642
2643     onRender : function(ct, position)
2644     {
2645         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2646
2647         if(!this.el){
2648             var cfg = Roo.apply({},  this.getAutoCreate());
2649             cfg.id = Roo.id();
2650             //if(!cfg.name){
2651             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2652             //}
2653             //if (!cfg.name.length) {
2654             //    delete cfg.name;
2655            // }
2656             if (this.cls) {
2657                 cfg.cls += ' ' + this.cls;
2658             }
2659             if (this.style) {
2660                 cfg.style = this.style;
2661             }
2662             this.el = Roo.get(document.body).createChild(cfg, position);
2663         }
2664         //var type = this.el.dom.type;
2665
2666
2667         if(this.tabIndex !== undefined){
2668             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2669         }
2670
2671         this.dialogEl = this.el.select('.modal-dialog',true).first();
2672         this.bodyEl = this.el.select('.modal-body',true).first();
2673         this.closeEl = this.el.select('.modal-header .close', true).first();
2674         this.headerEl = this.el.select('.modal-header',true).first();
2675         this.titleEl = this.el.select('.modal-title',true).first();
2676         this.footerEl = this.el.select('.modal-footer',true).first();
2677
2678         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2679         
2680         //this.el.addClass("x-dlg-modal");
2681
2682         if (this.buttons.length) {
2683             Roo.each(this.buttons, function(bb) {
2684                 var b = Roo.apply({}, bb);
2685                 b.xns = b.xns || Roo.bootstrap;
2686                 b.xtype = b.xtype || 'Button';
2687                 if (typeof(b.listeners) == 'undefined') {
2688                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2689                 }
2690
2691                 var btn = Roo.factory(b);
2692
2693                 btn.render(this.el.select('.modal-footer div').first());
2694
2695             },this);
2696         }
2697         // render the children.
2698         var nitems = [];
2699
2700         if(typeof(this.items) != 'undefined'){
2701             var items = this.items;
2702             delete this.items;
2703
2704             for(var i =0;i < items.length;i++) {
2705                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2706             }
2707         }
2708
2709         this.items = nitems;
2710
2711         // where are these used - they used to be body/close/footer
2712
2713
2714         this.initEvents();
2715         //this.el.addClass([this.fieldClass, this.cls]);
2716
2717     },
2718
2719     getAutoCreate : function(){
2720
2721
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2819         if (this.fitwindow) {
2820             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2821             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2822             this.setSize(w,h);
2823         }
2824     },
2825
2826     setSize : function(w,h)
2827     {
2828         if (!w && !h) {
2829             return;
2830         }
2831         this.resizeTo(w,h);
2832     },
2833
2834     show : function() {
2835
2836         if (!this.rendered) {
2837             this.render();
2838         }
2839
2840         //this.el.setStyle('display', 'block');
2841         this.el.removeClass('hideing');        
2842         this.el.addClass('show');
2843  
2844         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2845             var _this = this;
2846             (function(){
2847                 this.el.addClass('in');
2848             }).defer(50, this);
2849         }else{
2850             this.el.addClass('in');
2851
2852         }
2853
2854         // not sure how we can show data in here..
2855         //if (this.tmpl) {
2856         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2857         //}
2858
2859         Roo.get(document.body).addClass("x-body-masked");
2860         
2861         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2862         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2863         this.maskEl.addClass('show');
2864         
2865         this.resize();
2866         
2867         this.fireEvent('show', this);
2868
2869         // set zindex here - otherwise it appears to be ignored...
2870         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2871
2872         (function () {
2873             this.items.forEach( function(e) {
2874                 e.layout ? e.layout() : false;
2875
2876             });
2877         }).defer(100,this);
2878
2879     },
2880     hide : function()
2881     {
2882         if(this.fireEvent("beforehide", this) !== false){
2883             this.maskEl.removeClass('show');
2884             Roo.get(document.body).removeClass("x-body-masked");
2885             this.el.removeClass('in');
2886             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2887
2888             if(this.animate){ // why
2889                 this.el.addClass('hideing');
2890                 (function(){
2891                     if (!this.el.hasClass('hideing')) {
2892                         return; // it's been shown again...
2893                     }
2894                     this.el.removeClass('show');
2895                     this.el.removeClass('hideing');
2896                 }).defer(150,this);
2897                 
2898             }else{
2899                  this.el.removeClass('show');
2900             }
2901             this.fireEvent('hide', this);
2902         }
2903     },
2904     isVisible : function()
2905     {
2906         
2907         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2908         
2909     },
2910
2911     addButton : function(str, cb)
2912     {
2913
2914
2915         var b = Roo.apply({}, { html : str } );
2916         b.xns = b.xns || Roo.bootstrap;
2917         b.xtype = b.xtype || 'Button';
2918         if (typeof(b.listeners) == 'undefined') {
2919             b.listeners = { click : cb.createDelegate(this)  };
2920         }
2921
2922         var btn = Roo.factory(b);
2923
2924         btn.render(this.el.select('.modal-footer div').first());
2925
2926         return btn;
2927
2928     },
2929
2930     setDefaultButton : function(btn)
2931     {
2932         //this.el.select('.modal-footer').()
2933     },
2934     diff : false,
2935
2936     resizeTo: function(w,h)
2937     {
2938         // skip.. ?? why??
2939
2940         this.dialogEl.setWidth(w);
2941         if (this.diff === false) {
2942             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2943         }
2944
2945         this.bodyEl.setHeight(h-this.diff);
2946
2947         this.fireEvent('resize', this);
2948
2949     },
2950     setContentSize  : function(w, h)
2951     {
2952
2953     },
2954     onButtonClick: function(btn,e)
2955     {
2956         //Roo.log([a,b,c]);
2957         this.fireEvent('btnclick', btn.name, e);
2958     },
2959      /**
2960      * Set the title of the Dialog
2961      * @param {String} str new Title
2962      */
2963     setTitle: function(str) {
2964         this.titleEl.dom.innerHTML = str;
2965     },
2966     /**
2967      * Set the body of the Dialog
2968      * @param {String} str new Title
2969      */
2970     setBody: function(str) {
2971         this.bodyEl.dom.innerHTML = str;
2972     },
2973     /**
2974      * Set the body of the Dialog using the template
2975      * @param {Obj} data - apply this data to the template and replace the body contents.
2976      */
2977     applyBody: function(obj)
2978     {
2979         if (!this.tmpl) {
2980             Roo.log("Error - using apply Body without a template");
2981             //code
2982         }
2983         this.tmpl.overwrite(this.bodyEl, obj);
2984     }
2985
2986 });
2987
2988
2989 Roo.apply(Roo.bootstrap.Modal,  {
2990     /**
2991          * Button config that displays a single OK button
2992          * @type Object
2993          */
2994         OK :  [{
2995             name : 'ok',
2996             weight : 'primary',
2997             html : 'OK'
2998         }],
2999         /**
3000          * Button config that displays Yes and No buttons
3001          * @type Object
3002          */
3003         YESNO : [
3004             {
3005                 name  : 'no',
3006                 html : 'No'
3007             },
3008             {
3009                 name  :'yes',
3010                 weight : 'primary',
3011                 html : 'Yes'
3012             }
3013         ],
3014
3015         /**
3016          * Button config that displays OK and Cancel buttons
3017          * @type Object
3018          */
3019         OKCANCEL : [
3020             {
3021                name : 'cancel',
3022                 html : 'Cancel'
3023             },
3024             {
3025                 name : 'ok',
3026                 weight : 'primary',
3027                 html : 'OK'
3028             }
3029         ],
3030         /**
3031          * Button config that displays Yes, No and Cancel buttons
3032          * @type Object
3033          */
3034         YESNOCANCEL : [
3035             {
3036                 name : 'yes',
3037                 weight : 'primary',
3038                 html : 'Yes'
3039             },
3040             {
3041                 name : 'no',
3042                 html : 'No'
3043             },
3044             {
3045                 name : 'cancel',
3046                 html : 'Cancel'
3047             }
3048         ],
3049         
3050         zIndex : 10001
3051 });
3052 /*
3053  * - LGPL
3054  *
3055  * messagebox - can be used as a replace
3056  * 
3057  */
3058 /**
3059  * @class Roo.MessageBox
3060  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3061  * Example usage:
3062  *<pre><code>
3063 // Basic alert:
3064 Roo.Msg.alert('Status', 'Changes saved successfully.');
3065
3066 // Prompt for user data:
3067 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3068     if (btn == 'ok'){
3069         // process text value...
3070     }
3071 });
3072
3073 // Show a dialog using config options:
3074 Roo.Msg.show({
3075    title:'Save Changes?',
3076    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3077    buttons: Roo.Msg.YESNOCANCEL,
3078    fn: processResult,
3079    animEl: 'elId'
3080 });
3081 </code></pre>
3082  * @singleton
3083  */
3084 Roo.bootstrap.MessageBox = function(){
3085     var dlg, opt, mask, waitTimer;
3086     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3087     var buttons, activeTextEl, bwidth;
3088
3089     
3090     // private
3091     var handleButton = function(button){
3092         dlg.hide();
3093         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3094     };
3095
3096     // private
3097     var handleHide = function(){
3098         if(opt && opt.cls){
3099             dlg.el.removeClass(opt.cls);
3100         }
3101         //if(waitTimer){
3102         //    Roo.TaskMgr.stop(waitTimer);
3103         //    waitTimer = null;
3104         //}
3105     };
3106
3107     // private
3108     var updateButtons = function(b){
3109         var width = 0;
3110         if(!b){
3111             buttons["ok"].hide();
3112             buttons["cancel"].hide();
3113             buttons["yes"].hide();
3114             buttons["no"].hide();
3115             //dlg.footer.dom.style.display = 'none';
3116             return width;
3117         }
3118         dlg.footerEl.dom.style.display = '';
3119         for(var k in buttons){
3120             if(typeof buttons[k] != "function"){
3121                 if(b[k]){
3122                     buttons[k].show();
3123                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3124                     width += buttons[k].el.getWidth()+15;
3125                 }else{
3126                     buttons[k].hide();
3127                 }
3128             }
3129         }
3130         return width;
3131     };
3132
3133     // private
3134     var handleEsc = function(d, k, e){
3135         if(opt && opt.closable !== false){
3136             dlg.hide();
3137         }
3138         if(e){
3139             e.stopEvent();
3140         }
3141     };
3142
3143     return {
3144         /**
3145          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3146          * @return {Roo.BasicDialog} The BasicDialog element
3147          */
3148         getDialog : function(){
3149            if(!dlg){
3150                 dlg = new Roo.bootstrap.Modal( {
3151                     //draggable: true,
3152                     //resizable:false,
3153                     //constraintoviewport:false,
3154                     //fixedcenter:true,
3155                     //collapsible : false,
3156                     //shim:true,
3157                     //modal: true,
3158                 //    width: 'auto',
3159                   //  height:100,
3160                     //buttonAlign:"center",
3161                     closeClick : function(){
3162                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3163                             handleButton("no");
3164                         }else{
3165                             handleButton("cancel");
3166                         }
3167                     }
3168                 });
3169                 dlg.render();
3170                 dlg.on("hide", handleHide);
3171                 mask = dlg.mask;
3172                 //dlg.addKeyListener(27, handleEsc);
3173                 buttons = {};
3174                 this.buttons = buttons;
3175                 var bt = this.buttonText;
3176                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3177                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3178                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3179                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3180                 //Roo.log(buttons);
3181                 bodyEl = dlg.bodyEl.createChild({
3182
3183                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3184                         '<textarea class="roo-mb-textarea"></textarea>' +
3185                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3186                 });
3187                 msgEl = bodyEl.dom.firstChild;
3188                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3189                 textboxEl.enableDisplayMode();
3190                 textboxEl.addKeyListener([10,13], function(){
3191                     if(dlg.isVisible() && opt && opt.buttons){
3192                         if(opt.buttons.ok){
3193                             handleButton("ok");
3194                         }else if(opt.buttons.yes){
3195                             handleButton("yes");
3196                         }
3197                     }
3198                 });
3199                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3200                 textareaEl.enableDisplayMode();
3201                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3202                 progressEl.enableDisplayMode();
3203                 
3204                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3205                 var pf = progressEl.dom.firstChild;
3206                 if (pf) {
3207                     pp = Roo.get(pf.firstChild);
3208                     pp.setHeight(pf.offsetHeight);
3209                 }
3210                 
3211             }
3212             return dlg;
3213         },
3214
3215         /**
3216          * Updates the message box body text
3217          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3218          * the XHTML-compliant non-breaking space character '&amp;#160;')
3219          * @return {Roo.MessageBox} This message box
3220          */
3221         updateText : function(text)
3222         {
3223             if(!dlg.isVisible() && !opt.width){
3224                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3225                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3226             }
3227             msgEl.innerHTML = text || '&#160;';
3228       
3229             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3230             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3231             var w = Math.max(
3232                     Math.min(opt.width || cw , this.maxWidth), 
3233                     Math.max(opt.minWidth || this.minWidth, bwidth)
3234             );
3235             if(opt.prompt){
3236                 activeTextEl.setWidth(w);
3237             }
3238             if(dlg.isVisible()){
3239                 dlg.fixedcenter = false;
3240             }
3241             // to big, make it scroll. = But as usual stupid IE does not support
3242             // !important..
3243             
3244             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3245                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3246                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3247             } else {
3248                 bodyEl.dom.style.height = '';
3249                 bodyEl.dom.style.overflowY = '';
3250             }
3251             if (cw > w) {
3252                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3253             } else {
3254                 bodyEl.dom.style.overflowX = '';
3255             }
3256             
3257             dlg.setContentSize(w, bodyEl.getHeight());
3258             if(dlg.isVisible()){
3259                 dlg.fixedcenter = true;
3260             }
3261             return this;
3262         },
3263
3264         /**
3265          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3266          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3267          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3268          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3269          * @return {Roo.MessageBox} This message box
3270          */
3271         updateProgress : function(value, text){
3272             if(text){
3273                 this.updateText(text);
3274             }
3275             
3276             if (pp) { // weird bug on my firefox - for some reason this is not defined
3277                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3278                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3279             }
3280             return this;
3281         },        
3282
3283         /**
3284          * Returns true if the message box is currently displayed
3285          * @return {Boolean} True if the message box is visible, else false
3286          */
3287         isVisible : function(){
3288             return dlg && dlg.isVisible();  
3289         },
3290
3291         /**
3292          * Hides the message box if it is displayed
3293          */
3294         hide : function(){
3295             if(this.isVisible()){
3296                 dlg.hide();
3297             }  
3298         },
3299
3300         /**
3301          * Displays a new message box, or reinitializes an existing message box, based on the config options
3302          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3303          * The following config object properties are supported:
3304          * <pre>
3305 Property    Type             Description
3306 ----------  ---------------  ------------------------------------------------------------------------------------
3307 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3308                                    closes (defaults to undefined)
3309 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3310                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3311 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3312                                    progress and wait dialogs will ignore this property and always hide the
3313                                    close button as they can only be closed programmatically.
3314 cls               String           A custom CSS class to apply to the message box element
3315 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3316                                    displayed (defaults to 75)
3317 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3318                                    function will be btn (the name of the button that was clicked, if applicable,
3319                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3320                                    Progress and wait dialogs will ignore this option since they do not respond to
3321                                    user actions and can only be closed programmatically, so any required function
3322                                    should be called by the same code after it closes the dialog.
3323 icon              String           A CSS class that provides a background image to be used as an icon for
3324                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3325 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3326 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3327 modal             Boolean          False to allow user interaction with the page while the message box is
3328                                    displayed (defaults to true)
3329 msg               String           A string that will replace the existing message box body text (defaults
3330                                    to the XHTML-compliant non-breaking space character '&#160;')
3331 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3332 progress          Boolean          True to display a progress bar (defaults to false)
3333 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3334 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3335 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3336 title             String           The title text
3337 value             String           The string value to set into the active textbox element if displayed
3338 wait              Boolean          True to display a progress bar (defaults to false)
3339 width             Number           The width of the dialog in pixels
3340 </pre>
3341          *
3342          * Example usage:
3343          * <pre><code>
3344 Roo.Msg.show({
3345    title: 'Address',
3346    msg: 'Please enter your address:',
3347    width: 300,
3348    buttons: Roo.MessageBox.OKCANCEL,
3349    multiline: true,
3350    fn: saveAddress,
3351    animEl: 'addAddressBtn'
3352 });
3353 </code></pre>
3354          * @param {Object} config Configuration options
3355          * @return {Roo.MessageBox} This message box
3356          */
3357         show : function(options)
3358         {
3359             
3360             // this causes nightmares if you show one dialog after another
3361             // especially on callbacks..
3362              
3363             if(this.isVisible()){
3364                 
3365                 this.hide();
3366                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3367                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3368                 Roo.log("New Dialog Message:" +  options.msg )
3369                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3370                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3371                 
3372             }
3373             var d = this.getDialog();
3374             opt = options;
3375             d.setTitle(opt.title || "&#160;");
3376             d.closeEl.setDisplayed(opt.closable !== false);
3377             activeTextEl = textboxEl;
3378             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3379             if(opt.prompt){
3380                 if(opt.multiline){
3381                     textboxEl.hide();
3382                     textareaEl.show();
3383                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3384                         opt.multiline : this.defaultTextHeight);
3385                     activeTextEl = textareaEl;
3386                 }else{
3387                     textboxEl.show();
3388                     textareaEl.hide();
3389                 }
3390             }else{
3391                 textboxEl.hide();
3392                 textareaEl.hide();
3393             }
3394             progressEl.setDisplayed(opt.progress === true);
3395             this.updateProgress(0);
3396             activeTextEl.dom.value = opt.value || "";
3397             if(opt.prompt){
3398                 dlg.setDefaultButton(activeTextEl);
3399             }else{
3400                 var bs = opt.buttons;
3401                 var db = null;
3402                 if(bs && bs.ok){
3403                     db = buttons["ok"];
3404                 }else if(bs && bs.yes){
3405                     db = buttons["yes"];
3406                 }
3407                 dlg.setDefaultButton(db);
3408             }
3409             bwidth = updateButtons(opt.buttons);
3410             this.updateText(opt.msg);
3411             if(opt.cls){
3412                 d.el.addClass(opt.cls);
3413             }
3414             d.proxyDrag = opt.proxyDrag === true;
3415             d.modal = opt.modal !== false;
3416             d.mask = opt.modal !== false ? mask : false;
3417             if(!d.isVisible()){
3418                 // force it to the end of the z-index stack so it gets a cursor in FF
3419                 document.body.appendChild(dlg.el.dom);
3420                 d.animateTarget = null;
3421                 d.show(options.animEl);
3422             }
3423             return this;
3424         },
3425
3426         /**
3427          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3428          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3429          * and closing the message box when the process is complete.
3430          * @param {String} title The title bar text
3431          * @param {String} msg The message box body text
3432          * @return {Roo.MessageBox} This message box
3433          */
3434         progress : function(title, msg){
3435             this.show({
3436                 title : title,
3437                 msg : msg,
3438                 buttons: false,
3439                 progress:true,
3440                 closable:false,
3441                 minWidth: this.minProgressWidth,
3442                 modal : true
3443             });
3444             return this;
3445         },
3446
3447         /**
3448          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3449          * If a callback function is passed it will be called after the user clicks the button, and the
3450          * id of the button that was clicked will be passed as the only parameter to the callback
3451          * (could also be the top-right close button).
3452          * @param {String} title The title bar text
3453          * @param {String} msg The message box body text
3454          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3455          * @param {Object} scope (optional) The scope of the callback function
3456          * @return {Roo.MessageBox} This message box
3457          */
3458         alert : function(title, msg, fn, scope)
3459         {
3460             this.show({
3461                 title : title,
3462                 msg : msg,
3463                 buttons: this.OK,
3464                 fn: fn,
3465                 closable : false,
3466                 scope : scope,
3467                 modal : true
3468             });
3469             return this;
3470         },
3471
3472         /**
3473          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3474          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3475          * You are responsible for closing the message box when the process is complete.
3476          * @param {String} msg The message box body text
3477          * @param {String} title (optional) The title bar text
3478          * @return {Roo.MessageBox} This message box
3479          */
3480         wait : function(msg, title){
3481             this.show({
3482                 title : title,
3483                 msg : msg,
3484                 buttons: false,
3485                 closable:false,
3486                 progress:true,
3487                 modal:true,
3488                 width:300,
3489                 wait:true
3490             });
3491             waitTimer = Roo.TaskMgr.start({
3492                 run: function(i){
3493                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3494                 },
3495                 interval: 1000
3496             });
3497             return this;
3498         },
3499
3500         /**
3501          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3502          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3503          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3504          * @param {String} title The title bar text
3505          * @param {String} msg The message box body text
3506          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3507          * @param {Object} scope (optional) The scope of the callback function
3508          * @return {Roo.MessageBox} This message box
3509          */
3510         confirm : function(title, msg, fn, scope){
3511             this.show({
3512                 title : title,
3513                 msg : msg,
3514                 buttons: this.YESNO,
3515                 fn: fn,
3516                 scope : scope,
3517                 modal : true
3518             });
3519             return this;
3520         },
3521
3522         /**
3523          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3524          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3525          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3526          * (could also be the top-right close button) and the text that was entered will be passed as the two
3527          * parameters to the callback.
3528          * @param {String} title The title bar text
3529          * @param {String} msg The message box body text
3530          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3531          * @param {Object} scope (optional) The scope of the callback function
3532          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3533          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3534          * @return {Roo.MessageBox} This message box
3535          */
3536         prompt : function(title, msg, fn, scope, multiline){
3537             this.show({
3538                 title : title,
3539                 msg : msg,
3540                 buttons: this.OKCANCEL,
3541                 fn: fn,
3542                 minWidth:250,
3543                 scope : scope,
3544                 prompt:true,
3545                 multiline: multiline,
3546                 modal : true
3547             });
3548             return this;
3549         },
3550
3551         /**
3552          * Button config that displays a single OK button
3553          * @type Object
3554          */
3555         OK : {ok:true},
3556         /**
3557          * Button config that displays Yes and No buttons
3558          * @type Object
3559          */
3560         YESNO : {yes:true, no:true},
3561         /**
3562          * Button config that displays OK and Cancel buttons
3563          * @type Object
3564          */
3565         OKCANCEL : {ok:true, cancel:true},
3566         /**
3567          * Button config that displays Yes, No and Cancel buttons
3568          * @type Object
3569          */
3570         YESNOCANCEL : {yes:true, no:true, cancel:true},
3571
3572         /**
3573          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3574          * @type Number
3575          */
3576         defaultTextHeight : 75,
3577         /**
3578          * The maximum width in pixels of the message box (defaults to 600)
3579          * @type Number
3580          */
3581         maxWidth : 600,
3582         /**
3583          * The minimum width in pixels of the message box (defaults to 100)
3584          * @type Number
3585          */
3586         minWidth : 100,
3587         /**
3588          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3589          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3590          * @type Number
3591          */
3592         minProgressWidth : 250,
3593         /**
3594          * An object containing the default button text strings that can be overriden for localized language support.
3595          * Supported properties are: ok, cancel, yes and no.
3596          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3597          * @type Object
3598          */
3599         buttonText : {
3600             ok : "OK",
3601             cancel : "Cancel",
3602             yes : "Yes",
3603             no : "No"
3604         }
3605     };
3606 }();
3607
3608 /**
3609  * Shorthand for {@link Roo.MessageBox}
3610  */
3611 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3612 Roo.Msg = Roo.Msg || Roo.MessageBox;
3613 /*
3614  * - LGPL
3615  *
3616  * navbar
3617  * 
3618  */
3619
3620 /**
3621  * @class Roo.bootstrap.Navbar
3622  * @extends Roo.bootstrap.Component
3623  * Bootstrap Navbar class
3624
3625  * @constructor
3626  * Create a new Navbar
3627  * @param {Object} config The config object
3628  */
3629
3630
3631 Roo.bootstrap.Navbar = function(config){
3632     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3633     this.addEvents({
3634         // raw events
3635         /**
3636          * @event beforetoggle
3637          * Fire before toggle the menu
3638          * @param {Roo.EventObject} e
3639          */
3640         "beforetoggle" : true
3641     });
3642 };
3643
3644 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3645     
3646     
3647    
3648     // private
3649     navItems : false,
3650     loadMask : false,
3651     
3652     
3653     getAutoCreate : function(){
3654         
3655         
3656         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3657         
3658     },
3659     
3660     initEvents :function ()
3661     {
3662         //Roo.log(this.el.select('.navbar-toggle',true));
3663         this.el.select('.navbar-toggle',true).on('click', function() {
3664             if(this.fireEvent('beforetoggle', this) !== false){
3665                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3666             }
3667             
3668         }, this);
3669         
3670         var mark = {
3671             tag: "div",
3672             cls:"x-dlg-mask"
3673         };
3674         
3675         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3676         
3677         var size = this.el.getSize();
3678         this.maskEl.setSize(size.width, size.height);
3679         this.maskEl.enableDisplayMode("block");
3680         this.maskEl.hide();
3681         
3682         if(this.loadMask){
3683             this.maskEl.show();
3684         }
3685     },
3686     
3687     
3688     getChildContainer : function()
3689     {
3690         if (this.el.select('.collapse').getCount()) {
3691             return this.el.select('.collapse',true).first();
3692         }
3693         
3694         return this.el;
3695     },
3696     
3697     mask : function()
3698     {
3699         this.maskEl.show();
3700     },
3701     
3702     unmask : function()
3703     {
3704         this.maskEl.hide();
3705     } 
3706     
3707     
3708     
3709     
3710 });
3711
3712
3713
3714  
3715
3716  /*
3717  * - LGPL
3718  *
3719  * navbar
3720  * 
3721  */
3722
3723 /**
3724  * @class Roo.bootstrap.NavSimplebar
3725  * @extends Roo.bootstrap.Navbar
3726  * Bootstrap Sidebar class
3727  *
3728  * @cfg {Boolean} inverse is inverted color
3729  * 
3730  * @cfg {String} type (nav | pills | tabs)
3731  * @cfg {Boolean} arrangement stacked | justified
3732  * @cfg {String} align (left | right) alignment
3733  * 
3734  * @cfg {Boolean} main (true|false) main nav bar? default false
3735  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3736  * 
3737  * @cfg {String} tag (header|footer|nav|div) default is nav 
3738
3739  * 
3740  * 
3741  * 
3742  * @constructor
3743  * Create a new Sidebar
3744  * @param {Object} config The config object
3745  */
3746
3747
3748 Roo.bootstrap.NavSimplebar = function(config){
3749     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3750 };
3751
3752 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3753     
3754     inverse: false,
3755     
3756     type: false,
3757     arrangement: '',
3758     align : false,
3759     
3760     
3761     
3762     main : false,
3763     
3764     
3765     tag : false,
3766     
3767     
3768     getAutoCreate : function(){
3769         
3770         
3771         var cfg = {
3772             tag : this.tag || 'div',
3773             cls : 'navbar'
3774         };
3775           
3776         
3777         cfg.cn = [
3778             {
3779                 cls: 'nav',
3780                 tag : 'ul'
3781             }
3782         ];
3783         
3784          
3785         this.type = this.type || 'nav';
3786         if (['tabs','pills'].indexOf(this.type)!==-1) {
3787             cfg.cn[0].cls += ' nav-' + this.type
3788         
3789         
3790         } else {
3791             if (this.type!=='nav') {
3792                 Roo.log('nav type must be nav/tabs/pills')
3793             }
3794             cfg.cn[0].cls += ' navbar-nav'
3795         }
3796         
3797         
3798         
3799         
3800         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3801             cfg.cn[0].cls += ' nav-' + this.arrangement;
3802         }
3803         
3804         
3805         if (this.align === 'right') {
3806             cfg.cn[0].cls += ' navbar-right';
3807         }
3808         
3809         if (this.inverse) {
3810             cfg.cls += ' navbar-inverse';
3811             
3812         }
3813         
3814         
3815         return cfg;
3816     
3817         
3818     }
3819     
3820     
3821     
3822 });
3823
3824
3825
3826  
3827
3828  
3829        /*
3830  * - LGPL
3831  *
3832  * navbar
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.NavHeaderbar
3838  * @extends Roo.bootstrap.NavSimplebar
3839  * Bootstrap Sidebar class
3840  *
3841  * @cfg {String} brand what is brand
3842  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3843  * @cfg {String} brand_href href of the brand
3844  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3845  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3846  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3847  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3848  * 
3849  * @constructor
3850  * Create a new Sidebar
3851  * @param {Object} config The config object
3852  */
3853
3854
3855 Roo.bootstrap.NavHeaderbar = function(config){
3856     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3857       
3858 };
3859
3860 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3861     
3862     position: '',
3863     brand: '',
3864     brand_href: false,
3865     srButton : true,
3866     autohide : false,
3867     desktopCenter : false,
3868    
3869     
3870     getAutoCreate : function(){
3871         
3872         var   cfg = {
3873             tag: this.nav || 'nav',
3874             cls: 'navbar',
3875             role: 'navigation',
3876             cn: []
3877         };
3878         
3879         var cn = cfg.cn;
3880         if (this.desktopCenter) {
3881             cn.push({cls : 'container', cn : []});
3882             cn = cn[0].cn;
3883         }
3884         
3885         if(this.srButton){
3886             cn.push({
3887                 tag: 'div',
3888                 cls: 'navbar-header',
3889                 cn: [
3890                     {
3891                         tag: 'button',
3892                         type: 'button',
3893                         cls: 'navbar-toggle',
3894                         'data-toggle': 'collapse',
3895                         cn: [
3896                             {
3897                                 tag: 'span',
3898                                 cls: 'sr-only',
3899                                 html: 'Toggle navigation'
3900                             },
3901                             {
3902                                 tag: 'span',
3903                                 cls: 'icon-bar'
3904                             },
3905                             {
3906                                 tag: 'span',
3907                                 cls: 'icon-bar'
3908                             },
3909                             {
3910                                 tag: 'span',
3911                                 cls: 'icon-bar'
3912                             }
3913                         ]
3914                     }
3915                 ]
3916             });
3917         }
3918         
3919         cn.push({
3920             tag: 'div',
3921             cls: 'collapse navbar-collapse',
3922             cn : []
3923         });
3924         
3925         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3926         
3927         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3928             cfg.cls += ' navbar-' + this.position;
3929             
3930             // tag can override this..
3931             
3932             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3933         }
3934         
3935         if (this.brand !== '') {
3936             cn[0].cn.push({
3937                 tag: 'a',
3938                 href: this.brand_href ? this.brand_href : '#',
3939                 cls: 'navbar-brand',
3940                 cn: [
3941                 this.brand
3942                 ]
3943             });
3944         }
3945         
3946         if(this.main){
3947             cfg.cls += ' main-nav';
3948         }
3949         
3950         
3951         return cfg;
3952
3953         
3954     },
3955     getHeaderChildContainer : function()
3956     {
3957         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3958             return this.el.select('.navbar-header',true).first();
3959         }
3960         
3961         return this.getChildContainer();
3962     },
3963     
3964     
3965     initEvents : function()
3966     {
3967         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3968         
3969         if (this.autohide) {
3970             
3971             var prevScroll = 0;
3972             var ft = this.el;
3973             
3974             Roo.get(document).on('scroll',function(e) {
3975                 var ns = Roo.get(document).getScroll().top;
3976                 var os = prevScroll;
3977                 prevScroll = ns;
3978                 
3979                 if(ns > os){
3980                     ft.removeClass('slideDown');
3981                     ft.addClass('slideUp');
3982                     return;
3983                 }
3984                 ft.removeClass('slideUp');
3985                 ft.addClass('slideDown');
3986                  
3987               
3988           },this);
3989         }
3990     }    
3991     
3992 });
3993
3994
3995
3996  
3997
3998  /*
3999  * - LGPL
4000  *
4001  * navbar
4002  * 
4003  */
4004
4005 /**
4006  * @class Roo.bootstrap.NavSidebar
4007  * @extends Roo.bootstrap.Navbar
4008  * Bootstrap Sidebar class
4009  * 
4010  * @constructor
4011  * Create a new Sidebar
4012  * @param {Object} config The config object
4013  */
4014
4015
4016 Roo.bootstrap.NavSidebar = function(config){
4017     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4018 };
4019
4020 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4021     
4022     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4023     
4024     getAutoCreate : function(){
4025         
4026         
4027         return  {
4028             tag: 'div',
4029             cls: 'sidebar sidebar-nav'
4030         };
4031     
4032         
4033     }
4034     
4035     
4036     
4037 });
4038
4039
4040
4041  
4042
4043  /*
4044  * - LGPL
4045  *
4046  * nav group
4047  * 
4048  */
4049
4050 /**
4051  * @class Roo.bootstrap.NavGroup
4052  * @extends Roo.bootstrap.Component
4053  * Bootstrap NavGroup class
4054  * @cfg {String} align (left|right)
4055  * @cfg {Boolean} inverse
4056  * @cfg {String} type (nav|pills|tab) default nav
4057  * @cfg {String} navId - reference Id for navbar.
4058
4059  * 
4060  * @constructor
4061  * Create a new nav group
4062  * @param {Object} config The config object
4063  */
4064
4065 Roo.bootstrap.NavGroup = function(config){
4066     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4067     this.navItems = [];
4068    
4069     Roo.bootstrap.NavGroup.register(this);
4070      this.addEvents({
4071         /**
4072              * @event changed
4073              * Fires when the active item changes
4074              * @param {Roo.bootstrap.NavGroup} this
4075              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4076              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4077          */
4078         'changed': true
4079      });
4080     
4081 };
4082
4083 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4084     
4085     align: '',
4086     inverse: false,
4087     form: false,
4088     type: 'nav',
4089     navId : '',
4090     // private
4091     
4092     navItems : false, 
4093     
4094     getAutoCreate : function()
4095     {
4096         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4097         
4098         cfg = {
4099             tag : 'ul',
4100             cls: 'nav' 
4101         };
4102         
4103         if (['tabs','pills'].indexOf(this.type)!==-1) {
4104             cfg.cls += ' nav-' + this.type
4105         } else {
4106             if (this.type!=='nav') {
4107                 Roo.log('nav type must be nav/tabs/pills')
4108             }
4109             cfg.cls += ' navbar-nav'
4110         }
4111         
4112         if (this.parent() && this.parent().sidebar) {
4113             cfg = {
4114                 tag: 'ul',
4115                 cls: 'dashboard-menu sidebar-menu'
4116             };
4117             
4118             return cfg;
4119         }
4120         
4121         if (this.form === true) {
4122             cfg = {
4123                 tag: 'form',
4124                 cls: 'navbar-form'
4125             };
4126             
4127             if (this.align === 'right') {
4128                 cfg.cls += ' navbar-right';
4129             } else {
4130                 cfg.cls += ' navbar-left';
4131             }
4132         }
4133         
4134         if (this.align === 'right') {
4135             cfg.cls += ' navbar-right';
4136         }
4137         
4138         if (this.inverse) {
4139             cfg.cls += ' navbar-inverse';
4140             
4141         }
4142         
4143         
4144         return cfg;
4145     },
4146     /**
4147     * sets the active Navigation item
4148     * @param {Roo.bootstrap.NavItem} the new current navitem
4149     */
4150     setActiveItem : function(item)
4151     {
4152         var prev = false;
4153         Roo.each(this.navItems, function(v){
4154             if (v == item) {
4155                 return ;
4156             }
4157             if (v.isActive()) {
4158                 v.setActive(false, true);
4159                 prev = v;
4160                 
4161             }
4162             
4163         });
4164
4165         item.setActive(true, true);
4166         this.fireEvent('changed', this, item, prev);
4167         
4168         
4169     },
4170     /**
4171     * gets the active Navigation item
4172     * @return {Roo.bootstrap.NavItem} the current navitem
4173     */
4174     getActive : function()
4175     {
4176         
4177         var prev = false;
4178         Roo.each(this.navItems, function(v){
4179             
4180             if (v.isActive()) {
4181                 prev = v;
4182                 
4183             }
4184             
4185         });
4186         return prev;
4187     },
4188     
4189     indexOfNav : function()
4190     {
4191         
4192         var prev = false;
4193         Roo.each(this.navItems, function(v,i){
4194             
4195             if (v.isActive()) {
4196                 prev = i;
4197                 
4198             }
4199             
4200         });
4201         return prev;
4202     },
4203     /**
4204     * adds a Navigation item
4205     * @param {Roo.bootstrap.NavItem} the navitem to add
4206     */
4207     addItem : function(cfg)
4208     {
4209         var cn = new Roo.bootstrap.NavItem(cfg);
4210         this.register(cn);
4211         cn.parentId = this.id;
4212         cn.onRender(this.el, null);
4213         return cn;
4214     },
4215     /**
4216     * register a Navigation item
4217     * @param {Roo.bootstrap.NavItem} the navitem to add
4218     */
4219     register : function(item)
4220     {
4221         this.navItems.push( item);
4222         item.navId = this.navId;
4223     
4224     },
4225     
4226     /**
4227     * clear all the Navigation item
4228     */
4229    
4230     clearAll : function()
4231     {
4232         this.navItems = [];
4233         this.el.dom.innerHTML = '';
4234     },
4235     
4236     getNavItem: function(tabId)
4237     {
4238         var ret = false;
4239         Roo.each(this.navItems, function(e) {
4240             if (e.tabId == tabId) {
4241                ret =  e;
4242                return false;
4243             }
4244             return true;
4245             
4246         });
4247         return ret;
4248     },
4249     
4250     setActiveNext : function()
4251     {
4252         var i = this.indexOfNav(this.getActive());
4253         if (i > this.navItems.length) {
4254             return;
4255         }
4256         this.setActiveItem(this.navItems[i+1]);
4257     },
4258     setActivePrev : function()
4259     {
4260         var i = this.indexOfNav(this.getActive());
4261         if (i  < 1) {
4262             return;
4263         }
4264         this.setActiveItem(this.navItems[i-1]);
4265     },
4266     clearWasActive : function(except) {
4267         Roo.each(this.navItems, function(e) {
4268             if (e.tabId != except.tabId && e.was_active) {
4269                e.was_active = false;
4270                return false;
4271             }
4272             return true;
4273             
4274         });
4275     },
4276     getWasActive : function ()
4277     {
4278         var r = false;
4279         Roo.each(this.navItems, function(e) {
4280             if (e.was_active) {
4281                r = e;
4282                return false;
4283             }
4284             return true;
4285             
4286         });
4287         return r;
4288     }
4289     
4290     
4291 });
4292
4293  
4294 Roo.apply(Roo.bootstrap.NavGroup, {
4295     
4296     groups: {},
4297      /**
4298     * register a Navigation Group
4299     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4300     */
4301     register : function(navgrp)
4302     {
4303         this.groups[navgrp.navId] = navgrp;
4304         
4305     },
4306     /**
4307     * fetch a Navigation Group based on the navigation ID
4308     * @param {string} the navgroup to add
4309     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4310     */
4311     get: function(navId) {
4312         if (typeof(this.groups[navId]) == 'undefined') {
4313             return false;
4314             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4315         }
4316         return this.groups[navId] ;
4317     }
4318     
4319     
4320     
4321 });
4322
4323  /*
4324  * - LGPL
4325  *
4326  * row
4327  * 
4328  */
4329
4330 /**
4331  * @class Roo.bootstrap.NavItem
4332  * @extends Roo.bootstrap.Component
4333  * Bootstrap Navbar.NavItem class
4334  * @cfg {String} href  link to
4335  * @cfg {String} html content of button
4336  * @cfg {String} badge text inside badge
4337  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4338  * @cfg {String} glyphicon name of glyphicon
4339  * @cfg {String} icon name of font awesome icon
4340  * @cfg {Boolean} active Is item active
4341  * @cfg {Boolean} disabled Is item disabled
4342  
4343  * @cfg {Boolean} preventDefault (true | false) default false
4344  * @cfg {String} tabId the tab that this item activates.
4345  * @cfg {String} tagtype (a|span) render as a href or span?
4346  * @cfg {Boolean} animateRef (true|false) link to element default false  
4347   
4348  * @constructor
4349  * Create a new Navbar Item
4350  * @param {Object} config The config object
4351  */
4352 Roo.bootstrap.NavItem = function(config){
4353     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4354     this.addEvents({
4355         // raw events
4356         /**
4357          * @event click
4358          * The raw click event for the entire grid.
4359          * @param {Roo.EventObject} e
4360          */
4361         "click" : true,
4362          /**
4363             * @event changed
4364             * Fires when the active item active state changes
4365             * @param {Roo.bootstrap.NavItem} this
4366             * @param {boolean} state the new state
4367              
4368          */
4369         'changed': true,
4370         /**
4371             * @event scrollto
4372             * Fires when scroll to element
4373             * @param {Roo.bootstrap.NavItem} this
4374             * @param {Object} options
4375             * @param {Roo.EventObject} e
4376              
4377          */
4378         'scrollto': true
4379     });
4380    
4381 };
4382
4383 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4384     
4385     href: false,
4386     html: '',
4387     badge: '',
4388     icon: false,
4389     glyphicon: false,
4390     active: false,
4391     preventDefault : false,
4392     tabId : false,
4393     tagtype : 'a',
4394     disabled : false,
4395     animateRef : false,
4396     was_active : false,
4397     
4398     getAutoCreate : function(){
4399          
4400         var cfg = {
4401             tag: 'li',
4402             cls: 'nav-item'
4403             
4404         };
4405         
4406         if (this.active) {
4407             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4408         }
4409         if (this.disabled) {
4410             cfg.cls += ' disabled';
4411         }
4412         
4413         if (this.href || this.html || this.glyphicon || this.icon) {
4414             cfg.cn = [
4415                 {
4416                     tag: this.tagtype,
4417                     href : this.href || "#",
4418                     html: this.html || ''
4419                 }
4420             ];
4421             
4422             if (this.icon) {
4423                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4424             }
4425
4426             if(this.glyphicon) {
4427                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4428             }
4429             
4430             if (this.menu) {
4431                 
4432                 cfg.cn[0].html += " <span class='caret'></span>";
4433              
4434             }
4435             
4436             if (this.badge !== '') {
4437                  
4438                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4439             }
4440         }
4441         
4442         
4443         
4444         return cfg;
4445     },
4446     initEvents: function() 
4447     {
4448         if (typeof (this.menu) != 'undefined') {
4449             this.menu.parentType = this.xtype;
4450             this.menu.triggerEl = this.el;
4451             this.menu = this.addxtype(Roo.apply({}, this.menu));
4452         }
4453         
4454         this.el.select('a',true).on('click', this.onClick, this);
4455         
4456         if(this.tagtype == 'span'){
4457             this.el.select('span',true).on('click', this.onClick, this);
4458         }
4459        
4460         // at this point parent should be available..
4461         this.parent().register(this);
4462     },
4463     
4464     onClick : function(e)
4465     {
4466         if (e.getTarget('.dropdown-menu-item')) {
4467             // did you click on a menu itemm.... - then don't trigger onclick..
4468             return;
4469         }
4470         
4471         if(
4472                 this.preventDefault || 
4473                 this.href == '#' 
4474         ){
4475             Roo.log("NavItem - prevent Default?");
4476             e.preventDefault();
4477         }
4478         
4479         if (this.disabled) {
4480             return;
4481         }
4482         
4483         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4484         if (tg && tg.transition) {
4485             Roo.log("waiting for the transitionend");
4486             return;
4487         }
4488         
4489         
4490         
4491         //Roo.log("fire event clicked");
4492         if(this.fireEvent('click', this, e) === false){
4493             return;
4494         };
4495         
4496         if(this.tagtype == 'span'){
4497             return;
4498         }
4499         
4500         //Roo.log(this.href);
4501         var ael = this.el.select('a',true).first();
4502         //Roo.log(ael);
4503         
4504         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4505             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4506             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4507                 return; // ignore... - it's a 'hash' to another page.
4508             }
4509             Roo.log("NavItem - prevent Default?");
4510             e.preventDefault();
4511             this.scrollToElement(e);
4512         }
4513         
4514         
4515         var p =  this.parent();
4516    
4517         if (['tabs','pills'].indexOf(p.type)!==-1) {
4518             if (typeof(p.setActiveItem) !== 'undefined') {
4519                 p.setActiveItem(this);
4520             }
4521         }
4522         
4523         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4524         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4525             // remove the collapsed menu expand...
4526             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4527         }
4528     },
4529     
4530     isActive: function () {
4531         return this.active
4532     },
4533     setActive : function(state, fire, is_was_active)
4534     {
4535         if (this.active && !state && this.navId) {
4536             this.was_active = true;
4537             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4538             if (nv) {
4539                 nv.clearWasActive(this);
4540             }
4541             
4542         }
4543         this.active = state;
4544         
4545         if (!state ) {
4546             this.el.removeClass('active');
4547         } else if (!this.el.hasClass('active')) {
4548             this.el.addClass('active');
4549         }
4550         if (fire) {
4551             this.fireEvent('changed', this, state);
4552         }
4553         
4554         // show a panel if it's registered and related..
4555         
4556         if (!this.navId || !this.tabId || !state || is_was_active) {
4557             return;
4558         }
4559         
4560         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4561         if (!tg) {
4562             return;
4563         }
4564         var pan = tg.getPanelByName(this.tabId);
4565         if (!pan) {
4566             return;
4567         }
4568         // if we can not flip to new panel - go back to old nav highlight..
4569         if (false == tg.showPanel(pan)) {
4570             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4571             if (nv) {
4572                 var onav = nv.getWasActive();
4573                 if (onav) {
4574                     onav.setActive(true, false, true);
4575                 }
4576             }
4577             
4578         }
4579         
4580         
4581         
4582     },
4583      // this should not be here...
4584     setDisabled : function(state)
4585     {
4586         this.disabled = state;
4587         if (!state ) {
4588             this.el.removeClass('disabled');
4589         } else if (!this.el.hasClass('disabled')) {
4590             this.el.addClass('disabled');
4591         }
4592         
4593     },
4594     
4595     /**
4596      * Fetch the element to display the tooltip on.
4597      * @return {Roo.Element} defaults to this.el
4598      */
4599     tooltipEl : function()
4600     {
4601         return this.el.select('' + this.tagtype + '', true).first();
4602     },
4603     
4604     scrollToElement : function(e)
4605     {
4606         var c = document.body;
4607         
4608         /*
4609          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4610          */
4611         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4612             c = document.documentElement;
4613         }
4614         
4615         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4616         
4617         if(!target){
4618             return;
4619         }
4620
4621         var o = target.calcOffsetsTo(c);
4622         
4623         var options = {
4624             target : target,
4625             value : o[1]
4626         };
4627         
4628         this.fireEvent('scrollto', this, options, e);
4629         
4630         Roo.get(c).scrollTo('top', options.value, true);
4631         
4632         return;
4633     }
4634 });
4635  
4636
4637  /*
4638  * - LGPL
4639  *
4640  * sidebar item
4641  *
4642  *  li
4643  *    <span> icon </span>
4644  *    <span> text </span>
4645  *    <span>badge </span>
4646  */
4647
4648 /**
4649  * @class Roo.bootstrap.NavSidebarItem
4650  * @extends Roo.bootstrap.NavItem
4651  * Bootstrap Navbar.NavSidebarItem class
4652  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4653  * {Boolean} open is the menu open
4654  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4655  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4656  * {String} buttonSize (sm|md|lg)the extra classes for the button
4657  * {Boolean} showArrow show arrow next to the text (default true)
4658  * @constructor
4659  * Create a new Navbar Button
4660  * @param {Object} config The config object
4661  */
4662 Roo.bootstrap.NavSidebarItem = function(config){
4663     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4664     this.addEvents({
4665         // raw events
4666         /**
4667          * @event click
4668          * The raw click event for the entire grid.
4669          * @param {Roo.EventObject} e
4670          */
4671         "click" : true,
4672          /**
4673             * @event changed
4674             * Fires when the active item active state changes
4675             * @param {Roo.bootstrap.NavSidebarItem} this
4676             * @param {boolean} state the new state
4677              
4678          */
4679         'changed': true
4680     });
4681    
4682 };
4683
4684 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4685     
4686     badgeWeight : 'default',
4687     
4688     open: false,
4689     
4690     buttonView : false,
4691     
4692     buttonWeight : 'default',
4693     
4694     buttonSize : 'md',
4695     
4696     showArrow : true,
4697     
4698     getAutoCreate : function(){
4699         
4700         
4701         var a = {
4702                 tag: 'a',
4703                 href : this.href || '#',
4704                 cls: '',
4705                 html : '',
4706                 cn : []
4707         };
4708         
4709         if(this.buttonView){
4710             a = {
4711                 tag: 'button',
4712                 href : this.href || '#',
4713                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4714                 html : this.html,
4715                 cn : []
4716             };
4717         }
4718         
4719         var cfg = {
4720             tag: 'li',
4721             cls: '',
4722             cn: [ a ]
4723         };
4724         
4725         if (this.active) {
4726             cfg.cls += ' active';
4727         }
4728         
4729         if (this.disabled) {
4730             cfg.cls += ' disabled';
4731         }
4732         if (this.open) {
4733             cfg.cls += ' open x-open';
4734         }
4735         // left icon..
4736         if (this.glyphicon || this.icon) {
4737             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4738             a.cn.push({ tag : 'i', cls : c }) ;
4739         }
4740         
4741         if(!this.buttonView){
4742             var span = {
4743                 tag: 'span',
4744                 html : this.html || ''
4745             };
4746
4747             a.cn.push(span);
4748             
4749         }
4750         
4751         if (this.badge !== '') {
4752             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4753         }
4754         
4755         if (this.menu) {
4756             
4757             if(this.showArrow){
4758                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4759             }
4760             
4761             a.cls += ' dropdown-toggle treeview' ;
4762         }
4763         
4764         return cfg;
4765     },
4766     
4767     initEvents : function()
4768     { 
4769         if (typeof (this.menu) != 'undefined') {
4770             this.menu.parentType = this.xtype;
4771             this.menu.triggerEl = this.el;
4772             this.menu = this.addxtype(Roo.apply({}, this.menu));
4773         }
4774         
4775         this.el.on('click', this.onClick, this);
4776         
4777         if(this.badge !== ''){
4778             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4779         }
4780         
4781     },
4782     
4783     onClick : function(e)
4784     {
4785         if(this.disabled){
4786             e.preventDefault();
4787             return;
4788         }
4789         
4790         if(this.preventDefault){
4791             e.preventDefault();
4792         }
4793         
4794         this.fireEvent('click', this);
4795     },
4796     
4797     disable : function()
4798     {
4799         this.setDisabled(true);
4800     },
4801     
4802     enable : function()
4803     {
4804         this.setDisabled(false);
4805     },
4806     
4807     setDisabled : function(state)
4808     {
4809         if(this.disabled == state){
4810             return;
4811         }
4812         
4813         this.disabled = state;
4814         
4815         if (state) {
4816             this.el.addClass('disabled');
4817             return;
4818         }
4819         
4820         this.el.removeClass('disabled');
4821         
4822         return;
4823     },
4824     
4825     setActive : function(state)
4826     {
4827         if(this.active == state){
4828             return;
4829         }
4830         
4831         this.active = state;
4832         
4833         if (state) {
4834             this.el.addClass('active');
4835             return;
4836         }
4837         
4838         this.el.removeClass('active');
4839         
4840         return;
4841     },
4842     
4843     isActive: function () 
4844     {
4845         return this.active;
4846     },
4847     
4848     setBadge : function(str)
4849     {
4850         if(!this.badgeEl){
4851             return;
4852         }
4853         
4854         this.badgeEl.dom.innerHTML = str;
4855     }
4856     
4857    
4858      
4859  
4860 });
4861  
4862
4863  /*
4864  * - LGPL
4865  *
4866  * row
4867  * 
4868  */
4869
4870 /**
4871  * @class Roo.bootstrap.Row
4872  * @extends Roo.bootstrap.Component
4873  * Bootstrap Row class (contains columns...)
4874  * 
4875  * @constructor
4876  * Create a new Row
4877  * @param {Object} config The config object
4878  */
4879
4880 Roo.bootstrap.Row = function(config){
4881     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4882 };
4883
4884 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4885     
4886     getAutoCreate : function(){
4887        return {
4888             cls: 'row clearfix'
4889        };
4890     }
4891     
4892     
4893 });
4894
4895  
4896
4897  /*
4898  * - LGPL
4899  *
4900  * element
4901  * 
4902  */
4903
4904 /**
4905  * @class Roo.bootstrap.Element
4906  * @extends Roo.bootstrap.Component
4907  * Bootstrap Element class
4908  * @cfg {String} html contents of the element
4909  * @cfg {String} tag tag of the element
4910  * @cfg {String} cls class of the element
4911  * @cfg {Boolean} preventDefault (true|false) default false
4912  * @cfg {Boolean} clickable (true|false) default false
4913  * 
4914  * @constructor
4915  * Create a new Element
4916  * @param {Object} config The config object
4917  */
4918
4919 Roo.bootstrap.Element = function(config){
4920     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4921     
4922     this.addEvents({
4923         // raw events
4924         /**
4925          * @event click
4926          * When a element is chick
4927          * @param {Roo.bootstrap.Element} this
4928          * @param {Roo.EventObject} e
4929          */
4930         "click" : true
4931     });
4932 };
4933
4934 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4935     
4936     tag: 'div',
4937     cls: '',
4938     html: '',
4939     preventDefault: false, 
4940     clickable: false,
4941     
4942     getAutoCreate : function(){
4943         
4944         var cfg = {
4945             tag: this.tag,
4946             // cls: this.cls, double assign in parent class Component.js :: onRender
4947             html: this.html
4948         };
4949         
4950         return cfg;
4951     },
4952     
4953     initEvents: function() 
4954     {
4955         Roo.bootstrap.Element.superclass.initEvents.call(this);
4956         
4957         if(this.clickable){
4958             this.el.on('click', this.onClick, this);
4959         }
4960         
4961     },
4962     
4963     onClick : function(e)
4964     {
4965         if(this.preventDefault){
4966             e.preventDefault();
4967         }
4968         
4969         this.fireEvent('click', this, e);
4970     },
4971     
4972     getValue : function()
4973     {
4974         return this.el.dom.innerHTML;
4975     },
4976     
4977     setValue : function(value)
4978     {
4979         this.el.dom.innerHTML = value;
4980     }
4981    
4982 });
4983
4984  
4985
4986  /*
4987  * - LGPL
4988  *
4989  * pagination
4990  * 
4991  */
4992
4993 /**
4994  * @class Roo.bootstrap.Pagination
4995  * @extends Roo.bootstrap.Component
4996  * Bootstrap Pagination class
4997  * @cfg {String} size xs | sm | md | lg
4998  * @cfg {Boolean} inverse false | true
4999  * 
5000  * @constructor
5001  * Create a new Pagination
5002  * @param {Object} config The config object
5003  */
5004
5005 Roo.bootstrap.Pagination = function(config){
5006     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5007 };
5008
5009 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5010     
5011     cls: false,
5012     size: false,
5013     inverse: false,
5014     
5015     getAutoCreate : function(){
5016         var cfg = {
5017             tag: 'ul',
5018                 cls: 'pagination'
5019         };
5020         if (this.inverse) {
5021             cfg.cls += ' inverse';
5022         }
5023         if (this.html) {
5024             cfg.html=this.html;
5025         }
5026         if (this.cls) {
5027             cfg.cls += " " + this.cls;
5028         }
5029         return cfg;
5030     }
5031    
5032 });
5033
5034  
5035
5036  /*
5037  * - LGPL
5038  *
5039  * Pagination item
5040  * 
5041  */
5042
5043
5044 /**
5045  * @class Roo.bootstrap.PaginationItem
5046  * @extends Roo.bootstrap.Component
5047  * Bootstrap PaginationItem class
5048  * @cfg {String} html text
5049  * @cfg {String} href the link
5050  * @cfg {Boolean} preventDefault (true | false) default true
5051  * @cfg {Boolean} active (true | false) default false
5052  * @cfg {Boolean} disabled default false
5053  * 
5054  * 
5055  * @constructor
5056  * Create a new PaginationItem
5057  * @param {Object} config The config object
5058  */
5059
5060
5061 Roo.bootstrap.PaginationItem = function(config){
5062     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5063     this.addEvents({
5064         // raw events
5065         /**
5066          * @event click
5067          * The raw click event for the entire grid.
5068          * @param {Roo.EventObject} e
5069          */
5070         "click" : true
5071     });
5072 };
5073
5074 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5075     
5076     href : false,
5077     html : false,
5078     preventDefault: true,
5079     active : false,
5080     cls : false,
5081     disabled: false,
5082     
5083     getAutoCreate : function(){
5084         var cfg= {
5085             tag: 'li',
5086             cn: [
5087                 {
5088                     tag : 'a',
5089                     href : this.href ? this.href : '#',
5090                     html : this.html ? this.html : ''
5091                 }
5092             ]
5093         };
5094         
5095         if(this.cls){
5096             cfg.cls = this.cls;
5097         }
5098         
5099         if(this.disabled){
5100             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5101         }
5102         
5103         if(this.active){
5104             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5105         }
5106         
5107         return cfg;
5108     },
5109     
5110     initEvents: function() {
5111         
5112         this.el.on('click', this.onClick, this);
5113         
5114     },
5115     onClick : function(e)
5116     {
5117         Roo.log('PaginationItem on click ');
5118         if(this.preventDefault){
5119             e.preventDefault();
5120         }
5121         
5122         if(this.disabled){
5123             return;
5124         }
5125         
5126         this.fireEvent('click', this, e);
5127     }
5128    
5129 });
5130
5131  
5132
5133  /*
5134  * - LGPL
5135  *
5136  * slider
5137  * 
5138  */
5139
5140
5141 /**
5142  * @class Roo.bootstrap.Slider
5143  * @extends Roo.bootstrap.Component
5144  * Bootstrap Slider class
5145  *    
5146  * @constructor
5147  * Create a new Slider
5148  * @param {Object} config The config object
5149  */
5150
5151 Roo.bootstrap.Slider = function(config){
5152     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5153 };
5154
5155 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5156     
5157     getAutoCreate : function(){
5158         
5159         var cfg = {
5160             tag: 'div',
5161             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5162             cn: [
5163                 {
5164                     tag: 'a',
5165                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5166                 }
5167             ]
5168         };
5169         
5170         return cfg;
5171     }
5172    
5173 });
5174
5175  /*
5176  * Based on:
5177  * Ext JS Library 1.1.1
5178  * Copyright(c) 2006-2007, Ext JS, LLC.
5179  *
5180  * Originally Released Under LGPL - original licence link has changed is not relivant.
5181  *
5182  * Fork - LGPL
5183  * <script type="text/javascript">
5184  */
5185  
5186
5187 /**
5188  * @class Roo.grid.ColumnModel
5189  * @extends Roo.util.Observable
5190  * This is the default implementation of a ColumnModel used by the Grid. It defines
5191  * the columns in the grid.
5192  * <br>Usage:<br>
5193  <pre><code>
5194  var colModel = new Roo.grid.ColumnModel([
5195         {header: "Ticker", width: 60, sortable: true, locked: true},
5196         {header: "Company Name", width: 150, sortable: true},
5197         {header: "Market Cap.", width: 100, sortable: true},
5198         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5199         {header: "Employees", width: 100, sortable: true, resizable: false}
5200  ]);
5201  </code></pre>
5202  * <p>
5203  
5204  * The config options listed for this class are options which may appear in each
5205  * individual column definition.
5206  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5207  * @constructor
5208  * @param {Object} config An Array of column config objects. See this class's
5209  * config objects for details.
5210 */
5211 Roo.grid.ColumnModel = function(config){
5212         /**
5213      * The config passed into the constructor
5214      */
5215     this.config = config;
5216     this.lookup = {};
5217
5218     // if no id, create one
5219     // if the column does not have a dataIndex mapping,
5220     // map it to the order it is in the config
5221     for(var i = 0, len = config.length; i < len; i++){
5222         var c = config[i];
5223         if(typeof c.dataIndex == "undefined"){
5224             c.dataIndex = i;
5225         }
5226         if(typeof c.renderer == "string"){
5227             c.renderer = Roo.util.Format[c.renderer];
5228         }
5229         if(typeof c.id == "undefined"){
5230             c.id = Roo.id();
5231         }
5232         if(c.editor && c.editor.xtype){
5233             c.editor  = Roo.factory(c.editor, Roo.grid);
5234         }
5235         if(c.editor && c.editor.isFormField){
5236             c.editor = new Roo.grid.GridEditor(c.editor);
5237         }
5238         this.lookup[c.id] = c;
5239     }
5240
5241     /**
5242      * The width of columns which have no width specified (defaults to 100)
5243      * @type Number
5244      */
5245     this.defaultWidth = 100;
5246
5247     /**
5248      * Default sortable of columns which have no sortable specified (defaults to false)
5249      * @type Boolean
5250      */
5251     this.defaultSortable = false;
5252
5253     this.addEvents({
5254         /**
5255              * @event widthchange
5256              * Fires when the width of a column changes.
5257              * @param {ColumnModel} this
5258              * @param {Number} columnIndex The column index
5259              * @param {Number} newWidth The new width
5260              */
5261             "widthchange": true,
5262         /**
5263              * @event headerchange
5264              * Fires when the text of a header changes.
5265              * @param {ColumnModel} this
5266              * @param {Number} columnIndex The column index
5267              * @param {Number} newText The new header text
5268              */
5269             "headerchange": true,
5270         /**
5271              * @event hiddenchange
5272              * Fires when a column is hidden or "unhidden".
5273              * @param {ColumnModel} this
5274              * @param {Number} columnIndex The column index
5275              * @param {Boolean} hidden true if hidden, false otherwise
5276              */
5277             "hiddenchange": true,
5278             /**
5279          * @event columnmoved
5280          * Fires when a column is moved.
5281          * @param {ColumnModel} this
5282          * @param {Number} oldIndex
5283          * @param {Number} newIndex
5284          */
5285         "columnmoved" : true,
5286         /**
5287          * @event columlockchange
5288          * Fires when a column's locked state is changed
5289          * @param {ColumnModel} this
5290          * @param {Number} colIndex
5291          * @param {Boolean} locked true if locked
5292          */
5293         "columnlockchange" : true
5294     });
5295     Roo.grid.ColumnModel.superclass.constructor.call(this);
5296 };
5297 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5298     /**
5299      * @cfg {String} header The header text to display in the Grid view.
5300      */
5301     /**
5302      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5303      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5304      * specified, the column's index is used as an index into the Record's data Array.
5305      */
5306     /**
5307      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5308      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5309      */
5310     /**
5311      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5312      * Defaults to the value of the {@link #defaultSortable} property.
5313      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5314      */
5315     /**
5316      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5317      */
5318     /**
5319      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5320      */
5321     /**
5322      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5323      */
5324     /**
5325      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5326      */
5327     /**
5328      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5329      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5330      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5331      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5332      */
5333        /**
5334      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5335      */
5336     /**
5337      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5338      */
5339     /**
5340      * @cfg {String} cursor (Optional)
5341      */
5342     /**
5343      * @cfg {String} tooltip (Optional)
5344      */
5345     /**
5346      * @cfg {Number} xs (Optional)
5347      */
5348     /**
5349      * @cfg {Number} sm (Optional)
5350      */
5351     /**
5352      * @cfg {Number} md (Optional)
5353      */
5354     /**
5355      * @cfg {Number} lg (Optional)
5356      */
5357     /**
5358      * Returns the id of the column at the specified index.
5359      * @param {Number} index The column index
5360      * @return {String} the id
5361      */
5362     getColumnId : function(index){
5363         return this.config[index].id;
5364     },
5365
5366     /**
5367      * Returns the column for a specified id.
5368      * @param {String} id The column id
5369      * @return {Object} the column
5370      */
5371     getColumnById : function(id){
5372         return this.lookup[id];
5373     },
5374
5375     
5376     /**
5377      * Returns the column for a specified dataIndex.
5378      * @param {String} dataIndex The column dataIndex
5379      * @return {Object|Boolean} the column or false if not found
5380      */
5381     getColumnByDataIndex: function(dataIndex){
5382         var index = this.findColumnIndex(dataIndex);
5383         return index > -1 ? this.config[index] : false;
5384     },
5385     
5386     /**
5387      * Returns the index for a specified column id.
5388      * @param {String} id The column id
5389      * @return {Number} the index, or -1 if not found
5390      */
5391     getIndexById : function(id){
5392         for(var i = 0, len = this.config.length; i < len; i++){
5393             if(this.config[i].id == id){
5394                 return i;
5395             }
5396         }
5397         return -1;
5398     },
5399     
5400     /**
5401      * Returns the index for a specified column dataIndex.
5402      * @param {String} dataIndex The column dataIndex
5403      * @return {Number} the index, or -1 if not found
5404      */
5405     
5406     findColumnIndex : function(dataIndex){
5407         for(var i = 0, len = this.config.length; i < len; i++){
5408             if(this.config[i].dataIndex == dataIndex){
5409                 return i;
5410             }
5411         }
5412         return -1;
5413     },
5414     
5415     
5416     moveColumn : function(oldIndex, newIndex){
5417         var c = this.config[oldIndex];
5418         this.config.splice(oldIndex, 1);
5419         this.config.splice(newIndex, 0, c);
5420         this.dataMap = null;
5421         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5422     },
5423
5424     isLocked : function(colIndex){
5425         return this.config[colIndex].locked === true;
5426     },
5427
5428     setLocked : function(colIndex, value, suppressEvent){
5429         if(this.isLocked(colIndex) == value){
5430             return;
5431         }
5432         this.config[colIndex].locked = value;
5433         if(!suppressEvent){
5434             this.fireEvent("columnlockchange", this, colIndex, value);
5435         }
5436     },
5437
5438     getTotalLockedWidth : function(){
5439         var totalWidth = 0;
5440         for(var i = 0; i < this.config.length; i++){
5441             if(this.isLocked(i) && !this.isHidden(i)){
5442                 this.totalWidth += this.getColumnWidth(i);
5443             }
5444         }
5445         return totalWidth;
5446     },
5447
5448     getLockedCount : function(){
5449         for(var i = 0, len = this.config.length; i < len; i++){
5450             if(!this.isLocked(i)){
5451                 return i;
5452             }
5453         }
5454         
5455         return this.config.length;
5456     },
5457
5458     /**
5459      * Returns the number of columns.
5460      * @return {Number}
5461      */
5462     getColumnCount : function(visibleOnly){
5463         if(visibleOnly === true){
5464             var c = 0;
5465             for(var i = 0, len = this.config.length; i < len; i++){
5466                 if(!this.isHidden(i)){
5467                     c++;
5468                 }
5469             }
5470             return c;
5471         }
5472         return this.config.length;
5473     },
5474
5475     /**
5476      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5477      * @param {Function} fn
5478      * @param {Object} scope (optional)
5479      * @return {Array} result
5480      */
5481     getColumnsBy : function(fn, scope){
5482         var r = [];
5483         for(var i = 0, len = this.config.length; i < len; i++){
5484             var c = this.config[i];
5485             if(fn.call(scope||this, c, i) === true){
5486                 r[r.length] = c;
5487             }
5488         }
5489         return r;
5490     },
5491
5492     /**
5493      * Returns true if the specified column is sortable.
5494      * @param {Number} col The column index
5495      * @return {Boolean}
5496      */
5497     isSortable : function(col){
5498         if(typeof this.config[col].sortable == "undefined"){
5499             return this.defaultSortable;
5500         }
5501         return this.config[col].sortable;
5502     },
5503
5504     /**
5505      * Returns the rendering (formatting) function defined for the column.
5506      * @param {Number} col The column index.
5507      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5508      */
5509     getRenderer : function(col){
5510         if(!this.config[col].renderer){
5511             return Roo.grid.ColumnModel.defaultRenderer;
5512         }
5513         return this.config[col].renderer;
5514     },
5515
5516     /**
5517      * Sets the rendering (formatting) function for a column.
5518      * @param {Number} col The column index
5519      * @param {Function} fn The function to use to process the cell's raw data
5520      * to return HTML markup for the grid view. The render function is called with
5521      * the following parameters:<ul>
5522      * <li>Data value.</li>
5523      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5524      * <li>css A CSS style string to apply to the table cell.</li>
5525      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5526      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5527      * <li>Row index</li>
5528      * <li>Column index</li>
5529      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5530      */
5531     setRenderer : function(col, fn){
5532         this.config[col].renderer = fn;
5533     },
5534
5535     /**
5536      * Returns the width for the specified column.
5537      * @param {Number} col The column index
5538      * @return {Number}
5539      */
5540     getColumnWidth : function(col){
5541         return this.config[col].width * 1 || this.defaultWidth;
5542     },
5543
5544     /**
5545      * Sets the width for a column.
5546      * @param {Number} col The column index
5547      * @param {Number} width The new width
5548      */
5549     setColumnWidth : function(col, width, suppressEvent){
5550         this.config[col].width = width;
5551         this.totalWidth = null;
5552         if(!suppressEvent){
5553              this.fireEvent("widthchange", this, col, width);
5554         }
5555     },
5556
5557     /**
5558      * Returns the total width of all columns.
5559      * @param {Boolean} includeHidden True to include hidden column widths
5560      * @return {Number}
5561      */
5562     getTotalWidth : function(includeHidden){
5563         if(!this.totalWidth){
5564             this.totalWidth = 0;
5565             for(var i = 0, len = this.config.length; i < len; i++){
5566                 if(includeHidden || !this.isHidden(i)){
5567                     this.totalWidth += this.getColumnWidth(i);
5568                 }
5569             }
5570         }
5571         return this.totalWidth;
5572     },
5573
5574     /**
5575      * Returns the header for the specified column.
5576      * @param {Number} col The column index
5577      * @return {String}
5578      */
5579     getColumnHeader : function(col){
5580         return this.config[col].header;
5581     },
5582
5583     /**
5584      * Sets the header for a column.
5585      * @param {Number} col The column index
5586      * @param {String} header The new header
5587      */
5588     setColumnHeader : function(col, header){
5589         this.config[col].header = header;
5590         this.fireEvent("headerchange", this, col, header);
5591     },
5592
5593     /**
5594      * Returns the tooltip for the specified column.
5595      * @param {Number} col The column index
5596      * @return {String}
5597      */
5598     getColumnTooltip : function(col){
5599             return this.config[col].tooltip;
5600     },
5601     /**
5602      * Sets the tooltip for a column.
5603      * @param {Number} col The column index
5604      * @param {String} tooltip The new tooltip
5605      */
5606     setColumnTooltip : function(col, tooltip){
5607             this.config[col].tooltip = tooltip;
5608     },
5609
5610     /**
5611      * Returns the dataIndex for the specified column.
5612      * @param {Number} col The column index
5613      * @return {Number}
5614      */
5615     getDataIndex : function(col){
5616         return this.config[col].dataIndex;
5617     },
5618
5619     /**
5620      * Sets the dataIndex for a column.
5621      * @param {Number} col The column index
5622      * @param {Number} dataIndex The new dataIndex
5623      */
5624     setDataIndex : function(col, dataIndex){
5625         this.config[col].dataIndex = dataIndex;
5626     },
5627
5628     
5629     
5630     /**
5631      * Returns true if the cell is editable.
5632      * @param {Number} colIndex The column index
5633      * @param {Number} rowIndex The row index - this is nto actually used..?
5634      * @return {Boolean}
5635      */
5636     isCellEditable : function(colIndex, rowIndex){
5637         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5638     },
5639
5640     /**
5641      * Returns the editor defined for the cell/column.
5642      * return false or null to disable editing.
5643      * @param {Number} colIndex The column index
5644      * @param {Number} rowIndex The row index
5645      * @return {Object}
5646      */
5647     getCellEditor : function(colIndex, rowIndex){
5648         return this.config[colIndex].editor;
5649     },
5650
5651     /**
5652      * Sets if a column is editable.
5653      * @param {Number} col The column index
5654      * @param {Boolean} editable True if the column is editable
5655      */
5656     setEditable : function(col, editable){
5657         this.config[col].editable = editable;
5658     },
5659
5660
5661     /**
5662      * Returns true if the column is hidden.
5663      * @param {Number} colIndex The column index
5664      * @return {Boolean}
5665      */
5666     isHidden : function(colIndex){
5667         return this.config[colIndex].hidden;
5668     },
5669
5670
5671     /**
5672      * Returns true if the column width cannot be changed
5673      */
5674     isFixed : function(colIndex){
5675         return this.config[colIndex].fixed;
5676     },
5677
5678     /**
5679      * Returns true if the column can be resized
5680      * @return {Boolean}
5681      */
5682     isResizable : function(colIndex){
5683         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5684     },
5685     /**
5686      * Sets if a column is hidden.
5687      * @param {Number} colIndex The column index
5688      * @param {Boolean} hidden True if the column is hidden
5689      */
5690     setHidden : function(colIndex, hidden){
5691         this.config[colIndex].hidden = hidden;
5692         this.totalWidth = null;
5693         this.fireEvent("hiddenchange", this, colIndex, hidden);
5694     },
5695
5696     /**
5697      * Sets the editor for a column.
5698      * @param {Number} col The column index
5699      * @param {Object} editor The editor object
5700      */
5701     setEditor : function(col, editor){
5702         this.config[col].editor = editor;
5703     }
5704 });
5705
5706 Roo.grid.ColumnModel.defaultRenderer = function(value)
5707 {
5708     if(typeof value == "object") {
5709         return value;
5710     }
5711         if(typeof value == "string" && value.length < 1){
5712             return "&#160;";
5713         }
5714     
5715         return String.format("{0}", value);
5716 };
5717
5718 // Alias for backwards compatibility
5719 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5720 /*
5721  * Based on:
5722  * Ext JS Library 1.1.1
5723  * Copyright(c) 2006-2007, Ext JS, LLC.
5724  *
5725  * Originally Released Under LGPL - original licence link has changed is not relivant.
5726  *
5727  * Fork - LGPL
5728  * <script type="text/javascript">
5729  */
5730  
5731 /**
5732  * @class Roo.LoadMask
5733  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5734  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5735  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5736  * element's UpdateManager load indicator and will be destroyed after the initial load.
5737  * @constructor
5738  * Create a new LoadMask
5739  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5740  * @param {Object} config The config object
5741  */
5742 Roo.LoadMask = function(el, config){
5743     this.el = Roo.get(el);
5744     Roo.apply(this, config);
5745     if(this.store){
5746         this.store.on('beforeload', this.onBeforeLoad, this);
5747         this.store.on('load', this.onLoad, this);
5748         this.store.on('loadexception', this.onLoadException, this);
5749         this.removeMask = false;
5750     }else{
5751         var um = this.el.getUpdateManager();
5752         um.showLoadIndicator = false; // disable the default indicator
5753         um.on('beforeupdate', this.onBeforeLoad, this);
5754         um.on('update', this.onLoad, this);
5755         um.on('failure', this.onLoad, this);
5756         this.removeMask = true;
5757     }
5758 };
5759
5760 Roo.LoadMask.prototype = {
5761     /**
5762      * @cfg {Boolean} removeMask
5763      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5764      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5765      */
5766     /**
5767      * @cfg {String} msg
5768      * The text to display in a centered loading message box (defaults to 'Loading...')
5769      */
5770     msg : 'Loading...',
5771     /**
5772      * @cfg {String} msgCls
5773      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5774      */
5775     msgCls : 'x-mask-loading',
5776
5777     /**
5778      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5779      * @type Boolean
5780      */
5781     disabled: false,
5782
5783     /**
5784      * Disables the mask to prevent it from being displayed
5785      */
5786     disable : function(){
5787        this.disabled = true;
5788     },
5789
5790     /**
5791      * Enables the mask so that it can be displayed
5792      */
5793     enable : function(){
5794         this.disabled = false;
5795     },
5796     
5797     onLoadException : function()
5798     {
5799         Roo.log(arguments);
5800         
5801         if (typeof(arguments[3]) != 'undefined') {
5802             Roo.MessageBox.alert("Error loading",arguments[3]);
5803         } 
5804         /*
5805         try {
5806             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5807                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5808             }   
5809         } catch(e) {
5810             
5811         }
5812         */
5813     
5814         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5815     },
5816     // private
5817     onLoad : function()
5818     {
5819         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5820     },
5821
5822     // private
5823     onBeforeLoad : function(){
5824         if(!this.disabled){
5825             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5826         }
5827     },
5828
5829     // private
5830     destroy : function(){
5831         if(this.store){
5832             this.store.un('beforeload', this.onBeforeLoad, this);
5833             this.store.un('load', this.onLoad, this);
5834             this.store.un('loadexception', this.onLoadException, this);
5835         }else{
5836             var um = this.el.getUpdateManager();
5837             um.un('beforeupdate', this.onBeforeLoad, this);
5838             um.un('update', this.onLoad, this);
5839             um.un('failure', this.onLoad, this);
5840         }
5841     }
5842 };/*
5843  * - LGPL
5844  *
5845  * table
5846  * 
5847  */
5848
5849 /**
5850  * @class Roo.bootstrap.Table
5851  * @extends Roo.bootstrap.Component
5852  * Bootstrap Table class
5853  * @cfg {String} cls table class
5854  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5855  * @cfg {String} bgcolor Specifies the background color for a table
5856  * @cfg {Number} border Specifies whether the table cells should have borders or not
5857  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5858  * @cfg {Number} cellspacing Specifies the space between cells
5859  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5860  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5861  * @cfg {String} sortable Specifies that the table should be sortable
5862  * @cfg {String} summary Specifies a summary of the content of a table
5863  * @cfg {Number} width Specifies the width of a table
5864  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5865  * 
5866  * @cfg {boolean} striped Should the rows be alternative striped
5867  * @cfg {boolean} bordered Add borders to the table
5868  * @cfg {boolean} hover Add hover highlighting
5869  * @cfg {boolean} condensed Format condensed
5870  * @cfg {boolean} responsive Format condensed
5871  * @cfg {Boolean} loadMask (true|false) default false
5872  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5873  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5874  * @cfg {Boolean} rowSelection (true|false) default false
5875  * @cfg {Boolean} cellSelection (true|false) default false
5876  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5877  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5878  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5879  
5880  * 
5881  * @constructor
5882  * Create a new Table
5883  * @param {Object} config The config object
5884  */
5885
5886 Roo.bootstrap.Table = function(config){
5887     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5888     
5889   
5890     
5891     // BC...
5892     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5893     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5894     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5895     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5896     
5897     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5898     if (this.sm) {
5899         this.sm.grid = this;
5900         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5901         this.sm = this.selModel;
5902         this.sm.xmodule = this.xmodule || false;
5903     }
5904     
5905     if (this.cm && typeof(this.cm.config) == 'undefined') {
5906         this.colModel = new Roo.grid.ColumnModel(this.cm);
5907         this.cm = this.colModel;
5908         this.cm.xmodule = this.xmodule || false;
5909     }
5910     if (this.store) {
5911         this.store= Roo.factory(this.store, Roo.data);
5912         this.ds = this.store;
5913         this.ds.xmodule = this.xmodule || false;
5914          
5915     }
5916     if (this.footer && this.store) {
5917         this.footer.dataSource = this.ds;
5918         this.footer = Roo.factory(this.footer);
5919     }
5920     
5921     /** @private */
5922     this.addEvents({
5923         /**
5924          * @event cellclick
5925          * Fires when a cell is clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Roo.Element} el
5928          * @param {Number} rowIndex
5929          * @param {Number} columnIndex
5930          * @param {Roo.EventObject} e
5931          */
5932         "cellclick" : true,
5933         /**
5934          * @event celldblclick
5935          * Fires when a cell is double clicked
5936          * @param {Roo.bootstrap.Table} this
5937          * @param {Roo.Element} el
5938          * @param {Number} rowIndex
5939          * @param {Number} columnIndex
5940          * @param {Roo.EventObject} e
5941          */
5942         "celldblclick" : true,
5943         /**
5944          * @event rowclick
5945          * Fires when a row is clicked
5946          * @param {Roo.bootstrap.Table} this
5947          * @param {Roo.Element} el
5948          * @param {Number} rowIndex
5949          * @param {Roo.EventObject} e
5950          */
5951         "rowclick" : true,
5952         /**
5953          * @event rowdblclick
5954          * Fires when a row is double clicked
5955          * @param {Roo.bootstrap.Table} this
5956          * @param {Roo.Element} el
5957          * @param {Number} rowIndex
5958          * @param {Roo.EventObject} e
5959          */
5960         "rowdblclick" : true,
5961         /**
5962          * @event mouseover
5963          * Fires when a mouseover occur
5964          * @param {Roo.bootstrap.Table} this
5965          * @param {Roo.Element} el
5966          * @param {Number} rowIndex
5967          * @param {Number} columnIndex
5968          * @param {Roo.EventObject} e
5969          */
5970         "mouseover" : true,
5971         /**
5972          * @event mouseout
5973          * Fires when a mouseout occur
5974          * @param {Roo.bootstrap.Table} this
5975          * @param {Roo.Element} el
5976          * @param {Number} rowIndex
5977          * @param {Number} columnIndex
5978          * @param {Roo.EventObject} e
5979          */
5980         "mouseout" : true,
5981         /**
5982          * @event rowclass
5983          * Fires when a row is rendered, so you can change add a style to it.
5984          * @param {Roo.bootstrap.Table} this
5985          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5986          */
5987         'rowclass' : true,
5988           /**
5989          * @event rowsrendered
5990          * Fires when all the  rows have been rendered
5991          * @param {Roo.bootstrap.Table} this
5992          */
5993         'rowsrendered' : true,
5994         /**
5995          * @event contextmenu
5996          * The raw contextmenu event for the entire grid.
5997          * @param {Roo.EventObject} e
5998          */
5999         "contextmenu" : true,
6000         /**
6001          * @event rowcontextmenu
6002          * Fires when a row is right clicked
6003          * @param {Roo.bootstrap.Table} this
6004          * @param {Number} rowIndex
6005          * @param {Roo.EventObject} e
6006          */
6007         "rowcontextmenu" : true,
6008         /**
6009          * @event cellcontextmenu
6010          * Fires when a cell is right clicked
6011          * @param {Roo.bootstrap.Table} this
6012          * @param {Number} rowIndex
6013          * @param {Number} cellIndex
6014          * @param {Roo.EventObject} e
6015          */
6016          "cellcontextmenu" : true,
6017          /**
6018          * @event headercontextmenu
6019          * Fires when a header is right clicked
6020          * @param {Roo.bootstrap.Table} this
6021          * @param {Number} columnIndex
6022          * @param {Roo.EventObject} e
6023          */
6024         "headercontextmenu" : true
6025     });
6026 };
6027
6028 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6029     
6030     cls: false,
6031     align: false,
6032     bgcolor: false,
6033     border: false,
6034     cellpadding: false,
6035     cellspacing: false,
6036     frame: false,
6037     rules: false,
6038     sortable: false,
6039     summary: false,
6040     width: false,
6041     striped : false,
6042     scrollBody : false,
6043     bordered: false,
6044     hover:  false,
6045     condensed : false,
6046     responsive : false,
6047     sm : false,
6048     cm : false,
6049     store : false,
6050     loadMask : false,
6051     footerShow : true,
6052     headerShow : true,
6053   
6054     rowSelection : false,
6055     cellSelection : false,
6056     layout : false,
6057     
6058     // Roo.Element - the tbody
6059     mainBody: false,
6060     // Roo.Element - thead element
6061     mainHead: false,
6062     
6063     container: false, // used by gridpanel...
6064     
6065     lazyLoad : false,
6066     
6067     CSS : Roo.util.CSS,
6068     
6069     getAutoCreate : function()
6070     {
6071         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6072         
6073         cfg = {
6074             tag: 'table',
6075             cls : 'table',
6076             cn : []
6077         };
6078         if (this.scrollBody) {
6079             cfg.cls += ' table-body-fixed';
6080         }    
6081         if (this.striped) {
6082             cfg.cls += ' table-striped';
6083         }
6084         
6085         if (this.hover) {
6086             cfg.cls += ' table-hover';
6087         }
6088         if (this.bordered) {
6089             cfg.cls += ' table-bordered';
6090         }
6091         if (this.condensed) {
6092             cfg.cls += ' table-condensed';
6093         }
6094         if (this.responsive) {
6095             cfg.cls += ' table-responsive';
6096         }
6097         
6098         if (this.cls) {
6099             cfg.cls+=  ' ' +this.cls;
6100         }
6101         
6102         // this lot should be simplifed...
6103         
6104         if (this.align) {
6105             cfg.align=this.align;
6106         }
6107         if (this.bgcolor) {
6108             cfg.bgcolor=this.bgcolor;
6109         }
6110         if (this.border) {
6111             cfg.border=this.border;
6112         }
6113         if (this.cellpadding) {
6114             cfg.cellpadding=this.cellpadding;
6115         }
6116         if (this.cellspacing) {
6117             cfg.cellspacing=this.cellspacing;
6118         }
6119         if (this.frame) {
6120             cfg.frame=this.frame;
6121         }
6122         if (this.rules) {
6123             cfg.rules=this.rules;
6124         }
6125         if (this.sortable) {
6126             cfg.sortable=this.sortable;
6127         }
6128         if (this.summary) {
6129             cfg.summary=this.summary;
6130         }
6131         if (this.width) {
6132             cfg.width=this.width;
6133         }
6134         if (this.layout) {
6135             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6136         }
6137         
6138         if(this.store || this.cm){
6139             if(this.headerShow){
6140                 cfg.cn.push(this.renderHeader());
6141             }
6142             
6143             cfg.cn.push(this.renderBody());
6144             
6145             if(this.footerShow){
6146                 cfg.cn.push(this.renderFooter());
6147             }
6148             // where does this come from?
6149             //cfg.cls+=  ' TableGrid';
6150         }
6151         
6152         return { cn : [ cfg ] };
6153     },
6154     
6155     initEvents : function()
6156     {   
6157         if(!this.store || !this.cm){
6158             return;
6159         }
6160         if (this.selModel) {
6161             this.selModel.initEvents();
6162         }
6163         
6164         
6165         //Roo.log('initEvents with ds!!!!');
6166         
6167         this.mainBody = this.el.select('tbody', true).first();
6168         this.mainHead = this.el.select('thead', true).first();
6169         
6170         
6171         
6172         
6173         var _this = this;
6174         
6175         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6176             e.on('click', _this.sort, _this);
6177         });
6178         
6179         this.mainBody.on("click", this.onClick, this);
6180         this.mainBody.on("dblclick", this.onDblClick, this);
6181         
6182         // why is this done????? = it breaks dialogs??
6183         //this.parent().el.setStyle('position', 'relative');
6184         
6185         
6186         if (this.footer) {
6187             this.footer.parentId = this.id;
6188             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6189             
6190             if(this.lazyLoad){
6191                 this.el.select('tfoot tr td').first().addClass('hide');
6192             }
6193         } 
6194         
6195         if(this.loadMask) {
6196             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6197         }
6198         
6199         this.store.on('load', this.onLoad, this);
6200         this.store.on('beforeload', this.onBeforeLoad, this);
6201         this.store.on('update', this.onUpdate, this);
6202         this.store.on('add', this.onAdd, this);
6203         this.store.on("clear", this.clear, this);
6204         
6205         this.el.on("contextmenu", this.onContextMenu, this);
6206         
6207         this.mainBody.on('scroll', this.onBodyScroll, this);
6208         
6209         this.cm.on("headerchange", this.onHeaderChange, this);
6210         
6211         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6212         
6213     },
6214     
6215     onContextMenu : function(e, t)
6216     {
6217         this.processEvent("contextmenu", e);
6218     },
6219     
6220     processEvent : function(name, e)
6221     {
6222         if (name != 'touchstart' ) {
6223             this.fireEvent(name, e);    
6224         }
6225         
6226         var t = e.getTarget();
6227         
6228         var cell = Roo.get(t);
6229         
6230         if(!cell){
6231             return;
6232         }
6233         
6234         if(cell.findParent('tfoot', false, true)){
6235             return;
6236         }
6237         
6238         if(cell.findParent('thead', false, true)){
6239             
6240             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6241                 cell = Roo.get(t).findParent('th', false, true);
6242                 if (!cell) {
6243                     Roo.log("failed to find th in thead?");
6244                     Roo.log(e.getTarget());
6245                     return;
6246                 }
6247             }
6248             
6249             var cellIndex = cell.dom.cellIndex;
6250             
6251             var ename = name == 'touchstart' ? 'click' : name;
6252             this.fireEvent("header" + ename, this, cellIndex, e);
6253             
6254             return;
6255         }
6256         
6257         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6258             cell = Roo.get(t).findParent('td', false, true);
6259             if (!cell) {
6260                 Roo.log("failed to find th in tbody?");
6261                 Roo.log(e.getTarget());
6262                 return;
6263             }
6264         }
6265         
6266         var row = cell.findParent('tr', false, true);
6267         var cellIndex = cell.dom.cellIndex;
6268         var rowIndex = row.dom.rowIndex - 1;
6269         
6270         if(row !== false){
6271             
6272             this.fireEvent("row" + name, this, rowIndex, e);
6273             
6274             if(cell !== false){
6275             
6276                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6277             }
6278         }
6279         
6280     },
6281     
6282     onMouseover : function(e, el)
6283     {
6284         var cell = Roo.get(el);
6285         
6286         if(!cell){
6287             return;
6288         }
6289         
6290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291             cell = cell.findParent('td', false, true);
6292         }
6293         
6294         var row = cell.findParent('tr', false, true);
6295         var cellIndex = cell.dom.cellIndex;
6296         var rowIndex = row.dom.rowIndex - 1; // start from 0
6297         
6298         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6299         
6300     },
6301     
6302     onMouseout : function(e, el)
6303     {
6304         var cell = Roo.get(el);
6305         
6306         if(!cell){
6307             return;
6308         }
6309         
6310         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6311             cell = cell.findParent('td', false, true);
6312         }
6313         
6314         var row = cell.findParent('tr', false, true);
6315         var cellIndex = cell.dom.cellIndex;
6316         var rowIndex = row.dom.rowIndex - 1; // start from 0
6317         
6318         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6319         
6320     },
6321     
6322     onClick : function(e, el)
6323     {
6324         var cell = Roo.get(el);
6325         
6326         if(!cell || (!this.cellSelection && !this.rowSelection)){
6327             return;
6328         }
6329         
6330         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6331             cell = cell.findParent('td', false, true);
6332         }
6333         
6334         if(!cell || typeof(cell) == 'undefined'){
6335             return;
6336         }
6337         
6338         var row = cell.findParent('tr', false, true);
6339         
6340         if(!row || typeof(row) == 'undefined'){
6341             return;
6342         }
6343         
6344         var cellIndex = cell.dom.cellIndex;
6345         var rowIndex = this.getRowIndex(row);
6346         
6347         // why??? - should these not be based on SelectionModel?
6348         if(this.cellSelection){
6349             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6350         }
6351         
6352         if(this.rowSelection){
6353             this.fireEvent('rowclick', this, row, rowIndex, e);
6354         }
6355         
6356         
6357     },
6358         
6359     onDblClick : function(e,el)
6360     {
6361         var cell = Roo.get(el);
6362         
6363         if(!cell || (!this.cellSelection && !this.rowSelection)){
6364             return;
6365         }
6366         
6367         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6368             cell = cell.findParent('td', false, true);
6369         }
6370         
6371         if(!cell || typeof(cell) == 'undefined'){
6372             return;
6373         }
6374         
6375         var row = cell.findParent('tr', false, true);
6376         
6377         if(!row || typeof(row) == 'undefined'){
6378             return;
6379         }
6380         
6381         var cellIndex = cell.dom.cellIndex;
6382         var rowIndex = this.getRowIndex(row);
6383         
6384         if(this.cellSelection){
6385             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6386         }
6387         
6388         if(this.rowSelection){
6389             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6390         }
6391     },
6392     
6393     sort : function(e,el)
6394     {
6395         var col = Roo.get(el);
6396         
6397         if(!col.hasClass('sortable')){
6398             return;
6399         }
6400         
6401         var sort = col.attr('sort');
6402         var dir = 'ASC';
6403         
6404         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6405             dir = 'DESC';
6406         }
6407         
6408         this.store.sortInfo = {field : sort, direction : dir};
6409         
6410         if (this.footer) {
6411             Roo.log("calling footer first");
6412             this.footer.onClick('first');
6413         } else {
6414         
6415             this.store.load({ params : { start : 0 } });
6416         }
6417     },
6418     
6419     renderHeader : function()
6420     {
6421         var header = {
6422             tag: 'thead',
6423             cn : []
6424         };
6425         
6426         var cm = this.cm;
6427         this.totalWidth = 0;
6428         
6429         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6430             
6431             var config = cm.config[i];
6432             
6433             var c = {
6434                 tag: 'th',
6435                 cls : 'x-hcol-' + i,
6436                 style : '',
6437                 html: cm.getColumnHeader(i)
6438             };
6439             
6440             var hh = '';
6441             
6442             if(typeof(config.sortable) != 'undefined' && config.sortable){
6443                 c.cls = 'sortable';
6444                 c.html = '<i class="glyphicon"></i>' + c.html;
6445             }
6446             
6447             if(typeof(config.lgHeader) != 'undefined'){
6448                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6449             }
6450             
6451             if(typeof(config.mdHeader) != 'undefined'){
6452                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6453             }
6454             
6455             if(typeof(config.smHeader) != 'undefined'){
6456                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6457             }
6458             
6459             if(typeof(config.xsHeader) != 'undefined'){
6460                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6461             }
6462             
6463             if(hh.length){
6464                 c.html = hh;
6465             }
6466             
6467             if(typeof(config.tooltip) != 'undefined'){
6468                 c.tooltip = config.tooltip;
6469             }
6470             
6471             if(typeof(config.colspan) != 'undefined'){
6472                 c.colspan = config.colspan;
6473             }
6474             
6475             if(typeof(config.hidden) != 'undefined' && config.hidden){
6476                 c.style += ' display:none;';
6477             }
6478             
6479             if(typeof(config.dataIndex) != 'undefined'){
6480                 c.sort = config.dataIndex;
6481             }
6482             
6483            
6484             
6485             if(typeof(config.align) != 'undefined' && config.align.length){
6486                 c.style += ' text-align:' + config.align + ';';
6487             }
6488             
6489             if(typeof(config.width) != 'undefined'){
6490                 c.style += ' width:' + config.width + 'px;';
6491                 this.totalWidth += config.width;
6492             } else {
6493                 this.totalWidth += 100; // assume minimum of 100 per column?
6494             }
6495             
6496             if(typeof(config.cls) != 'undefined'){
6497                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6498             }
6499             
6500             ['xs','sm','md','lg'].map(function(size){
6501                 
6502                 if(typeof(config[size]) == 'undefined'){
6503                     return;
6504                 }
6505                 
6506                 if (!config[size]) { // 0 = hidden
6507                     c.cls += ' hidden-' + size;
6508                     return;
6509                 }
6510                 
6511                 c.cls += ' col-' + size + '-' + config[size];
6512
6513             });
6514             
6515             header.cn.push(c)
6516         }
6517         
6518         return header;
6519     },
6520     
6521     renderBody : function()
6522     {
6523         var body = {
6524             tag: 'tbody',
6525             cn : [
6526                 {
6527                     tag: 'tr',
6528                     cn : [
6529                         {
6530                             tag : 'td',
6531                             colspan :  this.cm.getColumnCount()
6532                         }
6533                     ]
6534                 }
6535             ]
6536         };
6537         
6538         return body;
6539     },
6540     
6541     renderFooter : function()
6542     {
6543         var footer = {
6544             tag: 'tfoot',
6545             cn : [
6546                 {
6547                     tag: 'tr',
6548                     cn : [
6549                         {
6550                             tag : 'td',
6551                             colspan :  this.cm.getColumnCount()
6552                         }
6553                     ]
6554                 }
6555             ]
6556         };
6557         
6558         return footer;
6559     },
6560     
6561     
6562     
6563     onLoad : function()
6564     {
6565 //        Roo.log('ds onload');
6566         this.clear();
6567         
6568         var _this = this;
6569         var cm = this.cm;
6570         var ds = this.store;
6571         
6572         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6573             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6574             if (_this.store.sortInfo) {
6575                     
6576                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6577                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6578                 }
6579                 
6580                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6581                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6582                 }
6583             }
6584         });
6585         
6586         var tbody =  this.mainBody;
6587               
6588         if(ds.getCount() > 0){
6589             ds.data.each(function(d,rowIndex){
6590                 var row =  this.renderRow(cm, ds, rowIndex);
6591                 
6592                 tbody.createChild(row);
6593                 
6594                 var _this = this;
6595                 
6596                 if(row.cellObjects.length){
6597                     Roo.each(row.cellObjects, function(r){
6598                         _this.renderCellObject(r);
6599                     })
6600                 }
6601                 
6602             }, this);
6603         }
6604         
6605         Roo.each(this.el.select('tbody td', true).elements, function(e){
6606             e.on('mouseover', _this.onMouseover, _this);
6607         });
6608         
6609         Roo.each(this.el.select('tbody td', true).elements, function(e){
6610             e.on('mouseout', _this.onMouseout, _this);
6611         });
6612         this.fireEvent('rowsrendered', this);
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         
6826     },
6827      /**
6828      * Remove all rows
6829      */
6830     clear : function()
6831     {
6832         this.el.select('tbody', true).first().dom.innerHTML = '';
6833     },
6834     /**
6835      * Show or hide a row.
6836      * @param {Number} rowIndex to show or hide
6837      * @param {Boolean} state hide
6838      */
6839     setRowVisibility : function(rowIndex, state)
6840     {
6841         var bt = this.mainBody.dom;
6842         
6843         var rows = this.el.select('tbody > tr', true).elements;
6844         
6845         if(typeof(rows[rowIndex]) == 'undefined'){
6846             return;
6847         }
6848         rows[rowIndex].dom.style.display = state ? '' : 'none';
6849     },
6850     
6851     
6852     getSelectionModel : function(){
6853         if(!this.selModel){
6854             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6855         }
6856         return this.selModel;
6857     },
6858     /*
6859      * Render the Roo.bootstrap object from renderder
6860      */
6861     renderCellObject : function(r)
6862     {
6863         var _this = this;
6864         
6865         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6866         
6867         var t = r.cfg.render(r.container);
6868         
6869         if(r.cfg.cn){
6870             Roo.each(r.cfg.cn, function(c){
6871                 var child = {
6872                     container: t.getChildContainer(),
6873                     cfg: c
6874                 };
6875                 _this.renderCellObject(child);
6876             })
6877         }
6878     },
6879     
6880     getRowIndex : function(row)
6881     {
6882         var rowIndex = -1;
6883         
6884         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6885             if(el != row){
6886                 return;
6887             }
6888             
6889             rowIndex = index;
6890         });
6891         
6892         return rowIndex;
6893     },
6894      /**
6895      * Returns the grid's underlying element = used by panel.Grid
6896      * @return {Element} The element
6897      */
6898     getGridEl : function(){
6899         return this.el;
6900     },
6901      /**
6902      * Forces a resize - used by panel.Grid
6903      * @return {Element} The element
6904      */
6905     autoSize : function()
6906     {
6907         //var ctr = Roo.get(this.container.dom.parentElement);
6908         var ctr = Roo.get(this.el.dom);
6909         
6910         var thd = this.getGridEl().select('thead',true).first();
6911         var tbd = this.getGridEl().select('tbody', true).first();
6912         var tfd = this.getGridEl().select('tfoot', true).first();
6913         
6914         var cw = ctr.getWidth();
6915         
6916         if (tbd) {
6917             
6918             tbd.setSize(ctr.getWidth(),
6919                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6920             );
6921             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6922             cw -= barsize;
6923         }
6924         cw = Math.max(cw, this.totalWidth);
6925         this.getGridEl().select('tr',true).setWidth(cw);
6926         // resize 'expandable coloumn?
6927         
6928         return; // we doe not have a view in this design..
6929         
6930     },
6931     onBodyScroll: function()
6932     {
6933         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6934         if(this.mainHead){
6935             this.mainHead.setStyle({
6936                 'position' : 'relative',
6937                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6938             });
6939         }
6940         
6941         if(this.lazyLoad){
6942             
6943             var scrollHeight = this.mainBody.dom.scrollHeight;
6944             
6945             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6946             
6947             var height = this.mainBody.getHeight();
6948             
6949             if(scrollHeight - height == scrollTop) {
6950                 
6951                 var total = this.ds.getTotalCount();
6952                 
6953                 if(this.footer.cursor + this.footer.pageSize < total){
6954                     
6955                     this.footer.ds.load({
6956                         params : {
6957                             start : this.footer.cursor + this.footer.pageSize,
6958                             limit : this.footer.pageSize
6959                         },
6960                         add : true
6961                     });
6962                 }
6963             }
6964             
6965         }
6966     },
6967     
6968     onHeaderChange : function()
6969     {
6970         var header = this.renderHeader();
6971         var table = this.el.select('table', true).first();
6972         
6973         this.mainHead.remove();
6974         this.mainHead = table.createChild(header, this.mainBody, false);
6975     },
6976     
6977     onHiddenChange : function(colModel, colIndex, hidden)
6978     {
6979         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6980         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6981         
6982         this.CSS.updateRule(thSelector, "display", "");
6983         this.CSS.updateRule(tdSelector, "display", "");
6984         
6985         if(hidden){
6986             this.CSS.updateRule(thSelector, "display", "none");
6987             this.CSS.updateRule(tdSelector, "display", "none");
6988         }
6989         
6990         this.onHeaderChange();
6991         this.onLoad();
6992         
6993     }
6994     
6995 });
6996
6997  
6998
6999  /*
7000  * - LGPL
7001  *
7002  * table cell
7003  * 
7004  */
7005
7006 /**
7007  * @class Roo.bootstrap.TableCell
7008  * @extends Roo.bootstrap.Component
7009  * Bootstrap TableCell class
7010  * @cfg {String} html cell contain text
7011  * @cfg {String} cls cell class
7012  * @cfg {String} tag cell tag (td|th) default td
7013  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7014  * @cfg {String} align Aligns the content in a cell
7015  * @cfg {String} axis Categorizes cells
7016  * @cfg {String} bgcolor Specifies the background color of a cell
7017  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7018  * @cfg {Number} colspan Specifies the number of columns a cell should span
7019  * @cfg {String} headers Specifies one or more header cells a cell is related to
7020  * @cfg {Number} height Sets the height of a cell
7021  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7022  * @cfg {Number} rowspan Sets the number of rows a cell should span
7023  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7024  * @cfg {String} valign Vertical aligns the content in a cell
7025  * @cfg {Number} width Specifies the width of a cell
7026  * 
7027  * @constructor
7028  * Create a new TableCell
7029  * @param {Object} config The config object
7030  */
7031
7032 Roo.bootstrap.TableCell = function(config){
7033     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7034 };
7035
7036 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7037     
7038     html: false,
7039     cls: false,
7040     tag: false,
7041     abbr: false,
7042     align: false,
7043     axis: false,
7044     bgcolor: false,
7045     charoff: false,
7046     colspan: false,
7047     headers: false,
7048     height: false,
7049     nowrap: false,
7050     rowspan: false,
7051     scope: false,
7052     valign: false,
7053     width: false,
7054     
7055     
7056     getAutoCreate : function(){
7057         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7058         
7059         cfg = {
7060             tag: 'td'
7061         };
7062         
7063         if(this.tag){
7064             cfg.tag = this.tag;
7065         }
7066         
7067         if (this.html) {
7068             cfg.html=this.html
7069         }
7070         if (this.cls) {
7071             cfg.cls=this.cls
7072         }
7073         if (this.abbr) {
7074             cfg.abbr=this.abbr
7075         }
7076         if (this.align) {
7077             cfg.align=this.align
7078         }
7079         if (this.axis) {
7080             cfg.axis=this.axis
7081         }
7082         if (this.bgcolor) {
7083             cfg.bgcolor=this.bgcolor
7084         }
7085         if (this.charoff) {
7086             cfg.charoff=this.charoff
7087         }
7088         if (this.colspan) {
7089             cfg.colspan=this.colspan
7090         }
7091         if (this.headers) {
7092             cfg.headers=this.headers
7093         }
7094         if (this.height) {
7095             cfg.height=this.height
7096         }
7097         if (this.nowrap) {
7098             cfg.nowrap=this.nowrap
7099         }
7100         if (this.rowspan) {
7101             cfg.rowspan=this.rowspan
7102         }
7103         if (this.scope) {
7104             cfg.scope=this.scope
7105         }
7106         if (this.valign) {
7107             cfg.valign=this.valign
7108         }
7109         if (this.width) {
7110             cfg.width=this.width
7111         }
7112         
7113         
7114         return cfg;
7115     }
7116    
7117 });
7118
7119  
7120
7121  /*
7122  * - LGPL
7123  *
7124  * table row
7125  * 
7126  */
7127
7128 /**
7129  * @class Roo.bootstrap.TableRow
7130  * @extends Roo.bootstrap.Component
7131  * Bootstrap TableRow class
7132  * @cfg {String} cls row class
7133  * @cfg {String} align Aligns the content in a table row
7134  * @cfg {String} bgcolor Specifies a background color for a table row
7135  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7136  * @cfg {String} valign Vertical aligns the content in a table row
7137  * 
7138  * @constructor
7139  * Create a new TableRow
7140  * @param {Object} config The config object
7141  */
7142
7143 Roo.bootstrap.TableRow = function(config){
7144     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7145 };
7146
7147 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7148     
7149     cls: false,
7150     align: false,
7151     bgcolor: false,
7152     charoff: false,
7153     valign: false,
7154     
7155     getAutoCreate : function(){
7156         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7157         
7158         cfg = {
7159             tag: 'tr'
7160         };
7161             
7162         if(this.cls){
7163             cfg.cls = this.cls;
7164         }
7165         if(this.align){
7166             cfg.align = this.align;
7167         }
7168         if(this.bgcolor){
7169             cfg.bgcolor = this.bgcolor;
7170         }
7171         if(this.charoff){
7172             cfg.charoff = this.charoff;
7173         }
7174         if(this.valign){
7175             cfg.valign = this.valign;
7176         }
7177         
7178         return cfg;
7179     }
7180    
7181 });
7182
7183  
7184
7185  /*
7186  * - LGPL
7187  *
7188  * table body
7189  * 
7190  */
7191
7192 /**
7193  * @class Roo.bootstrap.TableBody
7194  * @extends Roo.bootstrap.Component
7195  * Bootstrap TableBody class
7196  * @cfg {String} cls element class
7197  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7198  * @cfg {String} align Aligns the content inside the element
7199  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7200  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7201  * 
7202  * @constructor
7203  * Create a new TableBody
7204  * @param {Object} config The config object
7205  */
7206
7207 Roo.bootstrap.TableBody = function(config){
7208     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7209 };
7210
7211 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7212     
7213     cls: false,
7214     tag: false,
7215     align: false,
7216     charoff: false,
7217     valign: false,
7218     
7219     getAutoCreate : function(){
7220         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7221         
7222         cfg = {
7223             tag: 'tbody'
7224         };
7225             
7226         if (this.cls) {
7227             cfg.cls=this.cls
7228         }
7229         if(this.tag){
7230             cfg.tag = this.tag;
7231         }
7232         
7233         if(this.align){
7234             cfg.align = this.align;
7235         }
7236         if(this.charoff){
7237             cfg.charoff = this.charoff;
7238         }
7239         if(this.valign){
7240             cfg.valign = this.valign;
7241         }
7242         
7243         return cfg;
7244     }
7245     
7246     
7247 //    initEvents : function()
7248 //    {
7249 //        
7250 //        if(!this.store){
7251 //            return;
7252 //        }
7253 //        
7254 //        this.store = Roo.factory(this.store, Roo.data);
7255 //        this.store.on('load', this.onLoad, this);
7256 //        
7257 //        this.store.load();
7258 //        
7259 //    },
7260 //    
7261 //    onLoad: function () 
7262 //    {   
7263 //        this.fireEvent('load', this);
7264 //    }
7265 //    
7266 //   
7267 });
7268
7269  
7270
7271  /*
7272  * Based on:
7273  * Ext JS Library 1.1.1
7274  * Copyright(c) 2006-2007, Ext JS, LLC.
7275  *
7276  * Originally Released Under LGPL - original licence link has changed is not relivant.
7277  *
7278  * Fork - LGPL
7279  * <script type="text/javascript">
7280  */
7281
7282 // as we use this in bootstrap.
7283 Roo.namespace('Roo.form');
7284  /**
7285  * @class Roo.form.Action
7286  * Internal Class used to handle form actions
7287  * @constructor
7288  * @param {Roo.form.BasicForm} el The form element or its id
7289  * @param {Object} config Configuration options
7290  */
7291
7292  
7293  
7294 // define the action interface
7295 Roo.form.Action = function(form, options){
7296     this.form = form;
7297     this.options = options || {};
7298 };
7299 /**
7300  * Client Validation Failed
7301  * @const 
7302  */
7303 Roo.form.Action.CLIENT_INVALID = 'client';
7304 /**
7305  * Server Validation Failed
7306  * @const 
7307  */
7308 Roo.form.Action.SERVER_INVALID = 'server';
7309  /**
7310  * Connect to Server Failed
7311  * @const 
7312  */
7313 Roo.form.Action.CONNECT_FAILURE = 'connect';
7314 /**
7315  * Reading Data from Server Failed
7316  * @const 
7317  */
7318 Roo.form.Action.LOAD_FAILURE = 'load';
7319
7320 Roo.form.Action.prototype = {
7321     type : 'default',
7322     failureType : undefined,
7323     response : undefined,
7324     result : undefined,
7325
7326     // interface method
7327     run : function(options){
7328
7329     },
7330
7331     // interface method
7332     success : function(response){
7333
7334     },
7335
7336     // interface method
7337     handleResponse : function(response){
7338
7339     },
7340
7341     // default connection failure
7342     failure : function(response){
7343         
7344         this.response = response;
7345         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7346         this.form.afterAction(this, false);
7347     },
7348
7349     processResponse : function(response){
7350         this.response = response;
7351         if(!response.responseText){
7352             return true;
7353         }
7354         this.result = this.handleResponse(response);
7355         return this.result;
7356     },
7357
7358     // utility functions used internally
7359     getUrl : function(appendParams){
7360         var url = this.options.url || this.form.url || this.form.el.dom.action;
7361         if(appendParams){
7362             var p = this.getParams();
7363             if(p){
7364                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7365             }
7366         }
7367         return url;
7368     },
7369
7370     getMethod : function(){
7371         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7372     },
7373
7374     getParams : function(){
7375         var bp = this.form.baseParams;
7376         var p = this.options.params;
7377         if(p){
7378             if(typeof p == "object"){
7379                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7380             }else if(typeof p == 'string' && bp){
7381                 p += '&' + Roo.urlEncode(bp);
7382             }
7383         }else if(bp){
7384             p = Roo.urlEncode(bp);
7385         }
7386         return p;
7387     },
7388
7389     createCallback : function(){
7390         return {
7391             success: this.success,
7392             failure: this.failure,
7393             scope: this,
7394             timeout: (this.form.timeout*1000),
7395             upload: this.form.fileUpload ? this.success : undefined
7396         };
7397     }
7398 };
7399
7400 Roo.form.Action.Submit = function(form, options){
7401     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7402 };
7403
7404 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7405     type : 'submit',
7406
7407     haveProgress : false,
7408     uploadComplete : false,
7409     
7410     // uploadProgress indicator.
7411     uploadProgress : function()
7412     {
7413         if (!this.form.progressUrl) {
7414             return;
7415         }
7416         
7417         if (!this.haveProgress) {
7418             Roo.MessageBox.progress("Uploading", "Uploading");
7419         }
7420         if (this.uploadComplete) {
7421            Roo.MessageBox.hide();
7422            return;
7423         }
7424         
7425         this.haveProgress = true;
7426    
7427         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7428         
7429         var c = new Roo.data.Connection();
7430         c.request({
7431             url : this.form.progressUrl,
7432             params: {
7433                 id : uid
7434             },
7435             method: 'GET',
7436             success : function(req){
7437                //console.log(data);
7438                 var rdata = false;
7439                 var edata;
7440                 try  {
7441                    rdata = Roo.decode(req.responseText)
7442                 } catch (e) {
7443                     Roo.log("Invalid data from server..");
7444                     Roo.log(edata);
7445                     return;
7446                 }
7447                 if (!rdata || !rdata.success) {
7448                     Roo.log(rdata);
7449                     Roo.MessageBox.alert(Roo.encode(rdata));
7450                     return;
7451                 }
7452                 var data = rdata.data;
7453                 
7454                 if (this.uploadComplete) {
7455                    Roo.MessageBox.hide();
7456                    return;
7457                 }
7458                    
7459                 if (data){
7460                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7461                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7462                     );
7463                 }
7464                 this.uploadProgress.defer(2000,this);
7465             },
7466        
7467             failure: function(data) {
7468                 Roo.log('progress url failed ');
7469                 Roo.log(data);
7470             },
7471             scope : this
7472         });
7473            
7474     },
7475     
7476     
7477     run : function()
7478     {
7479         // run get Values on the form, so it syncs any secondary forms.
7480         this.form.getValues();
7481         
7482         var o = this.options;
7483         var method = this.getMethod();
7484         var isPost = method == 'POST';
7485         if(o.clientValidation === false || this.form.isValid()){
7486             
7487             if (this.form.progressUrl) {
7488                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7489                     (new Date() * 1) + '' + Math.random());
7490                     
7491             } 
7492             
7493             
7494             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7495                 form:this.form.el.dom,
7496                 url:this.getUrl(!isPost),
7497                 method: method,
7498                 params:isPost ? this.getParams() : null,
7499                 isUpload: this.form.fileUpload
7500             }));
7501             
7502             this.uploadProgress();
7503
7504         }else if (o.clientValidation !== false){ // client validation failed
7505             this.failureType = Roo.form.Action.CLIENT_INVALID;
7506             this.form.afterAction(this, false);
7507         }
7508     },
7509
7510     success : function(response)
7511     {
7512         this.uploadComplete= true;
7513         if (this.haveProgress) {
7514             Roo.MessageBox.hide();
7515         }
7516         
7517         
7518         var result = this.processResponse(response);
7519         if(result === true || result.success){
7520             this.form.afterAction(this, true);
7521             return;
7522         }
7523         if(result.errors){
7524             this.form.markInvalid(result.errors);
7525             this.failureType = Roo.form.Action.SERVER_INVALID;
7526         }
7527         this.form.afterAction(this, false);
7528     },
7529     failure : function(response)
7530     {
7531         this.uploadComplete= true;
7532         if (this.haveProgress) {
7533             Roo.MessageBox.hide();
7534         }
7535         
7536         this.response = response;
7537         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7538         this.form.afterAction(this, false);
7539     },
7540     
7541     handleResponse : function(response){
7542         if(this.form.errorReader){
7543             var rs = this.form.errorReader.read(response);
7544             var errors = [];
7545             if(rs.records){
7546                 for(var i = 0, len = rs.records.length; i < len; i++) {
7547                     var r = rs.records[i];
7548                     errors[i] = r.data;
7549                 }
7550             }
7551             if(errors.length < 1){
7552                 errors = null;
7553             }
7554             return {
7555                 success : rs.success,
7556                 errors : errors
7557             };
7558         }
7559         var ret = false;
7560         try {
7561             ret = Roo.decode(response.responseText);
7562         } catch (e) {
7563             ret = {
7564                 success: false,
7565                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7566                 errors : []
7567             };
7568         }
7569         return ret;
7570         
7571     }
7572 });
7573
7574
7575 Roo.form.Action.Load = function(form, options){
7576     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7577     this.reader = this.form.reader;
7578 };
7579
7580 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7581     type : 'load',
7582
7583     run : function(){
7584         
7585         Roo.Ajax.request(Roo.apply(
7586                 this.createCallback(), {
7587                     method:this.getMethod(),
7588                     url:this.getUrl(false),
7589                     params:this.getParams()
7590         }));
7591     },
7592
7593     success : function(response){
7594         
7595         var result = this.processResponse(response);
7596         if(result === true || !result.success || !result.data){
7597             this.failureType = Roo.form.Action.LOAD_FAILURE;
7598             this.form.afterAction(this, false);
7599             return;
7600         }
7601         this.form.clearInvalid();
7602         this.form.setValues(result.data);
7603         this.form.afterAction(this, true);
7604     },
7605
7606     handleResponse : function(response){
7607         if(this.form.reader){
7608             var rs = this.form.reader.read(response);
7609             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7610             return {
7611                 success : rs.success,
7612                 data : data
7613             };
7614         }
7615         return Roo.decode(response.responseText);
7616     }
7617 });
7618
7619 Roo.form.Action.ACTION_TYPES = {
7620     'load' : Roo.form.Action.Load,
7621     'submit' : Roo.form.Action.Submit
7622 };/*
7623  * - LGPL
7624  *
7625  * form
7626  *
7627  */
7628
7629 /**
7630  * @class Roo.bootstrap.Form
7631  * @extends Roo.bootstrap.Component
7632  * Bootstrap Form class
7633  * @cfg {String} method  GET | POST (default POST)
7634  * @cfg {String} labelAlign top | left (default top)
7635  * @cfg {String} align left  | right - for navbars
7636  * @cfg {Boolean} loadMask load mask when submit (default true)
7637
7638  *
7639  * @constructor
7640  * Create a new Form
7641  * @param {Object} config The config object
7642  */
7643
7644
7645 Roo.bootstrap.Form = function(config){
7646     
7647     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7648     
7649     Roo.bootstrap.Form.popover.apply();
7650     
7651     this.addEvents({
7652         /**
7653          * @event clientvalidation
7654          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7655          * @param {Form} this
7656          * @param {Boolean} valid true if the form has passed client-side validation
7657          */
7658         clientvalidation: true,
7659         /**
7660          * @event beforeaction
7661          * Fires before any action is performed. Return false to cancel the action.
7662          * @param {Form} this
7663          * @param {Action} action The action to be performed
7664          */
7665         beforeaction: true,
7666         /**
7667          * @event actionfailed
7668          * Fires when an action fails.
7669          * @param {Form} this
7670          * @param {Action} action The action that failed
7671          */
7672         actionfailed : true,
7673         /**
7674          * @event actioncomplete
7675          * Fires when an action is completed.
7676          * @param {Form} this
7677          * @param {Action} action The action that completed
7678          */
7679         actioncomplete : true
7680     });
7681 };
7682
7683 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7684
7685      /**
7686      * @cfg {String} method
7687      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7688      */
7689     method : 'POST',
7690     /**
7691      * @cfg {String} url
7692      * The URL to use for form actions if one isn't supplied in the action options.
7693      */
7694     /**
7695      * @cfg {Boolean} fileUpload
7696      * Set to true if this form is a file upload.
7697      */
7698
7699     /**
7700      * @cfg {Object} baseParams
7701      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7702      */
7703
7704     /**
7705      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7706      */
7707     timeout: 30,
7708     /**
7709      * @cfg {Sting} align (left|right) for navbar forms
7710      */
7711     align : 'left',
7712
7713     // private
7714     activeAction : null,
7715
7716     /**
7717      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7718      * element by passing it or its id or mask the form itself by passing in true.
7719      * @type Mixed
7720      */
7721     waitMsgTarget : false,
7722
7723     loadMask : true,
7724     
7725     /**
7726      * @cfg {Boolean} errorMask (true|false) default false
7727      */
7728     errorMask : false,
7729     
7730     /**
7731      * @cfg {Number} maskOffset Default 100
7732      */
7733     maskOffset : 100,
7734     
7735     /**
7736      * @cfg {Boolean} maskBody
7737      */
7738     maskBody : false,
7739
7740     getAutoCreate : function(){
7741
7742         var cfg = {
7743             tag: 'form',
7744             method : this.method || 'POST',
7745             id : this.id || Roo.id(),
7746             cls : ''
7747         };
7748         if (this.parent().xtype.match(/^Nav/)) {
7749             cfg.cls = 'navbar-form navbar-' + this.align;
7750
7751         }
7752
7753         if (this.labelAlign == 'left' ) {
7754             cfg.cls += ' form-horizontal';
7755         }
7756
7757
7758         return cfg;
7759     },
7760     initEvents : function()
7761     {
7762         this.el.on('submit', this.onSubmit, this);
7763         // this was added as random key presses on the form where triggering form submit.
7764         this.el.on('keypress', function(e) {
7765             if (e.getCharCode() != 13) {
7766                 return true;
7767             }
7768             // we might need to allow it for textareas.. and some other items.
7769             // check e.getTarget().
7770
7771             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7772                 return true;
7773             }
7774
7775             Roo.log("keypress blocked");
7776
7777             e.preventDefault();
7778             return false;
7779         });
7780         
7781     },
7782     // private
7783     onSubmit : function(e){
7784         e.stopEvent();
7785     },
7786
7787      /**
7788      * Returns true if client-side validation on the form is successful.
7789      * @return Boolean
7790      */
7791     isValid : function(){
7792         var items = this.getItems();
7793         var valid = true;
7794         var target = false;
7795         
7796         items.each(function(f){
7797             
7798             if(f.validate()){
7799                 return;
7800             }
7801             
7802             valid = false;
7803
7804             if(!target && f.el.isVisible(true)){
7805                 target = f;
7806             }
7807            
7808         });
7809         
7810         if(this.errorMask && !valid){
7811             Roo.bootstrap.Form.popover.mask(this, target);
7812         }
7813         
7814         return valid;
7815     },
7816     
7817     /**
7818      * Returns true if any fields in this form have changed since their original load.
7819      * @return Boolean
7820      */
7821     isDirty : function(){
7822         var dirty = false;
7823         var items = this.getItems();
7824         items.each(function(f){
7825            if(f.isDirty()){
7826                dirty = true;
7827                return false;
7828            }
7829            return true;
7830         });
7831         return dirty;
7832     },
7833      /**
7834      * Performs a predefined action (submit or load) or custom actions you define on this form.
7835      * @param {String} actionName The name of the action type
7836      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7837      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7838      * accept other config options):
7839      * <pre>
7840 Property          Type             Description
7841 ----------------  ---------------  ----------------------------------------------------------------------------------
7842 url               String           The url for the action (defaults to the form's url)
7843 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7844 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7845 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7846                                    validate the form on the client (defaults to false)
7847      * </pre>
7848      * @return {BasicForm} this
7849      */
7850     doAction : function(action, options){
7851         if(typeof action == 'string'){
7852             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7853         }
7854         if(this.fireEvent('beforeaction', this, action) !== false){
7855             this.beforeAction(action);
7856             action.run.defer(100, action);
7857         }
7858         return this;
7859     },
7860
7861     // private
7862     beforeAction : function(action){
7863         var o = action.options;
7864         
7865         if(this.loadMask){
7866             
7867             if(this.maskBody){
7868                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7869             } else {
7870                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7871             }
7872         }
7873         // not really supported yet.. ??
7874
7875         //if(this.waitMsgTarget === true){
7876         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7877         //}else if(this.waitMsgTarget){
7878         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7879         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7880         //}else {
7881         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7882        // }
7883
7884     },
7885
7886     // private
7887     afterAction : function(action, success){
7888         this.activeAction = null;
7889         var o = action.options;
7890
7891         if(this.loadMask){
7892             
7893             if(this.maskBody){
7894                 Roo.get(document.body).unmask();
7895             } else {
7896                 this.el.unmask();
7897             }
7898         }
7899         
7900         //if(this.waitMsgTarget === true){
7901 //            this.el.unmask();
7902         //}else if(this.waitMsgTarget){
7903         //    this.waitMsgTarget.unmask();
7904         //}else{
7905         //    Roo.MessageBox.updateProgress(1);
7906         //    Roo.MessageBox.hide();
7907        // }
7908         //
7909         if(success){
7910             if(o.reset){
7911                 this.reset();
7912             }
7913             Roo.callback(o.success, o.scope, [this, action]);
7914             this.fireEvent('actioncomplete', this, action);
7915
7916         }else{
7917
7918             // failure condition..
7919             // we have a scenario where updates need confirming.
7920             // eg. if a locking scenario exists..
7921             // we look for { errors : { needs_confirm : true }} in the response.
7922             if (
7923                 (typeof(action.result) != 'undefined')  &&
7924                 (typeof(action.result.errors) != 'undefined')  &&
7925                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7926            ){
7927                 var _t = this;
7928                 Roo.log("not supported yet");
7929                  /*
7930
7931                 Roo.MessageBox.confirm(
7932                     "Change requires confirmation",
7933                     action.result.errorMsg,
7934                     function(r) {
7935                         if (r != 'yes') {
7936                             return;
7937                         }
7938                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7939                     }
7940
7941                 );
7942                 */
7943
7944
7945                 return;
7946             }
7947
7948             Roo.callback(o.failure, o.scope, [this, action]);
7949             // show an error message if no failed handler is set..
7950             if (!this.hasListener('actionfailed')) {
7951                 Roo.log("need to add dialog support");
7952                 /*
7953                 Roo.MessageBox.alert("Error",
7954                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7955                         action.result.errorMsg :
7956                         "Saving Failed, please check your entries or try again"
7957                 );
7958                 */
7959             }
7960
7961             this.fireEvent('actionfailed', this, action);
7962         }
7963
7964     },
7965     /**
7966      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7967      * @param {String} id The value to search for
7968      * @return Field
7969      */
7970     findField : function(id){
7971         var items = this.getItems();
7972         var field = items.get(id);
7973         if(!field){
7974              items.each(function(f){
7975                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7976                     field = f;
7977                     return false;
7978                 }
7979                 return true;
7980             });
7981         }
7982         return field || null;
7983     },
7984      /**
7985      * Mark fields in this form invalid in bulk.
7986      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7987      * @return {BasicForm} this
7988      */
7989     markInvalid : function(errors){
7990         if(errors instanceof Array){
7991             for(var i = 0, len = errors.length; i < len; i++){
7992                 var fieldError = errors[i];
7993                 var f = this.findField(fieldError.id);
7994                 if(f){
7995                     f.markInvalid(fieldError.msg);
7996                 }
7997             }
7998         }else{
7999             var field, id;
8000             for(id in errors){
8001                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8002                     field.markInvalid(errors[id]);
8003                 }
8004             }
8005         }
8006         //Roo.each(this.childForms || [], function (f) {
8007         //    f.markInvalid(errors);
8008         //});
8009
8010         return this;
8011     },
8012
8013     /**
8014      * Set values for fields in this form in bulk.
8015      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8016      * @return {BasicForm} this
8017      */
8018     setValues : function(values){
8019         if(values instanceof Array){ // array of objects
8020             for(var i = 0, len = values.length; i < len; i++){
8021                 var v = values[i];
8022                 var f = this.findField(v.id);
8023                 if(f){
8024                     f.setValue(v.value);
8025                     if(this.trackResetOnLoad){
8026                         f.originalValue = f.getValue();
8027                     }
8028                 }
8029             }
8030         }else{ // object hash
8031             var field, id;
8032             for(id in values){
8033                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8034
8035                     if (field.setFromData &&
8036                         field.valueField &&
8037                         field.displayField &&
8038                         // combos' with local stores can
8039                         // be queried via setValue()
8040                         // to set their value..
8041                         (field.store && !field.store.isLocal)
8042                         ) {
8043                         // it's a combo
8044                         var sd = { };
8045                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8046                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8047                         field.setFromData(sd);
8048
8049                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8050                         
8051                         field.setFromData(values);
8052                         
8053                     } else {
8054                         field.setValue(values[id]);
8055                     }
8056
8057
8058                     if(this.trackResetOnLoad){
8059                         field.originalValue = field.getValue();
8060                     }
8061                 }
8062             }
8063         }
8064
8065         //Roo.each(this.childForms || [], function (f) {
8066         //    f.setValues(values);
8067         //});
8068
8069         return this;
8070     },
8071
8072     /**
8073      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8074      * they are returned as an array.
8075      * @param {Boolean} asString
8076      * @return {Object}
8077      */
8078     getValues : function(asString){
8079         //if (this.childForms) {
8080             // copy values from the child forms
8081         //    Roo.each(this.childForms, function (f) {
8082         //        this.setValues(f.getValues());
8083         //    }, this);
8084         //}
8085
8086
8087
8088         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8089         if(asString === true){
8090             return fs;
8091         }
8092         return Roo.urlDecode(fs);
8093     },
8094
8095     /**
8096      * Returns the fields in this form as an object with key/value pairs.
8097      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8098      * @return {Object}
8099      */
8100     getFieldValues : function(with_hidden)
8101     {
8102         var items = this.getItems();
8103         var ret = {};
8104         items.each(function(f){
8105             
8106             if (!f.getName()) {
8107                 return;
8108             }
8109             
8110             var v = f.getValue();
8111             
8112             if (f.inputType =='radio') {
8113                 if (typeof(ret[f.getName()]) == 'undefined') {
8114                     ret[f.getName()] = ''; // empty..
8115                 }
8116
8117                 if (!f.el.dom.checked) {
8118                     return;
8119
8120                 }
8121                 v = f.el.dom.value;
8122
8123             }
8124             
8125             if(f.xtype == 'MoneyField'){
8126                 ret[f.currencyName] = f.getCurrency();
8127             }
8128
8129             // not sure if this supported any more..
8130             if ((typeof(v) == 'object') && f.getRawValue) {
8131                 v = f.getRawValue() ; // dates..
8132             }
8133             // combo boxes where name != hiddenName...
8134             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8135                 ret[f.name] = f.getRawValue();
8136             }
8137             ret[f.getName()] = v;
8138         });
8139
8140         return ret;
8141     },
8142
8143     /**
8144      * Clears all invalid messages in this form.
8145      * @return {BasicForm} this
8146      */
8147     clearInvalid : function(){
8148         var items = this.getItems();
8149
8150         items.each(function(f){
8151            f.clearInvalid();
8152         });
8153
8154         return this;
8155     },
8156
8157     /**
8158      * Resets this form.
8159      * @return {BasicForm} this
8160      */
8161     reset : function(){
8162         var items = this.getItems();
8163         items.each(function(f){
8164             f.reset();
8165         });
8166
8167         Roo.each(this.childForms || [], function (f) {
8168             f.reset();
8169         });
8170
8171
8172         return this;
8173     },
8174     
8175     getItems : function()
8176     {
8177         var r=new Roo.util.MixedCollection(false, function(o){
8178             return o.id || (o.id = Roo.id());
8179         });
8180         var iter = function(el) {
8181             if (el.inputEl) {
8182                 r.add(el);
8183             }
8184             if (!el.items) {
8185                 return;
8186             }
8187             Roo.each(el.items,function(e) {
8188                 iter(e);
8189             });
8190         };
8191
8192         iter(this);
8193         return r;
8194     },
8195     
8196     hideFields : function(items)
8197     {
8198         Roo.each(items, function(i){
8199             
8200             var f = this.findField(i);
8201             
8202             if(!f){
8203                 return;
8204             }
8205             
8206             if(f.xtype == 'DateField'){
8207                 f.setVisible(false);
8208                 return;
8209             }
8210             
8211             f.hide();
8212             
8213         }, this);
8214     },
8215     
8216     showFields : function(items)
8217     {
8218         Roo.each(items, function(i){
8219             
8220             var f = this.findField(i);
8221             
8222             if(!f){
8223                 return;
8224             }
8225             
8226             if(f.xtype == 'DateField'){
8227                 f.setVisible(true);
8228                 return;
8229             }
8230             
8231             f.show();
8232             
8233         }, this);
8234     }
8235
8236 });
8237
8238 Roo.apply(Roo.bootstrap.Form, {
8239     
8240     popover : {
8241         
8242         padding : 5,
8243         
8244         isApplied : false,
8245         
8246         isMasked : false,
8247         
8248         form : false,
8249         
8250         target : false,
8251         
8252         toolTip : false,
8253         
8254         intervalID : false,
8255         
8256         maskEl : false,
8257         
8258         apply : function()
8259         {
8260             if(this.isApplied){
8261                 return;
8262             }
8263             
8264             this.maskEl = {
8265                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8266                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8267                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8268                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8269             };
8270             
8271             this.maskEl.top.enableDisplayMode("block");
8272             this.maskEl.left.enableDisplayMode("block");
8273             this.maskEl.bottom.enableDisplayMode("block");
8274             this.maskEl.right.enableDisplayMode("block");
8275             
8276             this.toolTip = new Roo.bootstrap.Tooltip({
8277                 cls : 'roo-form-error-popover',
8278                 alignment : {
8279                     'left' : ['r-l', [-2,0], 'right'],
8280                     'right' : ['l-r', [2,0], 'left'],
8281                     'bottom' : ['tl-bl', [0,2], 'top'],
8282                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8283                 }
8284             });
8285             
8286             this.toolTip.render(Roo.get(document.body));
8287
8288             this.toolTip.el.enableDisplayMode("block");
8289             
8290             Roo.get(document.body).on('click', function(){
8291                 this.unmask();
8292             }, this);
8293             
8294             Roo.get(document.body).on('touchstart', function(){
8295                 this.unmask();
8296             }, this);
8297             
8298             this.isApplied = true
8299         },
8300         
8301         mask : function(form, target)
8302         {
8303             this.form = form;
8304             
8305             this.target = target;
8306             
8307             if(!this.form.errorMask || !target.el){
8308                 return;
8309             }
8310             
8311             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8312             
8313             Roo.log(scrollable);
8314             
8315             var ot = this.target.el.calcOffsetsTo(scrollable);
8316             
8317             var scrollTo = ot[1] - this.form.maskOffset;
8318             
8319             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8320             
8321             scrollable.scrollTo('top', scrollTo);
8322             
8323             var box = this.target.el.getBox();
8324             Roo.log(box);
8325             var zIndex = Roo.bootstrap.Modal.zIndex++;
8326
8327             
8328             this.maskEl.top.setStyle('position', 'absolute');
8329             this.maskEl.top.setStyle('z-index', zIndex);
8330             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8331             this.maskEl.top.setLeft(0);
8332             this.maskEl.top.setTop(0);
8333             this.maskEl.top.show();
8334             
8335             this.maskEl.left.setStyle('position', 'absolute');
8336             this.maskEl.left.setStyle('z-index', zIndex);
8337             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8338             this.maskEl.left.setLeft(0);
8339             this.maskEl.left.setTop(box.y - this.padding);
8340             this.maskEl.left.show();
8341
8342             this.maskEl.bottom.setStyle('position', 'absolute');
8343             this.maskEl.bottom.setStyle('z-index', zIndex);
8344             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8345             this.maskEl.bottom.setLeft(0);
8346             this.maskEl.bottom.setTop(box.bottom + this.padding);
8347             this.maskEl.bottom.show();
8348
8349             this.maskEl.right.setStyle('position', 'absolute');
8350             this.maskEl.right.setStyle('z-index', zIndex);
8351             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8352             this.maskEl.right.setLeft(box.right + this.padding);
8353             this.maskEl.right.setTop(box.y - this.padding);
8354             this.maskEl.right.show();
8355
8356             this.toolTip.bindEl = this.target.el;
8357
8358             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8359
8360             var tip = this.target.blankText;
8361
8362             if(this.target.getValue() !== '' ) {
8363                 
8364                 if (this.target.invalidText.length) {
8365                     tip = this.target.invalidText;
8366                 } else if (this.target.regexText.length){
8367                     tip = this.target.regexText;
8368                 }
8369             }
8370
8371             this.toolTip.show(tip);
8372
8373             this.intervalID = window.setInterval(function() {
8374                 Roo.bootstrap.Form.popover.unmask();
8375             }, 10000);
8376
8377             window.onwheel = function(){ return false;};
8378             
8379             (function(){ this.isMasked = true; }).defer(500, this);
8380             
8381         },
8382         
8383         unmask : function()
8384         {
8385             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8386                 return;
8387             }
8388             
8389             this.maskEl.top.setStyle('position', 'absolute');
8390             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8391             this.maskEl.top.hide();
8392
8393             this.maskEl.left.setStyle('position', 'absolute');
8394             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8395             this.maskEl.left.hide();
8396
8397             this.maskEl.bottom.setStyle('position', 'absolute');
8398             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8399             this.maskEl.bottom.hide();
8400
8401             this.maskEl.right.setStyle('position', 'absolute');
8402             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8403             this.maskEl.right.hide();
8404             
8405             this.toolTip.hide();
8406             
8407             this.toolTip.el.hide();
8408             
8409             window.onwheel = function(){ return true;};
8410             
8411             if(this.intervalID){
8412                 window.clearInterval(this.intervalID);
8413                 this.intervalID = false;
8414             }
8415             
8416             this.isMasked = false;
8417             
8418         }
8419         
8420     }
8421     
8422 });
8423
8424 /*
8425  * Based on:
8426  * Ext JS Library 1.1.1
8427  * Copyright(c) 2006-2007, Ext JS, LLC.
8428  *
8429  * Originally Released Under LGPL - original licence link has changed is not relivant.
8430  *
8431  * Fork - LGPL
8432  * <script type="text/javascript">
8433  */
8434 /**
8435  * @class Roo.form.VTypes
8436  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8437  * @singleton
8438  */
8439 Roo.form.VTypes = function(){
8440     // closure these in so they are only created once.
8441     var alpha = /^[a-zA-Z_]+$/;
8442     var alphanum = /^[a-zA-Z0-9_]+$/;
8443     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8444     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8445
8446     // All these messages and functions are configurable
8447     return {
8448         /**
8449          * The function used to validate email addresses
8450          * @param {String} value The email address
8451          */
8452         'email' : function(v){
8453             return email.test(v);
8454         },
8455         /**
8456          * The error text to display when the email validation function returns false
8457          * @type String
8458          */
8459         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8460         /**
8461          * The keystroke filter mask to be applied on email input
8462          * @type RegExp
8463          */
8464         'emailMask' : /[a-z0-9_\.\-@]/i,
8465
8466         /**
8467          * The function used to validate URLs
8468          * @param {String} value The URL
8469          */
8470         'url' : function(v){
8471             return url.test(v);
8472         },
8473         /**
8474          * The error text to display when the url validation function returns false
8475          * @type String
8476          */
8477         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8478         
8479         /**
8480          * The function used to validate alpha values
8481          * @param {String} value The value
8482          */
8483         'alpha' : function(v){
8484             return alpha.test(v);
8485         },
8486         /**
8487          * The error text to display when the alpha validation function returns false
8488          * @type String
8489          */
8490         'alphaText' : 'This field should only contain letters and _',
8491         /**
8492          * The keystroke filter mask to be applied on alpha input
8493          * @type RegExp
8494          */
8495         'alphaMask' : /[a-z_]/i,
8496
8497         /**
8498          * The function used to validate alphanumeric values
8499          * @param {String} value The value
8500          */
8501         'alphanum' : function(v){
8502             return alphanum.test(v);
8503         },
8504         /**
8505          * The error text to display when the alphanumeric validation function returns false
8506          * @type String
8507          */
8508         'alphanumText' : 'This field should only contain letters, numbers and _',
8509         /**
8510          * The keystroke filter mask to be applied on alphanumeric input
8511          * @type RegExp
8512          */
8513         'alphanumMask' : /[a-z0-9_]/i
8514     };
8515 }();/*
8516  * - LGPL
8517  *
8518  * Input
8519  * 
8520  */
8521
8522 /**
8523  * @class Roo.bootstrap.Input
8524  * @extends Roo.bootstrap.Component
8525  * Bootstrap Input class
8526  * @cfg {Boolean} disabled is it disabled
8527  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8528  * @cfg {String} name name of the input
8529  * @cfg {string} fieldLabel - the label associated
8530  * @cfg {string} placeholder - placeholder to put in text.
8531  * @cfg {string}  before - input group add on before
8532  * @cfg {string} after - input group add on after
8533  * @cfg {string} size - (lg|sm) or leave empty..
8534  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8535  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8536  * @cfg {Number} md colspan out of 12 for computer-sized screens
8537  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8538  * @cfg {string} value default value of the input
8539  * @cfg {Number} labelWidth set the width of label 
8540  * @cfg {Number} labellg set the width of label (1-12)
8541  * @cfg {Number} labelmd set the width of label (1-12)
8542  * @cfg {Number} labelsm set the width of label (1-12)
8543  * @cfg {Number} labelxs set the width of label (1-12)
8544  * @cfg {String} labelAlign (top|left)
8545  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8546  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8547  * @cfg {String} indicatorpos (left|right) default left
8548  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8549  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8550
8551  * @cfg {String} align (left|center|right) Default left
8552  * @cfg {Boolean} forceFeedback (true|false) Default false
8553  * 
8554  * @constructor
8555  * Create a new Input
8556  * @param {Object} config The config object
8557  */
8558
8559 Roo.bootstrap.Input = function(config){
8560     
8561     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8562     
8563     this.addEvents({
8564         /**
8565          * @event focus
8566          * Fires when this field receives input focus.
8567          * @param {Roo.form.Field} this
8568          */
8569         focus : true,
8570         /**
8571          * @event blur
8572          * Fires when this field loses input focus.
8573          * @param {Roo.form.Field} this
8574          */
8575         blur : true,
8576         /**
8577          * @event specialkey
8578          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8579          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8580          * @param {Roo.form.Field} this
8581          * @param {Roo.EventObject} e The event object
8582          */
8583         specialkey : true,
8584         /**
8585          * @event change
8586          * Fires just before the field blurs if the field value has changed.
8587          * @param {Roo.form.Field} this
8588          * @param {Mixed} newValue The new value
8589          * @param {Mixed} oldValue The original value
8590          */
8591         change : true,
8592         /**
8593          * @event invalid
8594          * Fires after the field has been marked as invalid.
8595          * @param {Roo.form.Field} this
8596          * @param {String} msg The validation message
8597          */
8598         invalid : true,
8599         /**
8600          * @event valid
8601          * Fires after the field has been validated with no errors.
8602          * @param {Roo.form.Field} this
8603          */
8604         valid : true,
8605          /**
8606          * @event keyup
8607          * Fires after the key up
8608          * @param {Roo.form.Field} this
8609          * @param {Roo.EventObject}  e The event Object
8610          */
8611         keyup : true
8612     });
8613 };
8614
8615 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8616      /**
8617      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8618       automatic validation (defaults to "keyup").
8619      */
8620     validationEvent : "keyup",
8621      /**
8622      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8623      */
8624     validateOnBlur : true,
8625     /**
8626      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8627      */
8628     validationDelay : 250,
8629      /**
8630      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8631      */
8632     focusClass : "x-form-focus",  // not needed???
8633     
8634        
8635     /**
8636      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8637      */
8638     invalidClass : "has-warning",
8639     
8640     /**
8641      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8642      */
8643     validClass : "has-success",
8644     
8645     /**
8646      * @cfg {Boolean} hasFeedback (true|false) default true
8647      */
8648     hasFeedback : true,
8649     
8650     /**
8651      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8652      */
8653     invalidFeedbackClass : "glyphicon-warning-sign",
8654     
8655     /**
8656      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8657      */
8658     validFeedbackClass : "glyphicon-ok",
8659     
8660     /**
8661      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8662      */
8663     selectOnFocus : false,
8664     
8665      /**
8666      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8667      */
8668     maskRe : null,
8669        /**
8670      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8671      */
8672     vtype : null,
8673     
8674       /**
8675      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8676      */
8677     disableKeyFilter : false,
8678     
8679        /**
8680      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8681      */
8682     disabled : false,
8683      /**
8684      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8685      */
8686     allowBlank : true,
8687     /**
8688      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8689      */
8690     blankText : "Please complete this mandatory field",
8691     
8692      /**
8693      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8694      */
8695     minLength : 0,
8696     /**
8697      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8698      */
8699     maxLength : Number.MAX_VALUE,
8700     /**
8701      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8702      */
8703     minLengthText : "The minimum length for this field is {0}",
8704     /**
8705      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8706      */
8707     maxLengthText : "The maximum length for this field is {0}",
8708   
8709     
8710     /**
8711      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8712      * If available, this function will be called only after the basic validators all return true, and will be passed the
8713      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8714      */
8715     validator : null,
8716     /**
8717      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8718      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8719      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8720      */
8721     regex : null,
8722     /**
8723      * @cfg {String} regexText -- Depricated - use Invalid Text
8724      */
8725     regexText : "",
8726     
8727     /**
8728      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8729      */
8730     invalidText : "",
8731     
8732     
8733     
8734     autocomplete: false,
8735     
8736     
8737     fieldLabel : '',
8738     inputType : 'text',
8739     
8740     name : false,
8741     placeholder: false,
8742     before : false,
8743     after : false,
8744     size : false,
8745     hasFocus : false,
8746     preventMark: false,
8747     isFormField : true,
8748     value : '',
8749     labelWidth : 2,
8750     labelAlign : false,
8751     readOnly : false,
8752     align : false,
8753     formatedValue : false,
8754     forceFeedback : false,
8755     
8756     indicatorpos : 'left',
8757     
8758     labellg : 0,
8759     labelmd : 0,
8760     labelsm : 0,
8761     labelxs : 0,
8762     
8763     capture : '',
8764     accept : '',
8765     
8766     parentLabelAlign : function()
8767     {
8768         var parent = this;
8769         while (parent.parent()) {
8770             parent = parent.parent();
8771             if (typeof(parent.labelAlign) !='undefined') {
8772                 return parent.labelAlign;
8773             }
8774         }
8775         return 'left';
8776         
8777     },
8778     
8779     getAutoCreate : function()
8780     {
8781         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8782         
8783         var id = Roo.id();
8784         
8785         var cfg = {};
8786         
8787         if(this.inputType != 'hidden'){
8788             cfg.cls = 'form-group' //input-group
8789         }
8790         
8791         var input =  {
8792             tag: 'input',
8793             id : id,
8794             type : this.inputType,
8795             value : this.value,
8796             cls : 'form-control',
8797             placeholder : this.placeholder || '',
8798             autocomplete : this.autocomplete || 'new-password'
8799         };
8800         
8801         if(this.capture.length){
8802             input.capture = this.capture;
8803         }
8804         
8805         if(this.accept.length){
8806             input.accept = this.accept + "/*";
8807         }
8808         
8809         if(this.align){
8810             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8811         }
8812         
8813         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8814             input.maxLength = this.maxLength;
8815         }
8816         
8817         if (this.disabled) {
8818             input.disabled=true;
8819         }
8820         
8821         if (this.readOnly) {
8822             input.readonly=true;
8823         }
8824         
8825         if (this.name) {
8826             input.name = this.name;
8827         }
8828         
8829         if (this.size) {
8830             input.cls += ' input-' + this.size;
8831         }
8832         
8833         var settings=this;
8834         ['xs','sm','md','lg'].map(function(size){
8835             if (settings[size]) {
8836                 cfg.cls += ' col-' + size + '-' + settings[size];
8837             }
8838         });
8839         
8840         var inputblock = input;
8841         
8842         var feedback = {
8843             tag: 'span',
8844             cls: 'glyphicon form-control-feedback'
8845         };
8846             
8847         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8848             
8849             inputblock = {
8850                 cls : 'has-feedback',
8851                 cn :  [
8852                     input,
8853                     feedback
8854                 ] 
8855             };  
8856         }
8857         
8858         if (this.before || this.after) {
8859             
8860             inputblock = {
8861                 cls : 'input-group',
8862                 cn :  [] 
8863             };
8864             
8865             if (this.before && typeof(this.before) == 'string') {
8866                 
8867                 inputblock.cn.push({
8868                     tag :'span',
8869                     cls : 'roo-input-before input-group-addon',
8870                     html : this.before
8871                 });
8872             }
8873             if (this.before && typeof(this.before) == 'object') {
8874                 this.before = Roo.factory(this.before);
8875                 
8876                 inputblock.cn.push({
8877                     tag :'span',
8878                     cls : 'roo-input-before input-group-' +
8879                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8880                 });
8881             }
8882             
8883             inputblock.cn.push(input);
8884             
8885             if (this.after && typeof(this.after) == 'string') {
8886                 inputblock.cn.push({
8887                     tag :'span',
8888                     cls : 'roo-input-after input-group-addon',
8889                     html : this.after
8890                 });
8891             }
8892             if (this.after && typeof(this.after) == 'object') {
8893                 this.after = Roo.factory(this.after);
8894                 
8895                 inputblock.cn.push({
8896                     tag :'span',
8897                     cls : 'roo-input-after input-group-' +
8898                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8899                 });
8900             }
8901             
8902             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8903                 inputblock.cls += ' has-feedback';
8904                 inputblock.cn.push(feedback);
8905             }
8906         };
8907         
8908         if (align ==='left' && this.fieldLabel.length) {
8909             
8910             cfg.cls += ' roo-form-group-label-left';
8911             
8912             cfg.cn = [
8913                 {
8914                     tag : 'i',
8915                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8916                     tooltip : 'This field is required'
8917                 },
8918                 {
8919                     tag: 'label',
8920                     'for' :  id,
8921                     cls : 'control-label',
8922                     html : this.fieldLabel
8923
8924                 },
8925                 {
8926                     cls : "", 
8927                     cn: [
8928                         inputblock
8929                     ]
8930                 }
8931             ];
8932             
8933             var labelCfg = cfg.cn[1];
8934             var contentCfg = cfg.cn[2];
8935             
8936             if(this.indicatorpos == 'right'){
8937                 cfg.cn = [
8938                     {
8939                         tag: 'label',
8940                         'for' :  id,
8941                         cls : 'control-label',
8942                         cn : [
8943                             {
8944                                 tag : 'span',
8945                                 html : this.fieldLabel
8946                             },
8947                             {
8948                                 tag : 'i',
8949                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8950                                 tooltip : 'This field is required'
8951                             }
8952                         ]
8953                     },
8954                     {
8955                         cls : "",
8956                         cn: [
8957                             inputblock
8958                         ]
8959                     }
8960
8961                 ];
8962                 
8963                 labelCfg = cfg.cn[0];
8964                 contentCfg = cfg.cn[1];
8965             
8966             }
8967             
8968             if(this.labelWidth > 12){
8969                 labelCfg.style = "width: " + this.labelWidth + 'px';
8970             }
8971             
8972             if(this.labelWidth < 13 && this.labelmd == 0){
8973                 this.labelmd = this.labelWidth;
8974             }
8975             
8976             if(this.labellg > 0){
8977                 labelCfg.cls += ' col-lg-' + this.labellg;
8978                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8979             }
8980             
8981             if(this.labelmd > 0){
8982                 labelCfg.cls += ' col-md-' + this.labelmd;
8983                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8984             }
8985             
8986             if(this.labelsm > 0){
8987                 labelCfg.cls += ' col-sm-' + this.labelsm;
8988                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8989             }
8990             
8991             if(this.labelxs > 0){
8992                 labelCfg.cls += ' col-xs-' + this.labelxs;
8993                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8994             }
8995             
8996             
8997         } else if ( this.fieldLabel.length) {
8998                 
8999             cfg.cn = [
9000                 {
9001                     tag : 'i',
9002                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9003                     tooltip : 'This field is required'
9004                 },
9005                 {
9006                     tag: 'label',
9007                    //cls : 'input-group-addon',
9008                     html : this.fieldLabel
9009
9010                 },
9011
9012                inputblock
9013
9014            ];
9015            
9016            if(this.indicatorpos == 'right'){
9017                 
9018                 cfg.cn = [
9019                     {
9020                         tag: 'label',
9021                        //cls : 'input-group-addon',
9022                         html : this.fieldLabel
9023
9024                     },
9025                     {
9026                         tag : 'i',
9027                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9028                         tooltip : 'This field is required'
9029                     },
9030
9031                    inputblock
9032
9033                ];
9034
9035             }
9036
9037         } else {
9038             
9039             cfg.cn = [
9040
9041                     inputblock
9042
9043             ];
9044                 
9045                 
9046         };
9047         
9048         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9049            cfg.cls += ' navbar-form';
9050         }
9051         
9052         if (this.parentType === 'NavGroup') {
9053            cfg.cls += ' navbar-form';
9054            cfg.tag = 'li';
9055         }
9056         
9057         return cfg;
9058         
9059     },
9060     /**
9061      * return the real input element.
9062      */
9063     inputEl: function ()
9064     {
9065         return this.el.select('input.form-control',true).first();
9066     },
9067     
9068     tooltipEl : function()
9069     {
9070         return this.inputEl();
9071     },
9072     
9073     indicatorEl : function()
9074     {
9075         var indicator = this.el.select('i.roo-required-indicator',true).first();
9076         
9077         if(!indicator){
9078             return false;
9079         }
9080         
9081         return indicator;
9082         
9083     },
9084     
9085     setDisabled : function(v)
9086     {
9087         var i  = this.inputEl().dom;
9088         if (!v) {
9089             i.removeAttribute('disabled');
9090             return;
9091             
9092         }
9093         i.setAttribute('disabled','true');
9094     },
9095     initEvents : function()
9096     {
9097           
9098         this.inputEl().on("keydown" , this.fireKey,  this);
9099         this.inputEl().on("focus", this.onFocus,  this);
9100         this.inputEl().on("blur", this.onBlur,  this);
9101         
9102         this.inputEl().relayEvent('keyup', this);
9103         
9104         this.indicator = this.indicatorEl();
9105         
9106         if(this.indicator){
9107             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9108         }
9109  
9110         // reference to original value for reset
9111         this.originalValue = this.getValue();
9112         //Roo.form.TextField.superclass.initEvents.call(this);
9113         if(this.validationEvent == 'keyup'){
9114             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9115             this.inputEl().on('keyup', this.filterValidation, this);
9116         }
9117         else if(this.validationEvent !== false){
9118             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9119         }
9120         
9121         if(this.selectOnFocus){
9122             this.on("focus", this.preFocus, this);
9123             
9124         }
9125         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9126             this.inputEl().on("keypress", this.filterKeys, this);
9127         } else {
9128             this.inputEl().relayEvent('keypress', this);
9129         }
9130        /* if(this.grow){
9131             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9132             this.el.on("click", this.autoSize,  this);
9133         }
9134         */
9135         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9136             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9137         }
9138         
9139         if (typeof(this.before) == 'object') {
9140             this.before.render(this.el.select('.roo-input-before',true).first());
9141         }
9142         if (typeof(this.after) == 'object') {
9143             this.after.render(this.el.select('.roo-input-after',true).first());
9144         }
9145         
9146         this.inputEl().on('change', this.onChange, this);
9147         
9148     },
9149     filterValidation : function(e){
9150         if(!e.isNavKeyPress()){
9151             this.validationTask.delay(this.validationDelay);
9152         }
9153     },
9154      /**
9155      * Validates the field value
9156      * @return {Boolean} True if the value is valid, else false
9157      */
9158     validate : function(){
9159         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9160         if(this.disabled || this.validateValue(this.getRawValue())){
9161             this.markValid();
9162             return true;
9163         }
9164         
9165         this.markInvalid();
9166         return false;
9167     },
9168     
9169     
9170     /**
9171      * Validates a value according to the field's validation rules and marks the field as invalid
9172      * if the validation fails
9173      * @param {Mixed} value The value to validate
9174      * @return {Boolean} True if the value is valid, else false
9175      */
9176     validateValue : function(value)
9177     {
9178         if(this.getVisibilityEl().hasClass('hidden')){
9179             return true;
9180         }
9181         
9182         if(value.length < 1)  { // if it's blank
9183             if(this.allowBlank){
9184                 return true;
9185             }
9186             return false;
9187         }
9188         
9189         if(value.length < this.minLength){
9190             return false;
9191         }
9192         if(value.length > this.maxLength){
9193             return false;
9194         }
9195         if(this.vtype){
9196             var vt = Roo.form.VTypes;
9197             if(!vt[this.vtype](value, this)){
9198                 return false;
9199             }
9200         }
9201         if(typeof this.validator == "function"){
9202             var msg = this.validator(value);
9203             if(msg !== true){
9204                 return false;
9205             }
9206             if (typeof(msg) == 'string') {
9207                 this.invalidText = msg;
9208             }
9209         }
9210         
9211         if(this.regex && !this.regex.test(value)){
9212             return false;
9213         }
9214         
9215         return true;
9216     },
9217     
9218      // private
9219     fireKey : function(e){
9220         //Roo.log('field ' + e.getKey());
9221         if(e.isNavKeyPress()){
9222             this.fireEvent("specialkey", this, e);
9223         }
9224     },
9225     focus : function (selectText){
9226         if(this.rendered){
9227             this.inputEl().focus();
9228             if(selectText === true){
9229                 this.inputEl().dom.select();
9230             }
9231         }
9232         return this;
9233     } ,
9234     
9235     onFocus : function(){
9236         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9237            // this.el.addClass(this.focusClass);
9238         }
9239         if(!this.hasFocus){
9240             this.hasFocus = true;
9241             this.startValue = this.getValue();
9242             this.fireEvent("focus", this);
9243         }
9244     },
9245     
9246     beforeBlur : Roo.emptyFn,
9247
9248     
9249     // private
9250     onBlur : function(){
9251         this.beforeBlur();
9252         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9253             //this.el.removeClass(this.focusClass);
9254         }
9255         this.hasFocus = false;
9256         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9257             this.validate();
9258         }
9259         var v = this.getValue();
9260         if(String(v) !== String(this.startValue)){
9261             this.fireEvent('change', this, v, this.startValue);
9262         }
9263         this.fireEvent("blur", this);
9264     },
9265     
9266     onChange : function(e)
9267     {
9268         var v = this.getValue();
9269         if(String(v) !== String(this.startValue)){
9270             this.fireEvent('change', this, v, this.startValue);
9271         }
9272         
9273     },
9274     
9275     /**
9276      * Resets the current field value to the originally loaded value and clears any validation messages
9277      */
9278     reset : function(){
9279         this.setValue(this.originalValue);
9280         this.validate();
9281     },
9282      /**
9283      * Returns the name of the field
9284      * @return {Mixed} name The name field
9285      */
9286     getName: function(){
9287         return this.name;
9288     },
9289      /**
9290      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9291      * @return {Mixed} value The field value
9292      */
9293     getValue : function(){
9294         
9295         var v = this.inputEl().getValue();
9296         
9297         return v;
9298     },
9299     /**
9300      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9301      * @return {Mixed} value The field value
9302      */
9303     getRawValue : function(){
9304         var v = this.inputEl().getValue();
9305         
9306         return v;
9307     },
9308     
9309     /**
9310      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9311      * @param {Mixed} value The value to set
9312      */
9313     setRawValue : function(v){
9314         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9315     },
9316     
9317     selectText : function(start, end){
9318         var v = this.getRawValue();
9319         if(v.length > 0){
9320             start = start === undefined ? 0 : start;
9321             end = end === undefined ? v.length : end;
9322             var d = this.inputEl().dom;
9323             if(d.setSelectionRange){
9324                 d.setSelectionRange(start, end);
9325             }else if(d.createTextRange){
9326                 var range = d.createTextRange();
9327                 range.moveStart("character", start);
9328                 range.moveEnd("character", v.length-end);
9329                 range.select();
9330             }
9331         }
9332     },
9333     
9334     /**
9335      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9336      * @param {Mixed} value The value to set
9337      */
9338     setValue : function(v){
9339         this.value = v;
9340         if(this.rendered){
9341             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9342             this.validate();
9343         }
9344     },
9345     
9346     /*
9347     processValue : function(value){
9348         if(this.stripCharsRe){
9349             var newValue = value.replace(this.stripCharsRe, '');
9350             if(newValue !== value){
9351                 this.setRawValue(newValue);
9352                 return newValue;
9353             }
9354         }
9355         return value;
9356     },
9357   */
9358     preFocus : function(){
9359         
9360         if(this.selectOnFocus){
9361             this.inputEl().dom.select();
9362         }
9363     },
9364     filterKeys : function(e){
9365         var k = e.getKey();
9366         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9367             return;
9368         }
9369         var c = e.getCharCode(), cc = String.fromCharCode(c);
9370         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9371             return;
9372         }
9373         if(!this.maskRe.test(cc)){
9374             e.stopEvent();
9375         }
9376     },
9377      /**
9378      * Clear any invalid styles/messages for this field
9379      */
9380     clearInvalid : function(){
9381         
9382         if(!this.el || this.preventMark){ // not rendered
9383             return;
9384         }
9385         
9386      
9387         this.el.removeClass(this.invalidClass);
9388         
9389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9390             
9391             var feedback = this.el.select('.form-control-feedback', true).first();
9392             
9393             if(feedback){
9394                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9395             }
9396             
9397         }
9398         
9399         this.fireEvent('valid', this);
9400     },
9401     
9402      /**
9403      * Mark this field as valid
9404      */
9405     markValid : function()
9406     {
9407         if(!this.el  || this.preventMark){ // not rendered...
9408             return;
9409         }
9410         
9411         this.el.removeClass([this.invalidClass, this.validClass]);
9412         
9413         var feedback = this.el.select('.form-control-feedback', true).first();
9414             
9415         if(feedback){
9416             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9417         }
9418         
9419         if(this.indicator){
9420             this.indicator.removeClass('visible');
9421             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9422         }
9423         
9424         if(this.disabled){
9425             return;
9426         }
9427         
9428         if(this.allowBlank && !this.getRawValue().length){
9429             return;
9430         }
9431         
9432         this.el.addClass(this.validClass);
9433         
9434         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9435             
9436             var feedback = this.el.select('.form-control-feedback', true).first();
9437             
9438             if(feedback){
9439                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9440                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9441             }
9442             
9443         }
9444         
9445         this.fireEvent('valid', this);
9446     },
9447     
9448      /**
9449      * Mark this field as invalid
9450      * @param {String} msg The validation message
9451      */
9452     markInvalid : function(msg)
9453     {
9454         if(!this.el  || this.preventMark){ // not rendered
9455             return;
9456         }
9457         
9458         this.el.removeClass([this.invalidClass, this.validClass]);
9459         
9460         var feedback = this.el.select('.form-control-feedback', true).first();
9461             
9462         if(feedback){
9463             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9464         }
9465
9466         if(this.disabled){
9467             return;
9468         }
9469         
9470         if(this.allowBlank && !this.getRawValue().length){
9471             return;
9472         }
9473         
9474         if(this.indicator){
9475             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9476             this.indicator.addClass('visible');
9477         }
9478         
9479         this.el.addClass(this.invalidClass);
9480         
9481         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9482             
9483             var feedback = this.el.select('.form-control-feedback', true).first();
9484             
9485             if(feedback){
9486                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9487                 
9488                 if(this.getValue().length || this.forceFeedback){
9489                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9490                 }
9491                 
9492             }
9493             
9494         }
9495         
9496         this.fireEvent('invalid', this, msg);
9497     },
9498     // private
9499     SafariOnKeyDown : function(event)
9500     {
9501         // this is a workaround for a password hang bug on chrome/ webkit.
9502         if (this.inputEl().dom.type != 'password') {
9503             return;
9504         }
9505         
9506         var isSelectAll = false;
9507         
9508         if(this.inputEl().dom.selectionEnd > 0){
9509             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9510         }
9511         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9512             event.preventDefault();
9513             this.setValue('');
9514             return;
9515         }
9516         
9517         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9518             
9519             event.preventDefault();
9520             // this is very hacky as keydown always get's upper case.
9521             //
9522             var cc = String.fromCharCode(event.getCharCode());
9523             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9524             
9525         }
9526     },
9527     adjustWidth : function(tag, w){
9528         tag = tag.toLowerCase();
9529         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9530             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9531                 if(tag == 'input'){
9532                     return w + 2;
9533                 }
9534                 if(tag == 'textarea'){
9535                     return w-2;
9536                 }
9537             }else if(Roo.isOpera){
9538                 if(tag == 'input'){
9539                     return w + 2;
9540                 }
9541                 if(tag == 'textarea'){
9542                     return w-2;
9543                 }
9544             }
9545         }
9546         return w;
9547     },
9548     
9549     setFieldLabel : function(v)
9550     {
9551         if(!this.rendered){
9552             return;
9553         }
9554         
9555         if(this.indicator){
9556             var ar = this.el.select('label > span',true);
9557             
9558             if (ar.elements.length) {
9559                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9560                 this.fieldLabel = v;
9561                 return;
9562             }
9563             
9564             var br = this.el.select('label',true);
9565             
9566             if(br.elements.length) {
9567                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9568                 this.fieldLabel = v;
9569                 return;
9570             }
9571             
9572             Roo.log('Cannot Found any of label > span || label in input');
9573             return;
9574         }
9575         
9576         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9577         this.fieldLabel = v;
9578         
9579         
9580     }
9581 });
9582
9583  
9584 /*
9585  * - LGPL
9586  *
9587  * Input
9588  * 
9589  */
9590
9591 /**
9592  * @class Roo.bootstrap.TextArea
9593  * @extends Roo.bootstrap.Input
9594  * Bootstrap TextArea class
9595  * @cfg {Number} cols Specifies the visible width of a text area
9596  * @cfg {Number} rows Specifies the visible number of lines in a text area
9597  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9598  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9599  * @cfg {string} html text
9600  * 
9601  * @constructor
9602  * Create a new TextArea
9603  * @param {Object} config The config object
9604  */
9605
9606 Roo.bootstrap.TextArea = function(config){
9607     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9608    
9609 };
9610
9611 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9612      
9613     cols : false,
9614     rows : 5,
9615     readOnly : false,
9616     warp : 'soft',
9617     resize : false,
9618     value: false,
9619     html: false,
9620     
9621     getAutoCreate : function(){
9622         
9623         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9624         
9625         var id = Roo.id();
9626         
9627         var cfg = {};
9628         
9629         if(this.inputType != 'hidden'){
9630             cfg.cls = 'form-group' //input-group
9631         }
9632         
9633         var input =  {
9634             tag: 'textarea',
9635             id : id,
9636             warp : this.warp,
9637             rows : this.rows,
9638             value : this.value || '',
9639             html: this.html || '',
9640             cls : 'form-control',
9641             placeholder : this.placeholder || '' 
9642             
9643         };
9644         
9645         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9646             input.maxLength = this.maxLength;
9647         }
9648         
9649         if(this.resize){
9650             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9651         }
9652         
9653         if(this.cols){
9654             input.cols = this.cols;
9655         }
9656         
9657         if (this.readOnly) {
9658             input.readonly = true;
9659         }
9660         
9661         if (this.name) {
9662             input.name = this.name;
9663         }
9664         
9665         if (this.size) {
9666             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9667         }
9668         
9669         var settings=this;
9670         ['xs','sm','md','lg'].map(function(size){
9671             if (settings[size]) {
9672                 cfg.cls += ' col-' + size + '-' + settings[size];
9673             }
9674         });
9675         
9676         var inputblock = input;
9677         
9678         if(this.hasFeedback && !this.allowBlank){
9679             
9680             var feedback = {
9681                 tag: 'span',
9682                 cls: 'glyphicon form-control-feedback'
9683             };
9684
9685             inputblock = {
9686                 cls : 'has-feedback',
9687                 cn :  [
9688                     input,
9689                     feedback
9690                 ] 
9691             };  
9692         }
9693         
9694         
9695         if (this.before || this.after) {
9696             
9697             inputblock = {
9698                 cls : 'input-group',
9699                 cn :  [] 
9700             };
9701             if (this.before) {
9702                 inputblock.cn.push({
9703                     tag :'span',
9704                     cls : 'input-group-addon',
9705                     html : this.before
9706                 });
9707             }
9708             
9709             inputblock.cn.push(input);
9710             
9711             if(this.hasFeedback && !this.allowBlank){
9712                 inputblock.cls += ' has-feedback';
9713                 inputblock.cn.push(feedback);
9714             }
9715             
9716             if (this.after) {
9717                 inputblock.cn.push({
9718                     tag :'span',
9719                     cls : 'input-group-addon',
9720                     html : this.after
9721                 });
9722             }
9723             
9724         }
9725         
9726         if (align ==='left' && this.fieldLabel.length) {
9727             cfg.cn = [
9728                 {
9729                     tag: 'label',
9730                     'for' :  id,
9731                     cls : 'control-label',
9732                     html : this.fieldLabel
9733                 },
9734                 {
9735                     cls : "",
9736                     cn: [
9737                         inputblock
9738                     ]
9739                 }
9740
9741             ];
9742             
9743             if(this.labelWidth > 12){
9744                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9745             }
9746
9747             if(this.labelWidth < 13 && this.labelmd == 0){
9748                 this.labelmd = this.labelWidth;
9749             }
9750
9751             if(this.labellg > 0){
9752                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9753                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9754             }
9755
9756             if(this.labelmd > 0){
9757                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9758                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9759             }
9760
9761             if(this.labelsm > 0){
9762                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9763                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9764             }
9765
9766             if(this.labelxs > 0){
9767                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9768                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9769             }
9770             
9771         } else if ( this.fieldLabel.length) {
9772             cfg.cn = [
9773
9774                {
9775                    tag: 'label',
9776                    //cls : 'input-group-addon',
9777                    html : this.fieldLabel
9778
9779                },
9780
9781                inputblock
9782
9783            ];
9784
9785         } else {
9786
9787             cfg.cn = [
9788
9789                 inputblock
9790
9791             ];
9792                 
9793         }
9794         
9795         if (this.disabled) {
9796             input.disabled=true;
9797         }
9798         
9799         return cfg;
9800         
9801     },
9802     /**
9803      * return the real textarea element.
9804      */
9805     inputEl: function ()
9806     {
9807         return this.el.select('textarea.form-control',true).first();
9808     },
9809     
9810     /**
9811      * Clear any invalid styles/messages for this field
9812      */
9813     clearInvalid : function()
9814     {
9815         
9816         if(!this.el || this.preventMark){ // not rendered
9817             return;
9818         }
9819         
9820         var label = this.el.select('label', true).first();
9821         var icon = this.el.select('i.fa-star', true).first();
9822         
9823         if(label && icon){
9824             icon.remove();
9825         }
9826         
9827         this.el.removeClass(this.invalidClass);
9828         
9829         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9830             
9831             var feedback = this.el.select('.form-control-feedback', true).first();
9832             
9833             if(feedback){
9834                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9835             }
9836             
9837         }
9838         
9839         this.fireEvent('valid', this);
9840     },
9841     
9842      /**
9843      * Mark this field as valid
9844      */
9845     markValid : function()
9846     {
9847         if(!this.el  || this.preventMark){ // not rendered
9848             return;
9849         }
9850         
9851         this.el.removeClass([this.invalidClass, this.validClass]);
9852         
9853         var feedback = this.el.select('.form-control-feedback', true).first();
9854             
9855         if(feedback){
9856             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9857         }
9858
9859         if(this.disabled || this.allowBlank){
9860             return;
9861         }
9862         
9863         var label = this.el.select('label', true).first();
9864         var icon = this.el.select('i.fa-star', true).first();
9865         
9866         if(label && icon){
9867             icon.remove();
9868         }
9869         
9870         this.el.addClass(this.validClass);
9871         
9872         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9873             
9874             var feedback = this.el.select('.form-control-feedback', true).first();
9875             
9876             if(feedback){
9877                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9878                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9879             }
9880             
9881         }
9882         
9883         this.fireEvent('valid', this);
9884     },
9885     
9886      /**
9887      * Mark this field as invalid
9888      * @param {String} msg The validation message
9889      */
9890     markInvalid : function(msg)
9891     {
9892         if(!this.el  || this.preventMark){ // not rendered
9893             return;
9894         }
9895         
9896         this.el.removeClass([this.invalidClass, this.validClass]);
9897         
9898         var feedback = this.el.select('.form-control-feedback', true).first();
9899             
9900         if(feedback){
9901             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9902         }
9903
9904         if(this.disabled || this.allowBlank){
9905             return;
9906         }
9907         
9908         var label = this.el.select('label', true).first();
9909         var icon = this.el.select('i.fa-star', true).first();
9910         
9911         if(!this.getValue().length && label && !icon){
9912             this.el.createChild({
9913                 tag : 'i',
9914                 cls : 'text-danger fa fa-lg fa-star',
9915                 tooltip : 'This field is required',
9916                 style : 'margin-right:5px;'
9917             }, label, true);
9918         }
9919
9920         this.el.addClass(this.invalidClass);
9921         
9922         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9923             
9924             var feedback = this.el.select('.form-control-feedback', true).first();
9925             
9926             if(feedback){
9927                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9928                 
9929                 if(this.getValue().length || this.forceFeedback){
9930                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9931                 }
9932                 
9933             }
9934             
9935         }
9936         
9937         this.fireEvent('invalid', this, msg);
9938     }
9939 });
9940
9941  
9942 /*
9943  * - LGPL
9944  *
9945  * trigger field - base class for combo..
9946  * 
9947  */
9948  
9949 /**
9950  * @class Roo.bootstrap.TriggerField
9951  * @extends Roo.bootstrap.Input
9952  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9953  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9954  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9955  * for which you can provide a custom implementation.  For example:
9956  * <pre><code>
9957 var trigger = new Roo.bootstrap.TriggerField();
9958 trigger.onTriggerClick = myTriggerFn;
9959 trigger.applyTo('my-field');
9960 </code></pre>
9961  *
9962  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9963  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9964  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9965  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9966  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9967
9968  * @constructor
9969  * Create a new TriggerField.
9970  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9971  * to the base TextField)
9972  */
9973 Roo.bootstrap.TriggerField = function(config){
9974     this.mimicing = false;
9975     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9976 };
9977
9978 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9979     /**
9980      * @cfg {String} triggerClass A CSS class to apply to the trigger
9981      */
9982      /**
9983      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9984      */
9985     hideTrigger:false,
9986
9987     /**
9988      * @cfg {Boolean} removable (true|false) special filter default false
9989      */
9990     removable : false,
9991     
9992     /** @cfg {Boolean} grow @hide */
9993     /** @cfg {Number} growMin @hide */
9994     /** @cfg {Number} growMax @hide */
9995
9996     /**
9997      * @hide 
9998      * @method
9999      */
10000     autoSize: Roo.emptyFn,
10001     // private
10002     monitorTab : true,
10003     // private
10004     deferHeight : true,
10005
10006     
10007     actionMode : 'wrap',
10008     
10009     caret : false,
10010     
10011     
10012     getAutoCreate : function(){
10013        
10014         var align = this.labelAlign || this.parentLabelAlign();
10015         
10016         var id = Roo.id();
10017         
10018         var cfg = {
10019             cls: 'form-group' //input-group
10020         };
10021         
10022         
10023         var input =  {
10024             tag: 'input',
10025             id : id,
10026             type : this.inputType,
10027             cls : 'form-control',
10028             autocomplete: 'new-password',
10029             placeholder : this.placeholder || '' 
10030             
10031         };
10032         if (this.name) {
10033             input.name = this.name;
10034         }
10035         if (this.size) {
10036             input.cls += ' input-' + this.size;
10037         }
10038         
10039         if (this.disabled) {
10040             input.disabled=true;
10041         }
10042         
10043         var inputblock = input;
10044         
10045         if(this.hasFeedback && !this.allowBlank){
10046             
10047             var feedback = {
10048                 tag: 'span',
10049                 cls: 'glyphicon form-control-feedback'
10050             };
10051             
10052             if(this.removable && !this.editable && !this.tickable){
10053                 inputblock = {
10054                     cls : 'has-feedback',
10055                     cn :  [
10056                         inputblock,
10057                         {
10058                             tag: 'button',
10059                             html : 'x',
10060                             cls : 'roo-combo-removable-btn close'
10061                         },
10062                         feedback
10063                     ] 
10064                 };
10065             } else {
10066                 inputblock = {
10067                     cls : 'has-feedback',
10068                     cn :  [
10069                         inputblock,
10070                         feedback
10071                     ] 
10072                 };
10073             }
10074
10075         } else {
10076             if(this.removable && !this.editable && !this.tickable){
10077                 inputblock = {
10078                     cls : 'roo-removable',
10079                     cn :  [
10080                         inputblock,
10081                         {
10082                             tag: 'button',
10083                             html : 'x',
10084                             cls : 'roo-combo-removable-btn close'
10085                         }
10086                     ] 
10087                 };
10088             }
10089         }
10090         
10091         if (this.before || this.after) {
10092             
10093             inputblock = {
10094                 cls : 'input-group',
10095                 cn :  [] 
10096             };
10097             if (this.before) {
10098                 inputblock.cn.push({
10099                     tag :'span',
10100                     cls : 'input-group-addon',
10101                     html : this.before
10102                 });
10103             }
10104             
10105             inputblock.cn.push(input);
10106             
10107             if(this.hasFeedback && !this.allowBlank){
10108                 inputblock.cls += ' has-feedback';
10109                 inputblock.cn.push(feedback);
10110             }
10111             
10112             if (this.after) {
10113                 inputblock.cn.push({
10114                     tag :'span',
10115                     cls : 'input-group-addon',
10116                     html : this.after
10117                 });
10118             }
10119             
10120         };
10121         
10122         var box = {
10123             tag: 'div',
10124             cn: [
10125                 {
10126                     tag: 'input',
10127                     type : 'hidden',
10128                     cls: 'form-hidden-field'
10129                 },
10130                 inputblock
10131             ]
10132             
10133         };
10134         
10135         if(this.multiple){
10136             box = {
10137                 tag: 'div',
10138                 cn: [
10139                     {
10140                         tag: 'input',
10141                         type : 'hidden',
10142                         cls: 'form-hidden-field'
10143                     },
10144                     {
10145                         tag: 'ul',
10146                         cls: 'roo-select2-choices',
10147                         cn:[
10148                             {
10149                                 tag: 'li',
10150                                 cls: 'roo-select2-search-field',
10151                                 cn: [
10152
10153                                     inputblock
10154                                 ]
10155                             }
10156                         ]
10157                     }
10158                 ]
10159             }
10160         };
10161         
10162         var combobox = {
10163             cls: 'roo-select2-container input-group',
10164             cn: [
10165                 box
10166 //                {
10167 //                    tag: 'ul',
10168 //                    cls: 'typeahead typeahead-long dropdown-menu',
10169 //                    style: 'display:none'
10170 //                }
10171             ]
10172         };
10173         
10174         if(!this.multiple && this.showToggleBtn){
10175             
10176             var caret = {
10177                         tag: 'span',
10178                         cls: 'caret'
10179              };
10180             if (this.caret != false) {
10181                 caret = {
10182                      tag: 'i',
10183                      cls: 'fa fa-' + this.caret
10184                 };
10185                 
10186             }
10187             
10188             combobox.cn.push({
10189                 tag :'span',
10190                 cls : 'input-group-addon btn dropdown-toggle',
10191                 cn : [
10192                     caret,
10193                     {
10194                         tag: 'span',
10195                         cls: 'combobox-clear',
10196                         cn  : [
10197                             {
10198                                 tag : 'i',
10199                                 cls: 'icon-remove'
10200                             }
10201                         ]
10202                     }
10203                 ]
10204
10205             })
10206         }
10207         
10208         if(this.multiple){
10209             combobox.cls += ' roo-select2-container-multi';
10210         }
10211         
10212         if (align ==='left' && this.fieldLabel.length) {
10213             
10214             cfg.cls += ' roo-form-group-label-left';
10215
10216             cfg.cn = [
10217                 {
10218                     tag : 'i',
10219                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10220                     tooltip : 'This field is required'
10221                 },
10222                 {
10223                     tag: 'label',
10224                     'for' :  id,
10225                     cls : 'control-label',
10226                     html : this.fieldLabel
10227
10228                 },
10229                 {
10230                     cls : "", 
10231                     cn: [
10232                         combobox
10233                     ]
10234                 }
10235
10236             ];
10237             
10238             var labelCfg = cfg.cn[1];
10239             var contentCfg = cfg.cn[2];
10240             
10241             if(this.indicatorpos == 'right'){
10242                 cfg.cn = [
10243                     {
10244                         tag: 'label',
10245                         'for' :  id,
10246                         cls : 'control-label',
10247                         cn : [
10248                             {
10249                                 tag : 'span',
10250                                 html : this.fieldLabel
10251                             },
10252                             {
10253                                 tag : 'i',
10254                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10255                                 tooltip : 'This field is required'
10256                             }
10257                         ]
10258                     },
10259                     {
10260                         cls : "", 
10261                         cn: [
10262                             combobox
10263                         ]
10264                     }
10265
10266                 ];
10267                 
10268                 labelCfg = cfg.cn[0];
10269                 contentCfg = cfg.cn[1];
10270             }
10271             
10272             if(this.labelWidth > 12){
10273                 labelCfg.style = "width: " + this.labelWidth + 'px';
10274             }
10275             
10276             if(this.labelWidth < 13 && this.labelmd == 0){
10277                 this.labelmd = this.labelWidth;
10278             }
10279             
10280             if(this.labellg > 0){
10281                 labelCfg.cls += ' col-lg-' + this.labellg;
10282                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10283             }
10284             
10285             if(this.labelmd > 0){
10286                 labelCfg.cls += ' col-md-' + this.labelmd;
10287                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10288             }
10289             
10290             if(this.labelsm > 0){
10291                 labelCfg.cls += ' col-sm-' + this.labelsm;
10292                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10293             }
10294             
10295             if(this.labelxs > 0){
10296                 labelCfg.cls += ' col-xs-' + this.labelxs;
10297                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10298             }
10299             
10300         } else if ( this.fieldLabel.length) {
10301 //                Roo.log(" label");
10302             cfg.cn = [
10303                 {
10304                    tag : 'i',
10305                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10306                    tooltip : 'This field is required'
10307                },
10308                {
10309                    tag: 'label',
10310                    //cls : 'input-group-addon',
10311                    html : this.fieldLabel
10312
10313                },
10314
10315                combobox
10316
10317             ];
10318             
10319             if(this.indicatorpos == 'right'){
10320                 
10321                 cfg.cn = [
10322                     {
10323                        tag: 'label',
10324                        cn : [
10325                            {
10326                                tag : 'span',
10327                                html : this.fieldLabel
10328                            },
10329                            {
10330                               tag : 'i',
10331                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10332                               tooltip : 'This field is required'
10333                            }
10334                        ]
10335
10336                     },
10337                     combobox
10338
10339                 ];
10340
10341             }
10342
10343         } else {
10344             
10345 //                Roo.log(" no label && no align");
10346                 cfg = combobox
10347                      
10348                 
10349         }
10350         
10351         var settings=this;
10352         ['xs','sm','md','lg'].map(function(size){
10353             if (settings[size]) {
10354                 cfg.cls += ' col-' + size + '-' + settings[size];
10355             }
10356         });
10357         
10358         return cfg;
10359         
10360     },
10361     
10362     
10363     
10364     // private
10365     onResize : function(w, h){
10366 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10367 //        if(typeof w == 'number'){
10368 //            var x = w - this.trigger.getWidth();
10369 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10370 //            this.trigger.setStyle('left', x+'px');
10371 //        }
10372     },
10373
10374     // private
10375     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10376
10377     // private
10378     getResizeEl : function(){
10379         return this.inputEl();
10380     },
10381
10382     // private
10383     getPositionEl : function(){
10384         return this.inputEl();
10385     },
10386
10387     // private
10388     alignErrorIcon : function(){
10389         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10390     },
10391
10392     // private
10393     initEvents : function(){
10394         
10395         this.createList();
10396         
10397         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10398         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10399         if(!this.multiple && this.showToggleBtn){
10400             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10401             if(this.hideTrigger){
10402                 this.trigger.setDisplayed(false);
10403             }
10404             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10405         }
10406         
10407         if(this.multiple){
10408             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10409         }
10410         
10411         if(this.removable && !this.editable && !this.tickable){
10412             var close = this.closeTriggerEl();
10413             
10414             if(close){
10415                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10416                 close.on('click', this.removeBtnClick, this, close);
10417             }
10418         }
10419         
10420         //this.trigger.addClassOnOver('x-form-trigger-over');
10421         //this.trigger.addClassOnClick('x-form-trigger-click');
10422         
10423         //if(!this.width){
10424         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10425         //}
10426     },
10427     
10428     closeTriggerEl : function()
10429     {
10430         var close = this.el.select('.roo-combo-removable-btn', true).first();
10431         return close ? close : false;
10432     },
10433     
10434     removeBtnClick : function(e, h, el)
10435     {
10436         e.preventDefault();
10437         
10438         if(this.fireEvent("remove", this) !== false){
10439             this.reset();
10440             this.fireEvent("afterremove", this)
10441         }
10442     },
10443     
10444     createList : function()
10445     {
10446         this.list = Roo.get(document.body).createChild({
10447             tag: 'ul',
10448             cls: 'typeahead typeahead-long dropdown-menu',
10449             style: 'display:none'
10450         });
10451         
10452         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10453         
10454     },
10455
10456     // private
10457     initTrigger : function(){
10458        
10459     },
10460
10461     // private
10462     onDestroy : function(){
10463         if(this.trigger){
10464             this.trigger.removeAllListeners();
10465           //  this.trigger.remove();
10466         }
10467         //if(this.wrap){
10468         //    this.wrap.remove();
10469         //}
10470         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10471     },
10472
10473     // private
10474     onFocus : function(){
10475         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10476         /*
10477         if(!this.mimicing){
10478             this.wrap.addClass('x-trigger-wrap-focus');
10479             this.mimicing = true;
10480             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10481             if(this.monitorTab){
10482                 this.el.on("keydown", this.checkTab, this);
10483             }
10484         }
10485         */
10486     },
10487
10488     // private
10489     checkTab : function(e){
10490         if(e.getKey() == e.TAB){
10491             this.triggerBlur();
10492         }
10493     },
10494
10495     // private
10496     onBlur : function(){
10497         // do nothing
10498     },
10499
10500     // private
10501     mimicBlur : function(e, t){
10502         /*
10503         if(!this.wrap.contains(t) && this.validateBlur()){
10504             this.triggerBlur();
10505         }
10506         */
10507     },
10508
10509     // private
10510     triggerBlur : function(){
10511         this.mimicing = false;
10512         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10513         if(this.monitorTab){
10514             this.el.un("keydown", this.checkTab, this);
10515         }
10516         //this.wrap.removeClass('x-trigger-wrap-focus');
10517         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10518     },
10519
10520     // private
10521     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10522     validateBlur : function(e, t){
10523         return true;
10524     },
10525
10526     // private
10527     onDisable : function(){
10528         this.inputEl().dom.disabled = true;
10529         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10530         //if(this.wrap){
10531         //    this.wrap.addClass('x-item-disabled');
10532         //}
10533     },
10534
10535     // private
10536     onEnable : function(){
10537         this.inputEl().dom.disabled = false;
10538         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10539         //if(this.wrap){
10540         //    this.el.removeClass('x-item-disabled');
10541         //}
10542     },
10543
10544     // private
10545     onShow : function(){
10546         var ae = this.getActionEl();
10547         
10548         if(ae){
10549             ae.dom.style.display = '';
10550             ae.dom.style.visibility = 'visible';
10551         }
10552     },
10553
10554     // private
10555     
10556     onHide : function(){
10557         var ae = this.getActionEl();
10558         ae.dom.style.display = 'none';
10559     },
10560
10561     /**
10562      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10563      * by an implementing function.
10564      * @method
10565      * @param {EventObject} e
10566      */
10567     onTriggerClick : Roo.emptyFn
10568 });
10569  /*
10570  * Based on:
10571  * Ext JS Library 1.1.1
10572  * Copyright(c) 2006-2007, Ext JS, LLC.
10573  *
10574  * Originally Released Under LGPL - original licence link has changed is not relivant.
10575  *
10576  * Fork - LGPL
10577  * <script type="text/javascript">
10578  */
10579
10580
10581 /**
10582  * @class Roo.data.SortTypes
10583  * @singleton
10584  * Defines the default sorting (casting?) comparison functions used when sorting data.
10585  */
10586 Roo.data.SortTypes = {
10587     /**
10588      * Default sort that does nothing
10589      * @param {Mixed} s The value being converted
10590      * @return {Mixed} The comparison value
10591      */
10592     none : function(s){
10593         return s;
10594     },
10595     
10596     /**
10597      * The regular expression used to strip tags
10598      * @type {RegExp}
10599      * @property
10600      */
10601     stripTagsRE : /<\/?[^>]+>/gi,
10602     
10603     /**
10604      * Strips all HTML tags to sort on text only
10605      * @param {Mixed} s The value being converted
10606      * @return {String} The comparison value
10607      */
10608     asText : function(s){
10609         return String(s).replace(this.stripTagsRE, "");
10610     },
10611     
10612     /**
10613      * Strips all HTML tags to sort on text only - Case insensitive
10614      * @param {Mixed} s The value being converted
10615      * @return {String} The comparison value
10616      */
10617     asUCText : function(s){
10618         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10619     },
10620     
10621     /**
10622      * Case insensitive string
10623      * @param {Mixed} s The value being converted
10624      * @return {String} The comparison value
10625      */
10626     asUCString : function(s) {
10627         return String(s).toUpperCase();
10628     },
10629     
10630     /**
10631      * Date sorting
10632      * @param {Mixed} s The value being converted
10633      * @return {Number} The comparison value
10634      */
10635     asDate : function(s) {
10636         if(!s){
10637             return 0;
10638         }
10639         if(s instanceof Date){
10640             return s.getTime();
10641         }
10642         return Date.parse(String(s));
10643     },
10644     
10645     /**
10646      * Float sorting
10647      * @param {Mixed} s The value being converted
10648      * @return {Float} The comparison value
10649      */
10650     asFloat : function(s) {
10651         var val = parseFloat(String(s).replace(/,/g, ""));
10652         if(isNaN(val)) {
10653             val = 0;
10654         }
10655         return val;
10656     },
10657     
10658     /**
10659      * Integer sorting
10660      * @param {Mixed} s The value being converted
10661      * @return {Number} The comparison value
10662      */
10663     asInt : function(s) {
10664         var val = parseInt(String(s).replace(/,/g, ""));
10665         if(isNaN(val)) {
10666             val = 0;
10667         }
10668         return val;
10669     }
10670 };/*
10671  * Based on:
10672  * Ext JS Library 1.1.1
10673  * Copyright(c) 2006-2007, Ext JS, LLC.
10674  *
10675  * Originally Released Under LGPL - original licence link has changed is not relivant.
10676  *
10677  * Fork - LGPL
10678  * <script type="text/javascript">
10679  */
10680
10681 /**
10682 * @class Roo.data.Record
10683  * Instances of this class encapsulate both record <em>definition</em> information, and record
10684  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10685  * to access Records cached in an {@link Roo.data.Store} object.<br>
10686  * <p>
10687  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10688  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10689  * objects.<br>
10690  * <p>
10691  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10692  * @constructor
10693  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10694  * {@link #create}. The parameters are the same.
10695  * @param {Array} data An associative Array of data values keyed by the field name.
10696  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10697  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10698  * not specified an integer id is generated.
10699  */
10700 Roo.data.Record = function(data, id){
10701     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10702     this.data = data;
10703 };
10704
10705 /**
10706  * Generate a constructor for a specific record layout.
10707  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10708  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10709  * Each field definition object may contain the following properties: <ul>
10710  * <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,
10711  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10712  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10713  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10714  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10715  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10716  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10717  * this may be omitted.</p></li>
10718  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10719  * <ul><li>auto (Default, implies no conversion)</li>
10720  * <li>string</li>
10721  * <li>int</li>
10722  * <li>float</li>
10723  * <li>boolean</li>
10724  * <li>date</li></ul></p></li>
10725  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10726  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10727  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10728  * by the Reader into an object that will be stored in the Record. It is passed the
10729  * following parameters:<ul>
10730  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10731  * </ul></p></li>
10732  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10733  * </ul>
10734  * <br>usage:<br><pre><code>
10735 var TopicRecord = Roo.data.Record.create(
10736     {name: 'title', mapping: 'topic_title'},
10737     {name: 'author', mapping: 'username'},
10738     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10739     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10740     {name: 'lastPoster', mapping: 'user2'},
10741     {name: 'excerpt', mapping: 'post_text'}
10742 );
10743
10744 var myNewRecord = new TopicRecord({
10745     title: 'Do my job please',
10746     author: 'noobie',
10747     totalPosts: 1,
10748     lastPost: new Date(),
10749     lastPoster: 'Animal',
10750     excerpt: 'No way dude!'
10751 });
10752 myStore.add(myNewRecord);
10753 </code></pre>
10754  * @method create
10755  * @static
10756  */
10757 Roo.data.Record.create = function(o){
10758     var f = function(){
10759         f.superclass.constructor.apply(this, arguments);
10760     };
10761     Roo.extend(f, Roo.data.Record);
10762     var p = f.prototype;
10763     p.fields = new Roo.util.MixedCollection(false, function(field){
10764         return field.name;
10765     });
10766     for(var i = 0, len = o.length; i < len; i++){
10767         p.fields.add(new Roo.data.Field(o[i]));
10768     }
10769     f.getField = function(name){
10770         return p.fields.get(name);  
10771     };
10772     return f;
10773 };
10774
10775 Roo.data.Record.AUTO_ID = 1000;
10776 Roo.data.Record.EDIT = 'edit';
10777 Roo.data.Record.REJECT = 'reject';
10778 Roo.data.Record.COMMIT = 'commit';
10779
10780 Roo.data.Record.prototype = {
10781     /**
10782      * Readonly flag - true if this record has been modified.
10783      * @type Boolean
10784      */
10785     dirty : false,
10786     editing : false,
10787     error: null,
10788     modified: null,
10789
10790     // private
10791     join : function(store){
10792         this.store = store;
10793     },
10794
10795     /**
10796      * Set the named field to the specified value.
10797      * @param {String} name The name of the field to set.
10798      * @param {Object} value The value to set the field to.
10799      */
10800     set : function(name, value){
10801         if(this.data[name] == value){
10802             return;
10803         }
10804         this.dirty = true;
10805         if(!this.modified){
10806             this.modified = {};
10807         }
10808         if(typeof this.modified[name] == 'undefined'){
10809             this.modified[name] = this.data[name];
10810         }
10811         this.data[name] = value;
10812         if(!this.editing && this.store){
10813             this.store.afterEdit(this);
10814         }       
10815     },
10816
10817     /**
10818      * Get the value of the named field.
10819      * @param {String} name The name of the field to get the value of.
10820      * @return {Object} The value of the field.
10821      */
10822     get : function(name){
10823         return this.data[name]; 
10824     },
10825
10826     // private
10827     beginEdit : function(){
10828         this.editing = true;
10829         this.modified = {}; 
10830     },
10831
10832     // private
10833     cancelEdit : function(){
10834         this.editing = false;
10835         delete this.modified;
10836     },
10837
10838     // private
10839     endEdit : function(){
10840         this.editing = false;
10841         if(this.dirty && this.store){
10842             this.store.afterEdit(this);
10843         }
10844     },
10845
10846     /**
10847      * Usually called by the {@link Roo.data.Store} which owns the Record.
10848      * Rejects all changes made to the Record since either creation, or the last commit operation.
10849      * Modified fields are reverted to their original values.
10850      * <p>
10851      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10852      * of reject operations.
10853      */
10854     reject : function(){
10855         var m = this.modified;
10856         for(var n in m){
10857             if(typeof m[n] != "function"){
10858                 this.data[n] = m[n];
10859             }
10860         }
10861         this.dirty = false;
10862         delete this.modified;
10863         this.editing = false;
10864         if(this.store){
10865             this.store.afterReject(this);
10866         }
10867     },
10868
10869     /**
10870      * Usually called by the {@link Roo.data.Store} which owns the Record.
10871      * Commits all changes made to the Record since either creation, or the last commit operation.
10872      * <p>
10873      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10874      * of commit operations.
10875      */
10876     commit : function(){
10877         this.dirty = false;
10878         delete this.modified;
10879         this.editing = false;
10880         if(this.store){
10881             this.store.afterCommit(this);
10882         }
10883     },
10884
10885     // private
10886     hasError : function(){
10887         return this.error != null;
10888     },
10889
10890     // private
10891     clearError : function(){
10892         this.error = null;
10893     },
10894
10895     /**
10896      * Creates a copy of this record.
10897      * @param {String} id (optional) A new record id if you don't want to use this record's id
10898      * @return {Record}
10899      */
10900     copy : function(newId) {
10901         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10902     }
10903 };/*
10904  * Based on:
10905  * Ext JS Library 1.1.1
10906  * Copyright(c) 2006-2007, Ext JS, LLC.
10907  *
10908  * Originally Released Under LGPL - original licence link has changed is not relivant.
10909  *
10910  * Fork - LGPL
10911  * <script type="text/javascript">
10912  */
10913
10914
10915
10916 /**
10917  * @class Roo.data.Store
10918  * @extends Roo.util.Observable
10919  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10920  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10921  * <p>
10922  * 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
10923  * has no knowledge of the format of the data returned by the Proxy.<br>
10924  * <p>
10925  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10926  * instances from the data object. These records are cached and made available through accessor functions.
10927  * @constructor
10928  * Creates a new Store.
10929  * @param {Object} config A config object containing the objects needed for the Store to access data,
10930  * and read the data into Records.
10931  */
10932 Roo.data.Store = function(config){
10933     this.data = new Roo.util.MixedCollection(false);
10934     this.data.getKey = function(o){
10935         return o.id;
10936     };
10937     this.baseParams = {};
10938     // private
10939     this.paramNames = {
10940         "start" : "start",
10941         "limit" : "limit",
10942         "sort" : "sort",
10943         "dir" : "dir",
10944         "multisort" : "_multisort"
10945     };
10946
10947     if(config && config.data){
10948         this.inlineData = config.data;
10949         delete config.data;
10950     }
10951
10952     Roo.apply(this, config);
10953     
10954     if(this.reader){ // reader passed
10955         this.reader = Roo.factory(this.reader, Roo.data);
10956         this.reader.xmodule = this.xmodule || false;
10957         if(!this.recordType){
10958             this.recordType = this.reader.recordType;
10959         }
10960         if(this.reader.onMetaChange){
10961             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10962         }
10963     }
10964
10965     if(this.recordType){
10966         this.fields = this.recordType.prototype.fields;
10967     }
10968     this.modified = [];
10969
10970     this.addEvents({
10971         /**
10972          * @event datachanged
10973          * Fires when the data cache has changed, and a widget which is using this Store
10974          * as a Record cache should refresh its view.
10975          * @param {Store} this
10976          */
10977         datachanged : true,
10978         /**
10979          * @event metachange
10980          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10981          * @param {Store} this
10982          * @param {Object} meta The JSON metadata
10983          */
10984         metachange : true,
10985         /**
10986          * @event add
10987          * Fires when Records have been added to the Store
10988          * @param {Store} this
10989          * @param {Roo.data.Record[]} records The array of Records added
10990          * @param {Number} index The index at which the record(s) were added
10991          */
10992         add : true,
10993         /**
10994          * @event remove
10995          * Fires when a Record has been removed from the Store
10996          * @param {Store} this
10997          * @param {Roo.data.Record} record The Record that was removed
10998          * @param {Number} index The index at which the record was removed
10999          */
11000         remove : true,
11001         /**
11002          * @event update
11003          * Fires when a Record has been updated
11004          * @param {Store} this
11005          * @param {Roo.data.Record} record The Record that was updated
11006          * @param {String} operation The update operation being performed.  Value may be one of:
11007          * <pre><code>
11008  Roo.data.Record.EDIT
11009  Roo.data.Record.REJECT
11010  Roo.data.Record.COMMIT
11011          * </code></pre>
11012          */
11013         update : true,
11014         /**
11015          * @event clear
11016          * Fires when the data cache has been cleared.
11017          * @param {Store} this
11018          */
11019         clear : true,
11020         /**
11021          * @event beforeload
11022          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11023          * the load action will be canceled.
11024          * @param {Store} this
11025          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11026          */
11027         beforeload : true,
11028         /**
11029          * @event beforeloadadd
11030          * Fires after a new set of Records has been loaded.
11031          * @param {Store} this
11032          * @param {Roo.data.Record[]} records The Records that were loaded
11033          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11034          */
11035         beforeloadadd : true,
11036         /**
11037          * @event load
11038          * Fires after a new set of Records has been loaded, before they are added to the store.
11039          * @param {Store} this
11040          * @param {Roo.data.Record[]} records The Records that were loaded
11041          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11042          * @params {Object} return from reader
11043          */
11044         load : true,
11045         /**
11046          * @event loadexception
11047          * Fires if an exception occurs in the Proxy during loading.
11048          * Called with the signature of the Proxy's "loadexception" event.
11049          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11050          * 
11051          * @param {Proxy} 
11052          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11053          * @param {Object} load options 
11054          * @param {Object} jsonData from your request (normally this contains the Exception)
11055          */
11056         loadexception : true
11057     });
11058     
11059     if(this.proxy){
11060         this.proxy = Roo.factory(this.proxy, Roo.data);
11061         this.proxy.xmodule = this.xmodule || false;
11062         this.relayEvents(this.proxy,  ["loadexception"]);
11063     }
11064     this.sortToggle = {};
11065     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11066
11067     Roo.data.Store.superclass.constructor.call(this);
11068
11069     if(this.inlineData){
11070         this.loadData(this.inlineData);
11071         delete this.inlineData;
11072     }
11073 };
11074
11075 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11076      /**
11077     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11078     * without a remote query - used by combo/forms at present.
11079     */
11080     
11081     /**
11082     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11083     */
11084     /**
11085     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11086     */
11087     /**
11088     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11089     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11090     */
11091     /**
11092     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11093     * on any HTTP request
11094     */
11095     /**
11096     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11097     */
11098     /**
11099     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11100     */
11101     multiSort: false,
11102     /**
11103     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11104     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11105     */
11106     remoteSort : false,
11107
11108     /**
11109     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11110      * loaded or when a record is removed. (defaults to false).
11111     */
11112     pruneModifiedRecords : false,
11113
11114     // private
11115     lastOptions : null,
11116
11117     /**
11118      * Add Records to the Store and fires the add event.
11119      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11120      */
11121     add : function(records){
11122         records = [].concat(records);
11123         for(var i = 0, len = records.length; i < len; i++){
11124             records[i].join(this);
11125         }
11126         var index = this.data.length;
11127         this.data.addAll(records);
11128         this.fireEvent("add", this, records, index);
11129     },
11130
11131     /**
11132      * Remove a Record from the Store and fires the remove event.
11133      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11134      */
11135     remove : function(record){
11136         var index = this.data.indexOf(record);
11137         this.data.removeAt(index);
11138  
11139         if(this.pruneModifiedRecords){
11140             this.modified.remove(record);
11141         }
11142         this.fireEvent("remove", this, record, index);
11143     },
11144
11145     /**
11146      * Remove all Records from the Store and fires the clear event.
11147      */
11148     removeAll : function(){
11149         this.data.clear();
11150         if(this.pruneModifiedRecords){
11151             this.modified = [];
11152         }
11153         this.fireEvent("clear", this);
11154     },
11155
11156     /**
11157      * Inserts Records to the Store at the given index and fires the add event.
11158      * @param {Number} index The start index at which to insert the passed Records.
11159      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11160      */
11161     insert : function(index, records){
11162         records = [].concat(records);
11163         for(var i = 0, len = records.length; i < len; i++){
11164             this.data.insert(index, records[i]);
11165             records[i].join(this);
11166         }
11167         this.fireEvent("add", this, records, index);
11168     },
11169
11170     /**
11171      * Get the index within the cache of the passed Record.
11172      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11173      * @return {Number} The index of the passed Record. Returns -1 if not found.
11174      */
11175     indexOf : function(record){
11176         return this.data.indexOf(record);
11177     },
11178
11179     /**
11180      * Get the index within the cache of the Record with the passed id.
11181      * @param {String} id The id of the Record to find.
11182      * @return {Number} The index of the Record. Returns -1 if not found.
11183      */
11184     indexOfId : function(id){
11185         return this.data.indexOfKey(id);
11186     },
11187
11188     /**
11189      * Get the Record with the specified id.
11190      * @param {String} id The id of the Record to find.
11191      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11192      */
11193     getById : function(id){
11194         return this.data.key(id);
11195     },
11196
11197     /**
11198      * Get the Record at the specified index.
11199      * @param {Number} index The index of the Record to find.
11200      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11201      */
11202     getAt : function(index){
11203         return this.data.itemAt(index);
11204     },
11205
11206     /**
11207      * Returns a range of Records between specified indices.
11208      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11209      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11210      * @return {Roo.data.Record[]} An array of Records
11211      */
11212     getRange : function(start, end){
11213         return this.data.getRange(start, end);
11214     },
11215
11216     // private
11217     storeOptions : function(o){
11218         o = Roo.apply({}, o);
11219         delete o.callback;
11220         delete o.scope;
11221         this.lastOptions = o;
11222     },
11223
11224     /**
11225      * Loads the Record cache from the configured Proxy using the configured Reader.
11226      * <p>
11227      * If using remote paging, then the first load call must specify the <em>start</em>
11228      * and <em>limit</em> properties in the options.params property to establish the initial
11229      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11230      * <p>
11231      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11232      * and this call will return before the new data has been loaded. Perform any post-processing
11233      * in a callback function, or in a "load" event handler.</strong>
11234      * <p>
11235      * @param {Object} options An object containing properties which control loading options:<ul>
11236      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11237      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11238      * passed the following arguments:<ul>
11239      * <li>r : Roo.data.Record[]</li>
11240      * <li>options: Options object from the load call</li>
11241      * <li>success: Boolean success indicator</li></ul></li>
11242      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11243      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11244      * </ul>
11245      */
11246     load : function(options){
11247         options = options || {};
11248         if(this.fireEvent("beforeload", this, options) !== false){
11249             this.storeOptions(options);
11250             var p = Roo.apply(options.params || {}, this.baseParams);
11251             // if meta was not loaded from remote source.. try requesting it.
11252             if (!this.reader.metaFromRemote) {
11253                 p._requestMeta = 1;
11254             }
11255             if(this.sortInfo && this.remoteSort){
11256                 var pn = this.paramNames;
11257                 p[pn["sort"]] = this.sortInfo.field;
11258                 p[pn["dir"]] = this.sortInfo.direction;
11259             }
11260             if (this.multiSort) {
11261                 var pn = this.paramNames;
11262                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11263             }
11264             
11265             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11266         }
11267     },
11268
11269     /**
11270      * Reloads the Record cache from the configured Proxy using the configured Reader and
11271      * the options from the last load operation performed.
11272      * @param {Object} options (optional) An object containing properties which may override the options
11273      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11274      * the most recently used options are reused).
11275      */
11276     reload : function(options){
11277         this.load(Roo.applyIf(options||{}, this.lastOptions));
11278     },
11279
11280     // private
11281     // Called as a callback by the Reader during a load operation.
11282     loadRecords : function(o, options, success){
11283         if(!o || success === false){
11284             if(success !== false){
11285                 this.fireEvent("load", this, [], options, o);
11286             }
11287             if(options.callback){
11288                 options.callback.call(options.scope || this, [], options, false);
11289             }
11290             return;
11291         }
11292         // if data returned failure - throw an exception.
11293         if (o.success === false) {
11294             // show a message if no listener is registered.
11295             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11296                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11297             }
11298             // loadmask wil be hooked into this..
11299             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11300             return;
11301         }
11302         var r = o.records, t = o.totalRecords || r.length;
11303         
11304         this.fireEvent("beforeloadadd", this, r, options, o);
11305         
11306         if(!options || options.add !== true){
11307             if(this.pruneModifiedRecords){
11308                 this.modified = [];
11309             }
11310             for(var i = 0, len = r.length; i < len; i++){
11311                 r[i].join(this);
11312             }
11313             if(this.snapshot){
11314                 this.data = this.snapshot;
11315                 delete this.snapshot;
11316             }
11317             this.data.clear();
11318             this.data.addAll(r);
11319             this.totalLength = t;
11320             this.applySort();
11321             this.fireEvent("datachanged", this);
11322         }else{
11323             this.totalLength = Math.max(t, this.data.length+r.length);
11324             this.add(r);
11325         }
11326         
11327         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11328                 
11329             var e = new Roo.data.Record({});
11330
11331             e.set(this.parent.displayField, this.parent.emptyTitle);
11332             e.set(this.parent.valueField, '');
11333
11334             this.insert(0, e);
11335         }
11336             
11337         this.fireEvent("load", this, r, options, o);
11338         if(options.callback){
11339             options.callback.call(options.scope || this, r, options, true);
11340         }
11341     },
11342
11343
11344     /**
11345      * Loads data from a passed data block. A Reader which understands the format of the data
11346      * must have been configured in the constructor.
11347      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11348      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11349      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11350      */
11351     loadData : function(o, append){
11352         var r = this.reader.readRecords(o);
11353         this.loadRecords(r, {add: append}, true);
11354     },
11355
11356     /**
11357      * Gets the number of cached records.
11358      * <p>
11359      * <em>If using paging, this may not be the total size of the dataset. If the data object
11360      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11361      * the data set size</em>
11362      */
11363     getCount : function(){
11364         return this.data.length || 0;
11365     },
11366
11367     /**
11368      * Gets the total number of records in the dataset as returned by the server.
11369      * <p>
11370      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11371      * the dataset size</em>
11372      */
11373     getTotalCount : function(){
11374         return this.totalLength || 0;
11375     },
11376
11377     /**
11378      * Returns the sort state of the Store as an object with two properties:
11379      * <pre><code>
11380  field {String} The name of the field by which the Records are sorted
11381  direction {String} The sort order, "ASC" or "DESC"
11382      * </code></pre>
11383      */
11384     getSortState : function(){
11385         return this.sortInfo;
11386     },
11387
11388     // private
11389     applySort : function(){
11390         if(this.sortInfo && !this.remoteSort){
11391             var s = this.sortInfo, f = s.field;
11392             var st = this.fields.get(f).sortType;
11393             var fn = function(r1, r2){
11394                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11395                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11396             };
11397             this.data.sort(s.direction, fn);
11398             if(this.snapshot && this.snapshot != this.data){
11399                 this.snapshot.sort(s.direction, fn);
11400             }
11401         }
11402     },
11403
11404     /**
11405      * Sets the default sort column and order to be used by the next load operation.
11406      * @param {String} fieldName The name of the field to sort by.
11407      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11408      */
11409     setDefaultSort : function(field, dir){
11410         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11411     },
11412
11413     /**
11414      * Sort the Records.
11415      * If remote sorting is used, the sort is performed on the server, and the cache is
11416      * reloaded. If local sorting is used, the cache is sorted internally.
11417      * @param {String} fieldName The name of the field to sort by.
11418      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11419      */
11420     sort : function(fieldName, dir){
11421         var f = this.fields.get(fieldName);
11422         if(!dir){
11423             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11424             
11425             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11426                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11427             }else{
11428                 dir = f.sortDir;
11429             }
11430         }
11431         this.sortToggle[f.name] = dir;
11432         this.sortInfo = {field: f.name, direction: dir};
11433         if(!this.remoteSort){
11434             this.applySort();
11435             this.fireEvent("datachanged", this);
11436         }else{
11437             this.load(this.lastOptions);
11438         }
11439     },
11440
11441     /**
11442      * Calls the specified function for each of the Records in the cache.
11443      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11444      * Returning <em>false</em> aborts and exits the iteration.
11445      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11446      */
11447     each : function(fn, scope){
11448         this.data.each(fn, scope);
11449     },
11450
11451     /**
11452      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11453      * (e.g., during paging).
11454      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11455      */
11456     getModifiedRecords : function(){
11457         return this.modified;
11458     },
11459
11460     // private
11461     createFilterFn : function(property, value, anyMatch){
11462         if(!value.exec){ // not a regex
11463             value = String(value);
11464             if(value.length == 0){
11465                 return false;
11466             }
11467             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11468         }
11469         return function(r){
11470             return value.test(r.data[property]);
11471         };
11472     },
11473
11474     /**
11475      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11476      * @param {String} property A field on your records
11477      * @param {Number} start The record index to start at (defaults to 0)
11478      * @param {Number} end The last record index to include (defaults to length - 1)
11479      * @return {Number} The sum
11480      */
11481     sum : function(property, start, end){
11482         var rs = this.data.items, v = 0;
11483         start = start || 0;
11484         end = (end || end === 0) ? end : rs.length-1;
11485
11486         for(var i = start; i <= end; i++){
11487             v += (rs[i].data[property] || 0);
11488         }
11489         return v;
11490     },
11491
11492     /**
11493      * Filter the records by a specified property.
11494      * @param {String} field A field on your records
11495      * @param {String/RegExp} value Either a string that the field
11496      * should start with or a RegExp to test against the field
11497      * @param {Boolean} anyMatch True to match any part not just the beginning
11498      */
11499     filter : function(property, value, anyMatch){
11500         var fn = this.createFilterFn(property, value, anyMatch);
11501         return fn ? this.filterBy(fn) : this.clearFilter();
11502     },
11503
11504     /**
11505      * Filter by a function. The specified function will be called with each
11506      * record in this data source. If the function returns true the record is included,
11507      * otherwise it is filtered.
11508      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11509      * @param {Object} scope (optional) The scope of the function (defaults to this)
11510      */
11511     filterBy : function(fn, scope){
11512         this.snapshot = this.snapshot || this.data;
11513         this.data = this.queryBy(fn, scope||this);
11514         this.fireEvent("datachanged", this);
11515     },
11516
11517     /**
11518      * Query the records by a specified property.
11519      * @param {String} field A field on your records
11520      * @param {String/RegExp} value Either a string that the field
11521      * should start with or a RegExp to test against the field
11522      * @param {Boolean} anyMatch True to match any part not just the beginning
11523      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11524      */
11525     query : function(property, value, anyMatch){
11526         var fn = this.createFilterFn(property, value, anyMatch);
11527         return fn ? this.queryBy(fn) : this.data.clone();
11528     },
11529
11530     /**
11531      * Query by a function. The specified function will be called with each
11532      * record in this data source. If the function returns true the record is included
11533      * in the results.
11534      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11535      * @param {Object} scope (optional) The scope of the function (defaults to this)
11536       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11537      **/
11538     queryBy : function(fn, scope){
11539         var data = this.snapshot || this.data;
11540         return data.filterBy(fn, scope||this);
11541     },
11542
11543     /**
11544      * Collects unique values for a particular dataIndex from this store.
11545      * @param {String} dataIndex The property to collect
11546      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11547      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11548      * @return {Array} An array of the unique values
11549      **/
11550     collect : function(dataIndex, allowNull, bypassFilter){
11551         var d = (bypassFilter === true && this.snapshot) ?
11552                 this.snapshot.items : this.data.items;
11553         var v, sv, r = [], l = {};
11554         for(var i = 0, len = d.length; i < len; i++){
11555             v = d[i].data[dataIndex];
11556             sv = String(v);
11557             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11558                 l[sv] = true;
11559                 r[r.length] = v;
11560             }
11561         }
11562         return r;
11563     },
11564
11565     /**
11566      * Revert to a view of the Record cache with no filtering applied.
11567      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11568      */
11569     clearFilter : function(suppressEvent){
11570         if(this.snapshot && this.snapshot != this.data){
11571             this.data = this.snapshot;
11572             delete this.snapshot;
11573             if(suppressEvent !== true){
11574                 this.fireEvent("datachanged", this);
11575             }
11576         }
11577     },
11578
11579     // private
11580     afterEdit : function(record){
11581         if(this.modified.indexOf(record) == -1){
11582             this.modified.push(record);
11583         }
11584         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11585     },
11586     
11587     // private
11588     afterReject : function(record){
11589         this.modified.remove(record);
11590         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11591     },
11592
11593     // private
11594     afterCommit : function(record){
11595         this.modified.remove(record);
11596         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11597     },
11598
11599     /**
11600      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11601      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11602      */
11603     commitChanges : function(){
11604         var m = this.modified.slice(0);
11605         this.modified = [];
11606         for(var i = 0, len = m.length; i < len; i++){
11607             m[i].commit();
11608         }
11609     },
11610
11611     /**
11612      * Cancel outstanding changes on all changed records.
11613      */
11614     rejectChanges : function(){
11615         var m = this.modified.slice(0);
11616         this.modified = [];
11617         for(var i = 0, len = m.length; i < len; i++){
11618             m[i].reject();
11619         }
11620     },
11621
11622     onMetaChange : function(meta, rtype, o){
11623         this.recordType = rtype;
11624         this.fields = rtype.prototype.fields;
11625         delete this.snapshot;
11626         this.sortInfo = meta.sortInfo || this.sortInfo;
11627         this.modified = [];
11628         this.fireEvent('metachange', this, this.reader.meta);
11629     },
11630     
11631     moveIndex : function(data, type)
11632     {
11633         var index = this.indexOf(data);
11634         
11635         var newIndex = index + type;
11636         
11637         this.remove(data);
11638         
11639         this.insert(newIndex, data);
11640         
11641     }
11642 });/*
11643  * Based on:
11644  * Ext JS Library 1.1.1
11645  * Copyright(c) 2006-2007, Ext JS, LLC.
11646  *
11647  * Originally Released Under LGPL - original licence link has changed is not relivant.
11648  *
11649  * Fork - LGPL
11650  * <script type="text/javascript">
11651  */
11652
11653 /**
11654  * @class Roo.data.SimpleStore
11655  * @extends Roo.data.Store
11656  * Small helper class to make creating Stores from Array data easier.
11657  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11658  * @cfg {Array} fields An array of field definition objects, or field name strings.
11659  * @cfg {Array} data The multi-dimensional array of data
11660  * @constructor
11661  * @param {Object} config
11662  */
11663 Roo.data.SimpleStore = function(config){
11664     Roo.data.SimpleStore.superclass.constructor.call(this, {
11665         isLocal : true,
11666         reader: new Roo.data.ArrayReader({
11667                 id: config.id
11668             },
11669             Roo.data.Record.create(config.fields)
11670         ),
11671         proxy : new Roo.data.MemoryProxy(config.data)
11672     });
11673     this.load();
11674 };
11675 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11676  * Based on:
11677  * Ext JS Library 1.1.1
11678  * Copyright(c) 2006-2007, Ext JS, LLC.
11679  *
11680  * Originally Released Under LGPL - original licence link has changed is not relivant.
11681  *
11682  * Fork - LGPL
11683  * <script type="text/javascript">
11684  */
11685
11686 /**
11687 /**
11688  * @extends Roo.data.Store
11689  * @class Roo.data.JsonStore
11690  * Small helper class to make creating Stores for JSON data easier. <br/>
11691 <pre><code>
11692 var store = new Roo.data.JsonStore({
11693     url: 'get-images.php',
11694     root: 'images',
11695     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11696 });
11697 </code></pre>
11698  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11699  * JsonReader and HttpProxy (unless inline data is provided).</b>
11700  * @cfg {Array} fields An array of field definition objects, or field name strings.
11701  * @constructor
11702  * @param {Object} config
11703  */
11704 Roo.data.JsonStore = function(c){
11705     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11706         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11707         reader: new Roo.data.JsonReader(c, c.fields)
11708     }));
11709 };
11710 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11711  * Based on:
11712  * Ext JS Library 1.1.1
11713  * Copyright(c) 2006-2007, Ext JS, LLC.
11714  *
11715  * Originally Released Under LGPL - original licence link has changed is not relivant.
11716  *
11717  * Fork - LGPL
11718  * <script type="text/javascript">
11719  */
11720
11721  
11722 Roo.data.Field = function(config){
11723     if(typeof config == "string"){
11724         config = {name: config};
11725     }
11726     Roo.apply(this, config);
11727     
11728     if(!this.type){
11729         this.type = "auto";
11730     }
11731     
11732     var st = Roo.data.SortTypes;
11733     // named sortTypes are supported, here we look them up
11734     if(typeof this.sortType == "string"){
11735         this.sortType = st[this.sortType];
11736     }
11737     
11738     // set default sortType for strings and dates
11739     if(!this.sortType){
11740         switch(this.type){
11741             case "string":
11742                 this.sortType = st.asUCString;
11743                 break;
11744             case "date":
11745                 this.sortType = st.asDate;
11746                 break;
11747             default:
11748                 this.sortType = st.none;
11749         }
11750     }
11751
11752     // define once
11753     var stripRe = /[\$,%]/g;
11754
11755     // prebuilt conversion function for this field, instead of
11756     // switching every time we're reading a value
11757     if(!this.convert){
11758         var cv, dateFormat = this.dateFormat;
11759         switch(this.type){
11760             case "":
11761             case "auto":
11762             case undefined:
11763                 cv = function(v){ return v; };
11764                 break;
11765             case "string":
11766                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11767                 break;
11768             case "int":
11769                 cv = function(v){
11770                     return v !== undefined && v !== null && v !== '' ?
11771                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11772                     };
11773                 break;
11774             case "float":
11775                 cv = function(v){
11776                     return v !== undefined && v !== null && v !== '' ?
11777                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11778                     };
11779                 break;
11780             case "bool":
11781             case "boolean":
11782                 cv = function(v){ return v === true || v === "true" || v == 1; };
11783                 break;
11784             case "date":
11785                 cv = function(v){
11786                     if(!v){
11787                         return '';
11788                     }
11789                     if(v instanceof Date){
11790                         return v;
11791                     }
11792                     if(dateFormat){
11793                         if(dateFormat == "timestamp"){
11794                             return new Date(v*1000);
11795                         }
11796                         return Date.parseDate(v, dateFormat);
11797                     }
11798                     var parsed = Date.parse(v);
11799                     return parsed ? new Date(parsed) : null;
11800                 };
11801              break;
11802             
11803         }
11804         this.convert = cv;
11805     }
11806 };
11807
11808 Roo.data.Field.prototype = {
11809     dateFormat: null,
11810     defaultValue: "",
11811     mapping: null,
11812     sortType : null,
11813     sortDir : "ASC"
11814 };/*
11815  * Based on:
11816  * Ext JS Library 1.1.1
11817  * Copyright(c) 2006-2007, Ext JS, LLC.
11818  *
11819  * Originally Released Under LGPL - original licence link has changed is not relivant.
11820  *
11821  * Fork - LGPL
11822  * <script type="text/javascript">
11823  */
11824  
11825 // Base class for reading structured data from a data source.  This class is intended to be
11826 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11827
11828 /**
11829  * @class Roo.data.DataReader
11830  * Base class for reading structured data from a data source.  This class is intended to be
11831  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11832  */
11833
11834 Roo.data.DataReader = function(meta, recordType){
11835     
11836     this.meta = meta;
11837     
11838     this.recordType = recordType instanceof Array ? 
11839         Roo.data.Record.create(recordType) : recordType;
11840 };
11841
11842 Roo.data.DataReader.prototype = {
11843      /**
11844      * Create an empty record
11845      * @param {Object} data (optional) - overlay some values
11846      * @return {Roo.data.Record} record created.
11847      */
11848     newRow :  function(d) {
11849         var da =  {};
11850         this.recordType.prototype.fields.each(function(c) {
11851             switch( c.type) {
11852                 case 'int' : da[c.name] = 0; break;
11853                 case 'date' : da[c.name] = new Date(); break;
11854                 case 'float' : da[c.name] = 0.0; break;
11855                 case 'boolean' : da[c.name] = false; break;
11856                 default : da[c.name] = ""; break;
11857             }
11858             
11859         });
11860         return new this.recordType(Roo.apply(da, d));
11861     }
11862     
11863 };/*
11864  * Based on:
11865  * Ext JS Library 1.1.1
11866  * Copyright(c) 2006-2007, Ext JS, LLC.
11867  *
11868  * Originally Released Under LGPL - original licence link has changed is not relivant.
11869  *
11870  * Fork - LGPL
11871  * <script type="text/javascript">
11872  */
11873
11874 /**
11875  * @class Roo.data.DataProxy
11876  * @extends Roo.data.Observable
11877  * This class is an abstract base class for implementations which provide retrieval of
11878  * unformatted data objects.<br>
11879  * <p>
11880  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11881  * (of the appropriate type which knows how to parse the data object) to provide a block of
11882  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11883  * <p>
11884  * Custom implementations must implement the load method as described in
11885  * {@link Roo.data.HttpProxy#load}.
11886  */
11887 Roo.data.DataProxy = function(){
11888     this.addEvents({
11889         /**
11890          * @event beforeload
11891          * Fires before a network request is made to retrieve a data object.
11892          * @param {Object} This DataProxy object.
11893          * @param {Object} params The params parameter to the load function.
11894          */
11895         beforeload : true,
11896         /**
11897          * @event load
11898          * Fires before the load method's callback is called.
11899          * @param {Object} This DataProxy object.
11900          * @param {Object} o The data object.
11901          * @param {Object} arg The callback argument object passed to the load function.
11902          */
11903         load : true,
11904         /**
11905          * @event loadexception
11906          * Fires if an Exception occurs during data retrieval.
11907          * @param {Object} This DataProxy object.
11908          * @param {Object} o The data object.
11909          * @param {Object} arg The callback argument object passed to the load function.
11910          * @param {Object} e The Exception.
11911          */
11912         loadexception : true
11913     });
11914     Roo.data.DataProxy.superclass.constructor.call(this);
11915 };
11916
11917 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11918
11919     /**
11920      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11921      */
11922 /*
11923  * Based on:
11924  * Ext JS Library 1.1.1
11925  * Copyright(c) 2006-2007, Ext JS, LLC.
11926  *
11927  * Originally Released Under LGPL - original licence link has changed is not relivant.
11928  *
11929  * Fork - LGPL
11930  * <script type="text/javascript">
11931  */
11932 /**
11933  * @class Roo.data.MemoryProxy
11934  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11935  * to the Reader when its load method is called.
11936  * @constructor
11937  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11938  */
11939 Roo.data.MemoryProxy = function(data){
11940     if (data.data) {
11941         data = data.data;
11942     }
11943     Roo.data.MemoryProxy.superclass.constructor.call(this);
11944     this.data = data;
11945 };
11946
11947 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11948     
11949     /**
11950      * Load data from the requested source (in this case an in-memory
11951      * data object passed to the constructor), read the data object into
11952      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11953      * process that block using the passed callback.
11954      * @param {Object} params This parameter is not used by the MemoryProxy class.
11955      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11956      * object into a block of Roo.data.Records.
11957      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11958      * The function must be passed <ul>
11959      * <li>The Record block object</li>
11960      * <li>The "arg" argument from the load function</li>
11961      * <li>A boolean success indicator</li>
11962      * </ul>
11963      * @param {Object} scope The scope in which to call the callback
11964      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11965      */
11966     load : function(params, reader, callback, scope, arg){
11967         params = params || {};
11968         var result;
11969         try {
11970             result = reader.readRecords(this.data);
11971         }catch(e){
11972             this.fireEvent("loadexception", this, arg, null, e);
11973             callback.call(scope, null, arg, false);
11974             return;
11975         }
11976         callback.call(scope, result, arg, true);
11977     },
11978     
11979     // private
11980     update : function(params, records){
11981         
11982     }
11983 });/*
11984  * Based on:
11985  * Ext JS Library 1.1.1
11986  * Copyright(c) 2006-2007, Ext JS, LLC.
11987  *
11988  * Originally Released Under LGPL - original licence link has changed is not relivant.
11989  *
11990  * Fork - LGPL
11991  * <script type="text/javascript">
11992  */
11993 /**
11994  * @class Roo.data.HttpProxy
11995  * @extends Roo.data.DataProxy
11996  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11997  * configured to reference a certain URL.<br><br>
11998  * <p>
11999  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12000  * from which the running page was served.<br><br>
12001  * <p>
12002  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12003  * <p>
12004  * Be aware that to enable the browser to parse an XML document, the server must set
12005  * the Content-Type header in the HTTP response to "text/xml".
12006  * @constructor
12007  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12008  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12009  * will be used to make the request.
12010  */
12011 Roo.data.HttpProxy = function(conn){
12012     Roo.data.HttpProxy.superclass.constructor.call(this);
12013     // is conn a conn config or a real conn?
12014     this.conn = conn;
12015     this.useAjax = !conn || !conn.events;
12016   
12017 };
12018
12019 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12020     // thse are take from connection...
12021     
12022     /**
12023      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12024      */
12025     /**
12026      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12027      * extra parameters to each request made by this object. (defaults to undefined)
12028      */
12029     /**
12030      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12031      *  to each request made by this object. (defaults to undefined)
12032      */
12033     /**
12034      * @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)
12035      */
12036     /**
12037      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12038      */
12039      /**
12040      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12041      * @type Boolean
12042      */
12043   
12044
12045     /**
12046      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12047      * @type Boolean
12048      */
12049     /**
12050      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12051      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12052      * a finer-grained basis than the DataProxy events.
12053      */
12054     getConnection : function(){
12055         return this.useAjax ? Roo.Ajax : this.conn;
12056     },
12057
12058     /**
12059      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12060      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12061      * process that block using the passed callback.
12062      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12063      * for the request to the remote server.
12064      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12065      * object into a block of Roo.data.Records.
12066      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12067      * The function must be passed <ul>
12068      * <li>The Record block object</li>
12069      * <li>The "arg" argument from the load function</li>
12070      * <li>A boolean success indicator</li>
12071      * </ul>
12072      * @param {Object} scope The scope in which to call the callback
12073      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12074      */
12075     load : function(params, reader, callback, scope, arg){
12076         if(this.fireEvent("beforeload", this, params) !== false){
12077             var  o = {
12078                 params : params || {},
12079                 request: {
12080                     callback : callback,
12081                     scope : scope,
12082                     arg : arg
12083                 },
12084                 reader: reader,
12085                 callback : this.loadResponse,
12086                 scope: this
12087             };
12088             if(this.useAjax){
12089                 Roo.applyIf(o, this.conn);
12090                 if(this.activeRequest){
12091                     Roo.Ajax.abort(this.activeRequest);
12092                 }
12093                 this.activeRequest = Roo.Ajax.request(o);
12094             }else{
12095                 this.conn.request(o);
12096             }
12097         }else{
12098             callback.call(scope||this, null, arg, false);
12099         }
12100     },
12101
12102     // private
12103     loadResponse : function(o, success, response){
12104         delete this.activeRequest;
12105         if(!success){
12106             this.fireEvent("loadexception", this, o, response);
12107             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12108             return;
12109         }
12110         var result;
12111         try {
12112             result = o.reader.read(response);
12113         }catch(e){
12114             this.fireEvent("loadexception", this, o, response, e);
12115             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12116             return;
12117         }
12118         
12119         this.fireEvent("load", this, o, o.request.arg);
12120         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12121     },
12122
12123     // private
12124     update : function(dataSet){
12125
12126     },
12127
12128     // private
12129     updateResponse : function(dataSet){
12130
12131     }
12132 });/*
12133  * Based on:
12134  * Ext JS Library 1.1.1
12135  * Copyright(c) 2006-2007, Ext JS, LLC.
12136  *
12137  * Originally Released Under LGPL - original licence link has changed is not relivant.
12138  *
12139  * Fork - LGPL
12140  * <script type="text/javascript">
12141  */
12142
12143 /**
12144  * @class Roo.data.ScriptTagProxy
12145  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12146  * other than the originating domain of the running page.<br><br>
12147  * <p>
12148  * <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
12149  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12150  * <p>
12151  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12152  * source code that is used as the source inside a &lt;script> tag.<br><br>
12153  * <p>
12154  * In order for the browser to process the returned data, the server must wrap the data object
12155  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12156  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12157  * depending on whether the callback name was passed:
12158  * <p>
12159  * <pre><code>
12160 boolean scriptTag = false;
12161 String cb = request.getParameter("callback");
12162 if (cb != null) {
12163     scriptTag = true;
12164     response.setContentType("text/javascript");
12165 } else {
12166     response.setContentType("application/x-json");
12167 }
12168 Writer out = response.getWriter();
12169 if (scriptTag) {
12170     out.write(cb + "(");
12171 }
12172 out.print(dataBlock.toJsonString());
12173 if (scriptTag) {
12174     out.write(");");
12175 }
12176 </pre></code>
12177  *
12178  * @constructor
12179  * @param {Object} config A configuration object.
12180  */
12181 Roo.data.ScriptTagProxy = function(config){
12182     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12183     Roo.apply(this, config);
12184     this.head = document.getElementsByTagName("head")[0];
12185 };
12186
12187 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12188
12189 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12190     /**
12191      * @cfg {String} url The URL from which to request the data object.
12192      */
12193     /**
12194      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12195      */
12196     timeout : 30000,
12197     /**
12198      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12199      * the server the name of the callback function set up by the load call to process the returned data object.
12200      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12201      * javascript output which calls this named function passing the data object as its only parameter.
12202      */
12203     callbackParam : "callback",
12204     /**
12205      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12206      * name to the request.
12207      */
12208     nocache : true,
12209
12210     /**
12211      * Load data from the configured URL, read the data object into
12212      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12213      * process that block using the passed callback.
12214      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12215      * for the request to the remote server.
12216      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12217      * object into a block of Roo.data.Records.
12218      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12219      * The function must be passed <ul>
12220      * <li>The Record block object</li>
12221      * <li>The "arg" argument from the load function</li>
12222      * <li>A boolean success indicator</li>
12223      * </ul>
12224      * @param {Object} scope The scope in which to call the callback
12225      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12226      */
12227     load : function(params, reader, callback, scope, arg){
12228         if(this.fireEvent("beforeload", this, params) !== false){
12229
12230             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12231
12232             var url = this.url;
12233             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12234             if(this.nocache){
12235                 url += "&_dc=" + (new Date().getTime());
12236             }
12237             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12238             var trans = {
12239                 id : transId,
12240                 cb : "stcCallback"+transId,
12241                 scriptId : "stcScript"+transId,
12242                 params : params,
12243                 arg : arg,
12244                 url : url,
12245                 callback : callback,
12246                 scope : scope,
12247                 reader : reader
12248             };
12249             var conn = this;
12250
12251             window[trans.cb] = function(o){
12252                 conn.handleResponse(o, trans);
12253             };
12254
12255             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12256
12257             if(this.autoAbort !== false){
12258                 this.abort();
12259             }
12260
12261             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12262
12263             var script = document.createElement("script");
12264             script.setAttribute("src", url);
12265             script.setAttribute("type", "text/javascript");
12266             script.setAttribute("id", trans.scriptId);
12267             this.head.appendChild(script);
12268
12269             this.trans = trans;
12270         }else{
12271             callback.call(scope||this, null, arg, false);
12272         }
12273     },
12274
12275     // private
12276     isLoading : function(){
12277         return this.trans ? true : false;
12278     },
12279
12280     /**
12281      * Abort the current server request.
12282      */
12283     abort : function(){
12284         if(this.isLoading()){
12285             this.destroyTrans(this.trans);
12286         }
12287     },
12288
12289     // private
12290     destroyTrans : function(trans, isLoaded){
12291         this.head.removeChild(document.getElementById(trans.scriptId));
12292         clearTimeout(trans.timeoutId);
12293         if(isLoaded){
12294             window[trans.cb] = undefined;
12295             try{
12296                 delete window[trans.cb];
12297             }catch(e){}
12298         }else{
12299             // if hasn't been loaded, wait for load to remove it to prevent script error
12300             window[trans.cb] = function(){
12301                 window[trans.cb] = undefined;
12302                 try{
12303                     delete window[trans.cb];
12304                 }catch(e){}
12305             };
12306         }
12307     },
12308
12309     // private
12310     handleResponse : function(o, trans){
12311         this.trans = false;
12312         this.destroyTrans(trans, true);
12313         var result;
12314         try {
12315             result = trans.reader.readRecords(o);
12316         }catch(e){
12317             this.fireEvent("loadexception", this, o, trans.arg, e);
12318             trans.callback.call(trans.scope||window, null, trans.arg, false);
12319             return;
12320         }
12321         this.fireEvent("load", this, o, trans.arg);
12322         trans.callback.call(trans.scope||window, result, trans.arg, true);
12323     },
12324
12325     // private
12326     handleFailure : function(trans){
12327         this.trans = false;
12328         this.destroyTrans(trans, false);
12329         this.fireEvent("loadexception", this, null, trans.arg);
12330         trans.callback.call(trans.scope||window, null, trans.arg, false);
12331     }
12332 });/*
12333  * Based on:
12334  * Ext JS Library 1.1.1
12335  * Copyright(c) 2006-2007, Ext JS, LLC.
12336  *
12337  * Originally Released Under LGPL - original licence link has changed is not relivant.
12338  *
12339  * Fork - LGPL
12340  * <script type="text/javascript">
12341  */
12342
12343 /**
12344  * @class Roo.data.JsonReader
12345  * @extends Roo.data.DataReader
12346  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12347  * based on mappings in a provided Roo.data.Record constructor.
12348  * 
12349  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12350  * in the reply previously. 
12351  * 
12352  * <p>
12353  * Example code:
12354  * <pre><code>
12355 var RecordDef = Roo.data.Record.create([
12356     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12357     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12358 ]);
12359 var myReader = new Roo.data.JsonReader({
12360     totalProperty: "results",    // The property which contains the total dataset size (optional)
12361     root: "rows",                // The property which contains an Array of row objects
12362     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12363 }, RecordDef);
12364 </code></pre>
12365  * <p>
12366  * This would consume a JSON file like this:
12367  * <pre><code>
12368 { 'results': 2, 'rows': [
12369     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12370     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12371 }
12372 </code></pre>
12373  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12374  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12375  * paged from the remote server.
12376  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12377  * @cfg {String} root name of the property which contains the Array of row objects.
12378  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12379  * @cfg {Array} fields Array of field definition objects
12380  * @constructor
12381  * Create a new JsonReader
12382  * @param {Object} meta Metadata configuration options
12383  * @param {Object} recordType Either an Array of field definition objects,
12384  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12385  */
12386 Roo.data.JsonReader = function(meta, recordType){
12387     
12388     meta = meta || {};
12389     // set some defaults:
12390     Roo.applyIf(meta, {
12391         totalProperty: 'total',
12392         successProperty : 'success',
12393         root : 'data',
12394         id : 'id'
12395     });
12396     
12397     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12398 };
12399 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12400     
12401     /**
12402      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12403      * Used by Store query builder to append _requestMeta to params.
12404      * 
12405      */
12406     metaFromRemote : false,
12407     /**
12408      * This method is only used by a DataProxy which has retrieved data from a remote server.
12409      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12410      * @return {Object} data A data block which is used by an Roo.data.Store object as
12411      * a cache of Roo.data.Records.
12412      */
12413     read : function(response){
12414         var json = response.responseText;
12415        
12416         var o = /* eval:var:o */ eval("("+json+")");
12417         if(!o) {
12418             throw {message: "JsonReader.read: Json object not found"};
12419         }
12420         
12421         if(o.metaData){
12422             
12423             delete this.ef;
12424             this.metaFromRemote = true;
12425             this.meta = o.metaData;
12426             this.recordType = Roo.data.Record.create(o.metaData.fields);
12427             this.onMetaChange(this.meta, this.recordType, o);
12428         }
12429         return this.readRecords(o);
12430     },
12431
12432     // private function a store will implement
12433     onMetaChange : function(meta, recordType, o){
12434
12435     },
12436
12437     /**
12438          * @ignore
12439          */
12440     simpleAccess: function(obj, subsc) {
12441         return obj[subsc];
12442     },
12443
12444         /**
12445          * @ignore
12446          */
12447     getJsonAccessor: function(){
12448         var re = /[\[\.]/;
12449         return function(expr) {
12450             try {
12451                 return(re.test(expr))
12452                     ? new Function("obj", "return obj." + expr)
12453                     : function(obj){
12454                         return obj[expr];
12455                     };
12456             } catch(e){}
12457             return Roo.emptyFn;
12458         };
12459     }(),
12460
12461     /**
12462      * Create a data block containing Roo.data.Records from an XML document.
12463      * @param {Object} o An object which contains an Array of row objects in the property specified
12464      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12465      * which contains the total size of the dataset.
12466      * @return {Object} data A data block which is used by an Roo.data.Store object as
12467      * a cache of Roo.data.Records.
12468      */
12469     readRecords : function(o){
12470         /**
12471          * After any data loads, the raw JSON data is available for further custom processing.
12472          * @type Object
12473          */
12474         this.o = o;
12475         var s = this.meta, Record = this.recordType,
12476             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12477
12478 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12479         if (!this.ef) {
12480             if(s.totalProperty) {
12481                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12482                 }
12483                 if(s.successProperty) {
12484                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12485                 }
12486                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12487                 if (s.id) {
12488                         var g = this.getJsonAccessor(s.id);
12489                         this.getId = function(rec) {
12490                                 var r = g(rec);  
12491                                 return (r === undefined || r === "") ? null : r;
12492                         };
12493                 } else {
12494                         this.getId = function(){return null;};
12495                 }
12496             this.ef = [];
12497             for(var jj = 0; jj < fl; jj++){
12498                 f = fi[jj];
12499                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12500                 this.ef[jj] = this.getJsonAccessor(map);
12501             }
12502         }
12503
12504         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12505         if(s.totalProperty){
12506             var vt = parseInt(this.getTotal(o), 10);
12507             if(!isNaN(vt)){
12508                 totalRecords = vt;
12509             }
12510         }
12511         if(s.successProperty){
12512             var vs = this.getSuccess(o);
12513             if(vs === false || vs === 'false'){
12514                 success = false;
12515             }
12516         }
12517         var records = [];
12518         for(var i = 0; i < c; i++){
12519                 var n = root[i];
12520             var values = {};
12521             var id = this.getId(n);
12522             for(var j = 0; j < fl; j++){
12523                 f = fi[j];
12524             var v = this.ef[j](n);
12525             if (!f.convert) {
12526                 Roo.log('missing convert for ' + f.name);
12527                 Roo.log(f);
12528                 continue;
12529             }
12530             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12531             }
12532             var record = new Record(values, id);
12533             record.json = n;
12534             records[i] = record;
12535         }
12536         return {
12537             raw : o,
12538             success : success,
12539             records : records,
12540             totalRecords : totalRecords
12541         };
12542     }
12543 });/*
12544  * Based on:
12545  * Ext JS Library 1.1.1
12546  * Copyright(c) 2006-2007, Ext JS, LLC.
12547  *
12548  * Originally Released Under LGPL - original licence link has changed is not relivant.
12549  *
12550  * Fork - LGPL
12551  * <script type="text/javascript">
12552  */
12553
12554 /**
12555  * @class Roo.data.ArrayReader
12556  * @extends Roo.data.DataReader
12557  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12558  * Each element of that Array represents a row of data fields. The
12559  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12560  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12561  * <p>
12562  * Example code:.
12563  * <pre><code>
12564 var RecordDef = Roo.data.Record.create([
12565     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12566     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12567 ]);
12568 var myReader = new Roo.data.ArrayReader({
12569     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12570 }, RecordDef);
12571 </code></pre>
12572  * <p>
12573  * This would consume an Array like this:
12574  * <pre><code>
12575 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12576   </code></pre>
12577  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12578  * @constructor
12579  * Create a new JsonReader
12580  * @param {Object} meta Metadata configuration options.
12581  * @param {Object} recordType Either an Array of field definition objects
12582  * as specified to {@link Roo.data.Record#create},
12583  * or an {@link Roo.data.Record} object
12584  * created using {@link Roo.data.Record#create}.
12585  */
12586 Roo.data.ArrayReader = function(meta, recordType){
12587     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12588 };
12589
12590 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12591     /**
12592      * Create a data block containing Roo.data.Records from an XML document.
12593      * @param {Object} o An Array of row objects which represents the dataset.
12594      * @return {Object} data A data block which is used by an Roo.data.Store object as
12595      * a cache of Roo.data.Records.
12596      */
12597     readRecords : function(o){
12598         var sid = this.meta ? this.meta.id : null;
12599         var recordType = this.recordType, fields = recordType.prototype.fields;
12600         var records = [];
12601         var root = o;
12602             for(var i = 0; i < root.length; i++){
12603                     var n = root[i];
12604                 var values = {};
12605                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12606                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12607                 var f = fields.items[j];
12608                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12609                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12610                 v = f.convert(v);
12611                 values[f.name] = v;
12612             }
12613                 var record = new recordType(values, id);
12614                 record.json = n;
12615                 records[records.length] = record;
12616             }
12617             return {
12618                 records : records,
12619                 totalRecords : records.length
12620             };
12621     }
12622 });/*
12623  * - LGPL
12624  * * 
12625  */
12626
12627 /**
12628  * @class Roo.bootstrap.ComboBox
12629  * @extends Roo.bootstrap.TriggerField
12630  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12631  * @cfg {Boolean} append (true|false) default false
12632  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12633  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12634  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12635  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12636  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12637  * @cfg {Boolean} animate default true
12638  * @cfg {Boolean} emptyResultText only for touch device
12639  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12640  * @cfg {String} emptyTitle default ''
12641  * @constructor
12642  * Create a new ComboBox.
12643  * @param {Object} config Configuration options
12644  */
12645 Roo.bootstrap.ComboBox = function(config){
12646     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12647     this.addEvents({
12648         /**
12649          * @event expand
12650          * Fires when the dropdown list is expanded
12651         * @param {Roo.bootstrap.ComboBox} combo This combo box
12652         */
12653         'expand' : true,
12654         /**
12655          * @event collapse
12656          * Fires when the dropdown list is collapsed
12657         * @param {Roo.bootstrap.ComboBox} combo This combo box
12658         */
12659         'collapse' : true,
12660         /**
12661          * @event beforeselect
12662          * Fires before a list item is selected. Return false to cancel the selection.
12663         * @param {Roo.bootstrap.ComboBox} combo This combo box
12664         * @param {Roo.data.Record} record The data record returned from the underlying store
12665         * @param {Number} index The index of the selected item in the dropdown list
12666         */
12667         'beforeselect' : true,
12668         /**
12669          * @event select
12670          * Fires when a list item is selected
12671         * @param {Roo.bootstrap.ComboBox} combo This combo box
12672         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12673         * @param {Number} index The index of the selected item in the dropdown list
12674         */
12675         'select' : true,
12676         /**
12677          * @event beforequery
12678          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12679          * The event object passed has these properties:
12680         * @param {Roo.bootstrap.ComboBox} combo This combo box
12681         * @param {String} query The query
12682         * @param {Boolean} forceAll true to force "all" query
12683         * @param {Boolean} cancel true to cancel the query
12684         * @param {Object} e The query event object
12685         */
12686         'beforequery': true,
12687          /**
12688          * @event add
12689          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12690         * @param {Roo.bootstrap.ComboBox} combo This combo box
12691         */
12692         'add' : true,
12693         /**
12694          * @event edit
12695          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12696         * @param {Roo.bootstrap.ComboBox} combo This combo box
12697         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12698         */
12699         'edit' : true,
12700         /**
12701          * @event remove
12702          * Fires when the remove value from the combobox array
12703         * @param {Roo.bootstrap.ComboBox} combo This combo box
12704         */
12705         'remove' : true,
12706         /**
12707          * @event afterremove
12708          * Fires when the remove value from the combobox array
12709         * @param {Roo.bootstrap.ComboBox} combo This combo box
12710         */
12711         'afterremove' : true,
12712         /**
12713          * @event specialfilter
12714          * Fires when specialfilter
12715             * @param {Roo.bootstrap.ComboBox} combo This combo box
12716             */
12717         'specialfilter' : true,
12718         /**
12719          * @event tick
12720          * Fires when tick the element
12721             * @param {Roo.bootstrap.ComboBox} combo This combo box
12722             */
12723         'tick' : true,
12724         /**
12725          * @event touchviewdisplay
12726          * Fires when touch view require special display (default is using displayField)
12727             * @param {Roo.bootstrap.ComboBox} combo This combo box
12728             * @param {Object} cfg set html .
12729             */
12730         'touchviewdisplay' : true
12731         
12732     });
12733     
12734     this.item = [];
12735     this.tickItems = [];
12736     
12737     this.selectedIndex = -1;
12738     if(this.mode == 'local'){
12739         if(config.queryDelay === undefined){
12740             this.queryDelay = 10;
12741         }
12742         if(config.minChars === undefined){
12743             this.minChars = 0;
12744         }
12745     }
12746 };
12747
12748 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12749      
12750     /**
12751      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12752      * rendering into an Roo.Editor, defaults to false)
12753      */
12754     /**
12755      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12756      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12757      */
12758     /**
12759      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12760      */
12761     /**
12762      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12763      * the dropdown list (defaults to undefined, with no header element)
12764      */
12765
12766      /**
12767      * @cfg {String/Roo.Template} tpl The template to use to render the output
12768      */
12769      
12770      /**
12771      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12772      */
12773     listWidth: undefined,
12774     /**
12775      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12776      * mode = 'remote' or 'text' if mode = 'local')
12777      */
12778     displayField: undefined,
12779     
12780     /**
12781      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12782      * mode = 'remote' or 'value' if mode = 'local'). 
12783      * Note: use of a valueField requires the user make a selection
12784      * in order for a value to be mapped.
12785      */
12786     valueField: undefined,
12787     /**
12788      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12789      */
12790     modalTitle : '',
12791     
12792     /**
12793      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12794      * field's data value (defaults to the underlying DOM element's name)
12795      */
12796     hiddenName: undefined,
12797     /**
12798      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12799      */
12800     listClass: '',
12801     /**
12802      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12803      */
12804     selectedClass: 'active',
12805     
12806     /**
12807      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12808      */
12809     shadow:'sides',
12810     /**
12811      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12812      * anchor positions (defaults to 'tl-bl')
12813      */
12814     listAlign: 'tl-bl?',
12815     /**
12816      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12817      */
12818     maxHeight: 300,
12819     /**
12820      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12821      * query specified by the allQuery config option (defaults to 'query')
12822      */
12823     triggerAction: 'query',
12824     /**
12825      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12826      * (defaults to 4, does not apply if editable = false)
12827      */
12828     minChars : 4,
12829     /**
12830      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12831      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12832      */
12833     typeAhead: false,
12834     /**
12835      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12836      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12837      */
12838     queryDelay: 500,
12839     /**
12840      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12841      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12842      */
12843     pageSize: 0,
12844     /**
12845      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12846      * when editable = true (defaults to false)
12847      */
12848     selectOnFocus:false,
12849     /**
12850      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12851      */
12852     queryParam: 'query',
12853     /**
12854      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12855      * when mode = 'remote' (defaults to 'Loading...')
12856      */
12857     loadingText: 'Loading...',
12858     /**
12859      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12860      */
12861     resizable: false,
12862     /**
12863      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12864      */
12865     handleHeight : 8,
12866     /**
12867      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12868      * traditional select (defaults to true)
12869      */
12870     editable: true,
12871     /**
12872      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12873      */
12874     allQuery: '',
12875     /**
12876      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12877      */
12878     mode: 'remote',
12879     /**
12880      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12881      * listWidth has a higher value)
12882      */
12883     minListWidth : 70,
12884     /**
12885      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12886      * allow the user to set arbitrary text into the field (defaults to false)
12887      */
12888     forceSelection:false,
12889     /**
12890      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12891      * if typeAhead = true (defaults to 250)
12892      */
12893     typeAheadDelay : 250,
12894     /**
12895      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12896      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12897      */
12898     valueNotFoundText : undefined,
12899     /**
12900      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12901      */
12902     blockFocus : false,
12903     
12904     /**
12905      * @cfg {Boolean} disableClear Disable showing of clear button.
12906      */
12907     disableClear : false,
12908     /**
12909      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12910      */
12911     alwaysQuery : false,
12912     
12913     /**
12914      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12915      */
12916     multiple : false,
12917     
12918     /**
12919      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12920      */
12921     invalidClass : "has-warning",
12922     
12923     /**
12924      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12925      */
12926     validClass : "has-success",
12927     
12928     /**
12929      * @cfg {Boolean} specialFilter (true|false) special filter default false
12930      */
12931     specialFilter : false,
12932     
12933     /**
12934      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12935      */
12936     mobileTouchView : true,
12937     
12938     /**
12939      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12940      */
12941     useNativeIOS : false,
12942     
12943     ios_options : false,
12944     
12945     //private
12946     addicon : false,
12947     editicon: false,
12948     
12949     page: 0,
12950     hasQuery: false,
12951     append: false,
12952     loadNext: false,
12953     autoFocus : true,
12954     tickable : false,
12955     btnPosition : 'right',
12956     triggerList : true,
12957     showToggleBtn : true,
12958     animate : true,
12959     emptyResultText: 'Empty',
12960     triggerText : 'Select',
12961     emptyTitle : '',
12962     
12963     // element that contains real text value.. (when hidden is used..)
12964     
12965     getAutoCreate : function()
12966     {   
12967         var cfg = false;
12968         //render
12969         /*
12970          * Render classic select for iso
12971          */
12972         
12973         if(Roo.isIOS && this.useNativeIOS){
12974             cfg = this.getAutoCreateNativeIOS();
12975             return cfg;
12976         }
12977         
12978         /*
12979          * Touch Devices
12980          */
12981         
12982         if(Roo.isTouch && this.mobileTouchView){
12983             cfg = this.getAutoCreateTouchView();
12984             return cfg;;
12985         }
12986         
12987         /*
12988          *  Normal ComboBox
12989          */
12990         if(!this.tickable){
12991             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12992             return cfg;
12993         }
12994         
12995         /*
12996          *  ComboBox with tickable selections
12997          */
12998              
12999         var align = this.labelAlign || this.parentLabelAlign();
13000         
13001         cfg = {
13002             cls : 'form-group roo-combobox-tickable' //input-group
13003         };
13004         
13005         var btn_text_select = '';
13006         var btn_text_done = '';
13007         var btn_text_cancel = '';
13008         
13009         if (this.btn_text_show) {
13010             btn_text_select = 'Select';
13011             btn_text_done = 'Done';
13012             btn_text_cancel = 'Cancel'; 
13013         }
13014         
13015         var buttons = {
13016             tag : 'div',
13017             cls : 'tickable-buttons',
13018             cn : [
13019                 {
13020                     tag : 'button',
13021                     type : 'button',
13022                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13023                     //html : this.triggerText
13024                     html: btn_text_select
13025                 },
13026                 {
13027                     tag : 'button',
13028                     type : 'button',
13029                     name : 'ok',
13030                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13031                     //html : 'Done'
13032                     html: btn_text_done
13033                 },
13034                 {
13035                     tag : 'button',
13036                     type : 'button',
13037                     name : 'cancel',
13038                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13039                     //html : 'Cancel'
13040                     html: btn_text_cancel
13041                 }
13042             ]
13043         };
13044         
13045         if(this.editable){
13046             buttons.cn.unshift({
13047                 tag: 'input',
13048                 cls: 'roo-select2-search-field-input'
13049             });
13050         }
13051         
13052         var _this = this;
13053         
13054         Roo.each(buttons.cn, function(c){
13055             if (_this.size) {
13056                 c.cls += ' btn-' + _this.size;
13057             }
13058
13059             if (_this.disabled) {
13060                 c.disabled = true;
13061             }
13062         });
13063         
13064         var box = {
13065             tag: 'div',
13066             cn: [
13067                 {
13068                     tag: 'input',
13069                     type : 'hidden',
13070                     cls: 'form-hidden-field'
13071                 },
13072                 {
13073                     tag: 'ul',
13074                     cls: 'roo-select2-choices',
13075                     cn:[
13076                         {
13077                             tag: 'li',
13078                             cls: 'roo-select2-search-field',
13079                             cn: [
13080                                 buttons
13081                             ]
13082                         }
13083                     ]
13084                 }
13085             ]
13086         };
13087         
13088         var combobox = {
13089             cls: 'roo-select2-container input-group roo-select2-container-multi',
13090             cn: [
13091                 box
13092 //                {
13093 //                    tag: 'ul',
13094 //                    cls: 'typeahead typeahead-long dropdown-menu',
13095 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13096 //                }
13097             ]
13098         };
13099         
13100         if(this.hasFeedback && !this.allowBlank){
13101             
13102             var feedback = {
13103                 tag: 'span',
13104                 cls: 'glyphicon form-control-feedback'
13105             };
13106
13107             combobox.cn.push(feedback);
13108         }
13109         
13110         
13111         if (align ==='left' && this.fieldLabel.length) {
13112             
13113             cfg.cls += ' roo-form-group-label-left';
13114             
13115             cfg.cn = [
13116                 {
13117                     tag : 'i',
13118                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13119                     tooltip : 'This field is required'
13120                 },
13121                 {
13122                     tag: 'label',
13123                     'for' :  id,
13124                     cls : 'control-label',
13125                     html : this.fieldLabel
13126
13127                 },
13128                 {
13129                     cls : "", 
13130                     cn: [
13131                         combobox
13132                     ]
13133                 }
13134
13135             ];
13136             
13137             var labelCfg = cfg.cn[1];
13138             var contentCfg = cfg.cn[2];
13139             
13140
13141             if(this.indicatorpos == 'right'){
13142                 
13143                 cfg.cn = [
13144                     {
13145                         tag: 'label',
13146                         'for' :  id,
13147                         cls : 'control-label',
13148                         cn : [
13149                             {
13150                                 tag : 'span',
13151                                 html : this.fieldLabel
13152                             },
13153                             {
13154                                 tag : 'i',
13155                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13156                                 tooltip : 'This field is required'
13157                             }
13158                         ]
13159                     },
13160                     {
13161                         cls : "",
13162                         cn: [
13163                             combobox
13164                         ]
13165                     }
13166
13167                 ];
13168                 
13169                 
13170                 
13171                 labelCfg = cfg.cn[0];
13172                 contentCfg = cfg.cn[1];
13173             
13174             }
13175             
13176             if(this.labelWidth > 12){
13177                 labelCfg.style = "width: " + this.labelWidth + 'px';
13178             }
13179             
13180             if(this.labelWidth < 13 && this.labelmd == 0){
13181                 this.labelmd = this.labelWidth;
13182             }
13183             
13184             if(this.labellg > 0){
13185                 labelCfg.cls += ' col-lg-' + this.labellg;
13186                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13187             }
13188             
13189             if(this.labelmd > 0){
13190                 labelCfg.cls += ' col-md-' + this.labelmd;
13191                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13192             }
13193             
13194             if(this.labelsm > 0){
13195                 labelCfg.cls += ' col-sm-' + this.labelsm;
13196                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13197             }
13198             
13199             if(this.labelxs > 0){
13200                 labelCfg.cls += ' col-xs-' + this.labelxs;
13201                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13202             }
13203                 
13204                 
13205         } else if ( this.fieldLabel.length) {
13206 //                Roo.log(" label");
13207                  cfg.cn = [
13208                     {
13209                         tag : 'i',
13210                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13211                         tooltip : 'This field is required'
13212                     },
13213                     {
13214                         tag: 'label',
13215                         //cls : 'input-group-addon',
13216                         html : this.fieldLabel
13217                     },
13218                     combobox
13219                 ];
13220                 
13221                 if(this.indicatorpos == 'right'){
13222                     cfg.cn = [
13223                         {
13224                             tag: 'label',
13225                             //cls : 'input-group-addon',
13226                             html : this.fieldLabel
13227                         },
13228                         {
13229                             tag : 'i',
13230                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13231                             tooltip : 'This field is required'
13232                         },
13233                         combobox
13234                     ];
13235                     
13236                 }
13237
13238         } else {
13239             
13240 //                Roo.log(" no label && no align");
13241                 cfg = combobox
13242                      
13243                 
13244         }
13245          
13246         var settings=this;
13247         ['xs','sm','md','lg'].map(function(size){
13248             if (settings[size]) {
13249                 cfg.cls += ' col-' + size + '-' + settings[size];
13250             }
13251         });
13252         
13253         return cfg;
13254         
13255     },
13256     
13257     _initEventsCalled : false,
13258     
13259     // private
13260     initEvents: function()
13261     {   
13262         if (this._initEventsCalled) { // as we call render... prevent looping...
13263             return;
13264         }
13265         this._initEventsCalled = true;
13266         
13267         if (!this.store) {
13268             throw "can not find store for combo";
13269         }
13270         
13271         this.indicator = this.indicatorEl();
13272         
13273         this.store = Roo.factory(this.store, Roo.data);
13274         this.store.parent = this;
13275         
13276         // if we are building from html. then this element is so complex, that we can not really
13277         // use the rendered HTML.
13278         // so we have to trash and replace the previous code.
13279         if (Roo.XComponent.build_from_html) {
13280             // remove this element....
13281             var e = this.el.dom, k=0;
13282             while (e ) { e = e.previousSibling;  ++k;}
13283
13284             this.el.remove();
13285             
13286             this.el=false;
13287             this.rendered = false;
13288             
13289             this.render(this.parent().getChildContainer(true), k);
13290         }
13291         
13292         if(Roo.isIOS && this.useNativeIOS){
13293             this.initIOSView();
13294             return;
13295         }
13296         
13297         /*
13298          * Touch Devices
13299          */
13300         
13301         if(Roo.isTouch && this.mobileTouchView){
13302             this.initTouchView();
13303             return;
13304         }
13305         
13306         if(this.tickable){
13307             this.initTickableEvents();
13308             return;
13309         }
13310         
13311         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13312         
13313         if(this.hiddenName){
13314             
13315             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13316             
13317             this.hiddenField.dom.value =
13318                 this.hiddenValue !== undefined ? this.hiddenValue :
13319                 this.value !== undefined ? this.value : '';
13320
13321             // prevent input submission
13322             this.el.dom.removeAttribute('name');
13323             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13324              
13325              
13326         }
13327         //if(Roo.isGecko){
13328         //    this.el.dom.setAttribute('autocomplete', 'off');
13329         //}
13330         
13331         var cls = 'x-combo-list';
13332         
13333         //this.list = new Roo.Layer({
13334         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13335         //});
13336         
13337         var _this = this;
13338         
13339         (function(){
13340             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13341             _this.list.setWidth(lw);
13342         }).defer(100);
13343         
13344         this.list.on('mouseover', this.onViewOver, this);
13345         this.list.on('mousemove', this.onViewMove, this);
13346         this.list.on('scroll', this.onViewScroll, this);
13347         
13348         /*
13349         this.list.swallowEvent('mousewheel');
13350         this.assetHeight = 0;
13351
13352         if(this.title){
13353             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13354             this.assetHeight += this.header.getHeight();
13355         }
13356
13357         this.innerList = this.list.createChild({cls:cls+'-inner'});
13358         this.innerList.on('mouseover', this.onViewOver, this);
13359         this.innerList.on('mousemove', this.onViewMove, this);
13360         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13361         
13362         if(this.allowBlank && !this.pageSize && !this.disableClear){
13363             this.footer = this.list.createChild({cls:cls+'-ft'});
13364             this.pageTb = new Roo.Toolbar(this.footer);
13365            
13366         }
13367         if(this.pageSize){
13368             this.footer = this.list.createChild({cls:cls+'-ft'});
13369             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13370                     {pageSize: this.pageSize});
13371             
13372         }
13373         
13374         if (this.pageTb && this.allowBlank && !this.disableClear) {
13375             var _this = this;
13376             this.pageTb.add(new Roo.Toolbar.Fill(), {
13377                 cls: 'x-btn-icon x-btn-clear',
13378                 text: '&#160;',
13379                 handler: function()
13380                 {
13381                     _this.collapse();
13382                     _this.clearValue();
13383                     _this.onSelect(false, -1);
13384                 }
13385             });
13386         }
13387         if (this.footer) {
13388             this.assetHeight += this.footer.getHeight();
13389         }
13390         */
13391             
13392         if(!this.tpl){
13393             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13394         }
13395
13396         this.view = new Roo.View(this.list, this.tpl, {
13397             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13398         });
13399         //this.view.wrapEl.setDisplayed(false);
13400         this.view.on('click', this.onViewClick, this);
13401         
13402         
13403         this.store.on('beforeload', this.onBeforeLoad, this);
13404         this.store.on('load', this.onLoad, this);
13405         this.store.on('loadexception', this.onLoadException, this);
13406         /*
13407         if(this.resizable){
13408             this.resizer = new Roo.Resizable(this.list,  {
13409                pinned:true, handles:'se'
13410             });
13411             this.resizer.on('resize', function(r, w, h){
13412                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13413                 this.listWidth = w;
13414                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13415                 this.restrictHeight();
13416             }, this);
13417             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13418         }
13419         */
13420         if(!this.editable){
13421             this.editable = true;
13422             this.setEditable(false);
13423         }
13424         
13425         /*
13426         
13427         if (typeof(this.events.add.listeners) != 'undefined') {
13428             
13429             this.addicon = this.wrap.createChild(
13430                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13431        
13432             this.addicon.on('click', function(e) {
13433                 this.fireEvent('add', this);
13434             }, this);
13435         }
13436         if (typeof(this.events.edit.listeners) != 'undefined') {
13437             
13438             this.editicon = this.wrap.createChild(
13439                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13440             if (this.addicon) {
13441                 this.editicon.setStyle('margin-left', '40px');
13442             }
13443             this.editicon.on('click', function(e) {
13444                 
13445                 // we fire even  if inothing is selected..
13446                 this.fireEvent('edit', this, this.lastData );
13447                 
13448             }, this);
13449         }
13450         */
13451         
13452         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13453             "up" : function(e){
13454                 this.inKeyMode = true;
13455                 this.selectPrev();
13456             },
13457
13458             "down" : function(e){
13459                 if(!this.isExpanded()){
13460                     this.onTriggerClick();
13461                 }else{
13462                     this.inKeyMode = true;
13463                     this.selectNext();
13464                 }
13465             },
13466
13467             "enter" : function(e){
13468 //                this.onViewClick();
13469                 //return true;
13470                 this.collapse();
13471                 
13472                 if(this.fireEvent("specialkey", this, e)){
13473                     this.onViewClick(false);
13474                 }
13475                 
13476                 return true;
13477             },
13478
13479             "esc" : function(e){
13480                 this.collapse();
13481             },
13482
13483             "tab" : function(e){
13484                 this.collapse();
13485                 
13486                 if(this.fireEvent("specialkey", this, e)){
13487                     this.onViewClick(false);
13488                 }
13489                 
13490                 return true;
13491             },
13492
13493             scope : this,
13494
13495             doRelay : function(foo, bar, hname){
13496                 if(hname == 'down' || this.scope.isExpanded()){
13497                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13498                 }
13499                 return true;
13500             },
13501
13502             forceKeyDown: true
13503         });
13504         
13505         
13506         this.queryDelay = Math.max(this.queryDelay || 10,
13507                 this.mode == 'local' ? 10 : 250);
13508         
13509         
13510         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13511         
13512         if(this.typeAhead){
13513             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13514         }
13515         if(this.editable !== false){
13516             this.inputEl().on("keyup", this.onKeyUp, this);
13517         }
13518         if(this.forceSelection){
13519             this.inputEl().on('blur', this.doForce, this);
13520         }
13521         
13522         if(this.multiple){
13523             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13524             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13525         }
13526     },
13527     
13528     initTickableEvents: function()
13529     {   
13530         this.createList();
13531         
13532         if(this.hiddenName){
13533             
13534             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13535             
13536             this.hiddenField.dom.value =
13537                 this.hiddenValue !== undefined ? this.hiddenValue :
13538                 this.value !== undefined ? this.value : '';
13539
13540             // prevent input submission
13541             this.el.dom.removeAttribute('name');
13542             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13543              
13544              
13545         }
13546         
13547 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13548         
13549         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13550         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13551         if(this.triggerList){
13552             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13553         }
13554          
13555         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13556         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13557         
13558         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13559         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13560         
13561         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13562         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13563         
13564         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13565         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13566         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13567         
13568         this.okBtn.hide();
13569         this.cancelBtn.hide();
13570         
13571         var _this = this;
13572         
13573         (function(){
13574             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13575             _this.list.setWidth(lw);
13576         }).defer(100);
13577         
13578         this.list.on('mouseover', this.onViewOver, this);
13579         this.list.on('mousemove', this.onViewMove, this);
13580         
13581         this.list.on('scroll', this.onViewScroll, this);
13582         
13583         if(!this.tpl){
13584             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>';
13585         }
13586
13587         this.view = new Roo.View(this.list, this.tpl, {
13588             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13589         });
13590         
13591         //this.view.wrapEl.setDisplayed(false);
13592         this.view.on('click', this.onViewClick, this);
13593         
13594         
13595         
13596         this.store.on('beforeload', this.onBeforeLoad, this);
13597         this.store.on('load', this.onLoad, this);
13598         this.store.on('loadexception', this.onLoadException, this);
13599         
13600         if(this.editable){
13601             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13602                 "up" : function(e){
13603                     this.inKeyMode = true;
13604                     this.selectPrev();
13605                 },
13606
13607                 "down" : function(e){
13608                     this.inKeyMode = true;
13609                     this.selectNext();
13610                 },
13611
13612                 "enter" : function(e){
13613                     if(this.fireEvent("specialkey", this, e)){
13614                         this.onViewClick(false);
13615                     }
13616                     
13617                     return true;
13618                 },
13619
13620                 "esc" : function(e){
13621                     this.onTickableFooterButtonClick(e, false, false);
13622                 },
13623
13624                 "tab" : function(e){
13625                     this.fireEvent("specialkey", this, e);
13626                     
13627                     this.onTickableFooterButtonClick(e, false, false);
13628                     
13629                     return true;
13630                 },
13631
13632                 scope : this,
13633
13634                 doRelay : function(e, fn, key){
13635                     if(this.scope.isExpanded()){
13636                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13637                     }
13638                     return true;
13639                 },
13640
13641                 forceKeyDown: true
13642             });
13643         }
13644         
13645         this.queryDelay = Math.max(this.queryDelay || 10,
13646                 this.mode == 'local' ? 10 : 250);
13647         
13648         
13649         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13650         
13651         if(this.typeAhead){
13652             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13653         }
13654         
13655         if(this.editable !== false){
13656             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13657         }
13658         
13659         this.indicator = this.indicatorEl();
13660         
13661         if(this.indicator){
13662             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13663             this.indicator.hide();
13664         }
13665         
13666     },
13667
13668     onDestroy : function(){
13669         if(this.view){
13670             this.view.setStore(null);
13671             this.view.el.removeAllListeners();
13672             this.view.el.remove();
13673             this.view.purgeListeners();
13674         }
13675         if(this.list){
13676             this.list.dom.innerHTML  = '';
13677         }
13678         
13679         if(this.store){
13680             this.store.un('beforeload', this.onBeforeLoad, this);
13681             this.store.un('load', this.onLoad, this);
13682             this.store.un('loadexception', this.onLoadException, this);
13683         }
13684         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13685     },
13686
13687     // private
13688     fireKey : function(e){
13689         if(e.isNavKeyPress() && !this.list.isVisible()){
13690             this.fireEvent("specialkey", this, e);
13691         }
13692     },
13693
13694     // private
13695     onResize: function(w, h){
13696 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13697 //        
13698 //        if(typeof w != 'number'){
13699 //            // we do not handle it!?!?
13700 //            return;
13701 //        }
13702 //        var tw = this.trigger.getWidth();
13703 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13704 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13705 //        var x = w - tw;
13706 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13707 //            
13708 //        //this.trigger.setStyle('left', x+'px');
13709 //        
13710 //        if(this.list && this.listWidth === undefined){
13711 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13712 //            this.list.setWidth(lw);
13713 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13714 //        }
13715         
13716     
13717         
13718     },
13719
13720     /**
13721      * Allow or prevent the user from directly editing the field text.  If false is passed,
13722      * the user will only be able to select from the items defined in the dropdown list.  This method
13723      * is the runtime equivalent of setting the 'editable' config option at config time.
13724      * @param {Boolean} value True to allow the user to directly edit the field text
13725      */
13726     setEditable : function(value){
13727         if(value == this.editable){
13728             return;
13729         }
13730         this.editable = value;
13731         if(!value){
13732             this.inputEl().dom.setAttribute('readOnly', true);
13733             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13734             this.inputEl().addClass('x-combo-noedit');
13735         }else{
13736             this.inputEl().dom.setAttribute('readOnly', false);
13737             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13738             this.inputEl().removeClass('x-combo-noedit');
13739         }
13740     },
13741
13742     // private
13743     
13744     onBeforeLoad : function(combo,opts){
13745         if(!this.hasFocus){
13746             return;
13747         }
13748          if (!opts.add) {
13749             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13750          }
13751         this.restrictHeight();
13752         this.selectedIndex = -1;
13753     },
13754
13755     // private
13756     onLoad : function(){
13757         
13758         this.hasQuery = false;
13759         
13760         if(!this.hasFocus){
13761             return;
13762         }
13763         
13764         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13765             this.loading.hide();
13766         }
13767         
13768         if(this.store.getCount() > 0){
13769             
13770             this.expand();
13771             this.restrictHeight();
13772             if(this.lastQuery == this.allQuery){
13773                 if(this.editable && !this.tickable){
13774                     this.inputEl().dom.select();
13775                 }
13776                 
13777                 if(
13778                     !this.selectByValue(this.value, true) &&
13779                     this.autoFocus && 
13780                     (
13781                         !this.store.lastOptions ||
13782                         typeof(this.store.lastOptions.add) == 'undefined' || 
13783                         this.store.lastOptions.add != true
13784                     )
13785                 ){
13786                     this.select(0, true);
13787                 }
13788             }else{
13789                 if(this.autoFocus){
13790                     this.selectNext();
13791                 }
13792                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13793                     this.taTask.delay(this.typeAheadDelay);
13794                 }
13795             }
13796         }else{
13797             this.onEmptyResults();
13798         }
13799         
13800         //this.el.focus();
13801     },
13802     // private
13803     onLoadException : function()
13804     {
13805         this.hasQuery = false;
13806         
13807         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13808             this.loading.hide();
13809         }
13810         
13811         if(this.tickable && this.editable){
13812             return;
13813         }
13814         
13815         this.collapse();
13816         // only causes errors at present
13817         //Roo.log(this.store.reader.jsonData);
13818         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13819             // fixme
13820             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13821         //}
13822         
13823         
13824     },
13825     // private
13826     onTypeAhead : function(){
13827         if(this.store.getCount() > 0){
13828             var r = this.store.getAt(0);
13829             var newValue = r.data[this.displayField];
13830             var len = newValue.length;
13831             var selStart = this.getRawValue().length;
13832             
13833             if(selStart != len){
13834                 this.setRawValue(newValue);
13835                 this.selectText(selStart, newValue.length);
13836             }
13837         }
13838     },
13839
13840     // private
13841     onSelect : function(record, index){
13842         
13843         if(this.fireEvent('beforeselect', this, record, index) !== false){
13844         
13845             this.setFromData(index > -1 ? record.data : false);
13846             
13847             this.collapse();
13848             this.fireEvent('select', this, record, index);
13849         }
13850     },
13851
13852     /**
13853      * Returns the currently selected field value or empty string if no value is set.
13854      * @return {String} value The selected value
13855      */
13856     getValue : function()
13857     {
13858         if(Roo.isIOS && this.useNativeIOS){
13859             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13860         }
13861         
13862         if(this.multiple){
13863             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13864         }
13865         
13866         if(this.valueField){
13867             return typeof this.value != 'undefined' ? this.value : '';
13868         }else{
13869             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13870         }
13871     },
13872     
13873     getRawValue : function()
13874     {
13875         if(Roo.isIOS && this.useNativeIOS){
13876             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13877         }
13878         
13879         var v = this.inputEl().getValue();
13880         
13881         return v;
13882     },
13883
13884     /**
13885      * Clears any text/value currently set in the field
13886      */
13887     clearValue : function(){
13888         
13889         if(this.hiddenField){
13890             this.hiddenField.dom.value = '';
13891         }
13892         this.value = '';
13893         this.setRawValue('');
13894         this.lastSelectionText = '';
13895         this.lastData = false;
13896         
13897         var close = this.closeTriggerEl();
13898         
13899         if(close){
13900             close.hide();
13901         }
13902         
13903         this.validate();
13904         
13905     },
13906
13907     /**
13908      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13909      * will be displayed in the field.  If the value does not match the data value of an existing item,
13910      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13911      * Otherwise the field will be blank (although the value will still be set).
13912      * @param {String} value The value to match
13913      */
13914     setValue : function(v)
13915     {
13916         if(Roo.isIOS && this.useNativeIOS){
13917             this.setIOSValue(v);
13918             return;
13919         }
13920         
13921         if(this.multiple){
13922             this.syncValue();
13923             return;
13924         }
13925         
13926         var text = v;
13927         if(this.valueField){
13928             var r = this.findRecord(this.valueField, v);
13929             if(r){
13930                 text = r.data[this.displayField];
13931             }else if(this.valueNotFoundText !== undefined){
13932                 text = this.valueNotFoundText;
13933             }
13934         }
13935         this.lastSelectionText = text;
13936         if(this.hiddenField){
13937             this.hiddenField.dom.value = v;
13938         }
13939         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13940         this.value = v;
13941         
13942         var close = this.closeTriggerEl();
13943         
13944         if(close){
13945             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13946         }
13947         
13948         this.validate();
13949     },
13950     /**
13951      * @property {Object} the last set data for the element
13952      */
13953     
13954     lastData : false,
13955     /**
13956      * Sets the value of the field based on a object which is related to the record format for the store.
13957      * @param {Object} value the value to set as. or false on reset?
13958      */
13959     setFromData : function(o){
13960         
13961         if(this.multiple){
13962             this.addItem(o);
13963             return;
13964         }
13965             
13966         var dv = ''; // display value
13967         var vv = ''; // value value..
13968         this.lastData = o;
13969         if (this.displayField) {
13970             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13971         } else {
13972             // this is an error condition!!!
13973             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13974         }
13975         
13976         if(this.valueField){
13977             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13978         }
13979         
13980         var close = this.closeTriggerEl();
13981         
13982         if(close){
13983             if(dv.length || vv * 1 > 0){
13984                 close.show() ;
13985                 this.blockFocus=true;
13986             } else {
13987                 close.hide();
13988             }             
13989         }
13990         
13991         if(this.hiddenField){
13992             this.hiddenField.dom.value = vv;
13993             
13994             this.lastSelectionText = dv;
13995             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13996             this.value = vv;
13997             return;
13998         }
13999         // no hidden field.. - we store the value in 'value', but still display
14000         // display field!!!!
14001         this.lastSelectionText = dv;
14002         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14003         this.value = vv;
14004         
14005         
14006         
14007     },
14008     // private
14009     reset : function(){
14010         // overridden so that last data is reset..
14011         
14012         if(this.multiple){
14013             this.clearItem();
14014             return;
14015         }
14016         
14017         this.setValue(this.originalValue);
14018         //this.clearInvalid();
14019         this.lastData = false;
14020         if (this.view) {
14021             this.view.clearSelections();
14022         }
14023         
14024         this.validate();
14025     },
14026     // private
14027     findRecord : function(prop, value){
14028         var record;
14029         if(this.store.getCount() > 0){
14030             this.store.each(function(r){
14031                 if(r.data[prop] == value){
14032                     record = r;
14033                     return false;
14034                 }
14035                 return true;
14036             });
14037         }
14038         return record;
14039     },
14040     
14041     getName: function()
14042     {
14043         // returns hidden if it's set..
14044         if (!this.rendered) {return ''};
14045         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14046         
14047     },
14048     // private
14049     onViewMove : function(e, t){
14050         this.inKeyMode = false;
14051     },
14052
14053     // private
14054     onViewOver : function(e, t){
14055         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14056             return;
14057         }
14058         var item = this.view.findItemFromChild(t);
14059         
14060         if(item){
14061             var index = this.view.indexOf(item);
14062             this.select(index, false);
14063         }
14064     },
14065
14066     // private
14067     onViewClick : function(view, doFocus, el, e)
14068     {
14069         var index = this.view.getSelectedIndexes()[0];
14070         
14071         var r = this.store.getAt(index);
14072         
14073         if(this.tickable){
14074             
14075             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14076                 return;
14077             }
14078             
14079             var rm = false;
14080             var _this = this;
14081             
14082             Roo.each(this.tickItems, function(v,k){
14083                 
14084                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14085                     Roo.log(v);
14086                     _this.tickItems.splice(k, 1);
14087                     
14088                     if(typeof(e) == 'undefined' && view == false){
14089                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14090                     }
14091                     
14092                     rm = true;
14093                     return;
14094                 }
14095             });
14096             
14097             if(rm){
14098                 return;
14099             }
14100             
14101             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14102                 this.tickItems.push(r.data);
14103             }
14104             
14105             if(typeof(e) == 'undefined' && view == false){
14106                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14107             }
14108                     
14109             return;
14110         }
14111         
14112         if(r){
14113             this.onSelect(r, index);
14114         }
14115         if(doFocus !== false && !this.blockFocus){
14116             this.inputEl().focus();
14117         }
14118     },
14119
14120     // private
14121     restrictHeight : function(){
14122         //this.innerList.dom.style.height = '';
14123         //var inner = this.innerList.dom;
14124         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14125         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14126         //this.list.beginUpdate();
14127         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14128         this.list.alignTo(this.inputEl(), this.listAlign);
14129         this.list.alignTo(this.inputEl(), this.listAlign);
14130         //this.list.endUpdate();
14131     },
14132
14133     // private
14134     onEmptyResults : function(){
14135         
14136         if(this.tickable && this.editable){
14137             this.hasFocus = false;
14138             this.restrictHeight();
14139             return;
14140         }
14141         
14142         this.collapse();
14143     },
14144
14145     /**
14146      * Returns true if the dropdown list is expanded, else false.
14147      */
14148     isExpanded : function(){
14149         return this.list.isVisible();
14150     },
14151
14152     /**
14153      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14154      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14155      * @param {String} value The data value of the item to select
14156      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14157      * selected item if it is not currently in view (defaults to true)
14158      * @return {Boolean} True if the value matched an item in the list, else false
14159      */
14160     selectByValue : function(v, scrollIntoView){
14161         if(v !== undefined && v !== null){
14162             var r = this.findRecord(this.valueField || this.displayField, v);
14163             if(r){
14164                 this.select(this.store.indexOf(r), scrollIntoView);
14165                 return true;
14166             }
14167         }
14168         return false;
14169     },
14170
14171     /**
14172      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14173      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14174      * @param {Number} index The zero-based index of the list item to select
14175      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14176      * selected item if it is not currently in view (defaults to true)
14177      */
14178     select : function(index, scrollIntoView){
14179         this.selectedIndex = index;
14180         this.view.select(index);
14181         if(scrollIntoView !== false){
14182             var el = this.view.getNode(index);
14183             /*
14184              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14185              */
14186             if(el){
14187                 this.list.scrollChildIntoView(el, false);
14188             }
14189         }
14190     },
14191
14192     // private
14193     selectNext : function(){
14194         var ct = this.store.getCount();
14195         if(ct > 0){
14196             if(this.selectedIndex == -1){
14197                 this.select(0);
14198             }else if(this.selectedIndex < ct-1){
14199                 this.select(this.selectedIndex+1);
14200             }
14201         }
14202     },
14203
14204     // private
14205     selectPrev : function(){
14206         var ct = this.store.getCount();
14207         if(ct > 0){
14208             if(this.selectedIndex == -1){
14209                 this.select(0);
14210             }else if(this.selectedIndex != 0){
14211                 this.select(this.selectedIndex-1);
14212             }
14213         }
14214     },
14215
14216     // private
14217     onKeyUp : function(e){
14218         if(this.editable !== false && !e.isSpecialKey()){
14219             this.lastKey = e.getKey();
14220             this.dqTask.delay(this.queryDelay);
14221         }
14222     },
14223
14224     // private
14225     validateBlur : function(){
14226         return !this.list || !this.list.isVisible();   
14227     },
14228
14229     // private
14230     initQuery : function(){
14231         
14232         var v = this.getRawValue();
14233         
14234         if(this.tickable && this.editable){
14235             v = this.tickableInputEl().getValue();
14236         }
14237         
14238         this.doQuery(v);
14239     },
14240
14241     // private
14242     doForce : function(){
14243         if(this.inputEl().dom.value.length > 0){
14244             this.inputEl().dom.value =
14245                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14246              
14247         }
14248     },
14249
14250     /**
14251      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14252      * query allowing the query action to be canceled if needed.
14253      * @param {String} query The SQL query to execute
14254      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14255      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14256      * saved in the current store (defaults to false)
14257      */
14258     doQuery : function(q, forceAll){
14259         
14260         if(q === undefined || q === null){
14261             q = '';
14262         }
14263         var qe = {
14264             query: q,
14265             forceAll: forceAll,
14266             combo: this,
14267             cancel:false
14268         };
14269         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14270             return false;
14271         }
14272         q = qe.query;
14273         
14274         forceAll = qe.forceAll;
14275         if(forceAll === true || (q.length >= this.minChars)){
14276             
14277             this.hasQuery = true;
14278             
14279             if(this.lastQuery != q || this.alwaysQuery){
14280                 this.lastQuery = q;
14281                 if(this.mode == 'local'){
14282                     this.selectedIndex = -1;
14283                     if(forceAll){
14284                         this.store.clearFilter();
14285                     }else{
14286                         
14287                         if(this.specialFilter){
14288                             this.fireEvent('specialfilter', this);
14289                             this.onLoad();
14290                             return;
14291                         }
14292                         
14293                         this.store.filter(this.displayField, q);
14294                     }
14295                     
14296                     this.store.fireEvent("datachanged", this.store);
14297                     
14298                     this.onLoad();
14299                     
14300                     
14301                 }else{
14302                     
14303                     this.store.baseParams[this.queryParam] = q;
14304                     
14305                     var options = {params : this.getParams(q)};
14306                     
14307                     if(this.loadNext){
14308                         options.add = true;
14309                         options.params.start = this.page * this.pageSize;
14310                     }
14311                     
14312                     this.store.load(options);
14313                     
14314                     /*
14315                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14316                      *  we should expand the list on onLoad
14317                      *  so command out it
14318                      */
14319 //                    this.expand();
14320                 }
14321             }else{
14322                 this.selectedIndex = -1;
14323                 this.onLoad();   
14324             }
14325         }
14326         
14327         this.loadNext = false;
14328     },
14329     
14330     // private
14331     getParams : function(q){
14332         var p = {};
14333         //p[this.queryParam] = q;
14334         
14335         if(this.pageSize){
14336             p.start = 0;
14337             p.limit = this.pageSize;
14338         }
14339         return p;
14340     },
14341
14342     /**
14343      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14344      */
14345     collapse : function(){
14346         if(!this.isExpanded()){
14347             return;
14348         }
14349         
14350         this.list.hide();
14351         
14352         this.hasFocus = false;
14353         
14354         if(this.tickable){
14355             this.okBtn.hide();
14356             this.cancelBtn.hide();
14357             this.trigger.show();
14358             
14359             if(this.editable){
14360                 this.tickableInputEl().dom.value = '';
14361                 this.tickableInputEl().blur();
14362             }
14363             
14364         }
14365         
14366         Roo.get(document).un('mousedown', this.collapseIf, this);
14367         Roo.get(document).un('mousewheel', this.collapseIf, this);
14368         if (!this.editable) {
14369             Roo.get(document).un('keydown', this.listKeyPress, this);
14370         }
14371         this.fireEvent('collapse', this);
14372         
14373         this.validate();
14374     },
14375
14376     // private
14377     collapseIf : function(e){
14378         var in_combo  = e.within(this.el);
14379         var in_list =  e.within(this.list);
14380         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14381         
14382         if (in_combo || in_list || is_list) {
14383             //e.stopPropagation();
14384             return;
14385         }
14386         
14387         if(this.tickable){
14388             this.onTickableFooterButtonClick(e, false, false);
14389         }
14390
14391         this.collapse();
14392         
14393     },
14394
14395     /**
14396      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14397      */
14398     expand : function(){
14399        
14400         if(this.isExpanded() || !this.hasFocus){
14401             return;
14402         }
14403         
14404         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14405         this.list.setWidth(lw);
14406         
14407         Roo.log('expand');
14408         
14409         this.list.show();
14410         
14411         this.restrictHeight();
14412         
14413         if(this.tickable){
14414             
14415             this.tickItems = Roo.apply([], this.item);
14416             
14417             this.okBtn.show();
14418             this.cancelBtn.show();
14419             this.trigger.hide();
14420             
14421             if(this.editable){
14422                 this.tickableInputEl().focus();
14423             }
14424             
14425         }
14426         
14427         Roo.get(document).on('mousedown', this.collapseIf, this);
14428         Roo.get(document).on('mousewheel', this.collapseIf, this);
14429         if (!this.editable) {
14430             Roo.get(document).on('keydown', this.listKeyPress, this);
14431         }
14432         
14433         this.fireEvent('expand', this);
14434     },
14435
14436     // private
14437     // Implements the default empty TriggerField.onTriggerClick function
14438     onTriggerClick : function(e)
14439     {
14440         Roo.log('trigger click');
14441         
14442         if(this.disabled || !this.triggerList){
14443             return;
14444         }
14445         
14446         this.page = 0;
14447         this.loadNext = false;
14448         
14449         if(this.isExpanded()){
14450             this.collapse();
14451             if (!this.blockFocus) {
14452                 this.inputEl().focus();
14453             }
14454             
14455         }else {
14456             this.hasFocus = true;
14457             if(this.triggerAction == 'all') {
14458                 this.doQuery(this.allQuery, true);
14459             } else {
14460                 this.doQuery(this.getRawValue());
14461             }
14462             if (!this.blockFocus) {
14463                 this.inputEl().focus();
14464             }
14465         }
14466     },
14467     
14468     onTickableTriggerClick : function(e)
14469     {
14470         if(this.disabled){
14471             return;
14472         }
14473         
14474         this.page = 0;
14475         this.loadNext = false;
14476         this.hasFocus = true;
14477         
14478         if(this.triggerAction == 'all') {
14479             this.doQuery(this.allQuery, true);
14480         } else {
14481             this.doQuery(this.getRawValue());
14482         }
14483     },
14484     
14485     onSearchFieldClick : function(e)
14486     {
14487         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14488             this.onTickableFooterButtonClick(e, false, false);
14489             return;
14490         }
14491         
14492         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14493             return;
14494         }
14495         
14496         this.page = 0;
14497         this.loadNext = false;
14498         this.hasFocus = true;
14499         
14500         if(this.triggerAction == 'all') {
14501             this.doQuery(this.allQuery, true);
14502         } else {
14503             this.doQuery(this.getRawValue());
14504         }
14505     },
14506     
14507     listKeyPress : function(e)
14508     {
14509         //Roo.log('listkeypress');
14510         // scroll to first matching element based on key pres..
14511         if (e.isSpecialKey()) {
14512             return false;
14513         }
14514         var k = String.fromCharCode(e.getKey()).toUpperCase();
14515         //Roo.log(k);
14516         var match  = false;
14517         var csel = this.view.getSelectedNodes();
14518         var cselitem = false;
14519         if (csel.length) {
14520             var ix = this.view.indexOf(csel[0]);
14521             cselitem  = this.store.getAt(ix);
14522             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14523                 cselitem = false;
14524             }
14525             
14526         }
14527         
14528         this.store.each(function(v) { 
14529             if (cselitem) {
14530                 // start at existing selection.
14531                 if (cselitem.id == v.id) {
14532                     cselitem = false;
14533                 }
14534                 return true;
14535             }
14536                 
14537             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14538                 match = this.store.indexOf(v);
14539                 return false;
14540             }
14541             return true;
14542         }, this);
14543         
14544         if (match === false) {
14545             return true; // no more action?
14546         }
14547         // scroll to?
14548         this.view.select(match);
14549         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14550         sn.scrollIntoView(sn.dom.parentNode, false);
14551     },
14552     
14553     onViewScroll : function(e, t){
14554         
14555         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){
14556             return;
14557         }
14558         
14559         this.hasQuery = true;
14560         
14561         this.loading = this.list.select('.loading', true).first();
14562         
14563         if(this.loading === null){
14564             this.list.createChild({
14565                 tag: 'div',
14566                 cls: 'loading roo-select2-more-results roo-select2-active',
14567                 html: 'Loading more results...'
14568             });
14569             
14570             this.loading = this.list.select('.loading', true).first();
14571             
14572             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14573             
14574             this.loading.hide();
14575         }
14576         
14577         this.loading.show();
14578         
14579         var _combo = this;
14580         
14581         this.page++;
14582         this.loadNext = true;
14583         
14584         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14585         
14586         return;
14587     },
14588     
14589     addItem : function(o)
14590     {   
14591         var dv = ''; // display value
14592         
14593         if (this.displayField) {
14594             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14595         } else {
14596             // this is an error condition!!!
14597             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14598         }
14599         
14600         if(!dv.length){
14601             return;
14602         }
14603         
14604         var choice = this.choices.createChild({
14605             tag: 'li',
14606             cls: 'roo-select2-search-choice',
14607             cn: [
14608                 {
14609                     tag: 'div',
14610                     html: dv
14611                 },
14612                 {
14613                     tag: 'a',
14614                     href: '#',
14615                     cls: 'roo-select2-search-choice-close fa fa-times',
14616                     tabindex: '-1'
14617                 }
14618             ]
14619             
14620         }, this.searchField);
14621         
14622         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14623         
14624         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14625         
14626         this.item.push(o);
14627         
14628         this.lastData = o;
14629         
14630         this.syncValue();
14631         
14632         this.inputEl().dom.value = '';
14633         
14634         this.validate();
14635     },
14636     
14637     onRemoveItem : function(e, _self, o)
14638     {
14639         e.preventDefault();
14640         
14641         this.lastItem = Roo.apply([], this.item);
14642         
14643         var index = this.item.indexOf(o.data) * 1;
14644         
14645         if( index < 0){
14646             Roo.log('not this item?!');
14647             return;
14648         }
14649         
14650         this.item.splice(index, 1);
14651         o.item.remove();
14652         
14653         this.syncValue();
14654         
14655         this.fireEvent('remove', this, e);
14656         
14657         this.validate();
14658         
14659     },
14660     
14661     syncValue : function()
14662     {
14663         if(!this.item.length){
14664             this.clearValue();
14665             return;
14666         }
14667             
14668         var value = [];
14669         var _this = this;
14670         Roo.each(this.item, function(i){
14671             if(_this.valueField){
14672                 value.push(i[_this.valueField]);
14673                 return;
14674             }
14675
14676             value.push(i);
14677         });
14678
14679         this.value = value.join(',');
14680
14681         if(this.hiddenField){
14682             this.hiddenField.dom.value = this.value;
14683         }
14684         
14685         this.store.fireEvent("datachanged", this.store);
14686         
14687         this.validate();
14688     },
14689     
14690     clearItem : function()
14691     {
14692         if(!this.multiple){
14693             return;
14694         }
14695         
14696         this.item = [];
14697         
14698         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14699            c.remove();
14700         });
14701         
14702         this.syncValue();
14703         
14704         this.validate();
14705         
14706         if(this.tickable && !Roo.isTouch){
14707             this.view.refresh();
14708         }
14709     },
14710     
14711     inputEl: function ()
14712     {
14713         if(Roo.isIOS && this.useNativeIOS){
14714             return this.el.select('select.roo-ios-select', true).first();
14715         }
14716         
14717         if(Roo.isTouch && this.mobileTouchView){
14718             return this.el.select('input.form-control',true).first();
14719         }
14720         
14721         if(this.tickable){
14722             return this.searchField;
14723         }
14724         
14725         return this.el.select('input.form-control',true).first();
14726     },
14727     
14728     onTickableFooterButtonClick : function(e, btn, el)
14729     {
14730         e.preventDefault();
14731         
14732         this.lastItem = Roo.apply([], this.item);
14733         
14734         if(btn && btn.name == 'cancel'){
14735             this.tickItems = Roo.apply([], this.item);
14736             this.collapse();
14737             return;
14738         }
14739         
14740         this.clearItem();
14741         
14742         var _this = this;
14743         
14744         Roo.each(this.tickItems, function(o){
14745             _this.addItem(o);
14746         });
14747         
14748         this.collapse();
14749         
14750     },
14751     
14752     validate : function()
14753     {
14754         if(this.getVisibilityEl().hasClass('hidden')){
14755             return true;
14756         }
14757         
14758         var v = this.getRawValue();
14759         
14760         if(this.multiple){
14761             v = this.getValue();
14762         }
14763         
14764         if(this.disabled || this.allowBlank || v.length){
14765             this.markValid();
14766             return true;
14767         }
14768         
14769         this.markInvalid();
14770         return false;
14771     },
14772     
14773     tickableInputEl : function()
14774     {
14775         if(!this.tickable || !this.editable){
14776             return this.inputEl();
14777         }
14778         
14779         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14780     },
14781     
14782     
14783     getAutoCreateTouchView : function()
14784     {
14785         var id = Roo.id();
14786         
14787         var cfg = {
14788             cls: 'form-group' //input-group
14789         };
14790         
14791         var input =  {
14792             tag: 'input',
14793             id : id,
14794             type : this.inputType,
14795             cls : 'form-control x-combo-noedit',
14796             autocomplete: 'new-password',
14797             placeholder : this.placeholder || '',
14798             readonly : true
14799         };
14800         
14801         if (this.name) {
14802             input.name = this.name;
14803         }
14804         
14805         if (this.size) {
14806             input.cls += ' input-' + this.size;
14807         }
14808         
14809         if (this.disabled) {
14810             input.disabled = true;
14811         }
14812         
14813         var inputblock = {
14814             cls : '',
14815             cn : [
14816                 input
14817             ]
14818         };
14819         
14820         if(this.before){
14821             inputblock.cls += ' input-group';
14822             
14823             inputblock.cn.unshift({
14824                 tag :'span',
14825                 cls : 'input-group-addon',
14826                 html : this.before
14827             });
14828         }
14829         
14830         if(this.removable && !this.multiple){
14831             inputblock.cls += ' roo-removable';
14832             
14833             inputblock.cn.push({
14834                 tag: 'button',
14835                 html : 'x',
14836                 cls : 'roo-combo-removable-btn close'
14837             });
14838         }
14839
14840         if(this.hasFeedback && !this.allowBlank){
14841             
14842             inputblock.cls += ' has-feedback';
14843             
14844             inputblock.cn.push({
14845                 tag: 'span',
14846                 cls: 'glyphicon form-control-feedback'
14847             });
14848             
14849         }
14850         
14851         if (this.after) {
14852             
14853             inputblock.cls += (this.before) ? '' : ' input-group';
14854             
14855             inputblock.cn.push({
14856                 tag :'span',
14857                 cls : 'input-group-addon',
14858                 html : this.after
14859             });
14860         }
14861
14862         var box = {
14863             tag: 'div',
14864             cn: [
14865                 {
14866                     tag: 'input',
14867                     type : 'hidden',
14868                     cls: 'form-hidden-field'
14869                 },
14870                 inputblock
14871             ]
14872             
14873         };
14874         
14875         if(this.multiple){
14876             box = {
14877                 tag: 'div',
14878                 cn: [
14879                     {
14880                         tag: 'input',
14881                         type : 'hidden',
14882                         cls: 'form-hidden-field'
14883                     },
14884                     {
14885                         tag: 'ul',
14886                         cls: 'roo-select2-choices',
14887                         cn:[
14888                             {
14889                                 tag: 'li',
14890                                 cls: 'roo-select2-search-field',
14891                                 cn: [
14892
14893                                     inputblock
14894                                 ]
14895                             }
14896                         ]
14897                     }
14898                 ]
14899             }
14900         };
14901         
14902         var combobox = {
14903             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14904             cn: [
14905                 box
14906             ]
14907         };
14908         
14909         if(!this.multiple && this.showToggleBtn){
14910             
14911             var caret = {
14912                         tag: 'span',
14913                         cls: 'caret'
14914             };
14915             
14916             if (this.caret != false) {
14917                 caret = {
14918                      tag: 'i',
14919                      cls: 'fa fa-' + this.caret
14920                 };
14921                 
14922             }
14923             
14924             combobox.cn.push({
14925                 tag :'span',
14926                 cls : 'input-group-addon btn dropdown-toggle',
14927                 cn : [
14928                     caret,
14929                     {
14930                         tag: 'span',
14931                         cls: 'combobox-clear',
14932                         cn  : [
14933                             {
14934                                 tag : 'i',
14935                                 cls: 'icon-remove'
14936                             }
14937                         ]
14938                     }
14939                 ]
14940
14941             })
14942         }
14943         
14944         if(this.multiple){
14945             combobox.cls += ' roo-select2-container-multi';
14946         }
14947         
14948         var align = this.labelAlign || this.parentLabelAlign();
14949         
14950         if (align ==='left' && this.fieldLabel.length) {
14951
14952             cfg.cn = [
14953                 {
14954                    tag : 'i',
14955                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14956                    tooltip : 'This field is required'
14957                 },
14958                 {
14959                     tag: 'label',
14960                     cls : 'control-label',
14961                     html : this.fieldLabel
14962
14963                 },
14964                 {
14965                     cls : '', 
14966                     cn: [
14967                         combobox
14968                     ]
14969                 }
14970             ];
14971             
14972             var labelCfg = cfg.cn[1];
14973             var contentCfg = cfg.cn[2];
14974             
14975
14976             if(this.indicatorpos == 'right'){
14977                 cfg.cn = [
14978                     {
14979                         tag: 'label',
14980                         'for' :  id,
14981                         cls : 'control-label',
14982                         cn : [
14983                             {
14984                                 tag : 'span',
14985                                 html : this.fieldLabel
14986                             },
14987                             {
14988                                 tag : 'i',
14989                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14990                                 tooltip : 'This field is required'
14991                             }
14992                         ]
14993                     },
14994                     {
14995                         cls : "",
14996                         cn: [
14997                             combobox
14998                         ]
14999                     }
15000
15001                 ];
15002                 
15003                 labelCfg = cfg.cn[0];
15004                 contentCfg = cfg.cn[1];
15005             }
15006             
15007            
15008             
15009             if(this.labelWidth > 12){
15010                 labelCfg.style = "width: " + this.labelWidth + 'px';
15011             }
15012             
15013             if(this.labelWidth < 13 && this.labelmd == 0){
15014                 this.labelmd = this.labelWidth;
15015             }
15016             
15017             if(this.labellg > 0){
15018                 labelCfg.cls += ' col-lg-' + this.labellg;
15019                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15020             }
15021             
15022             if(this.labelmd > 0){
15023                 labelCfg.cls += ' col-md-' + this.labelmd;
15024                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15025             }
15026             
15027             if(this.labelsm > 0){
15028                 labelCfg.cls += ' col-sm-' + this.labelsm;
15029                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15030             }
15031             
15032             if(this.labelxs > 0){
15033                 labelCfg.cls += ' col-xs-' + this.labelxs;
15034                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15035             }
15036                 
15037                 
15038         } else if ( this.fieldLabel.length) {
15039             cfg.cn = [
15040                 {
15041                    tag : 'i',
15042                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15043                    tooltip : 'This field is required'
15044                 },
15045                 {
15046                     tag: 'label',
15047                     cls : 'control-label',
15048                     html : this.fieldLabel
15049
15050                 },
15051                 {
15052                     cls : '', 
15053                     cn: [
15054                         combobox
15055                     ]
15056                 }
15057             ];
15058             
15059             if(this.indicatorpos == 'right'){
15060                 cfg.cn = [
15061                     {
15062                         tag: 'label',
15063                         cls : 'control-label',
15064                         html : this.fieldLabel,
15065                         cn : [
15066                             {
15067                                tag : 'i',
15068                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15069                                tooltip : 'This field is required'
15070                             }
15071                         ]
15072                     },
15073                     {
15074                         cls : '', 
15075                         cn: [
15076                             combobox
15077                         ]
15078                     }
15079                 ];
15080             }
15081         } else {
15082             cfg.cn = combobox;    
15083         }
15084         
15085         
15086         var settings = this;
15087         
15088         ['xs','sm','md','lg'].map(function(size){
15089             if (settings[size]) {
15090                 cfg.cls += ' col-' + size + '-' + settings[size];
15091             }
15092         });
15093         
15094         return cfg;
15095     },
15096     
15097     initTouchView : function()
15098     {
15099         this.renderTouchView();
15100         
15101         this.touchViewEl.on('scroll', function(){
15102             this.el.dom.scrollTop = 0;
15103         }, this);
15104         
15105         this.originalValue = this.getValue();
15106         
15107         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15108         
15109         this.inputEl().on("click", this.showTouchView, this);
15110         if (this.triggerEl) {
15111             this.triggerEl.on("click", this.showTouchView, this);
15112         }
15113         
15114         
15115         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15116         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15117         
15118         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15119         
15120         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15121         this.store.on('load', this.onTouchViewLoad, this);
15122         this.store.on('loadexception', this.onTouchViewLoadException, this);
15123         
15124         if(this.hiddenName){
15125             
15126             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15127             
15128             this.hiddenField.dom.value =
15129                 this.hiddenValue !== undefined ? this.hiddenValue :
15130                 this.value !== undefined ? this.value : '';
15131         
15132             this.el.dom.removeAttribute('name');
15133             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15134         }
15135         
15136         if(this.multiple){
15137             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15138             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15139         }
15140         
15141         if(this.removable && !this.multiple){
15142             var close = this.closeTriggerEl();
15143             if(close){
15144                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15145                 close.on('click', this.removeBtnClick, this, close);
15146             }
15147         }
15148         /*
15149          * fix the bug in Safari iOS8
15150          */
15151         this.inputEl().on("focus", function(e){
15152             document.activeElement.blur();
15153         }, this);
15154         
15155         return;
15156         
15157         
15158     },
15159     
15160     renderTouchView : function()
15161     {
15162         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15163         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15164         
15165         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15166         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15167         
15168         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15169         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15170         this.touchViewBodyEl.setStyle('overflow', 'auto');
15171         
15172         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15173         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15174         
15175         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15176         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15177         
15178     },
15179     
15180     showTouchView : function()
15181     {
15182         if(this.disabled){
15183             return;
15184         }
15185         
15186         this.touchViewHeaderEl.hide();
15187
15188         if(this.modalTitle.length){
15189             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15190             this.touchViewHeaderEl.show();
15191         }
15192
15193         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15194         this.touchViewEl.show();
15195
15196         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15197         
15198         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15199         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15200
15201         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15202
15203         if(this.modalTitle.length){
15204             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15205         }
15206         
15207         this.touchViewBodyEl.setHeight(bodyHeight);
15208
15209         if(this.animate){
15210             var _this = this;
15211             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15212         }else{
15213             this.touchViewEl.addClass('in');
15214         }
15215
15216         this.doTouchViewQuery();
15217         
15218     },
15219     
15220     hideTouchView : function()
15221     {
15222         this.touchViewEl.removeClass('in');
15223
15224         if(this.animate){
15225             var _this = this;
15226             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15227         }else{
15228             this.touchViewEl.setStyle('display', 'none');
15229         }
15230         
15231     },
15232     
15233     setTouchViewValue : function()
15234     {
15235         if(this.multiple){
15236             this.clearItem();
15237         
15238             var _this = this;
15239
15240             Roo.each(this.tickItems, function(o){
15241                 this.addItem(o);
15242             }, this);
15243         }
15244         
15245         this.hideTouchView();
15246     },
15247     
15248     doTouchViewQuery : function()
15249     {
15250         var qe = {
15251             query: '',
15252             forceAll: true,
15253             combo: this,
15254             cancel:false
15255         };
15256         
15257         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15258             return false;
15259         }
15260         
15261         if(!this.alwaysQuery || this.mode == 'local'){
15262             this.onTouchViewLoad();
15263             return;
15264         }
15265         
15266         this.store.load();
15267     },
15268     
15269     onTouchViewBeforeLoad : function(combo,opts)
15270     {
15271         return;
15272     },
15273
15274     // private
15275     onTouchViewLoad : function()
15276     {
15277         if(this.store.getCount() < 1){
15278             this.onTouchViewEmptyResults();
15279             return;
15280         }
15281         
15282         this.clearTouchView();
15283         
15284         var rawValue = this.getRawValue();
15285         
15286         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15287         
15288         this.tickItems = [];
15289         
15290         this.store.data.each(function(d, rowIndex){
15291             var row = this.touchViewListGroup.createChild(template);
15292             
15293             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15294                 row.addClass(d.data.cls);
15295             }
15296             
15297             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15298                 var cfg = {
15299                     data : d.data,
15300                     html : d.data[this.displayField]
15301                 };
15302                 
15303                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15304                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15305                 }
15306             }
15307             row.removeClass('selected');
15308             if(!this.multiple && this.valueField &&
15309                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15310             {
15311                 // radio buttons..
15312                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15313                 row.addClass('selected');
15314             }
15315             
15316             if(this.multiple && this.valueField &&
15317                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15318             {
15319                 
15320                 // checkboxes...
15321                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15322                 this.tickItems.push(d.data);
15323             }
15324             
15325             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15326             
15327         }, this);
15328         
15329         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15330         
15331         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15332
15333         if(this.modalTitle.length){
15334             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15335         }
15336
15337         var listHeight = this.touchViewListGroup.getHeight();
15338         
15339         var _this = this;
15340         
15341         if(firstChecked && listHeight > bodyHeight){
15342             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15343         }
15344         
15345     },
15346     
15347     onTouchViewLoadException : function()
15348     {
15349         this.hideTouchView();
15350     },
15351     
15352     onTouchViewEmptyResults : function()
15353     {
15354         this.clearTouchView();
15355         
15356         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15357         
15358         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15359         
15360     },
15361     
15362     clearTouchView : function()
15363     {
15364         this.touchViewListGroup.dom.innerHTML = '';
15365     },
15366     
15367     onTouchViewClick : function(e, el, o)
15368     {
15369         e.preventDefault();
15370         
15371         var row = o.row;
15372         var rowIndex = o.rowIndex;
15373         
15374         var r = this.store.getAt(rowIndex);
15375         
15376         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15377             
15378             if(!this.multiple){
15379                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15380                     c.dom.removeAttribute('checked');
15381                 }, this);
15382
15383                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15384
15385                 this.setFromData(r.data);
15386
15387                 var close = this.closeTriggerEl();
15388
15389                 if(close){
15390                     close.show();
15391                 }
15392
15393                 this.hideTouchView();
15394
15395                 this.fireEvent('select', this, r, rowIndex);
15396
15397                 return;
15398             }
15399
15400             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15401                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15402                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15403                 return;
15404             }
15405
15406             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15407             this.addItem(r.data);
15408             this.tickItems.push(r.data);
15409         }
15410     },
15411     
15412     getAutoCreateNativeIOS : function()
15413     {
15414         var cfg = {
15415             cls: 'form-group' //input-group,
15416         };
15417         
15418         var combobox =  {
15419             tag: 'select',
15420             cls : 'roo-ios-select'
15421         };
15422         
15423         if (this.name) {
15424             combobox.name = this.name;
15425         }
15426         
15427         if (this.disabled) {
15428             combobox.disabled = true;
15429         }
15430         
15431         var settings = this;
15432         
15433         ['xs','sm','md','lg'].map(function(size){
15434             if (settings[size]) {
15435                 cfg.cls += ' col-' + size + '-' + settings[size];
15436             }
15437         });
15438         
15439         cfg.cn = combobox;
15440         
15441         return cfg;
15442         
15443     },
15444     
15445     initIOSView : function()
15446     {
15447         this.store.on('load', this.onIOSViewLoad, this);
15448         
15449         return;
15450     },
15451     
15452     onIOSViewLoad : function()
15453     {
15454         if(this.store.getCount() < 1){
15455             return;
15456         }
15457         
15458         this.clearIOSView();
15459         
15460         if(this.allowBlank) {
15461             
15462             var default_text = '-- SELECT --';
15463             
15464             if(this.placeholder.length){
15465                 default_text = this.placeholder;
15466             }
15467             
15468             if(this.emptyTitle.length){
15469                 default_text += ' - ' + this.emptyTitle + ' -';
15470             }
15471             
15472             var opt = this.inputEl().createChild({
15473                 tag: 'option',
15474                 value : 0,
15475                 html : default_text
15476             });
15477             
15478             var o = {};
15479             o[this.valueField] = 0;
15480             o[this.displayField] = default_text;
15481             
15482             this.ios_options.push({
15483                 data : o,
15484                 el : opt
15485             });
15486             
15487         }
15488         
15489         this.store.data.each(function(d, rowIndex){
15490             
15491             var html = '';
15492             
15493             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15494                 html = d.data[this.displayField];
15495             }
15496             
15497             var value = '';
15498             
15499             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15500                 value = d.data[this.valueField];
15501             }
15502             
15503             var option = {
15504                 tag: 'option',
15505                 value : value,
15506                 html : html
15507             };
15508             
15509             if(this.value == d.data[this.valueField]){
15510                 option['selected'] = true;
15511             }
15512             
15513             var opt = this.inputEl().createChild(option);
15514             
15515             this.ios_options.push({
15516                 data : d.data,
15517                 el : opt
15518             });
15519             
15520         }, this);
15521         
15522         this.inputEl().on('change', function(){
15523            this.fireEvent('select', this);
15524         }, this);
15525         
15526     },
15527     
15528     clearIOSView: function()
15529     {
15530         this.inputEl().dom.innerHTML = '';
15531         
15532         this.ios_options = [];
15533     },
15534     
15535     setIOSValue: function(v)
15536     {
15537         this.value = v;
15538         
15539         if(!this.ios_options){
15540             return;
15541         }
15542         
15543         Roo.each(this.ios_options, function(opts){
15544            
15545            opts.el.dom.removeAttribute('selected');
15546            
15547            if(opts.data[this.valueField] != v){
15548                return;
15549            }
15550            
15551            opts.el.dom.setAttribute('selected', true);
15552            
15553         }, this);
15554     }
15555
15556     /** 
15557     * @cfg {Boolean} grow 
15558     * @hide 
15559     */
15560     /** 
15561     * @cfg {Number} growMin 
15562     * @hide 
15563     */
15564     /** 
15565     * @cfg {Number} growMax 
15566     * @hide 
15567     */
15568     /**
15569      * @hide
15570      * @method autoSize
15571      */
15572 });
15573
15574 Roo.apply(Roo.bootstrap.ComboBox,  {
15575     
15576     header : {
15577         tag: 'div',
15578         cls: 'modal-header',
15579         cn: [
15580             {
15581                 tag: 'h4',
15582                 cls: 'modal-title'
15583             }
15584         ]
15585     },
15586     
15587     body : {
15588         tag: 'div',
15589         cls: 'modal-body',
15590         cn: [
15591             {
15592                 tag: 'ul',
15593                 cls: 'list-group'
15594             }
15595         ]
15596     },
15597     
15598     listItemRadio : {
15599         tag: 'li',
15600         cls: 'list-group-item',
15601         cn: [
15602             {
15603                 tag: 'span',
15604                 cls: 'roo-combobox-list-group-item-value'
15605             },
15606             {
15607                 tag: 'div',
15608                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15609                 cn: [
15610                     {
15611                         tag: 'input',
15612                         type: 'radio'
15613                     },
15614                     {
15615                         tag: 'label'
15616                     }
15617                 ]
15618             }
15619         ]
15620     },
15621     
15622     listItemCheckbox : {
15623         tag: 'li',
15624         cls: 'list-group-item',
15625         cn: [
15626             {
15627                 tag: 'span',
15628                 cls: 'roo-combobox-list-group-item-value'
15629             },
15630             {
15631                 tag: 'div',
15632                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15633                 cn: [
15634                     {
15635                         tag: 'input',
15636                         type: 'checkbox'
15637                     },
15638                     {
15639                         tag: 'label'
15640                     }
15641                 ]
15642             }
15643         ]
15644     },
15645     
15646     emptyResult : {
15647         tag: 'div',
15648         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15649     },
15650     
15651     footer : {
15652         tag: 'div',
15653         cls: 'modal-footer',
15654         cn: [
15655             {
15656                 tag: 'div',
15657                 cls: 'row',
15658                 cn: [
15659                     {
15660                         tag: 'div',
15661                         cls: 'col-xs-6 text-left',
15662                         cn: {
15663                             tag: 'button',
15664                             cls: 'btn btn-danger roo-touch-view-cancel',
15665                             html: 'Cancel'
15666                         }
15667                     },
15668                     {
15669                         tag: 'div',
15670                         cls: 'col-xs-6 text-right',
15671                         cn: {
15672                             tag: 'button',
15673                             cls: 'btn btn-success roo-touch-view-ok',
15674                             html: 'OK'
15675                         }
15676                     }
15677                 ]
15678             }
15679         ]
15680         
15681     }
15682 });
15683
15684 Roo.apply(Roo.bootstrap.ComboBox,  {
15685     
15686     touchViewTemplate : {
15687         tag: 'div',
15688         cls: 'modal fade roo-combobox-touch-view',
15689         cn: [
15690             {
15691                 tag: 'div',
15692                 cls: 'modal-dialog',
15693                 style : 'position:fixed', // we have to fix position....
15694                 cn: [
15695                     {
15696                         tag: 'div',
15697                         cls: 'modal-content',
15698                         cn: [
15699                             Roo.bootstrap.ComboBox.header,
15700                             Roo.bootstrap.ComboBox.body,
15701                             Roo.bootstrap.ComboBox.footer
15702                         ]
15703                     }
15704                 ]
15705             }
15706         ]
15707     }
15708 });/*
15709  * Based on:
15710  * Ext JS Library 1.1.1
15711  * Copyright(c) 2006-2007, Ext JS, LLC.
15712  *
15713  * Originally Released Under LGPL - original licence link has changed is not relivant.
15714  *
15715  * Fork - LGPL
15716  * <script type="text/javascript">
15717  */
15718
15719 /**
15720  * @class Roo.View
15721  * @extends Roo.util.Observable
15722  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15723  * This class also supports single and multi selection modes. <br>
15724  * Create a data model bound view:
15725  <pre><code>
15726  var store = new Roo.data.Store(...);
15727
15728  var view = new Roo.View({
15729     el : "my-element",
15730     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15731  
15732     singleSelect: true,
15733     selectedClass: "ydataview-selected",
15734     store: store
15735  });
15736
15737  // listen for node click?
15738  view.on("click", function(vw, index, node, e){
15739  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15740  });
15741
15742  // load XML data
15743  dataModel.load("foobar.xml");
15744  </code></pre>
15745  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15746  * <br><br>
15747  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15748  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15749  * 
15750  * Note: old style constructor is still suported (container, template, config)
15751  * 
15752  * @constructor
15753  * Create a new View
15754  * @param {Object} config The config object
15755  * 
15756  */
15757 Roo.View = function(config, depreciated_tpl, depreciated_config){
15758     
15759     this.parent = false;
15760     
15761     if (typeof(depreciated_tpl) == 'undefined') {
15762         // new way.. - universal constructor.
15763         Roo.apply(this, config);
15764         this.el  = Roo.get(this.el);
15765     } else {
15766         // old format..
15767         this.el  = Roo.get(config);
15768         this.tpl = depreciated_tpl;
15769         Roo.apply(this, depreciated_config);
15770     }
15771     this.wrapEl  = this.el.wrap().wrap();
15772     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15773     
15774     
15775     if(typeof(this.tpl) == "string"){
15776         this.tpl = new Roo.Template(this.tpl);
15777     } else {
15778         // support xtype ctors..
15779         this.tpl = new Roo.factory(this.tpl, Roo);
15780     }
15781     
15782     
15783     this.tpl.compile();
15784     
15785     /** @private */
15786     this.addEvents({
15787         /**
15788          * @event beforeclick
15789          * Fires before a click is processed. Returns false to cancel the default action.
15790          * @param {Roo.View} this
15791          * @param {Number} index The index of the target node
15792          * @param {HTMLElement} node The target node
15793          * @param {Roo.EventObject} e The raw event object
15794          */
15795             "beforeclick" : true,
15796         /**
15797          * @event click
15798          * Fires when a template node is clicked.
15799          * @param {Roo.View} this
15800          * @param {Number} index The index of the target node
15801          * @param {HTMLElement} node The target node
15802          * @param {Roo.EventObject} e The raw event object
15803          */
15804             "click" : true,
15805         /**
15806          * @event dblclick
15807          * Fires when a template node is double clicked.
15808          * @param {Roo.View} this
15809          * @param {Number} index The index of the target node
15810          * @param {HTMLElement} node The target node
15811          * @param {Roo.EventObject} e The raw event object
15812          */
15813             "dblclick" : true,
15814         /**
15815          * @event contextmenu
15816          * Fires when a template node is right clicked.
15817          * @param {Roo.View} this
15818          * @param {Number} index The index of the target node
15819          * @param {HTMLElement} node The target node
15820          * @param {Roo.EventObject} e The raw event object
15821          */
15822             "contextmenu" : true,
15823         /**
15824          * @event selectionchange
15825          * Fires when the selected nodes change.
15826          * @param {Roo.View} this
15827          * @param {Array} selections Array of the selected nodes
15828          */
15829             "selectionchange" : true,
15830     
15831         /**
15832          * @event beforeselect
15833          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15834          * @param {Roo.View} this
15835          * @param {HTMLElement} node The node to be selected
15836          * @param {Array} selections Array of currently selected nodes
15837          */
15838             "beforeselect" : true,
15839         /**
15840          * @event preparedata
15841          * Fires on every row to render, to allow you to change the data.
15842          * @param {Roo.View} this
15843          * @param {Object} data to be rendered (change this)
15844          */
15845           "preparedata" : true
15846           
15847           
15848         });
15849
15850
15851
15852     this.el.on({
15853         "click": this.onClick,
15854         "dblclick": this.onDblClick,
15855         "contextmenu": this.onContextMenu,
15856         scope:this
15857     });
15858
15859     this.selections = [];
15860     this.nodes = [];
15861     this.cmp = new Roo.CompositeElementLite([]);
15862     if(this.store){
15863         this.store = Roo.factory(this.store, Roo.data);
15864         this.setStore(this.store, true);
15865     }
15866     
15867     if ( this.footer && this.footer.xtype) {
15868            
15869          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15870         
15871         this.footer.dataSource = this.store;
15872         this.footer.container = fctr;
15873         this.footer = Roo.factory(this.footer, Roo);
15874         fctr.insertFirst(this.el);
15875         
15876         // this is a bit insane - as the paging toolbar seems to detach the el..
15877 //        dom.parentNode.parentNode.parentNode
15878          // they get detached?
15879     }
15880     
15881     
15882     Roo.View.superclass.constructor.call(this);
15883     
15884     
15885 };
15886
15887 Roo.extend(Roo.View, Roo.util.Observable, {
15888     
15889      /**
15890      * @cfg {Roo.data.Store} store Data store to load data from.
15891      */
15892     store : false,
15893     
15894     /**
15895      * @cfg {String|Roo.Element} el The container element.
15896      */
15897     el : '',
15898     
15899     /**
15900      * @cfg {String|Roo.Template} tpl The template used by this View 
15901      */
15902     tpl : false,
15903     /**
15904      * @cfg {String} dataName the named area of the template to use as the data area
15905      *                          Works with domtemplates roo-name="name"
15906      */
15907     dataName: false,
15908     /**
15909      * @cfg {String} selectedClass The css class to add to selected nodes
15910      */
15911     selectedClass : "x-view-selected",
15912      /**
15913      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15914      */
15915     emptyText : "",
15916     
15917     /**
15918      * @cfg {String} text to display on mask (default Loading)
15919      */
15920     mask : false,
15921     /**
15922      * @cfg {Boolean} multiSelect Allow multiple selection
15923      */
15924     multiSelect : false,
15925     /**
15926      * @cfg {Boolean} singleSelect Allow single selection
15927      */
15928     singleSelect:  false,
15929     
15930     /**
15931      * @cfg {Boolean} toggleSelect - selecting 
15932      */
15933     toggleSelect : false,
15934     
15935     /**
15936      * @cfg {Boolean} tickable - selecting 
15937      */
15938     tickable : false,
15939     
15940     /**
15941      * Returns the element this view is bound to.
15942      * @return {Roo.Element}
15943      */
15944     getEl : function(){
15945         return this.wrapEl;
15946     },
15947     
15948     
15949
15950     /**
15951      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15952      */
15953     refresh : function(){
15954         //Roo.log('refresh');
15955         var t = this.tpl;
15956         
15957         // if we are using something like 'domtemplate', then
15958         // the what gets used is:
15959         // t.applySubtemplate(NAME, data, wrapping data..)
15960         // the outer template then get' applied with
15961         //     the store 'extra data'
15962         // and the body get's added to the
15963         //      roo-name="data" node?
15964         //      <span class='roo-tpl-{name}'></span> ?????
15965         
15966         
15967         
15968         this.clearSelections();
15969         this.el.update("");
15970         var html = [];
15971         var records = this.store.getRange();
15972         if(records.length < 1) {
15973             
15974             // is this valid??  = should it render a template??
15975             
15976             this.el.update(this.emptyText);
15977             return;
15978         }
15979         var el = this.el;
15980         if (this.dataName) {
15981             this.el.update(t.apply(this.store.meta)); //????
15982             el = this.el.child('.roo-tpl-' + this.dataName);
15983         }
15984         
15985         for(var i = 0, len = records.length; i < len; i++){
15986             var data = this.prepareData(records[i].data, i, records[i]);
15987             this.fireEvent("preparedata", this, data, i, records[i]);
15988             
15989             var d = Roo.apply({}, data);
15990             
15991             if(this.tickable){
15992                 Roo.apply(d, {'roo-id' : Roo.id()});
15993                 
15994                 var _this = this;
15995             
15996                 Roo.each(this.parent.item, function(item){
15997                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15998                         return;
15999                     }
16000                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16001                 });
16002             }
16003             
16004             html[html.length] = Roo.util.Format.trim(
16005                 this.dataName ?
16006                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16007                     t.apply(d)
16008             );
16009         }
16010         
16011         
16012         
16013         el.update(html.join(""));
16014         this.nodes = el.dom.childNodes;
16015         this.updateIndexes(0);
16016     },
16017     
16018
16019     /**
16020      * Function to override to reformat the data that is sent to
16021      * the template for each node.
16022      * DEPRICATED - use the preparedata event handler.
16023      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16024      * a JSON object for an UpdateManager bound view).
16025      */
16026     prepareData : function(data, index, record)
16027     {
16028         this.fireEvent("preparedata", this, data, index, record);
16029         return data;
16030     },
16031
16032     onUpdate : function(ds, record){
16033         // Roo.log('on update');   
16034         this.clearSelections();
16035         var index = this.store.indexOf(record);
16036         var n = this.nodes[index];
16037         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16038         n.parentNode.removeChild(n);
16039         this.updateIndexes(index, index);
16040     },
16041
16042     
16043     
16044 // --------- FIXME     
16045     onAdd : function(ds, records, index)
16046     {
16047         //Roo.log(['on Add', ds, records, index] );        
16048         this.clearSelections();
16049         if(this.nodes.length == 0){
16050             this.refresh();
16051             return;
16052         }
16053         var n = this.nodes[index];
16054         for(var i = 0, len = records.length; i < len; i++){
16055             var d = this.prepareData(records[i].data, i, records[i]);
16056             if(n){
16057                 this.tpl.insertBefore(n, d);
16058             }else{
16059                 
16060                 this.tpl.append(this.el, d);
16061             }
16062         }
16063         this.updateIndexes(index);
16064     },
16065
16066     onRemove : function(ds, record, index){
16067        // Roo.log('onRemove');
16068         this.clearSelections();
16069         var el = this.dataName  ?
16070             this.el.child('.roo-tpl-' + this.dataName) :
16071             this.el; 
16072         
16073         el.dom.removeChild(this.nodes[index]);
16074         this.updateIndexes(index);
16075     },
16076
16077     /**
16078      * Refresh an individual node.
16079      * @param {Number} index
16080      */
16081     refreshNode : function(index){
16082         this.onUpdate(this.store, this.store.getAt(index));
16083     },
16084
16085     updateIndexes : function(startIndex, endIndex){
16086         var ns = this.nodes;
16087         startIndex = startIndex || 0;
16088         endIndex = endIndex || ns.length - 1;
16089         for(var i = startIndex; i <= endIndex; i++){
16090             ns[i].nodeIndex = i;
16091         }
16092     },
16093
16094     /**
16095      * Changes the data store this view uses and refresh the view.
16096      * @param {Store} store
16097      */
16098     setStore : function(store, initial){
16099         if(!initial && this.store){
16100             this.store.un("datachanged", this.refresh);
16101             this.store.un("add", this.onAdd);
16102             this.store.un("remove", this.onRemove);
16103             this.store.un("update", this.onUpdate);
16104             this.store.un("clear", this.refresh);
16105             this.store.un("beforeload", this.onBeforeLoad);
16106             this.store.un("load", this.onLoad);
16107             this.store.un("loadexception", this.onLoad);
16108         }
16109         if(store){
16110           
16111             store.on("datachanged", this.refresh, this);
16112             store.on("add", this.onAdd, this);
16113             store.on("remove", this.onRemove, this);
16114             store.on("update", this.onUpdate, this);
16115             store.on("clear", this.refresh, this);
16116             store.on("beforeload", this.onBeforeLoad, this);
16117             store.on("load", this.onLoad, this);
16118             store.on("loadexception", this.onLoad, this);
16119         }
16120         
16121         if(store){
16122             this.refresh();
16123         }
16124     },
16125     /**
16126      * onbeforeLoad - masks the loading area.
16127      *
16128      */
16129     onBeforeLoad : function(store,opts)
16130     {
16131          //Roo.log('onBeforeLoad');   
16132         if (!opts.add) {
16133             this.el.update("");
16134         }
16135         this.el.mask(this.mask ? this.mask : "Loading" ); 
16136     },
16137     onLoad : function ()
16138     {
16139         this.el.unmask();
16140     },
16141     
16142
16143     /**
16144      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16145      * @param {HTMLElement} node
16146      * @return {HTMLElement} The template node
16147      */
16148     findItemFromChild : function(node){
16149         var el = this.dataName  ?
16150             this.el.child('.roo-tpl-' + this.dataName,true) :
16151             this.el.dom; 
16152         
16153         if(!node || node.parentNode == el){
16154                     return node;
16155             }
16156             var p = node.parentNode;
16157             while(p && p != el){
16158             if(p.parentNode == el){
16159                 return p;
16160             }
16161             p = p.parentNode;
16162         }
16163             return null;
16164     },
16165
16166     /** @ignore */
16167     onClick : function(e){
16168         var item = this.findItemFromChild(e.getTarget());
16169         if(item){
16170             var index = this.indexOf(item);
16171             if(this.onItemClick(item, index, e) !== false){
16172                 this.fireEvent("click", this, index, item, e);
16173             }
16174         }else{
16175             this.clearSelections();
16176         }
16177     },
16178
16179     /** @ignore */
16180     onContextMenu : function(e){
16181         var item = this.findItemFromChild(e.getTarget());
16182         if(item){
16183             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16184         }
16185     },
16186
16187     /** @ignore */
16188     onDblClick : function(e){
16189         var item = this.findItemFromChild(e.getTarget());
16190         if(item){
16191             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16192         }
16193     },
16194
16195     onItemClick : function(item, index, e)
16196     {
16197         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16198             return false;
16199         }
16200         if (this.toggleSelect) {
16201             var m = this.isSelected(item) ? 'unselect' : 'select';
16202             //Roo.log(m);
16203             var _t = this;
16204             _t[m](item, true, false);
16205             return true;
16206         }
16207         if(this.multiSelect || this.singleSelect){
16208             if(this.multiSelect && e.shiftKey && this.lastSelection){
16209                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16210             }else{
16211                 this.select(item, this.multiSelect && e.ctrlKey);
16212                 this.lastSelection = item;
16213             }
16214             
16215             if(!this.tickable){
16216                 e.preventDefault();
16217             }
16218             
16219         }
16220         return true;
16221     },
16222
16223     /**
16224      * Get the number of selected nodes.
16225      * @return {Number}
16226      */
16227     getSelectionCount : function(){
16228         return this.selections.length;
16229     },
16230
16231     /**
16232      * Get the currently selected nodes.
16233      * @return {Array} An array of HTMLElements
16234      */
16235     getSelectedNodes : function(){
16236         return this.selections;
16237     },
16238
16239     /**
16240      * Get the indexes of the selected nodes.
16241      * @return {Array}
16242      */
16243     getSelectedIndexes : function(){
16244         var indexes = [], s = this.selections;
16245         for(var i = 0, len = s.length; i < len; i++){
16246             indexes.push(s[i].nodeIndex);
16247         }
16248         return indexes;
16249     },
16250
16251     /**
16252      * Clear all selections
16253      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16254      */
16255     clearSelections : function(suppressEvent){
16256         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16257             this.cmp.elements = this.selections;
16258             this.cmp.removeClass(this.selectedClass);
16259             this.selections = [];
16260             if(!suppressEvent){
16261                 this.fireEvent("selectionchange", this, this.selections);
16262             }
16263         }
16264     },
16265
16266     /**
16267      * Returns true if the passed node is selected
16268      * @param {HTMLElement/Number} node The node or node index
16269      * @return {Boolean}
16270      */
16271     isSelected : function(node){
16272         var s = this.selections;
16273         if(s.length < 1){
16274             return false;
16275         }
16276         node = this.getNode(node);
16277         return s.indexOf(node) !== -1;
16278     },
16279
16280     /**
16281      * Selects nodes.
16282      * @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
16283      * @param {Boolean} keepExisting (optional) true to keep existing selections
16284      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16285      */
16286     select : function(nodeInfo, keepExisting, suppressEvent){
16287         if(nodeInfo instanceof Array){
16288             if(!keepExisting){
16289                 this.clearSelections(true);
16290             }
16291             for(var i = 0, len = nodeInfo.length; i < len; i++){
16292                 this.select(nodeInfo[i], true, true);
16293             }
16294             return;
16295         } 
16296         var node = this.getNode(nodeInfo);
16297         if(!node || this.isSelected(node)){
16298             return; // already selected.
16299         }
16300         if(!keepExisting){
16301             this.clearSelections(true);
16302         }
16303         
16304         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16305             Roo.fly(node).addClass(this.selectedClass);
16306             this.selections.push(node);
16307             if(!suppressEvent){
16308                 this.fireEvent("selectionchange", this, this.selections);
16309             }
16310         }
16311         
16312         
16313     },
16314       /**
16315      * Unselects nodes.
16316      * @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
16317      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16318      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16319      */
16320     unselect : function(nodeInfo, keepExisting, suppressEvent)
16321     {
16322         if(nodeInfo instanceof Array){
16323             Roo.each(this.selections, function(s) {
16324                 this.unselect(s, nodeInfo);
16325             }, this);
16326             return;
16327         }
16328         var node = this.getNode(nodeInfo);
16329         if(!node || !this.isSelected(node)){
16330             //Roo.log("not selected");
16331             return; // not selected.
16332         }
16333         // fireevent???
16334         var ns = [];
16335         Roo.each(this.selections, function(s) {
16336             if (s == node ) {
16337                 Roo.fly(node).removeClass(this.selectedClass);
16338
16339                 return;
16340             }
16341             ns.push(s);
16342         },this);
16343         
16344         this.selections= ns;
16345         this.fireEvent("selectionchange", this, this.selections);
16346     },
16347
16348     /**
16349      * Gets a template node.
16350      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16351      * @return {HTMLElement} The node or null if it wasn't found
16352      */
16353     getNode : function(nodeInfo){
16354         if(typeof nodeInfo == "string"){
16355             return document.getElementById(nodeInfo);
16356         }else if(typeof nodeInfo == "number"){
16357             return this.nodes[nodeInfo];
16358         }
16359         return nodeInfo;
16360     },
16361
16362     /**
16363      * Gets a range template nodes.
16364      * @param {Number} startIndex
16365      * @param {Number} endIndex
16366      * @return {Array} An array of nodes
16367      */
16368     getNodes : function(start, end){
16369         var ns = this.nodes;
16370         start = start || 0;
16371         end = typeof end == "undefined" ? ns.length - 1 : end;
16372         var nodes = [];
16373         if(start <= end){
16374             for(var i = start; i <= end; i++){
16375                 nodes.push(ns[i]);
16376             }
16377         } else{
16378             for(var i = start; i >= end; i--){
16379                 nodes.push(ns[i]);
16380             }
16381         }
16382         return nodes;
16383     },
16384
16385     /**
16386      * Finds the index of the passed node
16387      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16388      * @return {Number} The index of the node or -1
16389      */
16390     indexOf : function(node){
16391         node = this.getNode(node);
16392         if(typeof node.nodeIndex == "number"){
16393             return node.nodeIndex;
16394         }
16395         var ns = this.nodes;
16396         for(var i = 0, len = ns.length; i < len; i++){
16397             if(ns[i] == node){
16398                 return i;
16399             }
16400         }
16401         return -1;
16402     }
16403 });
16404 /*
16405  * - LGPL
16406  *
16407  * based on jquery fullcalendar
16408  * 
16409  */
16410
16411 Roo.bootstrap = Roo.bootstrap || {};
16412 /**
16413  * @class Roo.bootstrap.Calendar
16414  * @extends Roo.bootstrap.Component
16415  * Bootstrap Calendar class
16416  * @cfg {Boolean} loadMask (true|false) default false
16417  * @cfg {Object} header generate the user specific header of the calendar, default false
16418
16419  * @constructor
16420  * Create a new Container
16421  * @param {Object} config The config object
16422  */
16423
16424
16425
16426 Roo.bootstrap.Calendar = function(config){
16427     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16428      this.addEvents({
16429         /**
16430              * @event select
16431              * Fires when a date is selected
16432              * @param {DatePicker} this
16433              * @param {Date} date The selected date
16434              */
16435         'select': true,
16436         /**
16437              * @event monthchange
16438              * Fires when the displayed month changes 
16439              * @param {DatePicker} this
16440              * @param {Date} date The selected month
16441              */
16442         'monthchange': true,
16443         /**
16444              * @event evententer
16445              * Fires when mouse over an event
16446              * @param {Calendar} this
16447              * @param {event} Event
16448              */
16449         'evententer': true,
16450         /**
16451              * @event eventleave
16452              * Fires when the mouse leaves an
16453              * @param {Calendar} this
16454              * @param {event}
16455              */
16456         'eventleave': true,
16457         /**
16458              * @event eventclick
16459              * Fires when the mouse click an
16460              * @param {Calendar} this
16461              * @param {event}
16462              */
16463         'eventclick': true
16464         
16465     });
16466
16467 };
16468
16469 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16470     
16471      /**
16472      * @cfg {Number} startDay
16473      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16474      */
16475     startDay : 0,
16476     
16477     loadMask : false,
16478     
16479     header : false,
16480       
16481     getAutoCreate : function(){
16482         
16483         
16484         var fc_button = function(name, corner, style, content ) {
16485             return Roo.apply({},{
16486                 tag : 'span',
16487                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16488                          (corner.length ?
16489                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16490                             ''
16491                         ),
16492                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16493                 unselectable: 'on'
16494             });
16495         };
16496         
16497         var header = {};
16498         
16499         if(!this.header){
16500             header = {
16501                 tag : 'table',
16502                 cls : 'fc-header',
16503                 style : 'width:100%',
16504                 cn : [
16505                     {
16506                         tag: 'tr',
16507                         cn : [
16508                             {
16509                                 tag : 'td',
16510                                 cls : 'fc-header-left',
16511                                 cn : [
16512                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16513                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16514                                     { tag: 'span', cls: 'fc-header-space' },
16515                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16516
16517
16518                                 ]
16519                             },
16520
16521                             {
16522                                 tag : 'td',
16523                                 cls : 'fc-header-center',
16524                                 cn : [
16525                                     {
16526                                         tag: 'span',
16527                                         cls: 'fc-header-title',
16528                                         cn : {
16529                                             tag: 'H2',
16530                                             html : 'month / year'
16531                                         }
16532                                     }
16533
16534                                 ]
16535                             },
16536                             {
16537                                 tag : 'td',
16538                                 cls : 'fc-header-right',
16539                                 cn : [
16540                               /*      fc_button('month', 'left', '', 'month' ),
16541                                     fc_button('week', '', '', 'week' ),
16542                                     fc_button('day', 'right', '', 'day' )
16543                                 */    
16544
16545                                 ]
16546                             }
16547
16548                         ]
16549                     }
16550                 ]
16551             };
16552         }
16553         
16554         header = this.header;
16555         
16556        
16557         var cal_heads = function() {
16558             var ret = [];
16559             // fixme - handle this.
16560             
16561             for (var i =0; i < Date.dayNames.length; i++) {
16562                 var d = Date.dayNames[i];
16563                 ret.push({
16564                     tag: 'th',
16565                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16566                     html : d.substring(0,3)
16567                 });
16568                 
16569             }
16570             ret[0].cls += ' fc-first';
16571             ret[6].cls += ' fc-last';
16572             return ret;
16573         };
16574         var cal_cell = function(n) {
16575             return  {
16576                 tag: 'td',
16577                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16578                 cn : [
16579                     {
16580                         cn : [
16581                             {
16582                                 cls: 'fc-day-number',
16583                                 html: 'D'
16584                             },
16585                             {
16586                                 cls: 'fc-day-content',
16587                              
16588                                 cn : [
16589                                      {
16590                                         style: 'position: relative;' // height: 17px;
16591                                     }
16592                                 ]
16593                             }
16594                             
16595                             
16596                         ]
16597                     }
16598                 ]
16599                 
16600             }
16601         };
16602         var cal_rows = function() {
16603             
16604             var ret = [];
16605             for (var r = 0; r < 6; r++) {
16606                 var row= {
16607                     tag : 'tr',
16608                     cls : 'fc-week',
16609                     cn : []
16610                 };
16611                 
16612                 for (var i =0; i < Date.dayNames.length; i++) {
16613                     var d = Date.dayNames[i];
16614                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16615
16616                 }
16617                 row.cn[0].cls+=' fc-first';
16618                 row.cn[0].cn[0].style = 'min-height:90px';
16619                 row.cn[6].cls+=' fc-last';
16620                 ret.push(row);
16621                 
16622             }
16623             ret[0].cls += ' fc-first';
16624             ret[4].cls += ' fc-prev-last';
16625             ret[5].cls += ' fc-last';
16626             return ret;
16627             
16628         };
16629         
16630         var cal_table = {
16631             tag: 'table',
16632             cls: 'fc-border-separate',
16633             style : 'width:100%',
16634             cellspacing  : 0,
16635             cn : [
16636                 { 
16637                     tag: 'thead',
16638                     cn : [
16639                         { 
16640                             tag: 'tr',
16641                             cls : 'fc-first fc-last',
16642                             cn : cal_heads()
16643                         }
16644                     ]
16645                 },
16646                 { 
16647                     tag: 'tbody',
16648                     cn : cal_rows()
16649                 }
16650                   
16651             ]
16652         };
16653          
16654          var cfg = {
16655             cls : 'fc fc-ltr',
16656             cn : [
16657                 header,
16658                 {
16659                     cls : 'fc-content',
16660                     style : "position: relative;",
16661                     cn : [
16662                         {
16663                             cls : 'fc-view fc-view-month fc-grid',
16664                             style : 'position: relative',
16665                             unselectable : 'on',
16666                             cn : [
16667                                 {
16668                                     cls : 'fc-event-container',
16669                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16670                                 },
16671                                 cal_table
16672                             ]
16673                         }
16674                     ]
16675     
16676                 }
16677            ] 
16678             
16679         };
16680         
16681          
16682         
16683         return cfg;
16684     },
16685     
16686     
16687     initEvents : function()
16688     {
16689         if(!this.store){
16690             throw "can not find store for calendar";
16691         }
16692         
16693         var mark = {
16694             tag: "div",
16695             cls:"x-dlg-mask",
16696             style: "text-align:center",
16697             cn: [
16698                 {
16699                     tag: "div",
16700                     style: "background-color:white;width:50%;margin:250 auto",
16701                     cn: [
16702                         {
16703                             tag: "img",
16704                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16705                         },
16706                         {
16707                             tag: "span",
16708                             html: "Loading"
16709                         }
16710                         
16711                     ]
16712                 }
16713             ]
16714         };
16715         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16716         
16717         var size = this.el.select('.fc-content', true).first().getSize();
16718         this.maskEl.setSize(size.width, size.height);
16719         this.maskEl.enableDisplayMode("block");
16720         if(!this.loadMask){
16721             this.maskEl.hide();
16722         }
16723         
16724         this.store = Roo.factory(this.store, Roo.data);
16725         this.store.on('load', this.onLoad, this);
16726         this.store.on('beforeload', this.onBeforeLoad, this);
16727         
16728         this.resize();
16729         
16730         this.cells = this.el.select('.fc-day',true);
16731         //Roo.log(this.cells);
16732         this.textNodes = this.el.query('.fc-day-number');
16733         this.cells.addClassOnOver('fc-state-hover');
16734         
16735         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16736         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16737         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16738         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16739         
16740         this.on('monthchange', this.onMonthChange, this);
16741         
16742         this.update(new Date().clearTime());
16743     },
16744     
16745     resize : function() {
16746         var sz  = this.el.getSize();
16747         
16748         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16749         this.el.select('.fc-day-content div',true).setHeight(34);
16750     },
16751     
16752     
16753     // private
16754     showPrevMonth : function(e){
16755         this.update(this.activeDate.add("mo", -1));
16756     },
16757     showToday : function(e){
16758         this.update(new Date().clearTime());
16759     },
16760     // private
16761     showNextMonth : function(e){
16762         this.update(this.activeDate.add("mo", 1));
16763     },
16764
16765     // private
16766     showPrevYear : function(){
16767         this.update(this.activeDate.add("y", -1));
16768     },
16769
16770     // private
16771     showNextYear : function(){
16772         this.update(this.activeDate.add("y", 1));
16773     },
16774
16775     
16776    // private
16777     update : function(date)
16778     {
16779         var vd = this.activeDate;
16780         this.activeDate = date;
16781 //        if(vd && this.el){
16782 //            var t = date.getTime();
16783 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16784 //                Roo.log('using add remove');
16785 //                
16786 //                this.fireEvent('monthchange', this, date);
16787 //                
16788 //                this.cells.removeClass("fc-state-highlight");
16789 //                this.cells.each(function(c){
16790 //                   if(c.dateValue == t){
16791 //                       c.addClass("fc-state-highlight");
16792 //                       setTimeout(function(){
16793 //                            try{c.dom.firstChild.focus();}catch(e){}
16794 //                       }, 50);
16795 //                       return false;
16796 //                   }
16797 //                   return true;
16798 //                });
16799 //                return;
16800 //            }
16801 //        }
16802         
16803         var days = date.getDaysInMonth();
16804         
16805         var firstOfMonth = date.getFirstDateOfMonth();
16806         var startingPos = firstOfMonth.getDay()-this.startDay;
16807         
16808         if(startingPos < this.startDay){
16809             startingPos += 7;
16810         }
16811         
16812         var pm = date.add(Date.MONTH, -1);
16813         var prevStart = pm.getDaysInMonth()-startingPos;
16814 //        
16815         this.cells = this.el.select('.fc-day',true);
16816         this.textNodes = this.el.query('.fc-day-number');
16817         this.cells.addClassOnOver('fc-state-hover');
16818         
16819         var cells = this.cells.elements;
16820         var textEls = this.textNodes;
16821         
16822         Roo.each(cells, function(cell){
16823             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16824         });
16825         
16826         days += startingPos;
16827
16828         // convert everything to numbers so it's fast
16829         var day = 86400000;
16830         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16831         //Roo.log(d);
16832         //Roo.log(pm);
16833         //Roo.log(prevStart);
16834         
16835         var today = new Date().clearTime().getTime();
16836         var sel = date.clearTime().getTime();
16837         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16838         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16839         var ddMatch = this.disabledDatesRE;
16840         var ddText = this.disabledDatesText;
16841         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16842         var ddaysText = this.disabledDaysText;
16843         var format = this.format;
16844         
16845         var setCellClass = function(cal, cell){
16846             cell.row = 0;
16847             cell.events = [];
16848             cell.more = [];
16849             //Roo.log('set Cell Class');
16850             cell.title = "";
16851             var t = d.getTime();
16852             
16853             //Roo.log(d);
16854             
16855             cell.dateValue = t;
16856             if(t == today){
16857                 cell.className += " fc-today";
16858                 cell.className += " fc-state-highlight";
16859                 cell.title = cal.todayText;
16860             }
16861             if(t == sel){
16862                 // disable highlight in other month..
16863                 //cell.className += " fc-state-highlight";
16864                 
16865             }
16866             // disabling
16867             if(t < min) {
16868                 cell.className = " fc-state-disabled";
16869                 cell.title = cal.minText;
16870                 return;
16871             }
16872             if(t > max) {
16873                 cell.className = " fc-state-disabled";
16874                 cell.title = cal.maxText;
16875                 return;
16876             }
16877             if(ddays){
16878                 if(ddays.indexOf(d.getDay()) != -1){
16879                     cell.title = ddaysText;
16880                     cell.className = " fc-state-disabled";
16881                 }
16882             }
16883             if(ddMatch && format){
16884                 var fvalue = d.dateFormat(format);
16885                 if(ddMatch.test(fvalue)){
16886                     cell.title = ddText.replace("%0", fvalue);
16887                     cell.className = " fc-state-disabled";
16888                 }
16889             }
16890             
16891             if (!cell.initialClassName) {
16892                 cell.initialClassName = cell.dom.className;
16893             }
16894             
16895             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16896         };
16897
16898         var i = 0;
16899         
16900         for(; i < startingPos; i++) {
16901             textEls[i].innerHTML = (++prevStart);
16902             d.setDate(d.getDate()+1);
16903             
16904             cells[i].className = "fc-past fc-other-month";
16905             setCellClass(this, cells[i]);
16906         }
16907         
16908         var intDay = 0;
16909         
16910         for(; i < days; i++){
16911             intDay = i - startingPos + 1;
16912             textEls[i].innerHTML = (intDay);
16913             d.setDate(d.getDate()+1);
16914             
16915             cells[i].className = ''; // "x-date-active";
16916             setCellClass(this, cells[i]);
16917         }
16918         var extraDays = 0;
16919         
16920         for(; i < 42; i++) {
16921             textEls[i].innerHTML = (++extraDays);
16922             d.setDate(d.getDate()+1);
16923             
16924             cells[i].className = "fc-future fc-other-month";
16925             setCellClass(this, cells[i]);
16926         }
16927         
16928         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16929         
16930         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16931         
16932         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16933         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16934         
16935         if(totalRows != 6){
16936             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16937             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16938         }
16939         
16940         this.fireEvent('monthchange', this, date);
16941         
16942         
16943         /*
16944         if(!this.internalRender){
16945             var main = this.el.dom.firstChild;
16946             var w = main.offsetWidth;
16947             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16948             Roo.fly(main).setWidth(w);
16949             this.internalRender = true;
16950             // opera does not respect the auto grow header center column
16951             // then, after it gets a width opera refuses to recalculate
16952             // without a second pass
16953             if(Roo.isOpera && !this.secondPass){
16954                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16955                 this.secondPass = true;
16956                 this.update.defer(10, this, [date]);
16957             }
16958         }
16959         */
16960         
16961     },
16962     
16963     findCell : function(dt) {
16964         dt = dt.clearTime().getTime();
16965         var ret = false;
16966         this.cells.each(function(c){
16967             //Roo.log("check " +c.dateValue + '?=' + dt);
16968             if(c.dateValue == dt){
16969                 ret = c;
16970                 return false;
16971             }
16972             return true;
16973         });
16974         
16975         return ret;
16976     },
16977     
16978     findCells : function(ev) {
16979         var s = ev.start.clone().clearTime().getTime();
16980        // Roo.log(s);
16981         var e= ev.end.clone().clearTime().getTime();
16982        // Roo.log(e);
16983         var ret = [];
16984         this.cells.each(function(c){
16985              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16986             
16987             if(c.dateValue > e){
16988                 return ;
16989             }
16990             if(c.dateValue < s){
16991                 return ;
16992             }
16993             ret.push(c);
16994         });
16995         
16996         return ret;    
16997     },
16998     
16999 //    findBestRow: function(cells)
17000 //    {
17001 //        var ret = 0;
17002 //        
17003 //        for (var i =0 ; i < cells.length;i++) {
17004 //            ret  = Math.max(cells[i].rows || 0,ret);
17005 //        }
17006 //        return ret;
17007 //        
17008 //    },
17009     
17010     
17011     addItem : function(ev)
17012     {
17013         // look for vertical location slot in
17014         var cells = this.findCells(ev);
17015         
17016 //        ev.row = this.findBestRow(cells);
17017         
17018         // work out the location.
17019         
17020         var crow = false;
17021         var rows = [];
17022         for(var i =0; i < cells.length; i++) {
17023             
17024             cells[i].row = cells[0].row;
17025             
17026             if(i == 0){
17027                 cells[i].row = cells[i].row + 1;
17028             }
17029             
17030             if (!crow) {
17031                 crow = {
17032                     start : cells[i],
17033                     end :  cells[i]
17034                 };
17035                 continue;
17036             }
17037             if (crow.start.getY() == cells[i].getY()) {
17038                 // on same row.
17039                 crow.end = cells[i];
17040                 continue;
17041             }
17042             // different row.
17043             rows.push(crow);
17044             crow = {
17045                 start: cells[i],
17046                 end : cells[i]
17047             };
17048             
17049         }
17050         
17051         rows.push(crow);
17052         ev.els = [];
17053         ev.rows = rows;
17054         ev.cells = cells;
17055         
17056         cells[0].events.push(ev);
17057         
17058         this.calevents.push(ev);
17059     },
17060     
17061     clearEvents: function() {
17062         
17063         if(!this.calevents){
17064             return;
17065         }
17066         
17067         Roo.each(this.cells.elements, function(c){
17068             c.row = 0;
17069             c.events = [];
17070             c.more = [];
17071         });
17072         
17073         Roo.each(this.calevents, function(e) {
17074             Roo.each(e.els, function(el) {
17075                 el.un('mouseenter' ,this.onEventEnter, this);
17076                 el.un('mouseleave' ,this.onEventLeave, this);
17077                 el.remove();
17078             },this);
17079         },this);
17080         
17081         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17082             e.remove();
17083         });
17084         
17085     },
17086     
17087     renderEvents: function()
17088     {   
17089         var _this = this;
17090         
17091         this.cells.each(function(c) {
17092             
17093             if(c.row < 5){
17094                 return;
17095             }
17096             
17097             var ev = c.events;
17098             
17099             var r = 4;
17100             if(c.row != c.events.length){
17101                 r = 4 - (4 - (c.row - c.events.length));
17102             }
17103             
17104             c.events = ev.slice(0, r);
17105             c.more = ev.slice(r);
17106             
17107             if(c.more.length && c.more.length == 1){
17108                 c.events.push(c.more.pop());
17109             }
17110             
17111             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17112             
17113         });
17114             
17115         this.cells.each(function(c) {
17116             
17117             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17118             
17119             
17120             for (var e = 0; e < c.events.length; e++){
17121                 var ev = c.events[e];
17122                 var rows = ev.rows;
17123                 
17124                 for(var i = 0; i < rows.length; i++) {
17125                 
17126                     // how many rows should it span..
17127
17128                     var  cfg = {
17129                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17130                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17131
17132                         unselectable : "on",
17133                         cn : [
17134                             {
17135                                 cls: 'fc-event-inner',
17136                                 cn : [
17137     //                                {
17138     //                                  tag:'span',
17139     //                                  cls: 'fc-event-time',
17140     //                                  html : cells.length > 1 ? '' : ev.time
17141     //                                },
17142                                     {
17143                                       tag:'span',
17144                                       cls: 'fc-event-title',
17145                                       html : String.format('{0}', ev.title)
17146                                     }
17147
17148
17149                                 ]
17150                             },
17151                             {
17152                                 cls: 'ui-resizable-handle ui-resizable-e',
17153                                 html : '&nbsp;&nbsp;&nbsp'
17154                             }
17155
17156                         ]
17157                     };
17158
17159                     if (i == 0) {
17160                         cfg.cls += ' fc-event-start';
17161                     }
17162                     if ((i+1) == rows.length) {
17163                         cfg.cls += ' fc-event-end';
17164                     }
17165
17166                     var ctr = _this.el.select('.fc-event-container',true).first();
17167                     var cg = ctr.createChild(cfg);
17168
17169                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17170                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17171
17172                     var r = (c.more.length) ? 1 : 0;
17173                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17174                     cg.setWidth(ebox.right - sbox.x -2);
17175
17176                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17177                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17178                     cg.on('click', _this.onEventClick, _this, ev);
17179
17180                     ev.els.push(cg);
17181                     
17182                 }
17183                 
17184             }
17185             
17186             
17187             if(c.more.length){
17188                 var  cfg = {
17189                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17190                     style : 'position: absolute',
17191                     unselectable : "on",
17192                     cn : [
17193                         {
17194                             cls: 'fc-event-inner',
17195                             cn : [
17196                                 {
17197                                   tag:'span',
17198                                   cls: 'fc-event-title',
17199                                   html : 'More'
17200                                 }
17201
17202
17203                             ]
17204                         },
17205                         {
17206                             cls: 'ui-resizable-handle ui-resizable-e',
17207                             html : '&nbsp;&nbsp;&nbsp'
17208                         }
17209
17210                     ]
17211                 };
17212
17213                 var ctr = _this.el.select('.fc-event-container',true).first();
17214                 var cg = ctr.createChild(cfg);
17215
17216                 var sbox = c.select('.fc-day-content',true).first().getBox();
17217                 var ebox = c.select('.fc-day-content',true).first().getBox();
17218                 //Roo.log(cg);
17219                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17220                 cg.setWidth(ebox.right - sbox.x -2);
17221
17222                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17223                 
17224             }
17225             
17226         });
17227         
17228         
17229         
17230     },
17231     
17232     onEventEnter: function (e, el,event,d) {
17233         this.fireEvent('evententer', this, el, event);
17234     },
17235     
17236     onEventLeave: function (e, el,event,d) {
17237         this.fireEvent('eventleave', this, el, event);
17238     },
17239     
17240     onEventClick: function (e, el,event,d) {
17241         this.fireEvent('eventclick', this, el, event);
17242     },
17243     
17244     onMonthChange: function () {
17245         this.store.load();
17246     },
17247     
17248     onMoreEventClick: function(e, el, more)
17249     {
17250         var _this = this;
17251         
17252         this.calpopover.placement = 'right';
17253         this.calpopover.setTitle('More');
17254         
17255         this.calpopover.setContent('');
17256         
17257         var ctr = this.calpopover.el.select('.popover-content', true).first();
17258         
17259         Roo.each(more, function(m){
17260             var cfg = {
17261                 cls : 'fc-event-hori fc-event-draggable',
17262                 html : m.title
17263             };
17264             var cg = ctr.createChild(cfg);
17265             
17266             cg.on('click', _this.onEventClick, _this, m);
17267         });
17268         
17269         this.calpopover.show(el);
17270         
17271         
17272     },
17273     
17274     onLoad: function () 
17275     {   
17276         this.calevents = [];
17277         var cal = this;
17278         
17279         if(this.store.getCount() > 0){
17280             this.store.data.each(function(d){
17281                cal.addItem({
17282                     id : d.data.id,
17283                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17284                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17285                     time : d.data.start_time,
17286                     title : d.data.title,
17287                     description : d.data.description,
17288                     venue : d.data.venue
17289                 });
17290             });
17291         }
17292         
17293         this.renderEvents();
17294         
17295         if(this.calevents.length && this.loadMask){
17296             this.maskEl.hide();
17297         }
17298     },
17299     
17300     onBeforeLoad: function()
17301     {
17302         this.clearEvents();
17303         if(this.loadMask){
17304             this.maskEl.show();
17305         }
17306     }
17307 });
17308
17309  
17310  /*
17311  * - LGPL
17312  *
17313  * element
17314  * 
17315  */
17316
17317 /**
17318  * @class Roo.bootstrap.Popover
17319  * @extends Roo.bootstrap.Component
17320  * Bootstrap Popover class
17321  * @cfg {String} html contents of the popover   (or false to use children..)
17322  * @cfg {String} title of popover (or false to hide)
17323  * @cfg {String} placement how it is placed
17324  * @cfg {String} trigger click || hover (or false to trigger manually)
17325  * @cfg {String} over what (parent or false to trigger manually.)
17326  * @cfg {Number} delay - delay before showing
17327  
17328  * @constructor
17329  * Create a new Popover
17330  * @param {Object} config The config object
17331  */
17332
17333 Roo.bootstrap.Popover = function(config){
17334     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17335     
17336     this.addEvents({
17337         // raw events
17338          /**
17339          * @event show
17340          * After the popover show
17341          * 
17342          * @param {Roo.bootstrap.Popover} this
17343          */
17344         "show" : true,
17345         /**
17346          * @event hide
17347          * After the popover hide
17348          * 
17349          * @param {Roo.bootstrap.Popover} this
17350          */
17351         "hide" : true
17352     });
17353 };
17354
17355 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17356     
17357     title: 'Fill in a title',
17358     html: false,
17359     
17360     placement : 'right',
17361     trigger : 'hover', // hover
17362     
17363     delay : 0,
17364     
17365     over: 'parent',
17366     
17367     can_build_overlaid : false,
17368     
17369     getChildContainer : function()
17370     {
17371         return this.el.select('.popover-content',true).first();
17372     },
17373     
17374     getAutoCreate : function(){
17375          
17376         var cfg = {
17377            cls : 'popover roo-dynamic',
17378            style: 'display:block',
17379            cn : [
17380                 {
17381                     cls : 'arrow'
17382                 },
17383                 {
17384                     cls : 'popover-inner',
17385                     cn : [
17386                         {
17387                             tag: 'h3',
17388                             cls: 'popover-title',
17389                             html : this.title
17390                         },
17391                         {
17392                             cls : 'popover-content',
17393                             html : this.html
17394                         }
17395                     ]
17396                     
17397                 }
17398            ]
17399         };
17400         
17401         return cfg;
17402     },
17403     setTitle: function(str)
17404     {
17405         this.title = str;
17406         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17407     },
17408     setContent: function(str)
17409     {
17410         this.html = str;
17411         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17412     },
17413     // as it get's added to the bottom of the page.
17414     onRender : function(ct, position)
17415     {
17416         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17417         if(!this.el){
17418             var cfg = Roo.apply({},  this.getAutoCreate());
17419             cfg.id = Roo.id();
17420             
17421             if (this.cls) {
17422                 cfg.cls += ' ' + this.cls;
17423             }
17424             if (this.style) {
17425                 cfg.style = this.style;
17426             }
17427             //Roo.log("adding to ");
17428             this.el = Roo.get(document.body).createChild(cfg, position);
17429 //            Roo.log(this.el);
17430         }
17431         this.initEvents();
17432     },
17433     
17434     initEvents : function()
17435     {
17436         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17437         this.el.enableDisplayMode('block');
17438         this.el.hide();
17439         if (this.over === false) {
17440             return; 
17441         }
17442         if (this.triggers === false) {
17443             return;
17444         }
17445         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17446         var triggers = this.trigger ? this.trigger.split(' ') : [];
17447         Roo.each(triggers, function(trigger) {
17448         
17449             if (trigger == 'click') {
17450                 on_el.on('click', this.toggle, this);
17451             } else if (trigger != 'manual') {
17452                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17453                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17454       
17455                 on_el.on(eventIn  ,this.enter, this);
17456                 on_el.on(eventOut, this.leave, this);
17457             }
17458         }, this);
17459         
17460     },
17461     
17462     
17463     // private
17464     timeout : null,
17465     hoverState : null,
17466     
17467     toggle : function () {
17468         this.hoverState == 'in' ? this.leave() : this.enter();
17469     },
17470     
17471     enter : function () {
17472         
17473         clearTimeout(this.timeout);
17474     
17475         this.hoverState = 'in';
17476     
17477         if (!this.delay || !this.delay.show) {
17478             this.show();
17479             return;
17480         }
17481         var _t = this;
17482         this.timeout = setTimeout(function () {
17483             if (_t.hoverState == 'in') {
17484                 _t.show();
17485             }
17486         }, this.delay.show)
17487     },
17488     
17489     leave : function() {
17490         clearTimeout(this.timeout);
17491     
17492         this.hoverState = 'out';
17493     
17494         if (!this.delay || !this.delay.hide) {
17495             this.hide();
17496             return;
17497         }
17498         var _t = this;
17499         this.timeout = setTimeout(function () {
17500             if (_t.hoverState == 'out') {
17501                 _t.hide();
17502             }
17503         }, this.delay.hide)
17504     },
17505     
17506     show : function (on_el)
17507     {
17508         if (!on_el) {
17509             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17510         }
17511         
17512         // set content.
17513         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17514         if (this.html !== false) {
17515             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17516         }
17517         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17518         if (!this.title.length) {
17519             this.el.select('.popover-title',true).hide();
17520         }
17521         
17522         var placement = typeof this.placement == 'function' ?
17523             this.placement.call(this, this.el, on_el) :
17524             this.placement;
17525             
17526         var autoToken = /\s?auto?\s?/i;
17527         var autoPlace = autoToken.test(placement);
17528         if (autoPlace) {
17529             placement = placement.replace(autoToken, '') || 'top';
17530         }
17531         
17532         //this.el.detach()
17533         //this.el.setXY([0,0]);
17534         this.el.show();
17535         this.el.dom.style.display='block';
17536         this.el.addClass(placement);
17537         
17538         //this.el.appendTo(on_el);
17539         
17540         var p = this.getPosition();
17541         var box = this.el.getBox();
17542         
17543         if (autoPlace) {
17544             // fixme..
17545         }
17546         var align = Roo.bootstrap.Popover.alignment[placement];
17547         
17548 //        Roo.log(align);
17549         this.el.alignTo(on_el, align[0],align[1]);
17550         //var arrow = this.el.select('.arrow',true).first();
17551         //arrow.set(align[2], 
17552         
17553         this.el.addClass('in');
17554         
17555         
17556         if (this.el.hasClass('fade')) {
17557             // fade it?
17558         }
17559         
17560         this.hoverState = 'in';
17561         
17562         this.fireEvent('show', this);
17563         
17564     },
17565     hide : function()
17566     {
17567         this.el.setXY([0,0]);
17568         this.el.removeClass('in');
17569         this.el.hide();
17570         this.hoverState = null;
17571         
17572         this.fireEvent('hide', this);
17573     }
17574     
17575 });
17576
17577 Roo.bootstrap.Popover.alignment = {
17578     'left' : ['r-l', [-10,0], 'right'],
17579     'right' : ['l-r', [10,0], 'left'],
17580     'bottom' : ['t-b', [0,10], 'top'],
17581     'top' : [ 'b-t', [0,-10], 'bottom']
17582 };
17583
17584  /*
17585  * - LGPL
17586  *
17587  * Progress
17588  * 
17589  */
17590
17591 /**
17592  * @class Roo.bootstrap.Progress
17593  * @extends Roo.bootstrap.Component
17594  * Bootstrap Progress class
17595  * @cfg {Boolean} striped striped of the progress bar
17596  * @cfg {Boolean} active animated of the progress bar
17597  * 
17598  * 
17599  * @constructor
17600  * Create a new Progress
17601  * @param {Object} config The config object
17602  */
17603
17604 Roo.bootstrap.Progress = function(config){
17605     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17606 };
17607
17608 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17609     
17610     striped : false,
17611     active: false,
17612     
17613     getAutoCreate : function(){
17614         var cfg = {
17615             tag: 'div',
17616             cls: 'progress'
17617         };
17618         
17619         
17620         if(this.striped){
17621             cfg.cls += ' progress-striped';
17622         }
17623       
17624         if(this.active){
17625             cfg.cls += ' active';
17626         }
17627         
17628         
17629         return cfg;
17630     }
17631    
17632 });
17633
17634  
17635
17636  /*
17637  * - LGPL
17638  *
17639  * ProgressBar
17640  * 
17641  */
17642
17643 /**
17644  * @class Roo.bootstrap.ProgressBar
17645  * @extends Roo.bootstrap.Component
17646  * Bootstrap ProgressBar class
17647  * @cfg {Number} aria_valuenow aria-value now
17648  * @cfg {Number} aria_valuemin aria-value min
17649  * @cfg {Number} aria_valuemax aria-value max
17650  * @cfg {String} label label for the progress bar
17651  * @cfg {String} panel (success | info | warning | danger )
17652  * @cfg {String} role role of the progress bar
17653  * @cfg {String} sr_only text
17654  * 
17655  * 
17656  * @constructor
17657  * Create a new ProgressBar
17658  * @param {Object} config The config object
17659  */
17660
17661 Roo.bootstrap.ProgressBar = function(config){
17662     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17663 };
17664
17665 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17666     
17667     aria_valuenow : 0,
17668     aria_valuemin : 0,
17669     aria_valuemax : 100,
17670     label : false,
17671     panel : false,
17672     role : false,
17673     sr_only: false,
17674     
17675     getAutoCreate : function()
17676     {
17677         
17678         var cfg = {
17679             tag: 'div',
17680             cls: 'progress-bar',
17681             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17682         };
17683         
17684         if(this.sr_only){
17685             cfg.cn = {
17686                 tag: 'span',
17687                 cls: 'sr-only',
17688                 html: this.sr_only
17689             }
17690         }
17691         
17692         if(this.role){
17693             cfg.role = this.role;
17694         }
17695         
17696         if(this.aria_valuenow){
17697             cfg['aria-valuenow'] = this.aria_valuenow;
17698         }
17699         
17700         if(this.aria_valuemin){
17701             cfg['aria-valuemin'] = this.aria_valuemin;
17702         }
17703         
17704         if(this.aria_valuemax){
17705             cfg['aria-valuemax'] = this.aria_valuemax;
17706         }
17707         
17708         if(this.label && !this.sr_only){
17709             cfg.html = this.label;
17710         }
17711         
17712         if(this.panel){
17713             cfg.cls += ' progress-bar-' + this.panel;
17714         }
17715         
17716         return cfg;
17717     },
17718     
17719     update : function(aria_valuenow)
17720     {
17721         this.aria_valuenow = aria_valuenow;
17722         
17723         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17724     }
17725    
17726 });
17727
17728  
17729
17730  /*
17731  * - LGPL
17732  *
17733  * column
17734  * 
17735  */
17736
17737 /**
17738  * @class Roo.bootstrap.TabGroup
17739  * @extends Roo.bootstrap.Column
17740  * Bootstrap Column class
17741  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17742  * @cfg {Boolean} carousel true to make the group behave like a carousel
17743  * @cfg {Boolean} bullets show bullets for the panels
17744  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17745  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17746  * @cfg {Boolean} showarrow (true|false) show arrow default true
17747  * 
17748  * @constructor
17749  * Create a new TabGroup
17750  * @param {Object} config The config object
17751  */
17752
17753 Roo.bootstrap.TabGroup = function(config){
17754     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17755     if (!this.navId) {
17756         this.navId = Roo.id();
17757     }
17758     this.tabs = [];
17759     Roo.bootstrap.TabGroup.register(this);
17760     
17761 };
17762
17763 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17764     
17765     carousel : false,
17766     transition : false,
17767     bullets : 0,
17768     timer : 0,
17769     autoslide : false,
17770     slideFn : false,
17771     slideOnTouch : false,
17772     showarrow : true,
17773     
17774     getAutoCreate : function()
17775     {
17776         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17777         
17778         cfg.cls += ' tab-content';
17779         
17780         if (this.carousel) {
17781             cfg.cls += ' carousel slide';
17782             
17783             cfg.cn = [{
17784                cls : 'carousel-inner',
17785                cn : []
17786             }];
17787         
17788             if(this.bullets  && !Roo.isTouch){
17789                 
17790                 var bullets = {
17791                     cls : 'carousel-bullets',
17792                     cn : []
17793                 };
17794                
17795                 if(this.bullets_cls){
17796                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17797                 }
17798                 
17799                 bullets.cn.push({
17800                     cls : 'clear'
17801                 });
17802                 
17803                 cfg.cn[0].cn.push(bullets);
17804             }
17805             
17806             if(this.showarrow){
17807                 cfg.cn[0].cn.push({
17808                     tag : 'div',
17809                     class : 'carousel-arrow',
17810                     cn : [
17811                         {
17812                             tag : 'div',
17813                             class : 'carousel-prev',
17814                             cn : [
17815                                 {
17816                                     tag : 'i',
17817                                     class : 'fa fa-chevron-left'
17818                                 }
17819                             ]
17820                         },
17821                         {
17822                             tag : 'div',
17823                             class : 'carousel-next',
17824                             cn : [
17825                                 {
17826                                     tag : 'i',
17827                                     class : 'fa fa-chevron-right'
17828                                 }
17829                             ]
17830                         }
17831                     ]
17832                 });
17833             }
17834             
17835         }
17836         
17837         return cfg;
17838     },
17839     
17840     initEvents:  function()
17841     {
17842 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17843 //            this.el.on("touchstart", this.onTouchStart, this);
17844 //        }
17845         
17846         if(this.autoslide){
17847             var _this = this;
17848             
17849             this.slideFn = window.setInterval(function() {
17850                 _this.showPanelNext();
17851             }, this.timer);
17852         }
17853         
17854         if(this.showarrow){
17855             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17856             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17857         }
17858         
17859         
17860     },
17861     
17862 //    onTouchStart : function(e, el, o)
17863 //    {
17864 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17865 //            return;
17866 //        }
17867 //        
17868 //        this.showPanelNext();
17869 //    },
17870     
17871     
17872     getChildContainer : function()
17873     {
17874         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17875     },
17876     
17877     /**
17878     * register a Navigation item
17879     * @param {Roo.bootstrap.NavItem} the navitem to add
17880     */
17881     register : function(item)
17882     {
17883         this.tabs.push( item);
17884         item.navId = this.navId; // not really needed..
17885         this.addBullet();
17886     
17887     },
17888     
17889     getActivePanel : function()
17890     {
17891         var r = false;
17892         Roo.each(this.tabs, function(t) {
17893             if (t.active) {
17894                 r = t;
17895                 return false;
17896             }
17897             return null;
17898         });
17899         return r;
17900         
17901     },
17902     getPanelByName : function(n)
17903     {
17904         var r = false;
17905         Roo.each(this.tabs, function(t) {
17906             if (t.tabId == n) {
17907                 r = t;
17908                 return false;
17909             }
17910             return null;
17911         });
17912         return r;
17913     },
17914     indexOfPanel : function(p)
17915     {
17916         var r = false;
17917         Roo.each(this.tabs, function(t,i) {
17918             if (t.tabId == p.tabId) {
17919                 r = i;
17920                 return false;
17921             }
17922             return null;
17923         });
17924         return r;
17925     },
17926     /**
17927      * show a specific panel
17928      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17929      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17930      */
17931     showPanel : function (pan)
17932     {
17933         if(this.transition || typeof(pan) == 'undefined'){
17934             Roo.log("waiting for the transitionend");
17935             return;
17936         }
17937         
17938         if (typeof(pan) == 'number') {
17939             pan = this.tabs[pan];
17940         }
17941         
17942         if (typeof(pan) == 'string') {
17943             pan = this.getPanelByName(pan);
17944         }
17945         
17946         var cur = this.getActivePanel();
17947         
17948         if(!pan || !cur){
17949             Roo.log('pan or acitve pan is undefined');
17950             return false;
17951         }
17952         
17953         if (pan.tabId == this.getActivePanel().tabId) {
17954             return true;
17955         }
17956         
17957         if (false === cur.fireEvent('beforedeactivate')) {
17958             return false;
17959         }
17960         
17961         if(this.bullets > 0 && !Roo.isTouch){
17962             this.setActiveBullet(this.indexOfPanel(pan));
17963         }
17964         
17965         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17966             
17967             this.transition = true;
17968             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17969             var lr = dir == 'next' ? 'left' : 'right';
17970             pan.el.addClass(dir); // or prev
17971             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17972             cur.el.addClass(lr); // or right
17973             pan.el.addClass(lr);
17974             
17975             var _this = this;
17976             cur.el.on('transitionend', function() {
17977                 Roo.log("trans end?");
17978                 
17979                 pan.el.removeClass([lr,dir]);
17980                 pan.setActive(true);
17981                 
17982                 cur.el.removeClass([lr]);
17983                 cur.setActive(false);
17984                 
17985                 _this.transition = false;
17986                 
17987             }, this, { single:  true } );
17988             
17989             return true;
17990         }
17991         
17992         cur.setActive(false);
17993         pan.setActive(true);
17994         
17995         return true;
17996         
17997     },
17998     showPanelNext : function()
17999     {
18000         var i = this.indexOfPanel(this.getActivePanel());
18001         
18002         if (i >= this.tabs.length - 1 && !this.autoslide) {
18003             return;
18004         }
18005         
18006         if (i >= this.tabs.length - 1 && this.autoslide) {
18007             i = -1;
18008         }
18009         
18010         this.showPanel(this.tabs[i+1]);
18011     },
18012     
18013     showPanelPrev : function()
18014     {
18015         var i = this.indexOfPanel(this.getActivePanel());
18016         
18017         if (i  < 1 && !this.autoslide) {
18018             return;
18019         }
18020         
18021         if (i < 1 && this.autoslide) {
18022             i = this.tabs.length;
18023         }
18024         
18025         this.showPanel(this.tabs[i-1]);
18026     },
18027     
18028     
18029     addBullet: function()
18030     {
18031         if(!this.bullets || Roo.isTouch){
18032             return;
18033         }
18034         var ctr = this.el.select('.carousel-bullets',true).first();
18035         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18036         var bullet = ctr.createChild({
18037             cls : 'bullet bullet-' + i
18038         },ctr.dom.lastChild);
18039         
18040         
18041         var _this = this;
18042         
18043         bullet.on('click', (function(e, el, o, ii, t){
18044
18045             e.preventDefault();
18046
18047             this.showPanel(ii);
18048
18049             if(this.autoslide && this.slideFn){
18050                 clearInterval(this.slideFn);
18051                 this.slideFn = window.setInterval(function() {
18052                     _this.showPanelNext();
18053                 }, this.timer);
18054             }
18055
18056         }).createDelegate(this, [i, bullet], true));
18057                 
18058         
18059     },
18060      
18061     setActiveBullet : function(i)
18062     {
18063         if(Roo.isTouch){
18064             return;
18065         }
18066         
18067         Roo.each(this.el.select('.bullet', true).elements, function(el){
18068             el.removeClass('selected');
18069         });
18070
18071         var bullet = this.el.select('.bullet-' + i, true).first();
18072         
18073         if(!bullet){
18074             return;
18075         }
18076         
18077         bullet.addClass('selected');
18078     }
18079     
18080     
18081   
18082 });
18083
18084  
18085
18086  
18087  
18088 Roo.apply(Roo.bootstrap.TabGroup, {
18089     
18090     groups: {},
18091      /**
18092     * register a Navigation Group
18093     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18094     */
18095     register : function(navgrp)
18096     {
18097         this.groups[navgrp.navId] = navgrp;
18098         
18099     },
18100     /**
18101     * fetch a Navigation Group based on the navigation ID
18102     * if one does not exist , it will get created.
18103     * @param {string} the navgroup to add
18104     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18105     */
18106     get: function(navId) {
18107         if (typeof(this.groups[navId]) == 'undefined') {
18108             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18109         }
18110         return this.groups[navId] ;
18111     }
18112     
18113     
18114     
18115 });
18116
18117  /*
18118  * - LGPL
18119  *
18120  * TabPanel
18121  * 
18122  */
18123
18124 /**
18125  * @class Roo.bootstrap.TabPanel
18126  * @extends Roo.bootstrap.Component
18127  * Bootstrap TabPanel class
18128  * @cfg {Boolean} active panel active
18129  * @cfg {String} html panel content
18130  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18131  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18132  * @cfg {String} href click to link..
18133  * 
18134  * 
18135  * @constructor
18136  * Create a new TabPanel
18137  * @param {Object} config The config object
18138  */
18139
18140 Roo.bootstrap.TabPanel = function(config){
18141     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18142     this.addEvents({
18143         /**
18144              * @event changed
18145              * Fires when the active status changes
18146              * @param {Roo.bootstrap.TabPanel} this
18147              * @param {Boolean} state the new state
18148             
18149          */
18150         'changed': true,
18151         /**
18152              * @event beforedeactivate
18153              * Fires before a tab is de-activated - can be used to do validation on a form.
18154              * @param {Roo.bootstrap.TabPanel} this
18155              * @return {Boolean} false if there is an error
18156             
18157          */
18158         'beforedeactivate': true
18159      });
18160     
18161     this.tabId = this.tabId || Roo.id();
18162   
18163 };
18164
18165 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18166     
18167     active: false,
18168     html: false,
18169     tabId: false,
18170     navId : false,
18171     href : '',
18172     
18173     getAutoCreate : function(){
18174         var cfg = {
18175             tag: 'div',
18176             // item is needed for carousel - not sure if it has any effect otherwise
18177             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18178             html: this.html || ''
18179         };
18180         
18181         if(this.active){
18182             cfg.cls += ' active';
18183         }
18184         
18185         if(this.tabId){
18186             cfg.tabId = this.tabId;
18187         }
18188         
18189         
18190         return cfg;
18191     },
18192     
18193     initEvents:  function()
18194     {
18195         var p = this.parent();
18196         
18197         this.navId = this.navId || p.navId;
18198         
18199         if (typeof(this.navId) != 'undefined') {
18200             // not really needed.. but just in case.. parent should be a NavGroup.
18201             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18202             
18203             tg.register(this);
18204             
18205             var i = tg.tabs.length - 1;
18206             
18207             if(this.active && tg.bullets > 0 && i < tg.bullets){
18208                 tg.setActiveBullet(i);
18209             }
18210         }
18211         
18212         this.el.on('click', this.onClick, this);
18213         
18214         if(Roo.isTouch){
18215             this.el.on("touchstart", this.onTouchStart, this);
18216             this.el.on("touchmove", this.onTouchMove, this);
18217             this.el.on("touchend", this.onTouchEnd, this);
18218         }
18219         
18220     },
18221     
18222     onRender : function(ct, position)
18223     {
18224         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18225     },
18226     
18227     setActive : function(state)
18228     {
18229         Roo.log("panel - set active " + this.tabId + "=" + state);
18230         
18231         this.active = state;
18232         if (!state) {
18233             this.el.removeClass('active');
18234             
18235         } else  if (!this.el.hasClass('active')) {
18236             this.el.addClass('active');
18237         }
18238         
18239         this.fireEvent('changed', this, state);
18240     },
18241     
18242     onClick : function(e)
18243     {
18244         e.preventDefault();
18245         
18246         if(!this.href.length){
18247             return;
18248         }
18249         
18250         window.location.href = this.href;
18251     },
18252     
18253     startX : 0,
18254     startY : 0,
18255     endX : 0,
18256     endY : 0,
18257     swiping : false,
18258     
18259     onTouchStart : function(e)
18260     {
18261         this.swiping = false;
18262         
18263         this.startX = e.browserEvent.touches[0].clientX;
18264         this.startY = e.browserEvent.touches[0].clientY;
18265     },
18266     
18267     onTouchMove : function(e)
18268     {
18269         this.swiping = true;
18270         
18271         this.endX = e.browserEvent.touches[0].clientX;
18272         this.endY = e.browserEvent.touches[0].clientY;
18273     },
18274     
18275     onTouchEnd : function(e)
18276     {
18277         if(!this.swiping){
18278             this.onClick(e);
18279             return;
18280         }
18281         
18282         var tabGroup = this.parent();
18283         
18284         if(this.endX > this.startX){ // swiping right
18285             tabGroup.showPanelPrev();
18286             return;
18287         }
18288         
18289         if(this.startX > this.endX){ // swiping left
18290             tabGroup.showPanelNext();
18291             return;
18292         }
18293     }
18294     
18295     
18296 });
18297  
18298
18299  
18300
18301  /*
18302  * - LGPL
18303  *
18304  * DateField
18305  * 
18306  */
18307
18308 /**
18309  * @class Roo.bootstrap.DateField
18310  * @extends Roo.bootstrap.Input
18311  * Bootstrap DateField class
18312  * @cfg {Number} weekStart default 0
18313  * @cfg {String} viewMode default empty, (months|years)
18314  * @cfg {String} minViewMode default empty, (months|years)
18315  * @cfg {Number} startDate default -Infinity
18316  * @cfg {Number} endDate default Infinity
18317  * @cfg {Boolean} todayHighlight default false
18318  * @cfg {Boolean} todayBtn default false
18319  * @cfg {Boolean} calendarWeeks default false
18320  * @cfg {Object} daysOfWeekDisabled default empty
18321  * @cfg {Boolean} singleMode default false (true | false)
18322  * 
18323  * @cfg {Boolean} keyboardNavigation default true
18324  * @cfg {String} language default en
18325  * 
18326  * @constructor
18327  * Create a new DateField
18328  * @param {Object} config The config object
18329  */
18330
18331 Roo.bootstrap.DateField = function(config){
18332     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18333      this.addEvents({
18334             /**
18335              * @event show
18336              * Fires when this field show.
18337              * @param {Roo.bootstrap.DateField} this
18338              * @param {Mixed} date The date value
18339              */
18340             show : true,
18341             /**
18342              * @event show
18343              * Fires when this field hide.
18344              * @param {Roo.bootstrap.DateField} this
18345              * @param {Mixed} date The date value
18346              */
18347             hide : true,
18348             /**
18349              * @event select
18350              * Fires when select a date.
18351              * @param {Roo.bootstrap.DateField} this
18352              * @param {Mixed} date The date value
18353              */
18354             select : true,
18355             /**
18356              * @event beforeselect
18357              * Fires when before select a date.
18358              * @param {Roo.bootstrap.DateField} this
18359              * @param {Mixed} date The date value
18360              */
18361             beforeselect : true
18362         });
18363 };
18364
18365 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18366     
18367     /**
18368      * @cfg {String} format
18369      * The default date format string which can be overriden for localization support.  The format must be
18370      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18371      */
18372     format : "m/d/y",
18373     /**
18374      * @cfg {String} altFormats
18375      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18376      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18377      */
18378     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18379     
18380     weekStart : 0,
18381     
18382     viewMode : '',
18383     
18384     minViewMode : '',
18385     
18386     todayHighlight : false,
18387     
18388     todayBtn: false,
18389     
18390     language: 'en',
18391     
18392     keyboardNavigation: true,
18393     
18394     calendarWeeks: false,
18395     
18396     startDate: -Infinity,
18397     
18398     endDate: Infinity,
18399     
18400     daysOfWeekDisabled: [],
18401     
18402     _events: [],
18403     
18404     singleMode : false,
18405     
18406     UTCDate: function()
18407     {
18408         return new Date(Date.UTC.apply(Date, arguments));
18409     },
18410     
18411     UTCToday: function()
18412     {
18413         var today = new Date();
18414         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18415     },
18416     
18417     getDate: function() {
18418             var d = this.getUTCDate();
18419             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18420     },
18421     
18422     getUTCDate: function() {
18423             return this.date;
18424     },
18425     
18426     setDate: function(d) {
18427             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18428     },
18429     
18430     setUTCDate: function(d) {
18431             this.date = d;
18432             this.setValue(this.formatDate(this.date));
18433     },
18434         
18435     onRender: function(ct, position)
18436     {
18437         
18438         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18439         
18440         this.language = this.language || 'en';
18441         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18442         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18443         
18444         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18445         this.format = this.format || 'm/d/y';
18446         this.isInline = false;
18447         this.isInput = true;
18448         this.component = this.el.select('.add-on', true).first() || false;
18449         this.component = (this.component && this.component.length === 0) ? false : this.component;
18450         this.hasInput = this.component && this.inputEl().length;
18451         
18452         if (typeof(this.minViewMode === 'string')) {
18453             switch (this.minViewMode) {
18454                 case 'months':
18455                     this.minViewMode = 1;
18456                     break;
18457                 case 'years':
18458                     this.minViewMode = 2;
18459                     break;
18460                 default:
18461                     this.minViewMode = 0;
18462                     break;
18463             }
18464         }
18465         
18466         if (typeof(this.viewMode === 'string')) {
18467             switch (this.viewMode) {
18468                 case 'months':
18469                     this.viewMode = 1;
18470                     break;
18471                 case 'years':
18472                     this.viewMode = 2;
18473                     break;
18474                 default:
18475                     this.viewMode = 0;
18476                     break;
18477             }
18478         }
18479                 
18480         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18481         
18482 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18483         
18484         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18485         
18486         this.picker().on('mousedown', this.onMousedown, this);
18487         this.picker().on('click', this.onClick, this);
18488         
18489         this.picker().addClass('datepicker-dropdown');
18490         
18491         this.startViewMode = this.viewMode;
18492         
18493         if(this.singleMode){
18494             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18495                 v.setVisibilityMode(Roo.Element.DISPLAY);
18496                 v.hide();
18497             });
18498             
18499             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18500                 v.setStyle('width', '189px');
18501             });
18502         }
18503         
18504         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18505             if(!this.calendarWeeks){
18506                 v.remove();
18507                 return;
18508             }
18509             
18510             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18511             v.attr('colspan', function(i, val){
18512                 return parseInt(val) + 1;
18513             });
18514         });
18515                         
18516         
18517         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18518         
18519         this.setStartDate(this.startDate);
18520         this.setEndDate(this.endDate);
18521         
18522         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18523         
18524         this.fillDow();
18525         this.fillMonths();
18526         this.update();
18527         this.showMode();
18528         
18529         if(this.isInline) {
18530             this.show();
18531         }
18532     },
18533     
18534     picker : function()
18535     {
18536         return this.pickerEl;
18537 //        return this.el.select('.datepicker', true).first();
18538     },
18539     
18540     fillDow: function()
18541     {
18542         var dowCnt = this.weekStart;
18543         
18544         var dow = {
18545             tag: 'tr',
18546             cn: [
18547                 
18548             ]
18549         };
18550         
18551         if(this.calendarWeeks){
18552             dow.cn.push({
18553                 tag: 'th',
18554                 cls: 'cw',
18555                 html: '&nbsp;'
18556             })
18557         }
18558         
18559         while (dowCnt < this.weekStart + 7) {
18560             dow.cn.push({
18561                 tag: 'th',
18562                 cls: 'dow',
18563                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18564             });
18565         }
18566         
18567         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18568     },
18569     
18570     fillMonths: function()
18571     {    
18572         var i = 0;
18573         var months = this.picker().select('>.datepicker-months td', true).first();
18574         
18575         months.dom.innerHTML = '';
18576         
18577         while (i < 12) {
18578             var month = {
18579                 tag: 'span',
18580                 cls: 'month',
18581                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18582             };
18583             
18584             months.createChild(month);
18585         }
18586         
18587     },
18588     
18589     update: function()
18590     {
18591         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;
18592         
18593         if (this.date < this.startDate) {
18594             this.viewDate = new Date(this.startDate);
18595         } else if (this.date > this.endDate) {
18596             this.viewDate = new Date(this.endDate);
18597         } else {
18598             this.viewDate = new Date(this.date);
18599         }
18600         
18601         this.fill();
18602     },
18603     
18604     fill: function() 
18605     {
18606         var d = new Date(this.viewDate),
18607                 year = d.getUTCFullYear(),
18608                 month = d.getUTCMonth(),
18609                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18610                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18611                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18612                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18613                 currentDate = this.date && this.date.valueOf(),
18614                 today = this.UTCToday();
18615         
18616         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18617         
18618 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18619         
18620 //        this.picker.select('>tfoot th.today').
18621 //                                              .text(dates[this.language].today)
18622 //                                              .toggle(this.todayBtn !== false);
18623     
18624         this.updateNavArrows();
18625         this.fillMonths();
18626                                                 
18627         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18628         
18629         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18630          
18631         prevMonth.setUTCDate(day);
18632         
18633         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18634         
18635         var nextMonth = new Date(prevMonth);
18636         
18637         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18638         
18639         nextMonth = nextMonth.valueOf();
18640         
18641         var fillMonths = false;
18642         
18643         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18644         
18645         while(prevMonth.valueOf() <= nextMonth) {
18646             var clsName = '';
18647             
18648             if (prevMonth.getUTCDay() === this.weekStart) {
18649                 if(fillMonths){
18650                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18651                 }
18652                     
18653                 fillMonths = {
18654                     tag: 'tr',
18655                     cn: []
18656                 };
18657                 
18658                 if(this.calendarWeeks){
18659                     // ISO 8601: First week contains first thursday.
18660                     // ISO also states week starts on Monday, but we can be more abstract here.
18661                     var
18662                     // Start of current week: based on weekstart/current date
18663                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18664                     // Thursday of this week
18665                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18666                     // First Thursday of year, year from thursday
18667                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18668                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18669                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18670                     
18671                     fillMonths.cn.push({
18672                         tag: 'td',
18673                         cls: 'cw',
18674                         html: calWeek
18675                     });
18676                 }
18677             }
18678             
18679             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18680                 clsName += ' old';
18681             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18682                 clsName += ' new';
18683             }
18684             if (this.todayHighlight &&
18685                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18686                 prevMonth.getUTCMonth() == today.getMonth() &&
18687                 prevMonth.getUTCDate() == today.getDate()) {
18688                 clsName += ' today';
18689             }
18690             
18691             if (currentDate && prevMonth.valueOf() === currentDate) {
18692                 clsName += ' active';
18693             }
18694             
18695             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18696                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18697                     clsName += ' disabled';
18698             }
18699             
18700             fillMonths.cn.push({
18701                 tag: 'td',
18702                 cls: 'day ' + clsName,
18703                 html: prevMonth.getDate()
18704             });
18705             
18706             prevMonth.setDate(prevMonth.getDate()+1);
18707         }
18708           
18709         var currentYear = this.date && this.date.getUTCFullYear();
18710         var currentMonth = this.date && this.date.getUTCMonth();
18711         
18712         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18713         
18714         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18715             v.removeClass('active');
18716             
18717             if(currentYear === year && k === currentMonth){
18718                 v.addClass('active');
18719             }
18720             
18721             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18722                 v.addClass('disabled');
18723             }
18724             
18725         });
18726         
18727         
18728         year = parseInt(year/10, 10) * 10;
18729         
18730         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18731         
18732         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18733         
18734         year -= 1;
18735         for (var i = -1; i < 11; i++) {
18736             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18737                 tag: 'span',
18738                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18739                 html: year
18740             });
18741             
18742             year += 1;
18743         }
18744     },
18745     
18746     showMode: function(dir) 
18747     {
18748         if (dir) {
18749             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18750         }
18751         
18752         Roo.each(this.picker().select('>div',true).elements, function(v){
18753             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18754             v.hide();
18755         });
18756         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18757     },
18758     
18759     place: function()
18760     {
18761         if(this.isInline) {
18762             return;
18763         }
18764         
18765         this.picker().removeClass(['bottom', 'top']);
18766         
18767         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18768             /*
18769              * place to the top of element!
18770              *
18771              */
18772             
18773             this.picker().addClass('top');
18774             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18775             
18776             return;
18777         }
18778         
18779         this.picker().addClass('bottom');
18780         
18781         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18782     },
18783     
18784     parseDate : function(value)
18785     {
18786         if(!value || value instanceof Date){
18787             return value;
18788         }
18789         var v = Date.parseDate(value, this.format);
18790         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18791             v = Date.parseDate(value, 'Y-m-d');
18792         }
18793         if(!v && this.altFormats){
18794             if(!this.altFormatsArray){
18795                 this.altFormatsArray = this.altFormats.split("|");
18796             }
18797             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18798                 v = Date.parseDate(value, this.altFormatsArray[i]);
18799             }
18800         }
18801         return v;
18802     },
18803     
18804     formatDate : function(date, fmt)
18805     {   
18806         return (!date || !(date instanceof Date)) ?
18807         date : date.dateFormat(fmt || this.format);
18808     },
18809     
18810     onFocus : function()
18811     {
18812         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18813         this.show();
18814     },
18815     
18816     onBlur : function()
18817     {
18818         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18819         
18820         var d = this.inputEl().getValue();
18821         
18822         this.setValue(d);
18823                 
18824         this.hide();
18825     },
18826     
18827     show : function()
18828     {
18829         this.picker().show();
18830         this.update();
18831         this.place();
18832         
18833         this.fireEvent('show', this, this.date);
18834     },
18835     
18836     hide : function()
18837     {
18838         if(this.isInline) {
18839             return;
18840         }
18841         this.picker().hide();
18842         this.viewMode = this.startViewMode;
18843         this.showMode();
18844         
18845         this.fireEvent('hide', this, this.date);
18846         
18847     },
18848     
18849     onMousedown: function(e)
18850     {
18851         e.stopPropagation();
18852         e.preventDefault();
18853     },
18854     
18855     keyup: function(e)
18856     {
18857         Roo.bootstrap.DateField.superclass.keyup.call(this);
18858         this.update();
18859     },
18860
18861     setValue: function(v)
18862     {
18863         if(this.fireEvent('beforeselect', this, v) !== false){
18864             var d = new Date(this.parseDate(v) ).clearTime();
18865         
18866             if(isNaN(d.getTime())){
18867                 this.date = this.viewDate = '';
18868                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18869                 return;
18870             }
18871
18872             v = this.formatDate(d);
18873
18874             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18875
18876             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18877
18878             this.update();
18879
18880             this.fireEvent('select', this, this.date);
18881         }
18882     },
18883     
18884     getValue: function()
18885     {
18886         return this.formatDate(this.date);
18887     },
18888     
18889     fireKey: function(e)
18890     {
18891         if (!this.picker().isVisible()){
18892             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18893                 this.show();
18894             }
18895             return;
18896         }
18897         
18898         var dateChanged = false,
18899         dir, day, month,
18900         newDate, newViewDate;
18901         
18902         switch(e.keyCode){
18903             case 27: // escape
18904                 this.hide();
18905                 e.preventDefault();
18906                 break;
18907             case 37: // left
18908             case 39: // right
18909                 if (!this.keyboardNavigation) {
18910                     break;
18911                 }
18912                 dir = e.keyCode == 37 ? -1 : 1;
18913                 
18914                 if (e.ctrlKey){
18915                     newDate = this.moveYear(this.date, dir);
18916                     newViewDate = this.moveYear(this.viewDate, dir);
18917                 } else if (e.shiftKey){
18918                     newDate = this.moveMonth(this.date, dir);
18919                     newViewDate = this.moveMonth(this.viewDate, dir);
18920                 } else {
18921                     newDate = new Date(this.date);
18922                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18923                     newViewDate = new Date(this.viewDate);
18924                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18925                 }
18926                 if (this.dateWithinRange(newDate)){
18927                     this.date = newDate;
18928                     this.viewDate = newViewDate;
18929                     this.setValue(this.formatDate(this.date));
18930 //                    this.update();
18931                     e.preventDefault();
18932                     dateChanged = true;
18933                 }
18934                 break;
18935             case 38: // up
18936             case 40: // down
18937                 if (!this.keyboardNavigation) {
18938                     break;
18939                 }
18940                 dir = e.keyCode == 38 ? -1 : 1;
18941                 if (e.ctrlKey){
18942                     newDate = this.moveYear(this.date, dir);
18943                     newViewDate = this.moveYear(this.viewDate, dir);
18944                 } else if (e.shiftKey){
18945                     newDate = this.moveMonth(this.date, dir);
18946                     newViewDate = this.moveMonth(this.viewDate, dir);
18947                 } else {
18948                     newDate = new Date(this.date);
18949                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18950                     newViewDate = new Date(this.viewDate);
18951                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18952                 }
18953                 if (this.dateWithinRange(newDate)){
18954                     this.date = newDate;
18955                     this.viewDate = newViewDate;
18956                     this.setValue(this.formatDate(this.date));
18957 //                    this.update();
18958                     e.preventDefault();
18959                     dateChanged = true;
18960                 }
18961                 break;
18962             case 13: // enter
18963                 this.setValue(this.formatDate(this.date));
18964                 this.hide();
18965                 e.preventDefault();
18966                 break;
18967             case 9: // tab
18968                 this.setValue(this.formatDate(this.date));
18969                 this.hide();
18970                 break;
18971             case 16: // shift
18972             case 17: // ctrl
18973             case 18: // alt
18974                 break;
18975             default :
18976                 this.hide();
18977                 
18978         }
18979     },
18980     
18981     
18982     onClick: function(e) 
18983     {
18984         e.stopPropagation();
18985         e.preventDefault();
18986         
18987         var target = e.getTarget();
18988         
18989         if(target.nodeName.toLowerCase() === 'i'){
18990             target = Roo.get(target).dom.parentNode;
18991         }
18992         
18993         var nodeName = target.nodeName;
18994         var className = target.className;
18995         var html = target.innerHTML;
18996         //Roo.log(nodeName);
18997         
18998         switch(nodeName.toLowerCase()) {
18999             case 'th':
19000                 switch(className) {
19001                     case 'switch':
19002                         this.showMode(1);
19003                         break;
19004                     case 'prev':
19005                     case 'next':
19006                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19007                         switch(this.viewMode){
19008                                 case 0:
19009                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19010                                         break;
19011                                 case 1:
19012                                 case 2:
19013                                         this.viewDate = this.moveYear(this.viewDate, dir);
19014                                         break;
19015                         }
19016                         this.fill();
19017                         break;
19018                     case 'today':
19019                         var date = new Date();
19020                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19021 //                        this.fill()
19022                         this.setValue(this.formatDate(this.date));
19023                         
19024                         this.hide();
19025                         break;
19026                 }
19027                 break;
19028             case 'span':
19029                 if (className.indexOf('disabled') < 0) {
19030                     this.viewDate.setUTCDate(1);
19031                     if (className.indexOf('month') > -1) {
19032                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19033                     } else {
19034                         var year = parseInt(html, 10) || 0;
19035                         this.viewDate.setUTCFullYear(year);
19036                         
19037                     }
19038                     
19039                     if(this.singleMode){
19040                         this.setValue(this.formatDate(this.viewDate));
19041                         this.hide();
19042                         return;
19043                     }
19044                     
19045                     this.showMode(-1);
19046                     this.fill();
19047                 }
19048                 break;
19049                 
19050             case 'td':
19051                 //Roo.log(className);
19052                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19053                     var day = parseInt(html, 10) || 1;
19054                     var year = this.viewDate.getUTCFullYear(),
19055                         month = this.viewDate.getUTCMonth();
19056
19057                     if (className.indexOf('old') > -1) {
19058                         if(month === 0 ){
19059                             month = 11;
19060                             year -= 1;
19061                         }else{
19062                             month -= 1;
19063                         }
19064                     } else if (className.indexOf('new') > -1) {
19065                         if (month == 11) {
19066                             month = 0;
19067                             year += 1;
19068                         } else {
19069                             month += 1;
19070                         }
19071                     }
19072                     //Roo.log([year,month,day]);
19073                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19074                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19075 //                    this.fill();
19076                     //Roo.log(this.formatDate(this.date));
19077                     this.setValue(this.formatDate(this.date));
19078                     this.hide();
19079                 }
19080                 break;
19081         }
19082     },
19083     
19084     setStartDate: function(startDate)
19085     {
19086         this.startDate = startDate || -Infinity;
19087         if (this.startDate !== -Infinity) {
19088             this.startDate = this.parseDate(this.startDate);
19089         }
19090         this.update();
19091         this.updateNavArrows();
19092     },
19093
19094     setEndDate: function(endDate)
19095     {
19096         this.endDate = endDate || Infinity;
19097         if (this.endDate !== Infinity) {
19098             this.endDate = this.parseDate(this.endDate);
19099         }
19100         this.update();
19101         this.updateNavArrows();
19102     },
19103     
19104     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19105     {
19106         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19107         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19108             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19109         }
19110         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19111             return parseInt(d, 10);
19112         });
19113         this.update();
19114         this.updateNavArrows();
19115     },
19116     
19117     updateNavArrows: function() 
19118     {
19119         if(this.singleMode){
19120             return;
19121         }
19122         
19123         var d = new Date(this.viewDate),
19124         year = d.getUTCFullYear(),
19125         month = d.getUTCMonth();
19126         
19127         Roo.each(this.picker().select('.prev', true).elements, function(v){
19128             v.show();
19129             switch (this.viewMode) {
19130                 case 0:
19131
19132                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19133                         v.hide();
19134                     }
19135                     break;
19136                 case 1:
19137                 case 2:
19138                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19139                         v.hide();
19140                     }
19141                     break;
19142             }
19143         });
19144         
19145         Roo.each(this.picker().select('.next', true).elements, function(v){
19146             v.show();
19147             switch (this.viewMode) {
19148                 case 0:
19149
19150                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19151                         v.hide();
19152                     }
19153                     break;
19154                 case 1:
19155                 case 2:
19156                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19157                         v.hide();
19158                     }
19159                     break;
19160             }
19161         })
19162     },
19163     
19164     moveMonth: function(date, dir)
19165     {
19166         if (!dir) {
19167             return date;
19168         }
19169         var new_date = new Date(date.valueOf()),
19170         day = new_date.getUTCDate(),
19171         month = new_date.getUTCMonth(),
19172         mag = Math.abs(dir),
19173         new_month, test;
19174         dir = dir > 0 ? 1 : -1;
19175         if (mag == 1){
19176             test = dir == -1
19177             // If going back one month, make sure month is not current month
19178             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19179             ? function(){
19180                 return new_date.getUTCMonth() == month;
19181             }
19182             // If going forward one month, make sure month is as expected
19183             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19184             : function(){
19185                 return new_date.getUTCMonth() != new_month;
19186             };
19187             new_month = month + dir;
19188             new_date.setUTCMonth(new_month);
19189             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19190             if (new_month < 0 || new_month > 11) {
19191                 new_month = (new_month + 12) % 12;
19192             }
19193         } else {
19194             // For magnitudes >1, move one month at a time...
19195             for (var i=0; i<mag; i++) {
19196                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19197                 new_date = this.moveMonth(new_date, dir);
19198             }
19199             // ...then reset the day, keeping it in the new month
19200             new_month = new_date.getUTCMonth();
19201             new_date.setUTCDate(day);
19202             test = function(){
19203                 return new_month != new_date.getUTCMonth();
19204             };
19205         }
19206         // Common date-resetting loop -- if date is beyond end of month, make it
19207         // end of month
19208         while (test()){
19209             new_date.setUTCDate(--day);
19210             new_date.setUTCMonth(new_month);
19211         }
19212         return new_date;
19213     },
19214
19215     moveYear: function(date, dir)
19216     {
19217         return this.moveMonth(date, dir*12);
19218     },
19219
19220     dateWithinRange: function(date)
19221     {
19222         return date >= this.startDate && date <= this.endDate;
19223     },
19224
19225     
19226     remove: function() 
19227     {
19228         this.picker().remove();
19229     },
19230     
19231     validateValue : function(value)
19232     {
19233         if(this.getVisibilityEl().hasClass('hidden')){
19234             return true;
19235         }
19236         
19237         if(value.length < 1)  {
19238             if(this.allowBlank){
19239                 return true;
19240             }
19241             return false;
19242         }
19243         
19244         if(value.length < this.minLength){
19245             return false;
19246         }
19247         if(value.length > this.maxLength){
19248             return false;
19249         }
19250         if(this.vtype){
19251             var vt = Roo.form.VTypes;
19252             if(!vt[this.vtype](value, this)){
19253                 return false;
19254             }
19255         }
19256         if(typeof this.validator == "function"){
19257             var msg = this.validator(value);
19258             if(msg !== true){
19259                 return false;
19260             }
19261         }
19262         
19263         if(this.regex && !this.regex.test(value)){
19264             return false;
19265         }
19266         
19267         if(typeof(this.parseDate(value)) == 'undefined'){
19268             return false;
19269         }
19270         
19271         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19272             return false;
19273         }      
19274         
19275         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19276             return false;
19277         } 
19278         
19279         
19280         return true;
19281     },
19282     
19283     setVisible : function(visible)
19284     {
19285         if(!this.getEl()){
19286             return;
19287         }
19288         
19289         this.getEl().removeClass('hidden');
19290         
19291         if(visible){
19292             return;
19293         }
19294         
19295         this.getEl().addClass('hidden');
19296     }
19297    
19298 });
19299
19300 Roo.apply(Roo.bootstrap.DateField,  {
19301     
19302     head : {
19303         tag: 'thead',
19304         cn: [
19305         {
19306             tag: 'tr',
19307             cn: [
19308             {
19309                 tag: 'th',
19310                 cls: 'prev',
19311                 html: '<i class="fa fa-arrow-left"/>'
19312             },
19313             {
19314                 tag: 'th',
19315                 cls: 'switch',
19316                 colspan: '5'
19317             },
19318             {
19319                 tag: 'th',
19320                 cls: 'next',
19321                 html: '<i class="fa fa-arrow-right"/>'
19322             }
19323
19324             ]
19325         }
19326         ]
19327     },
19328     
19329     content : {
19330         tag: 'tbody',
19331         cn: [
19332         {
19333             tag: 'tr',
19334             cn: [
19335             {
19336                 tag: 'td',
19337                 colspan: '7'
19338             }
19339             ]
19340         }
19341         ]
19342     },
19343     
19344     footer : {
19345         tag: 'tfoot',
19346         cn: [
19347         {
19348             tag: 'tr',
19349             cn: [
19350             {
19351                 tag: 'th',
19352                 colspan: '7',
19353                 cls: 'today'
19354             }
19355                     
19356             ]
19357         }
19358         ]
19359     },
19360     
19361     dates:{
19362         en: {
19363             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19364             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19365             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19366             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19367             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19368             today: "Today"
19369         }
19370     },
19371     
19372     modes: [
19373     {
19374         clsName: 'days',
19375         navFnc: 'Month',
19376         navStep: 1
19377     },
19378     {
19379         clsName: 'months',
19380         navFnc: 'FullYear',
19381         navStep: 1
19382     },
19383     {
19384         clsName: 'years',
19385         navFnc: 'FullYear',
19386         navStep: 10
19387     }]
19388 });
19389
19390 Roo.apply(Roo.bootstrap.DateField,  {
19391   
19392     template : {
19393         tag: 'div',
19394         cls: 'datepicker dropdown-menu roo-dynamic',
19395         cn: [
19396         {
19397             tag: 'div',
19398             cls: 'datepicker-days',
19399             cn: [
19400             {
19401                 tag: 'table',
19402                 cls: 'table-condensed',
19403                 cn:[
19404                 Roo.bootstrap.DateField.head,
19405                 {
19406                     tag: 'tbody'
19407                 },
19408                 Roo.bootstrap.DateField.footer
19409                 ]
19410             }
19411             ]
19412         },
19413         {
19414             tag: 'div',
19415             cls: 'datepicker-months',
19416             cn: [
19417             {
19418                 tag: 'table',
19419                 cls: 'table-condensed',
19420                 cn:[
19421                 Roo.bootstrap.DateField.head,
19422                 Roo.bootstrap.DateField.content,
19423                 Roo.bootstrap.DateField.footer
19424                 ]
19425             }
19426             ]
19427         },
19428         {
19429             tag: 'div',
19430             cls: 'datepicker-years',
19431             cn: [
19432             {
19433                 tag: 'table',
19434                 cls: 'table-condensed',
19435                 cn:[
19436                 Roo.bootstrap.DateField.head,
19437                 Roo.bootstrap.DateField.content,
19438                 Roo.bootstrap.DateField.footer
19439                 ]
19440             }
19441             ]
19442         }
19443         ]
19444     }
19445 });
19446
19447  
19448
19449  /*
19450  * - LGPL
19451  *
19452  * TimeField
19453  * 
19454  */
19455
19456 /**
19457  * @class Roo.bootstrap.TimeField
19458  * @extends Roo.bootstrap.Input
19459  * Bootstrap DateField class
19460  * 
19461  * 
19462  * @constructor
19463  * Create a new TimeField
19464  * @param {Object} config The config object
19465  */
19466
19467 Roo.bootstrap.TimeField = function(config){
19468     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19469     this.addEvents({
19470             /**
19471              * @event show
19472              * Fires when this field show.
19473              * @param {Roo.bootstrap.DateField} thisthis
19474              * @param {Mixed} date The date value
19475              */
19476             show : true,
19477             /**
19478              * @event show
19479              * Fires when this field hide.
19480              * @param {Roo.bootstrap.DateField} this
19481              * @param {Mixed} date The date value
19482              */
19483             hide : true,
19484             /**
19485              * @event select
19486              * Fires when select a date.
19487              * @param {Roo.bootstrap.DateField} this
19488              * @param {Mixed} date The date value
19489              */
19490             select : true
19491         });
19492 };
19493
19494 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19495     
19496     /**
19497      * @cfg {String} format
19498      * The default time format string which can be overriden for localization support.  The format must be
19499      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19500      */
19501     format : "H:i",
19502        
19503     onRender: function(ct, position)
19504     {
19505         
19506         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19507                 
19508         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19509         
19510         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19511         
19512         this.pop = this.picker().select('>.datepicker-time',true).first();
19513         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19514         
19515         this.picker().on('mousedown', this.onMousedown, this);
19516         this.picker().on('click', this.onClick, this);
19517         
19518         this.picker().addClass('datepicker-dropdown');
19519     
19520         this.fillTime();
19521         this.update();
19522             
19523         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19524         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19525         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19526         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19527         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19528         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19529
19530     },
19531     
19532     fireKey: function(e){
19533         if (!this.picker().isVisible()){
19534             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19535                 this.show();
19536             }
19537             return;
19538         }
19539
19540         e.preventDefault();
19541         
19542         switch(e.keyCode){
19543             case 27: // escape
19544                 this.hide();
19545                 break;
19546             case 37: // left
19547             case 39: // right
19548                 this.onTogglePeriod();
19549                 break;
19550             case 38: // up
19551                 this.onIncrementMinutes();
19552                 break;
19553             case 40: // down
19554                 this.onDecrementMinutes();
19555                 break;
19556             case 13: // enter
19557             case 9: // tab
19558                 this.setTime();
19559                 break;
19560         }
19561     },
19562     
19563     onClick: function(e) {
19564         e.stopPropagation();
19565         e.preventDefault();
19566     },
19567     
19568     picker : function()
19569     {
19570         return this.el.select('.datepicker', true).first();
19571     },
19572     
19573     fillTime: function()
19574     {    
19575         var time = this.pop.select('tbody', true).first();
19576         
19577         time.dom.innerHTML = '';
19578         
19579         time.createChild({
19580             tag: 'tr',
19581             cn: [
19582                 {
19583                     tag: 'td',
19584                     cn: [
19585                         {
19586                             tag: 'a',
19587                             href: '#',
19588                             cls: 'btn',
19589                             cn: [
19590                                 {
19591                                     tag: 'span',
19592                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19593                                 }
19594                             ]
19595                         } 
19596                     ]
19597                 },
19598                 {
19599                     tag: 'td',
19600                     cls: 'separator'
19601                 },
19602                 {
19603                     tag: 'td',
19604                     cn: [
19605                         {
19606                             tag: 'a',
19607                             href: '#',
19608                             cls: 'btn',
19609                             cn: [
19610                                 {
19611                                     tag: 'span',
19612                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19613                                 }
19614                             ]
19615                         }
19616                     ]
19617                 },
19618                 {
19619                     tag: 'td',
19620                     cls: 'separator'
19621                 }
19622             ]
19623         });
19624         
19625         time.createChild({
19626             tag: 'tr',
19627             cn: [
19628                 {
19629                     tag: 'td',
19630                     cn: [
19631                         {
19632                             tag: 'span',
19633                             cls: 'timepicker-hour',
19634                             html: '00'
19635                         }  
19636                     ]
19637                 },
19638                 {
19639                     tag: 'td',
19640                     cls: 'separator',
19641                     html: ':'
19642                 },
19643                 {
19644                     tag: 'td',
19645                     cn: [
19646                         {
19647                             tag: 'span',
19648                             cls: 'timepicker-minute',
19649                             html: '00'
19650                         }  
19651                     ]
19652                 },
19653                 {
19654                     tag: 'td',
19655                     cls: 'separator'
19656                 },
19657                 {
19658                     tag: 'td',
19659                     cn: [
19660                         {
19661                             tag: 'button',
19662                             type: 'button',
19663                             cls: 'btn btn-primary period',
19664                             html: 'AM'
19665                             
19666                         }
19667                     ]
19668                 }
19669             ]
19670         });
19671         
19672         time.createChild({
19673             tag: 'tr',
19674             cn: [
19675                 {
19676                     tag: 'td',
19677                     cn: [
19678                         {
19679                             tag: 'a',
19680                             href: '#',
19681                             cls: 'btn',
19682                             cn: [
19683                                 {
19684                                     tag: 'span',
19685                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19686                                 }
19687                             ]
19688                         }
19689                     ]
19690                 },
19691                 {
19692                     tag: 'td',
19693                     cls: 'separator'
19694                 },
19695                 {
19696                     tag: 'td',
19697                     cn: [
19698                         {
19699                             tag: 'a',
19700                             href: '#',
19701                             cls: 'btn',
19702                             cn: [
19703                                 {
19704                                     tag: 'span',
19705                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19706                                 }
19707                             ]
19708                         }
19709                     ]
19710                 },
19711                 {
19712                     tag: 'td',
19713                     cls: 'separator'
19714                 }
19715             ]
19716         });
19717         
19718     },
19719     
19720     update: function()
19721     {
19722         
19723         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19724         
19725         this.fill();
19726     },
19727     
19728     fill: function() 
19729     {
19730         var hours = this.time.getHours();
19731         var minutes = this.time.getMinutes();
19732         var period = 'AM';
19733         
19734         if(hours > 11){
19735             period = 'PM';
19736         }
19737         
19738         if(hours == 0){
19739             hours = 12;
19740         }
19741         
19742         
19743         if(hours > 12){
19744             hours = hours - 12;
19745         }
19746         
19747         if(hours < 10){
19748             hours = '0' + hours;
19749         }
19750         
19751         if(minutes < 10){
19752             minutes = '0' + minutes;
19753         }
19754         
19755         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19756         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19757         this.pop.select('button', true).first().dom.innerHTML = period;
19758         
19759     },
19760     
19761     place: function()
19762     {   
19763         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19764         
19765         var cls = ['bottom'];
19766         
19767         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19768             cls.pop();
19769             cls.push('top');
19770         }
19771         
19772         cls.push('right');
19773         
19774         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19775             cls.pop();
19776             cls.push('left');
19777         }
19778         
19779         this.picker().addClass(cls.join('-'));
19780         
19781         var _this = this;
19782         
19783         Roo.each(cls, function(c){
19784             if(c == 'bottom'){
19785                 _this.picker().setTop(_this.inputEl().getHeight());
19786                 return;
19787             }
19788             if(c == 'top'){
19789                 _this.picker().setTop(0 - _this.picker().getHeight());
19790                 return;
19791             }
19792             
19793             if(c == 'left'){
19794                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19795                 return;
19796             }
19797             if(c == 'right'){
19798                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19799                 return;
19800             }
19801         });
19802         
19803     },
19804   
19805     onFocus : function()
19806     {
19807         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19808         this.show();
19809     },
19810     
19811     onBlur : function()
19812     {
19813         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19814         this.hide();
19815     },
19816     
19817     show : function()
19818     {
19819         this.picker().show();
19820         this.pop.show();
19821         this.update();
19822         this.place();
19823         
19824         this.fireEvent('show', this, this.date);
19825     },
19826     
19827     hide : function()
19828     {
19829         this.picker().hide();
19830         this.pop.hide();
19831         
19832         this.fireEvent('hide', this, this.date);
19833     },
19834     
19835     setTime : function()
19836     {
19837         this.hide();
19838         this.setValue(this.time.format(this.format));
19839         
19840         this.fireEvent('select', this, this.date);
19841         
19842         
19843     },
19844     
19845     onMousedown: function(e){
19846         e.stopPropagation();
19847         e.preventDefault();
19848     },
19849     
19850     onIncrementHours: function()
19851     {
19852         Roo.log('onIncrementHours');
19853         this.time = this.time.add(Date.HOUR, 1);
19854         this.update();
19855         
19856     },
19857     
19858     onDecrementHours: function()
19859     {
19860         Roo.log('onDecrementHours');
19861         this.time = this.time.add(Date.HOUR, -1);
19862         this.update();
19863     },
19864     
19865     onIncrementMinutes: function()
19866     {
19867         Roo.log('onIncrementMinutes');
19868         this.time = this.time.add(Date.MINUTE, 1);
19869         this.update();
19870     },
19871     
19872     onDecrementMinutes: function()
19873     {
19874         Roo.log('onDecrementMinutes');
19875         this.time = this.time.add(Date.MINUTE, -1);
19876         this.update();
19877     },
19878     
19879     onTogglePeriod: function()
19880     {
19881         Roo.log('onTogglePeriod');
19882         this.time = this.time.add(Date.HOUR, 12);
19883         this.update();
19884     }
19885     
19886    
19887 });
19888
19889 Roo.apply(Roo.bootstrap.TimeField,  {
19890     
19891     content : {
19892         tag: 'tbody',
19893         cn: [
19894             {
19895                 tag: 'tr',
19896                 cn: [
19897                 {
19898                     tag: 'td',
19899                     colspan: '7'
19900                 }
19901                 ]
19902             }
19903         ]
19904     },
19905     
19906     footer : {
19907         tag: 'tfoot',
19908         cn: [
19909             {
19910                 tag: 'tr',
19911                 cn: [
19912                 {
19913                     tag: 'th',
19914                     colspan: '7',
19915                     cls: '',
19916                     cn: [
19917                         {
19918                             tag: 'button',
19919                             cls: 'btn btn-info ok',
19920                             html: 'OK'
19921                         }
19922                     ]
19923                 }
19924
19925                 ]
19926             }
19927         ]
19928     }
19929 });
19930
19931 Roo.apply(Roo.bootstrap.TimeField,  {
19932   
19933     template : {
19934         tag: 'div',
19935         cls: 'datepicker dropdown-menu',
19936         cn: [
19937             {
19938                 tag: 'div',
19939                 cls: 'datepicker-time',
19940                 cn: [
19941                 {
19942                     tag: 'table',
19943                     cls: 'table-condensed',
19944                     cn:[
19945                     Roo.bootstrap.TimeField.content,
19946                     Roo.bootstrap.TimeField.footer
19947                     ]
19948                 }
19949                 ]
19950             }
19951         ]
19952     }
19953 });
19954
19955  
19956
19957  /*
19958  * - LGPL
19959  *
19960  * MonthField
19961  * 
19962  */
19963
19964 /**
19965  * @class Roo.bootstrap.MonthField
19966  * @extends Roo.bootstrap.Input
19967  * Bootstrap MonthField class
19968  * 
19969  * @cfg {String} language default en
19970  * 
19971  * @constructor
19972  * Create a new MonthField
19973  * @param {Object} config The config object
19974  */
19975
19976 Roo.bootstrap.MonthField = function(config){
19977     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19978     
19979     this.addEvents({
19980         /**
19981          * @event show
19982          * Fires when this field show.
19983          * @param {Roo.bootstrap.MonthField} this
19984          * @param {Mixed} date The date value
19985          */
19986         show : true,
19987         /**
19988          * @event show
19989          * Fires when this field hide.
19990          * @param {Roo.bootstrap.MonthField} this
19991          * @param {Mixed} date The date value
19992          */
19993         hide : true,
19994         /**
19995          * @event select
19996          * Fires when select a date.
19997          * @param {Roo.bootstrap.MonthField} this
19998          * @param {String} oldvalue The old value
19999          * @param {String} newvalue The new value
20000          */
20001         select : true
20002     });
20003 };
20004
20005 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20006     
20007     onRender: function(ct, position)
20008     {
20009         
20010         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20011         
20012         this.language = this.language || 'en';
20013         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20014         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20015         
20016         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20017         this.isInline = false;
20018         this.isInput = true;
20019         this.component = this.el.select('.add-on', true).first() || false;
20020         this.component = (this.component && this.component.length === 0) ? false : this.component;
20021         this.hasInput = this.component && this.inputEL().length;
20022         
20023         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20024         
20025         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20026         
20027         this.picker().on('mousedown', this.onMousedown, this);
20028         this.picker().on('click', this.onClick, this);
20029         
20030         this.picker().addClass('datepicker-dropdown');
20031         
20032         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20033             v.setStyle('width', '189px');
20034         });
20035         
20036         this.fillMonths();
20037         
20038         this.update();
20039         
20040         if(this.isInline) {
20041             this.show();
20042         }
20043         
20044     },
20045     
20046     setValue: function(v, suppressEvent)
20047     {   
20048         var o = this.getValue();
20049         
20050         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20051         
20052         this.update();
20053
20054         if(suppressEvent !== true){
20055             this.fireEvent('select', this, o, v);
20056         }
20057         
20058     },
20059     
20060     getValue: function()
20061     {
20062         return this.value;
20063     },
20064     
20065     onClick: function(e) 
20066     {
20067         e.stopPropagation();
20068         e.preventDefault();
20069         
20070         var target = e.getTarget();
20071         
20072         if(target.nodeName.toLowerCase() === 'i'){
20073             target = Roo.get(target).dom.parentNode;
20074         }
20075         
20076         var nodeName = target.nodeName;
20077         var className = target.className;
20078         var html = target.innerHTML;
20079         
20080         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20081             return;
20082         }
20083         
20084         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20085         
20086         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20087         
20088         this.hide();
20089                         
20090     },
20091     
20092     picker : function()
20093     {
20094         return this.pickerEl;
20095     },
20096     
20097     fillMonths: function()
20098     {    
20099         var i = 0;
20100         var months = this.picker().select('>.datepicker-months td', true).first();
20101         
20102         months.dom.innerHTML = '';
20103         
20104         while (i < 12) {
20105             var month = {
20106                 tag: 'span',
20107                 cls: 'month',
20108                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20109             };
20110             
20111             months.createChild(month);
20112         }
20113         
20114     },
20115     
20116     update: function()
20117     {
20118         var _this = this;
20119         
20120         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20121             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20122         }
20123         
20124         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20125             e.removeClass('active');
20126             
20127             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20128                 e.addClass('active');
20129             }
20130         })
20131     },
20132     
20133     place: function()
20134     {
20135         if(this.isInline) {
20136             return;
20137         }
20138         
20139         this.picker().removeClass(['bottom', 'top']);
20140         
20141         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20142             /*
20143              * place to the top of element!
20144              *
20145              */
20146             
20147             this.picker().addClass('top');
20148             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20149             
20150             return;
20151         }
20152         
20153         this.picker().addClass('bottom');
20154         
20155         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20156     },
20157     
20158     onFocus : function()
20159     {
20160         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20161         this.show();
20162     },
20163     
20164     onBlur : function()
20165     {
20166         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20167         
20168         var d = this.inputEl().getValue();
20169         
20170         this.setValue(d);
20171                 
20172         this.hide();
20173     },
20174     
20175     show : function()
20176     {
20177         this.picker().show();
20178         this.picker().select('>.datepicker-months', true).first().show();
20179         this.update();
20180         this.place();
20181         
20182         this.fireEvent('show', this, this.date);
20183     },
20184     
20185     hide : function()
20186     {
20187         if(this.isInline) {
20188             return;
20189         }
20190         this.picker().hide();
20191         this.fireEvent('hide', this, this.date);
20192         
20193     },
20194     
20195     onMousedown: function(e)
20196     {
20197         e.stopPropagation();
20198         e.preventDefault();
20199     },
20200     
20201     keyup: function(e)
20202     {
20203         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20204         this.update();
20205     },
20206
20207     fireKey: function(e)
20208     {
20209         if (!this.picker().isVisible()){
20210             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20211                 this.show();
20212             }
20213             return;
20214         }
20215         
20216         var dir;
20217         
20218         switch(e.keyCode){
20219             case 27: // escape
20220                 this.hide();
20221                 e.preventDefault();
20222                 break;
20223             case 37: // left
20224             case 39: // right
20225                 dir = e.keyCode == 37 ? -1 : 1;
20226                 
20227                 this.vIndex = this.vIndex + dir;
20228                 
20229                 if(this.vIndex < 0){
20230                     this.vIndex = 0;
20231                 }
20232                 
20233                 if(this.vIndex > 11){
20234                     this.vIndex = 11;
20235                 }
20236                 
20237                 if(isNaN(this.vIndex)){
20238                     this.vIndex = 0;
20239                 }
20240                 
20241                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20242                 
20243                 break;
20244             case 38: // up
20245             case 40: // down
20246                 
20247                 dir = e.keyCode == 38 ? -1 : 1;
20248                 
20249                 this.vIndex = this.vIndex + dir * 4;
20250                 
20251                 if(this.vIndex < 0){
20252                     this.vIndex = 0;
20253                 }
20254                 
20255                 if(this.vIndex > 11){
20256                     this.vIndex = 11;
20257                 }
20258                 
20259                 if(isNaN(this.vIndex)){
20260                     this.vIndex = 0;
20261                 }
20262                 
20263                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20264                 break;
20265                 
20266             case 13: // enter
20267                 
20268                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20269                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20270                 }
20271                 
20272                 this.hide();
20273                 e.preventDefault();
20274                 break;
20275             case 9: // tab
20276                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20277                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20278                 }
20279                 this.hide();
20280                 break;
20281             case 16: // shift
20282             case 17: // ctrl
20283             case 18: // alt
20284                 break;
20285             default :
20286                 this.hide();
20287                 
20288         }
20289     },
20290     
20291     remove: function() 
20292     {
20293         this.picker().remove();
20294     }
20295    
20296 });
20297
20298 Roo.apply(Roo.bootstrap.MonthField,  {
20299     
20300     content : {
20301         tag: 'tbody',
20302         cn: [
20303         {
20304             tag: 'tr',
20305             cn: [
20306             {
20307                 tag: 'td',
20308                 colspan: '7'
20309             }
20310             ]
20311         }
20312         ]
20313     },
20314     
20315     dates:{
20316         en: {
20317             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20318             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20319         }
20320     }
20321 });
20322
20323 Roo.apply(Roo.bootstrap.MonthField,  {
20324   
20325     template : {
20326         tag: 'div',
20327         cls: 'datepicker dropdown-menu roo-dynamic',
20328         cn: [
20329             {
20330                 tag: 'div',
20331                 cls: 'datepicker-months',
20332                 cn: [
20333                 {
20334                     tag: 'table',
20335                     cls: 'table-condensed',
20336                     cn:[
20337                         Roo.bootstrap.DateField.content
20338                     ]
20339                 }
20340                 ]
20341             }
20342         ]
20343     }
20344 });
20345
20346  
20347
20348  
20349  /*
20350  * - LGPL
20351  *
20352  * CheckBox
20353  * 
20354  */
20355
20356 /**
20357  * @class Roo.bootstrap.CheckBox
20358  * @extends Roo.bootstrap.Input
20359  * Bootstrap CheckBox class
20360  * 
20361  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20362  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20363  * @cfg {String} boxLabel The text that appears beside the checkbox
20364  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20365  * @cfg {Boolean} checked initnal the element
20366  * @cfg {Boolean} inline inline the element (default false)
20367  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20368  * @cfg {String} tooltip label tooltip
20369  * 
20370  * @constructor
20371  * Create a new CheckBox
20372  * @param {Object} config The config object
20373  */
20374
20375 Roo.bootstrap.CheckBox = function(config){
20376     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20377    
20378     this.addEvents({
20379         /**
20380         * @event check
20381         * Fires when the element is checked or unchecked.
20382         * @param {Roo.bootstrap.CheckBox} this This input
20383         * @param {Boolean} checked The new checked value
20384         */
20385        check : true,
20386        /**
20387         * @event click
20388         * Fires when the element is click.
20389         * @param {Roo.bootstrap.CheckBox} this This input
20390         */
20391        click : true
20392     });
20393     
20394 };
20395
20396 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20397   
20398     inputType: 'checkbox',
20399     inputValue: 1,
20400     valueOff: 0,
20401     boxLabel: false,
20402     checked: false,
20403     weight : false,
20404     inline: false,
20405     tooltip : '',
20406     
20407     getAutoCreate : function()
20408     {
20409         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20410         
20411         var id = Roo.id();
20412         
20413         var cfg = {};
20414         
20415         cfg.cls = 'form-group ' + this.inputType; //input-group
20416         
20417         if(this.inline){
20418             cfg.cls += ' ' + this.inputType + '-inline';
20419         }
20420         
20421         var input =  {
20422             tag: 'input',
20423             id : id,
20424             type : this.inputType,
20425             value : this.inputValue,
20426             cls : 'roo-' + this.inputType, //'form-box',
20427             placeholder : this.placeholder || ''
20428             
20429         };
20430         
20431         if(this.inputType != 'radio'){
20432             var hidden =  {
20433                 tag: 'input',
20434                 type : 'hidden',
20435                 cls : 'roo-hidden-value',
20436                 value : this.checked ? this.inputValue : this.valueOff
20437             };
20438         }
20439         
20440             
20441         if (this.weight) { // Validity check?
20442             cfg.cls += " " + this.inputType + "-" + this.weight;
20443         }
20444         
20445         if (this.disabled) {
20446             input.disabled=true;
20447         }
20448         
20449         if(this.checked){
20450             input.checked = this.checked;
20451         }
20452         
20453         if (this.name) {
20454             
20455             input.name = this.name;
20456             
20457             if(this.inputType != 'radio'){
20458                 hidden.name = this.name;
20459                 input.name = '_hidden_' + this.name;
20460             }
20461         }
20462         
20463         if (this.size) {
20464             input.cls += ' input-' + this.size;
20465         }
20466         
20467         var settings=this;
20468         
20469         ['xs','sm','md','lg'].map(function(size){
20470             if (settings[size]) {
20471                 cfg.cls += ' col-' + size + '-' + settings[size];
20472             }
20473         });
20474         
20475         var inputblock = input;
20476          
20477         if (this.before || this.after) {
20478             
20479             inputblock = {
20480                 cls : 'input-group',
20481                 cn :  [] 
20482             };
20483             
20484             if (this.before) {
20485                 inputblock.cn.push({
20486                     tag :'span',
20487                     cls : 'input-group-addon',
20488                     html : this.before
20489                 });
20490             }
20491             
20492             inputblock.cn.push(input);
20493             
20494             if(this.inputType != 'radio'){
20495                 inputblock.cn.push(hidden);
20496             }
20497             
20498             if (this.after) {
20499                 inputblock.cn.push({
20500                     tag :'span',
20501                     cls : 'input-group-addon',
20502                     html : this.after
20503                 });
20504             }
20505             
20506         }
20507         
20508         if (align ==='left' && this.fieldLabel.length) {
20509 //                Roo.log("left and has label");
20510             cfg.cn = [
20511                 {
20512                     tag: 'label',
20513                     'for' :  id,
20514                     cls : 'control-label',
20515                     html : this.fieldLabel
20516                 },
20517                 {
20518                     cls : "", 
20519                     cn: [
20520                         inputblock
20521                     ]
20522                 }
20523             ];
20524             
20525             if(this.labelWidth > 12){
20526                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20527             }
20528             
20529             if(this.labelWidth < 13 && this.labelmd == 0){
20530                 this.labelmd = this.labelWidth;
20531             }
20532             
20533             if(this.labellg > 0){
20534                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20535                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20536             }
20537             
20538             if(this.labelmd > 0){
20539                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20540                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20541             }
20542             
20543             if(this.labelsm > 0){
20544                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20545                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20546             }
20547             
20548             if(this.labelxs > 0){
20549                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20550                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20551             }
20552             
20553         } else if ( this.fieldLabel.length) {
20554 //                Roo.log(" label");
20555                 cfg.cn = [
20556                    
20557                     {
20558                         tag: this.boxLabel ? 'span' : 'label',
20559                         'for': id,
20560                         cls: 'control-label box-input-label',
20561                         //cls : 'input-group-addon',
20562                         html : this.fieldLabel
20563                     },
20564                     
20565                     inputblock
20566                     
20567                 ];
20568
20569         } else {
20570             
20571 //                Roo.log(" no label && no align");
20572                 cfg.cn = [  inputblock ] ;
20573                 
20574                 
20575         }
20576         
20577         if(this.boxLabel){
20578              var boxLabelCfg = {
20579                 tag: 'label',
20580                 //'for': id, // box label is handled by onclick - so no for...
20581                 cls: 'box-label',
20582                 html: this.boxLabel
20583             };
20584             
20585             if(this.tooltip){
20586                 boxLabelCfg.tooltip = this.tooltip;
20587             }
20588              
20589             cfg.cn.push(boxLabelCfg);
20590         }
20591         
20592         if(this.inputType != 'radio'){
20593             cfg.cn.push(hidden);
20594         }
20595         
20596         return cfg;
20597         
20598     },
20599     
20600     /**
20601      * return the real input element.
20602      */
20603     inputEl: function ()
20604     {
20605         return this.el.select('input.roo-' + this.inputType,true).first();
20606     },
20607     hiddenEl: function ()
20608     {
20609         return this.el.select('input.roo-hidden-value',true).first();
20610     },
20611     
20612     labelEl: function()
20613     {
20614         return this.el.select('label.control-label',true).first();
20615     },
20616     /* depricated... */
20617     
20618     label: function()
20619     {
20620         return this.labelEl();
20621     },
20622     
20623     boxLabelEl: function()
20624     {
20625         return this.el.select('label.box-label',true).first();
20626     },
20627     
20628     initEvents : function()
20629     {
20630 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20631         
20632         this.inputEl().on('click', this.onClick,  this);
20633         
20634         if (this.boxLabel) { 
20635             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20636         }
20637         
20638         this.startValue = this.getValue();
20639         
20640         if(this.groupId){
20641             Roo.bootstrap.CheckBox.register(this);
20642         }
20643     },
20644     
20645     onClick : function(e)
20646     {   
20647         if(this.fireEvent('click', this, e) !== false){
20648             this.setChecked(!this.checked);
20649         }
20650         
20651     },
20652     
20653     setChecked : function(state,suppressEvent)
20654     {
20655         this.startValue = this.getValue();
20656
20657         if(this.inputType == 'radio'){
20658             
20659             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20660                 e.dom.checked = false;
20661             });
20662             
20663             this.inputEl().dom.checked = true;
20664             
20665             this.inputEl().dom.value = this.inputValue;
20666             
20667             if(suppressEvent !== true){
20668                 this.fireEvent('check', this, true);
20669             }
20670             
20671             this.validate();
20672             
20673             return;
20674         }
20675         
20676         this.checked = state;
20677         
20678         this.inputEl().dom.checked = state;
20679         
20680         
20681         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20682         
20683         if(suppressEvent !== true){
20684             this.fireEvent('check', this, state);
20685         }
20686         
20687         this.validate();
20688     },
20689     
20690     getValue : function()
20691     {
20692         if(this.inputType == 'radio'){
20693             return this.getGroupValue();
20694         }
20695         
20696         return this.hiddenEl().dom.value;
20697         
20698     },
20699     
20700     getGroupValue : function()
20701     {
20702         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20703             return '';
20704         }
20705         
20706         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20707     },
20708     
20709     setValue : function(v,suppressEvent)
20710     {
20711         if(this.inputType == 'radio'){
20712             this.setGroupValue(v, suppressEvent);
20713             return;
20714         }
20715         
20716         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20717         
20718         this.validate();
20719     },
20720     
20721     setGroupValue : function(v, suppressEvent)
20722     {
20723         this.startValue = this.getValue();
20724         
20725         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20726             e.dom.checked = false;
20727             
20728             if(e.dom.value == v){
20729                 e.dom.checked = true;
20730             }
20731         });
20732         
20733         if(suppressEvent !== true){
20734             this.fireEvent('check', this, true);
20735         }
20736
20737         this.validate();
20738         
20739         return;
20740     },
20741     
20742     validate : function()
20743     {
20744         if(this.getVisibilityEl().hasClass('hidden')){
20745             return true;
20746         }
20747         
20748         if(
20749                 this.disabled || 
20750                 (this.inputType == 'radio' && this.validateRadio()) ||
20751                 (this.inputType == 'checkbox' && this.validateCheckbox())
20752         ){
20753             this.markValid();
20754             return true;
20755         }
20756         
20757         this.markInvalid();
20758         return false;
20759     },
20760     
20761     validateRadio : function()
20762     {
20763         if(this.getVisibilityEl().hasClass('hidden')){
20764             return true;
20765         }
20766         
20767         if(this.allowBlank){
20768             return true;
20769         }
20770         
20771         var valid = false;
20772         
20773         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20774             if(!e.dom.checked){
20775                 return;
20776             }
20777             
20778             valid = true;
20779             
20780             return false;
20781         });
20782         
20783         return valid;
20784     },
20785     
20786     validateCheckbox : function()
20787     {
20788         if(!this.groupId){
20789             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20790             //return (this.getValue() == this.inputValue) ? true : false;
20791         }
20792         
20793         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20794         
20795         if(!group){
20796             return false;
20797         }
20798         
20799         var r = false;
20800         
20801         for(var i in group){
20802             if(group[i].el.isVisible(true)){
20803                 r = false;
20804                 break;
20805             }
20806             
20807             r = true;
20808         }
20809         
20810         for(var i in group){
20811             if(r){
20812                 break;
20813             }
20814             
20815             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20816         }
20817         
20818         return r;
20819     },
20820     
20821     /**
20822      * Mark this field as valid
20823      */
20824     markValid : function()
20825     {
20826         var _this = this;
20827         
20828         this.fireEvent('valid', this);
20829         
20830         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20831         
20832         if(this.groupId){
20833             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20834         }
20835         
20836         if(label){
20837             label.markValid();
20838         }
20839
20840         if(this.inputType == 'radio'){
20841             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20842                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20843                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20844             });
20845             
20846             return;
20847         }
20848
20849         if(!this.groupId){
20850             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20851             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20852             return;
20853         }
20854         
20855         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20856         
20857         if(!group){
20858             return;
20859         }
20860         
20861         for(var i in group){
20862             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20863             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20864         }
20865     },
20866     
20867      /**
20868      * Mark this field as invalid
20869      * @param {String} msg The validation message
20870      */
20871     markInvalid : function(msg)
20872     {
20873         if(this.allowBlank){
20874             return;
20875         }
20876         
20877         var _this = this;
20878         
20879         this.fireEvent('invalid', this, msg);
20880         
20881         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20882         
20883         if(this.groupId){
20884             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20885         }
20886         
20887         if(label){
20888             label.markInvalid();
20889         }
20890             
20891         if(this.inputType == 'radio'){
20892             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20893                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20894                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20895             });
20896             
20897             return;
20898         }
20899         
20900         if(!this.groupId){
20901             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20902             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20903             return;
20904         }
20905         
20906         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20907         
20908         if(!group){
20909             return;
20910         }
20911         
20912         for(var i in group){
20913             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20914             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20915         }
20916         
20917     },
20918     
20919     clearInvalid : function()
20920     {
20921         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20922         
20923         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20924         
20925         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20926         
20927         if (label && label.iconEl) {
20928             label.iconEl.removeClass(label.validClass);
20929             label.iconEl.removeClass(label.invalidClass);
20930         }
20931     },
20932     
20933     disable : function()
20934     {
20935         if(this.inputType != 'radio'){
20936             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20937             return;
20938         }
20939         
20940         var _this = this;
20941         
20942         if(this.rendered){
20943             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20944                 _this.getActionEl().addClass(this.disabledClass);
20945                 e.dom.disabled = true;
20946             });
20947         }
20948         
20949         this.disabled = true;
20950         this.fireEvent("disable", this);
20951         return this;
20952     },
20953
20954     enable : function()
20955     {
20956         if(this.inputType != 'radio'){
20957             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20958             return;
20959         }
20960         
20961         var _this = this;
20962         
20963         if(this.rendered){
20964             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20965                 _this.getActionEl().removeClass(this.disabledClass);
20966                 e.dom.disabled = false;
20967             });
20968         }
20969         
20970         this.disabled = false;
20971         this.fireEvent("enable", this);
20972         return this;
20973     },
20974     
20975     setBoxLabel : function(v)
20976     {
20977         this.boxLabel = v;
20978         
20979         if(this.rendered){
20980             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20981         }
20982     }
20983
20984 });
20985
20986 Roo.apply(Roo.bootstrap.CheckBox, {
20987     
20988     groups: {},
20989     
20990      /**
20991     * register a CheckBox Group
20992     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20993     */
20994     register : function(checkbox)
20995     {
20996         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20997             this.groups[checkbox.groupId] = {};
20998         }
20999         
21000         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21001             return;
21002         }
21003         
21004         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21005         
21006     },
21007     /**
21008     * fetch a CheckBox Group based on the group ID
21009     * @param {string} the group ID
21010     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21011     */
21012     get: function(groupId) {
21013         if (typeof(this.groups[groupId]) == 'undefined') {
21014             return false;
21015         }
21016         
21017         return this.groups[groupId] ;
21018     }
21019     
21020     
21021 });
21022 /*
21023  * - LGPL
21024  *
21025  * RadioItem
21026  * 
21027  */
21028
21029 /**
21030  * @class Roo.bootstrap.Radio
21031  * @extends Roo.bootstrap.Component
21032  * Bootstrap Radio class
21033  * @cfg {String} boxLabel - the label associated
21034  * @cfg {String} value - the value of radio
21035  * 
21036  * @constructor
21037  * Create a new Radio
21038  * @param {Object} config The config object
21039  */
21040 Roo.bootstrap.Radio = function(config){
21041     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21042     
21043 };
21044
21045 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21046     
21047     boxLabel : '',
21048     
21049     value : '',
21050     
21051     getAutoCreate : function()
21052     {
21053         var cfg = {
21054             tag : 'div',
21055             cls : 'form-group radio',
21056             cn : [
21057                 {
21058                     tag : 'label',
21059                     cls : 'box-label',
21060                     html : this.boxLabel
21061                 }
21062             ]
21063         };
21064         
21065         return cfg;
21066     },
21067     
21068     initEvents : function() 
21069     {
21070         this.parent().register(this);
21071         
21072         this.el.on('click', this.onClick, this);
21073         
21074     },
21075     
21076     onClick : function(e)
21077     {
21078         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21079             this.setChecked(true);
21080         }
21081     },
21082     
21083     setChecked : function(state, suppressEvent)
21084     {
21085         this.parent().setValue(this.value, suppressEvent);
21086         
21087     },
21088     
21089     setBoxLabel : function(v)
21090     {
21091         this.boxLabel = v;
21092         
21093         if(this.rendered){
21094             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21095         }
21096     }
21097     
21098 });
21099  
21100
21101  /*
21102  * - LGPL
21103  *
21104  * Input
21105  * 
21106  */
21107
21108 /**
21109  * @class Roo.bootstrap.SecurePass
21110  * @extends Roo.bootstrap.Input
21111  * Bootstrap SecurePass class
21112  *
21113  * 
21114  * @constructor
21115  * Create a new SecurePass
21116  * @param {Object} config The config object
21117  */
21118  
21119 Roo.bootstrap.SecurePass = function (config) {
21120     // these go here, so the translation tool can replace them..
21121     this.errors = {
21122         PwdEmpty: "Please type a password, and then retype it to confirm.",
21123         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21124         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21125         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21126         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21127         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21128         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21129         TooWeak: "Your password is Too Weak."
21130     },
21131     this.meterLabel = "Password strength:";
21132     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21133     this.meterClass = [
21134         "roo-password-meter-tooweak", 
21135         "roo-password-meter-weak", 
21136         "roo-password-meter-medium", 
21137         "roo-password-meter-strong", 
21138         "roo-password-meter-grey"
21139     ];
21140     
21141     this.errors = {};
21142     
21143     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21144 }
21145
21146 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21147     /**
21148      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21149      * {
21150      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21151      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21152      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21153      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21154      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21155      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21156      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21157      * })
21158      */
21159     // private
21160     
21161     meterWidth: 300,
21162     errorMsg :'',    
21163     errors: false,
21164     imageRoot: '/',
21165     /**
21166      * @cfg {String/Object} Label for the strength meter (defaults to
21167      * 'Password strength:')
21168      */
21169     // private
21170     meterLabel: '',
21171     /**
21172      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21173      * ['Weak', 'Medium', 'Strong'])
21174      */
21175     // private    
21176     pwdStrengths: false,    
21177     // private
21178     strength: 0,
21179     // private
21180     _lastPwd: null,
21181     // private
21182     kCapitalLetter: 0,
21183     kSmallLetter: 1,
21184     kDigit: 2,
21185     kPunctuation: 3,
21186     
21187     insecure: false,
21188     // private
21189     initEvents: function ()
21190     {
21191         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21192
21193         if (this.el.is('input[type=password]') && Roo.isSafari) {
21194             this.el.on('keydown', this.SafariOnKeyDown, this);
21195         }
21196
21197         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21198     },
21199     // private
21200     onRender: function (ct, position)
21201     {
21202         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21203         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21204         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21205
21206         this.trigger.createChild({
21207                    cn: [
21208                     {
21209                     //id: 'PwdMeter',
21210                     tag: 'div',
21211                     cls: 'roo-password-meter-grey col-xs-12',
21212                     style: {
21213                         //width: 0,
21214                         //width: this.meterWidth + 'px'                                                
21215                         }
21216                     },
21217                     {                            
21218                          cls: 'roo-password-meter-text'                          
21219                     }
21220                 ]            
21221         });
21222
21223          
21224         if (this.hideTrigger) {
21225             this.trigger.setDisplayed(false);
21226         }
21227         this.setSize(this.width || '', this.height || '');
21228     },
21229     // private
21230     onDestroy: function ()
21231     {
21232         if (this.trigger) {
21233             this.trigger.removeAllListeners();
21234             this.trigger.remove();
21235         }
21236         if (this.wrap) {
21237             this.wrap.remove();
21238         }
21239         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21240     },
21241     // private
21242     checkStrength: function ()
21243     {
21244         var pwd = this.inputEl().getValue();
21245         if (pwd == this._lastPwd) {
21246             return;
21247         }
21248
21249         var strength;
21250         if (this.ClientSideStrongPassword(pwd)) {
21251             strength = 3;
21252         } else if (this.ClientSideMediumPassword(pwd)) {
21253             strength = 2;
21254         } else if (this.ClientSideWeakPassword(pwd)) {
21255             strength = 1;
21256         } else {
21257             strength = 0;
21258         }
21259         
21260         Roo.log('strength1: ' + strength);
21261         
21262         //var pm = this.trigger.child('div/div/div').dom;
21263         var pm = this.trigger.child('div/div');
21264         pm.removeClass(this.meterClass);
21265         pm.addClass(this.meterClass[strength]);
21266                 
21267         
21268         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21269                 
21270         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21271         
21272         this._lastPwd = pwd;
21273     },
21274     reset: function ()
21275     {
21276         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21277         
21278         this._lastPwd = '';
21279         
21280         var pm = this.trigger.child('div/div');
21281         pm.removeClass(this.meterClass);
21282         pm.addClass('roo-password-meter-grey');        
21283         
21284         
21285         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21286         
21287         pt.innerHTML = '';
21288         this.inputEl().dom.type='password';
21289     },
21290     // private
21291     validateValue: function (value)
21292     {
21293         
21294         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21295             return false;
21296         }
21297         if (value.length == 0) {
21298             if (this.allowBlank) {
21299                 this.clearInvalid();
21300                 return true;
21301             }
21302
21303             this.markInvalid(this.errors.PwdEmpty);
21304             this.errorMsg = this.errors.PwdEmpty;
21305             return false;
21306         }
21307         
21308         if(this.insecure){
21309             return true;
21310         }
21311         
21312         if ('[\x21-\x7e]*'.match(value)) {
21313             this.markInvalid(this.errors.PwdBadChar);
21314             this.errorMsg = this.errors.PwdBadChar;
21315             return false;
21316         }
21317         if (value.length < 6) {
21318             this.markInvalid(this.errors.PwdShort);
21319             this.errorMsg = this.errors.PwdShort;
21320             return false;
21321         }
21322         if (value.length > 16) {
21323             this.markInvalid(this.errors.PwdLong);
21324             this.errorMsg = this.errors.PwdLong;
21325             return false;
21326         }
21327         var strength;
21328         if (this.ClientSideStrongPassword(value)) {
21329             strength = 3;
21330         } else if (this.ClientSideMediumPassword(value)) {
21331             strength = 2;
21332         } else if (this.ClientSideWeakPassword(value)) {
21333             strength = 1;
21334         } else {
21335             strength = 0;
21336         }
21337
21338         
21339         if (strength < 2) {
21340             //this.markInvalid(this.errors.TooWeak);
21341             this.errorMsg = this.errors.TooWeak;
21342             //return false;
21343         }
21344         
21345         
21346         console.log('strength2: ' + strength);
21347         
21348         //var pm = this.trigger.child('div/div/div').dom;
21349         
21350         var pm = this.trigger.child('div/div');
21351         pm.removeClass(this.meterClass);
21352         pm.addClass(this.meterClass[strength]);
21353                 
21354         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21355                 
21356         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21357         
21358         this.errorMsg = ''; 
21359         return true;
21360     },
21361     // private
21362     CharacterSetChecks: function (type)
21363     {
21364         this.type = type;
21365         this.fResult = false;
21366     },
21367     // private
21368     isctype: function (character, type)
21369     {
21370         switch (type) {  
21371             case this.kCapitalLetter:
21372                 if (character >= 'A' && character <= 'Z') {
21373                     return true;
21374                 }
21375                 break;
21376             
21377             case this.kSmallLetter:
21378                 if (character >= 'a' && character <= 'z') {
21379                     return true;
21380                 }
21381                 break;
21382             
21383             case this.kDigit:
21384                 if (character >= '0' && character <= '9') {
21385                     return true;
21386                 }
21387                 break;
21388             
21389             case this.kPunctuation:
21390                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21391                     return true;
21392                 }
21393                 break;
21394             
21395             default:
21396                 return false;
21397         }
21398
21399     },
21400     // private
21401     IsLongEnough: function (pwd, size)
21402     {
21403         return !(pwd == null || isNaN(size) || pwd.length < size);
21404     },
21405     // private
21406     SpansEnoughCharacterSets: function (word, nb)
21407     {
21408         if (!this.IsLongEnough(word, nb))
21409         {
21410             return false;
21411         }
21412
21413         var characterSetChecks = new Array(
21414             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21415             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21416         );
21417         
21418         for (var index = 0; index < word.length; ++index) {
21419             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21420                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21421                     characterSetChecks[nCharSet].fResult = true;
21422                     break;
21423                 }
21424             }
21425         }
21426
21427         var nCharSets = 0;
21428         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21429             if (characterSetChecks[nCharSet].fResult) {
21430                 ++nCharSets;
21431             }
21432         }
21433
21434         if (nCharSets < nb) {
21435             return false;
21436         }
21437         return true;
21438     },
21439     // private
21440     ClientSideStrongPassword: function (pwd)
21441     {
21442         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21443     },
21444     // private
21445     ClientSideMediumPassword: function (pwd)
21446     {
21447         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21448     },
21449     // private
21450     ClientSideWeakPassword: function (pwd)
21451     {
21452         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21453     }
21454           
21455 })//<script type="text/javascript">
21456
21457 /*
21458  * Based  Ext JS Library 1.1.1
21459  * Copyright(c) 2006-2007, Ext JS, LLC.
21460  * LGPL
21461  *
21462  */
21463  
21464 /**
21465  * @class Roo.HtmlEditorCore
21466  * @extends Roo.Component
21467  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21468  *
21469  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21470  */
21471
21472 Roo.HtmlEditorCore = function(config){
21473     
21474     
21475     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21476     
21477     
21478     this.addEvents({
21479         /**
21480          * @event initialize
21481          * Fires when the editor is fully initialized (including the iframe)
21482          * @param {Roo.HtmlEditorCore} this
21483          */
21484         initialize: true,
21485         /**
21486          * @event activate
21487          * Fires when the editor is first receives the focus. Any insertion must wait
21488          * until after this event.
21489          * @param {Roo.HtmlEditorCore} this
21490          */
21491         activate: true,
21492          /**
21493          * @event beforesync
21494          * Fires before the textarea is updated with content from the editor iframe. Return false
21495          * to cancel the sync.
21496          * @param {Roo.HtmlEditorCore} this
21497          * @param {String} html
21498          */
21499         beforesync: true,
21500          /**
21501          * @event beforepush
21502          * Fires before the iframe editor is updated with content from the textarea. Return false
21503          * to cancel the push.
21504          * @param {Roo.HtmlEditorCore} this
21505          * @param {String} html
21506          */
21507         beforepush: true,
21508          /**
21509          * @event sync
21510          * Fires when the textarea is updated with content from the editor iframe.
21511          * @param {Roo.HtmlEditorCore} this
21512          * @param {String} html
21513          */
21514         sync: true,
21515          /**
21516          * @event push
21517          * Fires when the iframe editor is updated with content from the textarea.
21518          * @param {Roo.HtmlEditorCore} this
21519          * @param {String} html
21520          */
21521         push: true,
21522         
21523         /**
21524          * @event editorevent
21525          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21526          * @param {Roo.HtmlEditorCore} this
21527          */
21528         editorevent: true
21529         
21530     });
21531     
21532     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21533     
21534     // defaults : white / black...
21535     this.applyBlacklists();
21536     
21537     
21538     
21539 };
21540
21541
21542 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21543
21544
21545      /**
21546      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21547      */
21548     
21549     owner : false,
21550     
21551      /**
21552      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21553      *                        Roo.resizable.
21554      */
21555     resizable : false,
21556      /**
21557      * @cfg {Number} height (in pixels)
21558      */   
21559     height: 300,
21560    /**
21561      * @cfg {Number} width (in pixels)
21562      */   
21563     width: 500,
21564     
21565     /**
21566      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21567      * 
21568      */
21569     stylesheets: false,
21570     
21571     // id of frame..
21572     frameId: false,
21573     
21574     // private properties
21575     validationEvent : false,
21576     deferHeight: true,
21577     initialized : false,
21578     activated : false,
21579     sourceEditMode : false,
21580     onFocus : Roo.emptyFn,
21581     iframePad:3,
21582     hideMode:'offsets',
21583     
21584     clearUp: true,
21585     
21586     // blacklist + whitelisted elements..
21587     black: false,
21588     white: false,
21589      
21590     bodyCls : '',
21591
21592     /**
21593      * Protected method that will not generally be called directly. It
21594      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21595      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21596      */
21597     getDocMarkup : function(){
21598         // body styles..
21599         var st = '';
21600         
21601         // inherit styels from page...?? 
21602         if (this.stylesheets === false) {
21603             
21604             Roo.get(document.head).select('style').each(function(node) {
21605                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21606             });
21607             
21608             Roo.get(document.head).select('link').each(function(node) { 
21609                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21610             });
21611             
21612         } else if (!this.stylesheets.length) {
21613                 // simple..
21614                 st = '<style type="text/css">' +
21615                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21616                    '</style>';
21617         } else { 
21618             st = '<style type="text/css">' +
21619                     this.stylesheets +
21620                 '</style>';
21621         }
21622         
21623         st +=  '<style type="text/css">' +
21624             'IMG { cursor: pointer } ' +
21625         '</style>';
21626
21627         var cls = 'roo-htmleditor-body';
21628         
21629         if(this.bodyCls.length){
21630             cls += ' ' + this.bodyCls;
21631         }
21632         
21633         return '<html><head>' + st  +
21634             //<style type="text/css">' +
21635             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21636             //'</style>' +
21637             ' </head><body class="' +  cls + '"></body></html>';
21638     },
21639
21640     // private
21641     onRender : function(ct, position)
21642     {
21643         var _t = this;
21644         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21645         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21646         
21647         
21648         this.el.dom.style.border = '0 none';
21649         this.el.dom.setAttribute('tabIndex', -1);
21650         this.el.addClass('x-hidden hide');
21651         
21652         
21653         
21654         if(Roo.isIE){ // fix IE 1px bogus margin
21655             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21656         }
21657        
21658         
21659         this.frameId = Roo.id();
21660         
21661          
21662         
21663         var iframe = this.owner.wrap.createChild({
21664             tag: 'iframe',
21665             cls: 'form-control', // bootstrap..
21666             id: this.frameId,
21667             name: this.frameId,
21668             frameBorder : 'no',
21669             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21670         }, this.el
21671         );
21672         
21673         
21674         this.iframe = iframe.dom;
21675
21676          this.assignDocWin();
21677         
21678         this.doc.designMode = 'on';
21679        
21680         this.doc.open();
21681         this.doc.write(this.getDocMarkup());
21682         this.doc.close();
21683
21684         
21685         var task = { // must defer to wait for browser to be ready
21686             run : function(){
21687                 //console.log("run task?" + this.doc.readyState);
21688                 this.assignDocWin();
21689                 if(this.doc.body || this.doc.readyState == 'complete'){
21690                     try {
21691                         this.doc.designMode="on";
21692                     } catch (e) {
21693                         return;
21694                     }
21695                     Roo.TaskMgr.stop(task);
21696                     this.initEditor.defer(10, this);
21697                 }
21698             },
21699             interval : 10,
21700             duration: 10000,
21701             scope: this
21702         };
21703         Roo.TaskMgr.start(task);
21704
21705     },
21706
21707     // private
21708     onResize : function(w, h)
21709     {
21710          Roo.log('resize: ' +w + ',' + h );
21711         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21712         if(!this.iframe){
21713             return;
21714         }
21715         if(typeof w == 'number'){
21716             
21717             this.iframe.style.width = w + 'px';
21718         }
21719         if(typeof h == 'number'){
21720             
21721             this.iframe.style.height = h + 'px';
21722             if(this.doc){
21723                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21724             }
21725         }
21726         
21727     },
21728
21729     /**
21730      * Toggles the editor between standard and source edit mode.
21731      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21732      */
21733     toggleSourceEdit : function(sourceEditMode){
21734         
21735         this.sourceEditMode = sourceEditMode === true;
21736         
21737         if(this.sourceEditMode){
21738  
21739             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21740             
21741         }else{
21742             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21743             //this.iframe.className = '';
21744             this.deferFocus();
21745         }
21746         //this.setSize(this.owner.wrap.getSize());
21747         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21748     },
21749
21750     
21751   
21752
21753     /**
21754      * Protected method that will not generally be called directly. If you need/want
21755      * custom HTML cleanup, this is the method you should override.
21756      * @param {String} html The HTML to be cleaned
21757      * return {String} The cleaned HTML
21758      */
21759     cleanHtml : function(html){
21760         html = String(html);
21761         if(html.length > 5){
21762             if(Roo.isSafari){ // strip safari nonsense
21763                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21764             }
21765         }
21766         if(html == '&nbsp;'){
21767             html = '';
21768         }
21769         return html;
21770     },
21771
21772     /**
21773      * HTML Editor -> Textarea
21774      * Protected method that will not generally be called directly. Syncs the contents
21775      * of the editor iframe with the textarea.
21776      */
21777     syncValue : function(){
21778         if(this.initialized){
21779             var bd = (this.doc.body || this.doc.documentElement);
21780             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21781             var html = bd.innerHTML;
21782             if(Roo.isSafari){
21783                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21784                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21785                 if(m && m[1]){
21786                     html = '<div style="'+m[0]+'">' + html + '</div>';
21787                 }
21788             }
21789             html = this.cleanHtml(html);
21790             // fix up the special chars.. normaly like back quotes in word...
21791             // however we do not want to do this with chinese..
21792             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21793                 var cc = b.charCodeAt();
21794                 if (
21795                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21796                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21797                     (cc >= 0xf900 && cc < 0xfb00 )
21798                 ) {
21799                         return b;
21800                 }
21801                 return "&#"+cc+";" 
21802             });
21803             if(this.owner.fireEvent('beforesync', this, html) !== false){
21804                 this.el.dom.value = html;
21805                 this.owner.fireEvent('sync', this, html);
21806             }
21807         }
21808     },
21809
21810     /**
21811      * Protected method that will not generally be called directly. Pushes the value of the textarea
21812      * into the iframe editor.
21813      */
21814     pushValue : function(){
21815         if(this.initialized){
21816             var v = this.el.dom.value.trim();
21817             
21818 //            if(v.length < 1){
21819 //                v = '&#160;';
21820 //            }
21821             
21822             if(this.owner.fireEvent('beforepush', this, v) !== false){
21823                 var d = (this.doc.body || this.doc.documentElement);
21824                 d.innerHTML = v;
21825                 this.cleanUpPaste();
21826                 this.el.dom.value = d.innerHTML;
21827                 this.owner.fireEvent('push', this, v);
21828             }
21829         }
21830     },
21831
21832     // private
21833     deferFocus : function(){
21834         this.focus.defer(10, this);
21835     },
21836
21837     // doc'ed in Field
21838     focus : function(){
21839         if(this.win && !this.sourceEditMode){
21840             this.win.focus();
21841         }else{
21842             this.el.focus();
21843         }
21844     },
21845     
21846     assignDocWin: function()
21847     {
21848         var iframe = this.iframe;
21849         
21850          if(Roo.isIE){
21851             this.doc = iframe.contentWindow.document;
21852             this.win = iframe.contentWindow;
21853         } else {
21854 //            if (!Roo.get(this.frameId)) {
21855 //                return;
21856 //            }
21857 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21858 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21859             
21860             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21861                 return;
21862             }
21863             
21864             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21865             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21866         }
21867     },
21868     
21869     // private
21870     initEditor : function(){
21871         //console.log("INIT EDITOR");
21872         this.assignDocWin();
21873         
21874         
21875         
21876         this.doc.designMode="on";
21877         this.doc.open();
21878         this.doc.write(this.getDocMarkup());
21879         this.doc.close();
21880         
21881         var dbody = (this.doc.body || this.doc.documentElement);
21882         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21883         // this copies styles from the containing element into thsi one..
21884         // not sure why we need all of this..
21885         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21886         
21887         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21888         //ss['background-attachment'] = 'fixed'; // w3c
21889         dbody.bgProperties = 'fixed'; // ie
21890         //Roo.DomHelper.applyStyles(dbody, ss);
21891         Roo.EventManager.on(this.doc, {
21892             //'mousedown': this.onEditorEvent,
21893             'mouseup': this.onEditorEvent,
21894             'dblclick': this.onEditorEvent,
21895             'click': this.onEditorEvent,
21896             'keyup': this.onEditorEvent,
21897             buffer:100,
21898             scope: this
21899         });
21900         if(Roo.isGecko){
21901             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21902         }
21903         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21904             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21905         }
21906         this.initialized = true;
21907
21908         this.owner.fireEvent('initialize', this);
21909         this.pushValue();
21910     },
21911
21912     // private
21913     onDestroy : function(){
21914         
21915         
21916         
21917         if(this.rendered){
21918             
21919             //for (var i =0; i < this.toolbars.length;i++) {
21920             //    // fixme - ask toolbars for heights?
21921             //    this.toolbars[i].onDestroy();
21922            // }
21923             
21924             //this.wrap.dom.innerHTML = '';
21925             //this.wrap.remove();
21926         }
21927     },
21928
21929     // private
21930     onFirstFocus : function(){
21931         
21932         this.assignDocWin();
21933         
21934         
21935         this.activated = true;
21936          
21937     
21938         if(Roo.isGecko){ // prevent silly gecko errors
21939             this.win.focus();
21940             var s = this.win.getSelection();
21941             if(!s.focusNode || s.focusNode.nodeType != 3){
21942                 var r = s.getRangeAt(0);
21943                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21944                 r.collapse(true);
21945                 this.deferFocus();
21946             }
21947             try{
21948                 this.execCmd('useCSS', true);
21949                 this.execCmd('styleWithCSS', false);
21950             }catch(e){}
21951         }
21952         this.owner.fireEvent('activate', this);
21953     },
21954
21955     // private
21956     adjustFont: function(btn){
21957         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21958         //if(Roo.isSafari){ // safari
21959         //    adjust *= 2;
21960        // }
21961         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21962         if(Roo.isSafari){ // safari
21963             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21964             v =  (v < 10) ? 10 : v;
21965             v =  (v > 48) ? 48 : v;
21966             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21967             
21968         }
21969         
21970         
21971         v = Math.max(1, v+adjust);
21972         
21973         this.execCmd('FontSize', v  );
21974     },
21975
21976     onEditorEvent : function(e)
21977     {
21978         this.owner.fireEvent('editorevent', this, e);
21979       //  this.updateToolbar();
21980         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21981     },
21982
21983     insertTag : function(tg)
21984     {
21985         // could be a bit smarter... -> wrap the current selected tRoo..
21986         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21987             
21988             range = this.createRange(this.getSelection());
21989             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21990             wrappingNode.appendChild(range.extractContents());
21991             range.insertNode(wrappingNode);
21992
21993             return;
21994             
21995             
21996             
21997         }
21998         this.execCmd("formatblock",   tg);
21999         
22000     },
22001     
22002     insertText : function(txt)
22003     {
22004         
22005         
22006         var range = this.createRange();
22007         range.deleteContents();
22008                //alert(Sender.getAttribute('label'));
22009                
22010         range.insertNode(this.doc.createTextNode(txt));
22011     } ,
22012     
22013      
22014
22015     /**
22016      * Executes a Midas editor command on the editor document and performs necessary focus and
22017      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22018      * @param {String} cmd The Midas command
22019      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22020      */
22021     relayCmd : function(cmd, value){
22022         this.win.focus();
22023         this.execCmd(cmd, value);
22024         this.owner.fireEvent('editorevent', this);
22025         //this.updateToolbar();
22026         this.owner.deferFocus();
22027     },
22028
22029     /**
22030      * Executes a Midas editor command directly on the editor document.
22031      * For visual commands, you should use {@link #relayCmd} instead.
22032      * <b>This should only be called after the editor is initialized.</b>
22033      * @param {String} cmd The Midas command
22034      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22035      */
22036     execCmd : function(cmd, value){
22037         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22038         this.syncValue();
22039     },
22040  
22041  
22042    
22043     /**
22044      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22045      * to insert tRoo.
22046      * @param {String} text | dom node.. 
22047      */
22048     insertAtCursor : function(text)
22049     {
22050         
22051         if(!this.activated){
22052             return;
22053         }
22054         /*
22055         if(Roo.isIE){
22056             this.win.focus();
22057             var r = this.doc.selection.createRange();
22058             if(r){
22059                 r.collapse(true);
22060                 r.pasteHTML(text);
22061                 this.syncValue();
22062                 this.deferFocus();
22063             
22064             }
22065             return;
22066         }
22067         */
22068         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22069             this.win.focus();
22070             
22071             
22072             // from jquery ui (MIT licenced)
22073             var range, node;
22074             var win = this.win;
22075             
22076             if (win.getSelection && win.getSelection().getRangeAt) {
22077                 range = win.getSelection().getRangeAt(0);
22078                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22079                 range.insertNode(node);
22080             } else if (win.document.selection && win.document.selection.createRange) {
22081                 // no firefox support
22082                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22083                 win.document.selection.createRange().pasteHTML(txt);
22084             } else {
22085                 // no firefox support
22086                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22087                 this.execCmd('InsertHTML', txt);
22088             } 
22089             
22090             this.syncValue();
22091             
22092             this.deferFocus();
22093         }
22094     },
22095  // private
22096     mozKeyPress : function(e){
22097         if(e.ctrlKey){
22098             var c = e.getCharCode(), cmd;
22099           
22100             if(c > 0){
22101                 c = String.fromCharCode(c).toLowerCase();
22102                 switch(c){
22103                     case 'b':
22104                         cmd = 'bold';
22105                         break;
22106                     case 'i':
22107                         cmd = 'italic';
22108                         break;
22109                     
22110                     case 'u':
22111                         cmd = 'underline';
22112                         break;
22113                     
22114                     case 'v':
22115                         this.cleanUpPaste.defer(100, this);
22116                         return;
22117                         
22118                 }
22119                 if(cmd){
22120                     this.win.focus();
22121                     this.execCmd(cmd);
22122                     this.deferFocus();
22123                     e.preventDefault();
22124                 }
22125                 
22126             }
22127         }
22128     },
22129
22130     // private
22131     fixKeys : function(){ // load time branching for fastest keydown performance
22132         if(Roo.isIE){
22133             return function(e){
22134                 var k = e.getKey(), r;
22135                 if(k == e.TAB){
22136                     e.stopEvent();
22137                     r = this.doc.selection.createRange();
22138                     if(r){
22139                         r.collapse(true);
22140                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22141                         this.deferFocus();
22142                     }
22143                     return;
22144                 }
22145                 
22146                 if(k == e.ENTER){
22147                     r = this.doc.selection.createRange();
22148                     if(r){
22149                         var target = r.parentElement();
22150                         if(!target || target.tagName.toLowerCase() != 'li'){
22151                             e.stopEvent();
22152                             r.pasteHTML('<br />');
22153                             r.collapse(false);
22154                             r.select();
22155                         }
22156                     }
22157                 }
22158                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22159                     this.cleanUpPaste.defer(100, this);
22160                     return;
22161                 }
22162                 
22163                 
22164             };
22165         }else if(Roo.isOpera){
22166             return function(e){
22167                 var k = e.getKey();
22168                 if(k == e.TAB){
22169                     e.stopEvent();
22170                     this.win.focus();
22171                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22172                     this.deferFocus();
22173                 }
22174                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22175                     this.cleanUpPaste.defer(100, this);
22176                     return;
22177                 }
22178                 
22179             };
22180         }else if(Roo.isSafari){
22181             return function(e){
22182                 var k = e.getKey();
22183                 
22184                 if(k == e.TAB){
22185                     e.stopEvent();
22186                     this.execCmd('InsertText','\t');
22187                     this.deferFocus();
22188                     return;
22189                 }
22190                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22191                     this.cleanUpPaste.defer(100, this);
22192                     return;
22193                 }
22194                 
22195              };
22196         }
22197     }(),
22198     
22199     getAllAncestors: function()
22200     {
22201         var p = this.getSelectedNode();
22202         var a = [];
22203         if (!p) {
22204             a.push(p); // push blank onto stack..
22205             p = this.getParentElement();
22206         }
22207         
22208         
22209         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22210             a.push(p);
22211             p = p.parentNode;
22212         }
22213         a.push(this.doc.body);
22214         return a;
22215     },
22216     lastSel : false,
22217     lastSelNode : false,
22218     
22219     
22220     getSelection : function() 
22221     {
22222         this.assignDocWin();
22223         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22224     },
22225     
22226     getSelectedNode: function() 
22227     {
22228         // this may only work on Gecko!!!
22229         
22230         // should we cache this!!!!
22231         
22232         
22233         
22234          
22235         var range = this.createRange(this.getSelection()).cloneRange();
22236         
22237         if (Roo.isIE) {
22238             var parent = range.parentElement();
22239             while (true) {
22240                 var testRange = range.duplicate();
22241                 testRange.moveToElementText(parent);
22242                 if (testRange.inRange(range)) {
22243                     break;
22244                 }
22245                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22246                     break;
22247                 }
22248                 parent = parent.parentElement;
22249             }
22250             return parent;
22251         }
22252         
22253         // is ancestor a text element.
22254         var ac =  range.commonAncestorContainer;
22255         if (ac.nodeType == 3) {
22256             ac = ac.parentNode;
22257         }
22258         
22259         var ar = ac.childNodes;
22260          
22261         var nodes = [];
22262         var other_nodes = [];
22263         var has_other_nodes = false;
22264         for (var i=0;i<ar.length;i++) {
22265             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22266                 continue;
22267             }
22268             // fullly contained node.
22269             
22270             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22271                 nodes.push(ar[i]);
22272                 continue;
22273             }
22274             
22275             // probably selected..
22276             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22277                 other_nodes.push(ar[i]);
22278                 continue;
22279             }
22280             // outer..
22281             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22282                 continue;
22283             }
22284             
22285             
22286             has_other_nodes = true;
22287         }
22288         if (!nodes.length && other_nodes.length) {
22289             nodes= other_nodes;
22290         }
22291         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22292             return false;
22293         }
22294         
22295         return nodes[0];
22296     },
22297     createRange: function(sel)
22298     {
22299         // this has strange effects when using with 
22300         // top toolbar - not sure if it's a great idea.
22301         //this.editor.contentWindow.focus();
22302         if (typeof sel != "undefined") {
22303             try {
22304                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22305             } catch(e) {
22306                 return this.doc.createRange();
22307             }
22308         } else {
22309             return this.doc.createRange();
22310         }
22311     },
22312     getParentElement: function()
22313     {
22314         
22315         this.assignDocWin();
22316         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22317         
22318         var range = this.createRange(sel);
22319          
22320         try {
22321             var p = range.commonAncestorContainer;
22322             while (p.nodeType == 3) { // text node
22323                 p = p.parentNode;
22324             }
22325             return p;
22326         } catch (e) {
22327             return null;
22328         }
22329     
22330     },
22331     /***
22332      *
22333      * Range intersection.. the hard stuff...
22334      *  '-1' = before
22335      *  '0' = hits..
22336      *  '1' = after.
22337      *         [ -- selected range --- ]
22338      *   [fail]                        [fail]
22339      *
22340      *    basically..
22341      *      if end is before start or  hits it. fail.
22342      *      if start is after end or hits it fail.
22343      *
22344      *   if either hits (but other is outside. - then it's not 
22345      *   
22346      *    
22347      **/
22348     
22349     
22350     // @see http://www.thismuchiknow.co.uk/?p=64.
22351     rangeIntersectsNode : function(range, node)
22352     {
22353         var nodeRange = node.ownerDocument.createRange();
22354         try {
22355             nodeRange.selectNode(node);
22356         } catch (e) {
22357             nodeRange.selectNodeContents(node);
22358         }
22359     
22360         var rangeStartRange = range.cloneRange();
22361         rangeStartRange.collapse(true);
22362     
22363         var rangeEndRange = range.cloneRange();
22364         rangeEndRange.collapse(false);
22365     
22366         var nodeStartRange = nodeRange.cloneRange();
22367         nodeStartRange.collapse(true);
22368     
22369         var nodeEndRange = nodeRange.cloneRange();
22370         nodeEndRange.collapse(false);
22371     
22372         return rangeStartRange.compareBoundaryPoints(
22373                  Range.START_TO_START, nodeEndRange) == -1 &&
22374                rangeEndRange.compareBoundaryPoints(
22375                  Range.START_TO_START, nodeStartRange) == 1;
22376         
22377          
22378     },
22379     rangeCompareNode : function(range, node)
22380     {
22381         var nodeRange = node.ownerDocument.createRange();
22382         try {
22383             nodeRange.selectNode(node);
22384         } catch (e) {
22385             nodeRange.selectNodeContents(node);
22386         }
22387         
22388         
22389         range.collapse(true);
22390     
22391         nodeRange.collapse(true);
22392      
22393         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22394         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22395          
22396         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22397         
22398         var nodeIsBefore   =  ss == 1;
22399         var nodeIsAfter    = ee == -1;
22400         
22401         if (nodeIsBefore && nodeIsAfter) {
22402             return 0; // outer
22403         }
22404         if (!nodeIsBefore && nodeIsAfter) {
22405             return 1; //right trailed.
22406         }
22407         
22408         if (nodeIsBefore && !nodeIsAfter) {
22409             return 2;  // left trailed.
22410         }
22411         // fully contined.
22412         return 3;
22413     },
22414
22415     // private? - in a new class?
22416     cleanUpPaste :  function()
22417     {
22418         // cleans up the whole document..
22419         Roo.log('cleanuppaste');
22420         
22421         this.cleanUpChildren(this.doc.body);
22422         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22423         if (clean != this.doc.body.innerHTML) {
22424             this.doc.body.innerHTML = clean;
22425         }
22426         
22427     },
22428     
22429     cleanWordChars : function(input) {// change the chars to hex code
22430         var he = Roo.HtmlEditorCore;
22431         
22432         var output = input;
22433         Roo.each(he.swapCodes, function(sw) { 
22434             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22435             
22436             output = output.replace(swapper, sw[1]);
22437         });
22438         
22439         return output;
22440     },
22441     
22442     
22443     cleanUpChildren : function (n)
22444     {
22445         if (!n.childNodes.length) {
22446             return;
22447         }
22448         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22449            this.cleanUpChild(n.childNodes[i]);
22450         }
22451     },
22452     
22453     
22454         
22455     
22456     cleanUpChild : function (node)
22457     {
22458         var ed = this;
22459         //console.log(node);
22460         if (node.nodeName == "#text") {
22461             // clean up silly Windows -- stuff?
22462             return; 
22463         }
22464         if (node.nodeName == "#comment") {
22465             node.parentNode.removeChild(node);
22466             // clean up silly Windows -- stuff?
22467             return; 
22468         }
22469         var lcname = node.tagName.toLowerCase();
22470         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22471         // whitelist of tags..
22472         
22473         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22474             // remove node.
22475             node.parentNode.removeChild(node);
22476             return;
22477             
22478         }
22479         
22480         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22481         
22482         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22483         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22484         
22485         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22486         //    remove_keep_children = true;
22487         //}
22488         
22489         if (remove_keep_children) {
22490             this.cleanUpChildren(node);
22491             // inserts everything just before this node...
22492             while (node.childNodes.length) {
22493                 var cn = node.childNodes[0];
22494                 node.removeChild(cn);
22495                 node.parentNode.insertBefore(cn, node);
22496             }
22497             node.parentNode.removeChild(node);
22498             return;
22499         }
22500         
22501         if (!node.attributes || !node.attributes.length) {
22502             this.cleanUpChildren(node);
22503             return;
22504         }
22505         
22506         function cleanAttr(n,v)
22507         {
22508             
22509             if (v.match(/^\./) || v.match(/^\//)) {
22510                 return;
22511             }
22512             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22513                 return;
22514             }
22515             if (v.match(/^#/)) {
22516                 return;
22517             }
22518 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22519             node.removeAttribute(n);
22520             
22521         }
22522         
22523         var cwhite = this.cwhite;
22524         var cblack = this.cblack;
22525             
22526         function cleanStyle(n,v)
22527         {
22528             if (v.match(/expression/)) { //XSS?? should we even bother..
22529                 node.removeAttribute(n);
22530                 return;
22531             }
22532             
22533             var parts = v.split(/;/);
22534             var clean = [];
22535             
22536             Roo.each(parts, function(p) {
22537                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22538                 if (!p.length) {
22539                     return true;
22540                 }
22541                 var l = p.split(':').shift().replace(/\s+/g,'');
22542                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22543                 
22544                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22545 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22546                     //node.removeAttribute(n);
22547                     return true;
22548                 }
22549                 //Roo.log()
22550                 // only allow 'c whitelisted system attributes'
22551                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22552 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22553                     //node.removeAttribute(n);
22554                     return true;
22555                 }
22556                 
22557                 
22558                  
22559                 
22560                 clean.push(p);
22561                 return true;
22562             });
22563             if (clean.length) { 
22564                 node.setAttribute(n, clean.join(';'));
22565             } else {
22566                 node.removeAttribute(n);
22567             }
22568             
22569         }
22570         
22571         
22572         for (var i = node.attributes.length-1; i > -1 ; i--) {
22573             var a = node.attributes[i];
22574             //console.log(a);
22575             
22576             if (a.name.toLowerCase().substr(0,2)=='on')  {
22577                 node.removeAttribute(a.name);
22578                 continue;
22579             }
22580             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22581                 node.removeAttribute(a.name);
22582                 continue;
22583             }
22584             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22585                 cleanAttr(a.name,a.value); // fixme..
22586                 continue;
22587             }
22588             if (a.name == 'style') {
22589                 cleanStyle(a.name,a.value);
22590                 continue;
22591             }
22592             /// clean up MS crap..
22593             // tecnically this should be a list of valid class'es..
22594             
22595             
22596             if (a.name == 'class') {
22597                 if (a.value.match(/^Mso/)) {
22598                     node.className = '';
22599                 }
22600                 
22601                 if (a.value.match(/^body$/)) {
22602                     node.className = '';
22603                 }
22604                 continue;
22605             }
22606             
22607             // style cleanup!?
22608             // class cleanup?
22609             
22610         }
22611         
22612         
22613         this.cleanUpChildren(node);
22614         
22615         
22616     },
22617     
22618     /**
22619      * Clean up MS wordisms...
22620      */
22621     cleanWord : function(node)
22622     {
22623         
22624         
22625         if (!node) {
22626             this.cleanWord(this.doc.body);
22627             return;
22628         }
22629         if (node.nodeName == "#text") {
22630             // clean up silly Windows -- stuff?
22631             return; 
22632         }
22633         if (node.nodeName == "#comment") {
22634             node.parentNode.removeChild(node);
22635             // clean up silly Windows -- stuff?
22636             return; 
22637         }
22638         
22639         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22640             node.parentNode.removeChild(node);
22641             return;
22642         }
22643         
22644         // remove - but keep children..
22645         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22646             while (node.childNodes.length) {
22647                 var cn = node.childNodes[0];
22648                 node.removeChild(cn);
22649                 node.parentNode.insertBefore(cn, node);
22650             }
22651             node.parentNode.removeChild(node);
22652             this.iterateChildren(node, this.cleanWord);
22653             return;
22654         }
22655         // clean styles
22656         if (node.className.length) {
22657             
22658             var cn = node.className.split(/\W+/);
22659             var cna = [];
22660             Roo.each(cn, function(cls) {
22661                 if (cls.match(/Mso[a-zA-Z]+/)) {
22662                     return;
22663                 }
22664                 cna.push(cls);
22665             });
22666             node.className = cna.length ? cna.join(' ') : '';
22667             if (!cna.length) {
22668                 node.removeAttribute("class");
22669             }
22670         }
22671         
22672         if (node.hasAttribute("lang")) {
22673             node.removeAttribute("lang");
22674         }
22675         
22676         if (node.hasAttribute("style")) {
22677             
22678             var styles = node.getAttribute("style").split(";");
22679             var nstyle = [];
22680             Roo.each(styles, function(s) {
22681                 if (!s.match(/:/)) {
22682                     return;
22683                 }
22684                 var kv = s.split(":");
22685                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22686                     return;
22687                 }
22688                 // what ever is left... we allow.
22689                 nstyle.push(s);
22690             });
22691             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22692             if (!nstyle.length) {
22693                 node.removeAttribute('style');
22694             }
22695         }
22696         this.iterateChildren(node, this.cleanWord);
22697         
22698         
22699         
22700     },
22701     /**
22702      * iterateChildren of a Node, calling fn each time, using this as the scole..
22703      * @param {DomNode} node node to iterate children of.
22704      * @param {Function} fn method of this class to call on each item.
22705      */
22706     iterateChildren : function(node, fn)
22707     {
22708         if (!node.childNodes.length) {
22709                 return;
22710         }
22711         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22712            fn.call(this, node.childNodes[i])
22713         }
22714     },
22715     
22716     
22717     /**
22718      * cleanTableWidths.
22719      *
22720      * Quite often pasting from word etc.. results in tables with column and widths.
22721      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22722      *
22723      */
22724     cleanTableWidths : function(node)
22725     {
22726          
22727          
22728         if (!node) {
22729             this.cleanTableWidths(this.doc.body);
22730             return;
22731         }
22732         
22733         // ignore list...
22734         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22735             return; 
22736         }
22737         Roo.log(node.tagName);
22738         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22739             this.iterateChildren(node, this.cleanTableWidths);
22740             return;
22741         }
22742         if (node.hasAttribute('width')) {
22743             node.removeAttribute('width');
22744         }
22745         
22746          
22747         if (node.hasAttribute("style")) {
22748             // pretty basic...
22749             
22750             var styles = node.getAttribute("style").split(";");
22751             var nstyle = [];
22752             Roo.each(styles, function(s) {
22753                 if (!s.match(/:/)) {
22754                     return;
22755                 }
22756                 var kv = s.split(":");
22757                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22758                     return;
22759                 }
22760                 // what ever is left... we allow.
22761                 nstyle.push(s);
22762             });
22763             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22764             if (!nstyle.length) {
22765                 node.removeAttribute('style');
22766             }
22767         }
22768         
22769         this.iterateChildren(node, this.cleanTableWidths);
22770         
22771         
22772     },
22773     
22774     
22775     
22776     
22777     domToHTML : function(currentElement, depth, nopadtext) {
22778         
22779         depth = depth || 0;
22780         nopadtext = nopadtext || false;
22781     
22782         if (!currentElement) {
22783             return this.domToHTML(this.doc.body);
22784         }
22785         
22786         //Roo.log(currentElement);
22787         var j;
22788         var allText = false;
22789         var nodeName = currentElement.nodeName;
22790         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22791         
22792         if  (nodeName == '#text') {
22793             
22794             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22795         }
22796         
22797         
22798         var ret = '';
22799         if (nodeName != 'BODY') {
22800              
22801             var i = 0;
22802             // Prints the node tagName, such as <A>, <IMG>, etc
22803             if (tagName) {
22804                 var attr = [];
22805                 for(i = 0; i < currentElement.attributes.length;i++) {
22806                     // quoting?
22807                     var aname = currentElement.attributes.item(i).name;
22808                     if (!currentElement.attributes.item(i).value.length) {
22809                         continue;
22810                     }
22811                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22812                 }
22813                 
22814                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22815             } 
22816             else {
22817                 
22818                 // eack
22819             }
22820         } else {
22821             tagName = false;
22822         }
22823         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22824             return ret;
22825         }
22826         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22827             nopadtext = true;
22828         }
22829         
22830         
22831         // Traverse the tree
22832         i = 0;
22833         var currentElementChild = currentElement.childNodes.item(i);
22834         var allText = true;
22835         var innerHTML  = '';
22836         lastnode = '';
22837         while (currentElementChild) {
22838             // Formatting code (indent the tree so it looks nice on the screen)
22839             var nopad = nopadtext;
22840             if (lastnode == 'SPAN') {
22841                 nopad  = true;
22842             }
22843             // text
22844             if  (currentElementChild.nodeName == '#text') {
22845                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22846                 toadd = nopadtext ? toadd : toadd.trim();
22847                 if (!nopad && toadd.length > 80) {
22848                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22849                 }
22850                 innerHTML  += toadd;
22851                 
22852                 i++;
22853                 currentElementChild = currentElement.childNodes.item(i);
22854                 lastNode = '';
22855                 continue;
22856             }
22857             allText = false;
22858             
22859             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22860                 
22861             // Recursively traverse the tree structure of the child node
22862             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22863             lastnode = currentElementChild.nodeName;
22864             i++;
22865             currentElementChild=currentElement.childNodes.item(i);
22866         }
22867         
22868         ret += innerHTML;
22869         
22870         if (!allText) {
22871                 // The remaining code is mostly for formatting the tree
22872             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22873         }
22874         
22875         
22876         if (tagName) {
22877             ret+= "</"+tagName+">";
22878         }
22879         return ret;
22880         
22881     },
22882         
22883     applyBlacklists : function()
22884     {
22885         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22886         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22887         
22888         this.white = [];
22889         this.black = [];
22890         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22891             if (b.indexOf(tag) > -1) {
22892                 return;
22893             }
22894             this.white.push(tag);
22895             
22896         }, this);
22897         
22898         Roo.each(w, function(tag) {
22899             if (b.indexOf(tag) > -1) {
22900                 return;
22901             }
22902             if (this.white.indexOf(tag) > -1) {
22903                 return;
22904             }
22905             this.white.push(tag);
22906             
22907         }, this);
22908         
22909         
22910         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22911             if (w.indexOf(tag) > -1) {
22912                 return;
22913             }
22914             this.black.push(tag);
22915             
22916         }, this);
22917         
22918         Roo.each(b, function(tag) {
22919             if (w.indexOf(tag) > -1) {
22920                 return;
22921             }
22922             if (this.black.indexOf(tag) > -1) {
22923                 return;
22924             }
22925             this.black.push(tag);
22926             
22927         }, this);
22928         
22929         
22930         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22931         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22932         
22933         this.cwhite = [];
22934         this.cblack = [];
22935         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22936             if (b.indexOf(tag) > -1) {
22937                 return;
22938             }
22939             this.cwhite.push(tag);
22940             
22941         }, this);
22942         
22943         Roo.each(w, function(tag) {
22944             if (b.indexOf(tag) > -1) {
22945                 return;
22946             }
22947             if (this.cwhite.indexOf(tag) > -1) {
22948                 return;
22949             }
22950             this.cwhite.push(tag);
22951             
22952         }, this);
22953         
22954         
22955         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22956             if (w.indexOf(tag) > -1) {
22957                 return;
22958             }
22959             this.cblack.push(tag);
22960             
22961         }, this);
22962         
22963         Roo.each(b, function(tag) {
22964             if (w.indexOf(tag) > -1) {
22965                 return;
22966             }
22967             if (this.cblack.indexOf(tag) > -1) {
22968                 return;
22969             }
22970             this.cblack.push(tag);
22971             
22972         }, this);
22973     },
22974     
22975     setStylesheets : function(stylesheets)
22976     {
22977         if(typeof(stylesheets) == 'string'){
22978             Roo.get(this.iframe.contentDocument.head).createChild({
22979                 tag : 'link',
22980                 rel : 'stylesheet',
22981                 type : 'text/css',
22982                 href : stylesheets
22983             });
22984             
22985             return;
22986         }
22987         var _this = this;
22988      
22989         Roo.each(stylesheets, function(s) {
22990             if(!s.length){
22991                 return;
22992             }
22993             
22994             Roo.get(_this.iframe.contentDocument.head).createChild({
22995                 tag : 'link',
22996                 rel : 'stylesheet',
22997                 type : 'text/css',
22998                 href : s
22999             });
23000         });
23001
23002         
23003     },
23004     
23005     removeStylesheets : function()
23006     {
23007         var _this = this;
23008         
23009         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23010             s.remove();
23011         });
23012     },
23013     
23014     setStyle : function(style)
23015     {
23016         Roo.get(this.iframe.contentDocument.head).createChild({
23017             tag : 'style',
23018             type : 'text/css',
23019             html : style
23020         });
23021
23022         return;
23023     }
23024     
23025     // hide stuff that is not compatible
23026     /**
23027      * @event blur
23028      * @hide
23029      */
23030     /**
23031      * @event change
23032      * @hide
23033      */
23034     /**
23035      * @event focus
23036      * @hide
23037      */
23038     /**
23039      * @event specialkey
23040      * @hide
23041      */
23042     /**
23043      * @cfg {String} fieldClass @hide
23044      */
23045     /**
23046      * @cfg {String} focusClass @hide
23047      */
23048     /**
23049      * @cfg {String} autoCreate @hide
23050      */
23051     /**
23052      * @cfg {String} inputType @hide
23053      */
23054     /**
23055      * @cfg {String} invalidClass @hide
23056      */
23057     /**
23058      * @cfg {String} invalidText @hide
23059      */
23060     /**
23061      * @cfg {String} msgFx @hide
23062      */
23063     /**
23064      * @cfg {String} validateOnBlur @hide
23065      */
23066 });
23067
23068 Roo.HtmlEditorCore.white = [
23069         'area', 'br', 'img', 'input', 'hr', 'wbr',
23070         
23071        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23072        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23073        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23074        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23075        'table',   'ul',         'xmp', 
23076        
23077        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23078       'thead',   'tr', 
23079      
23080       'dir', 'menu', 'ol', 'ul', 'dl',
23081        
23082       'embed',  'object'
23083 ];
23084
23085
23086 Roo.HtmlEditorCore.black = [
23087     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23088         'applet', // 
23089         'base',   'basefont', 'bgsound', 'blink',  'body', 
23090         'frame',  'frameset', 'head',    'html',   'ilayer', 
23091         'iframe', 'layer',  'link',     'meta',    'object',   
23092         'script', 'style' ,'title',  'xml' // clean later..
23093 ];
23094 Roo.HtmlEditorCore.clean = [
23095     'script', 'style', 'title', 'xml'
23096 ];
23097 Roo.HtmlEditorCore.remove = [
23098     'font'
23099 ];
23100 // attributes..
23101
23102 Roo.HtmlEditorCore.ablack = [
23103     'on'
23104 ];
23105     
23106 Roo.HtmlEditorCore.aclean = [ 
23107     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23108 ];
23109
23110 // protocols..
23111 Roo.HtmlEditorCore.pwhite= [
23112         'http',  'https',  'mailto'
23113 ];
23114
23115 // white listed style attributes.
23116 Roo.HtmlEditorCore.cwhite= [
23117       //  'text-align', /// default is to allow most things..
23118       
23119          
23120 //        'font-size'//??
23121 ];
23122
23123 // black listed style attributes.
23124 Roo.HtmlEditorCore.cblack= [
23125       //  'font-size' -- this can be set by the project 
23126 ];
23127
23128
23129 Roo.HtmlEditorCore.swapCodes   =[ 
23130     [    8211, "--" ], 
23131     [    8212, "--" ], 
23132     [    8216,  "'" ],  
23133     [    8217, "'" ],  
23134     [    8220, '"' ],  
23135     [    8221, '"' ],  
23136     [    8226, "*" ],  
23137     [    8230, "..." ]
23138 ]; 
23139
23140     /*
23141  * - LGPL
23142  *
23143  * HtmlEditor
23144  * 
23145  */
23146
23147 /**
23148  * @class Roo.bootstrap.HtmlEditor
23149  * @extends Roo.bootstrap.TextArea
23150  * Bootstrap HtmlEditor class
23151
23152  * @constructor
23153  * Create a new HtmlEditor
23154  * @param {Object} config The config object
23155  */
23156
23157 Roo.bootstrap.HtmlEditor = function(config){
23158     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23159     if (!this.toolbars) {
23160         this.toolbars = [];
23161     }
23162     
23163     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23164     this.addEvents({
23165             /**
23166              * @event initialize
23167              * Fires when the editor is fully initialized (including the iframe)
23168              * @param {HtmlEditor} this
23169              */
23170             initialize: true,
23171             /**
23172              * @event activate
23173              * Fires when the editor is first receives the focus. Any insertion must wait
23174              * until after this event.
23175              * @param {HtmlEditor} this
23176              */
23177             activate: true,
23178              /**
23179              * @event beforesync
23180              * Fires before the textarea is updated with content from the editor iframe. Return false
23181              * to cancel the sync.
23182              * @param {HtmlEditor} this
23183              * @param {String} html
23184              */
23185             beforesync: true,
23186              /**
23187              * @event beforepush
23188              * Fires before the iframe editor is updated with content from the textarea. Return false
23189              * to cancel the push.
23190              * @param {HtmlEditor} this
23191              * @param {String} html
23192              */
23193             beforepush: true,
23194              /**
23195              * @event sync
23196              * Fires when the textarea is updated with content from the editor iframe.
23197              * @param {HtmlEditor} this
23198              * @param {String} html
23199              */
23200             sync: true,
23201              /**
23202              * @event push
23203              * Fires when the iframe editor is updated with content from the textarea.
23204              * @param {HtmlEditor} this
23205              * @param {String} html
23206              */
23207             push: true,
23208              /**
23209              * @event editmodechange
23210              * Fires when the editor switches edit modes
23211              * @param {HtmlEditor} this
23212              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23213              */
23214             editmodechange: true,
23215             /**
23216              * @event editorevent
23217              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23218              * @param {HtmlEditor} this
23219              */
23220             editorevent: true,
23221             /**
23222              * @event firstfocus
23223              * Fires when on first focus - needed by toolbars..
23224              * @param {HtmlEditor} this
23225              */
23226             firstfocus: true,
23227             /**
23228              * @event autosave
23229              * Auto save the htmlEditor value as a file into Events
23230              * @param {HtmlEditor} this
23231              */
23232             autosave: true,
23233             /**
23234              * @event savedpreview
23235              * preview the saved version of htmlEditor
23236              * @param {HtmlEditor} this
23237              */
23238             savedpreview: true
23239         });
23240 };
23241
23242
23243 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23244     
23245     
23246       /**
23247      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23248      */
23249     toolbars : false,
23250     
23251      /**
23252     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23253     */
23254     btns : [],
23255    
23256      /**
23257      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23258      *                        Roo.resizable.
23259      */
23260     resizable : false,
23261      /**
23262      * @cfg {Number} height (in pixels)
23263      */   
23264     height: 300,
23265    /**
23266      * @cfg {Number} width (in pixels)
23267      */   
23268     width: false,
23269     
23270     /**
23271      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23272      * 
23273      */
23274     stylesheets: false,
23275     
23276     // id of frame..
23277     frameId: false,
23278     
23279     // private properties
23280     validationEvent : false,
23281     deferHeight: true,
23282     initialized : false,
23283     activated : false,
23284     
23285     onFocus : Roo.emptyFn,
23286     iframePad:3,
23287     hideMode:'offsets',
23288     
23289     tbContainer : false,
23290     
23291     bodyCls : '',
23292     
23293     toolbarContainer :function() {
23294         return this.wrap.select('.x-html-editor-tb',true).first();
23295     },
23296
23297     /**
23298      * Protected method that will not generally be called directly. It
23299      * is called when the editor creates its toolbar. Override this method if you need to
23300      * add custom toolbar buttons.
23301      * @param {HtmlEditor} editor
23302      */
23303     createToolbar : function(){
23304         Roo.log('renewing');
23305         Roo.log("create toolbars");
23306         
23307         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23308         this.toolbars[0].render(this.toolbarContainer());
23309         
23310         return;
23311         
23312 //        if (!editor.toolbars || !editor.toolbars.length) {
23313 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23314 //        }
23315 //        
23316 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23317 //            editor.toolbars[i] = Roo.factory(
23318 //                    typeof(editor.toolbars[i]) == 'string' ?
23319 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23320 //                Roo.bootstrap.HtmlEditor);
23321 //            editor.toolbars[i].init(editor);
23322 //        }
23323     },
23324
23325      
23326     // private
23327     onRender : function(ct, position)
23328     {
23329        // Roo.log("Call onRender: " + this.xtype);
23330         var _t = this;
23331         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23332       
23333         this.wrap = this.inputEl().wrap({
23334             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23335         });
23336         
23337         this.editorcore.onRender(ct, position);
23338          
23339         if (this.resizable) {
23340             this.resizeEl = new Roo.Resizable(this.wrap, {
23341                 pinned : true,
23342                 wrap: true,
23343                 dynamic : true,
23344                 minHeight : this.height,
23345                 height: this.height,
23346                 handles : this.resizable,
23347                 width: this.width,
23348                 listeners : {
23349                     resize : function(r, w, h) {
23350                         _t.onResize(w,h); // -something
23351                     }
23352                 }
23353             });
23354             
23355         }
23356         this.createToolbar(this);
23357        
23358         
23359         if(!this.width && this.resizable){
23360             this.setSize(this.wrap.getSize());
23361         }
23362         if (this.resizeEl) {
23363             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23364             // should trigger onReize..
23365         }
23366         
23367     },
23368
23369     // private
23370     onResize : function(w, h)
23371     {
23372         Roo.log('resize: ' +w + ',' + h );
23373         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23374         var ew = false;
23375         var eh = false;
23376         
23377         if(this.inputEl() ){
23378             if(typeof w == 'number'){
23379                 var aw = w - this.wrap.getFrameWidth('lr');
23380                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23381                 ew = aw;
23382             }
23383             if(typeof h == 'number'){
23384                  var tbh = -11;  // fixme it needs to tool bar size!
23385                 for (var i =0; i < this.toolbars.length;i++) {
23386                     // fixme - ask toolbars for heights?
23387                     tbh += this.toolbars[i].el.getHeight();
23388                     //if (this.toolbars[i].footer) {
23389                     //    tbh += this.toolbars[i].footer.el.getHeight();
23390                     //}
23391                 }
23392               
23393                 
23394                 
23395                 
23396                 
23397                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23398                 ah -= 5; // knock a few pixes off for look..
23399                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23400                 var eh = ah;
23401             }
23402         }
23403         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23404         this.editorcore.onResize(ew,eh);
23405         
23406     },
23407
23408     /**
23409      * Toggles the editor between standard and source edit mode.
23410      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23411      */
23412     toggleSourceEdit : function(sourceEditMode)
23413     {
23414         this.editorcore.toggleSourceEdit(sourceEditMode);
23415         
23416         if(this.editorcore.sourceEditMode){
23417             Roo.log('editor - showing textarea');
23418             
23419 //            Roo.log('in');
23420 //            Roo.log(this.syncValue());
23421             this.syncValue();
23422             this.inputEl().removeClass(['hide', 'x-hidden']);
23423             this.inputEl().dom.removeAttribute('tabIndex');
23424             this.inputEl().focus();
23425         }else{
23426             Roo.log('editor - hiding textarea');
23427 //            Roo.log('out')
23428 //            Roo.log(this.pushValue()); 
23429             this.pushValue();
23430             
23431             this.inputEl().addClass(['hide', 'x-hidden']);
23432             this.inputEl().dom.setAttribute('tabIndex', -1);
23433             //this.deferFocus();
23434         }
23435          
23436         if(this.resizable){
23437             this.setSize(this.wrap.getSize());
23438         }
23439         
23440         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23441     },
23442  
23443     // private (for BoxComponent)
23444     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23445
23446     // private (for BoxComponent)
23447     getResizeEl : function(){
23448         return this.wrap;
23449     },
23450
23451     // private (for BoxComponent)
23452     getPositionEl : function(){
23453         return this.wrap;
23454     },
23455
23456     // private
23457     initEvents : function(){
23458         this.originalValue = this.getValue();
23459     },
23460
23461 //    /**
23462 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23463 //     * @method
23464 //     */
23465 //    markInvalid : Roo.emptyFn,
23466 //    /**
23467 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23468 //     * @method
23469 //     */
23470 //    clearInvalid : Roo.emptyFn,
23471
23472     setValue : function(v){
23473         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23474         this.editorcore.pushValue();
23475     },
23476
23477      
23478     // private
23479     deferFocus : function(){
23480         this.focus.defer(10, this);
23481     },
23482
23483     // doc'ed in Field
23484     focus : function(){
23485         this.editorcore.focus();
23486         
23487     },
23488       
23489
23490     // private
23491     onDestroy : function(){
23492         
23493         
23494         
23495         if(this.rendered){
23496             
23497             for (var i =0; i < this.toolbars.length;i++) {
23498                 // fixme - ask toolbars for heights?
23499                 this.toolbars[i].onDestroy();
23500             }
23501             
23502             this.wrap.dom.innerHTML = '';
23503             this.wrap.remove();
23504         }
23505     },
23506
23507     // private
23508     onFirstFocus : function(){
23509         //Roo.log("onFirstFocus");
23510         this.editorcore.onFirstFocus();
23511          for (var i =0; i < this.toolbars.length;i++) {
23512             this.toolbars[i].onFirstFocus();
23513         }
23514         
23515     },
23516     
23517     // private
23518     syncValue : function()
23519     {   
23520         this.editorcore.syncValue();
23521     },
23522     
23523     pushValue : function()
23524     {   
23525         this.editorcore.pushValue();
23526     }
23527      
23528     
23529     // hide stuff that is not compatible
23530     /**
23531      * @event blur
23532      * @hide
23533      */
23534     /**
23535      * @event change
23536      * @hide
23537      */
23538     /**
23539      * @event focus
23540      * @hide
23541      */
23542     /**
23543      * @event specialkey
23544      * @hide
23545      */
23546     /**
23547      * @cfg {String} fieldClass @hide
23548      */
23549     /**
23550      * @cfg {String} focusClass @hide
23551      */
23552     /**
23553      * @cfg {String} autoCreate @hide
23554      */
23555     /**
23556      * @cfg {String} inputType @hide
23557      */
23558     /**
23559      * @cfg {String} invalidClass @hide
23560      */
23561     /**
23562      * @cfg {String} invalidText @hide
23563      */
23564     /**
23565      * @cfg {String} msgFx @hide
23566      */
23567     /**
23568      * @cfg {String} validateOnBlur @hide
23569      */
23570 });
23571  
23572     
23573    
23574    
23575    
23576       
23577 Roo.namespace('Roo.bootstrap.htmleditor');
23578 /**
23579  * @class Roo.bootstrap.HtmlEditorToolbar1
23580  * Basic Toolbar
23581  * 
23582  * Usage:
23583  *
23584  new Roo.bootstrap.HtmlEditor({
23585     ....
23586     toolbars : [
23587         new Roo.bootstrap.HtmlEditorToolbar1({
23588             disable : { fonts: 1 , format: 1, ..., ... , ...],
23589             btns : [ .... ]
23590         })
23591     }
23592      
23593  * 
23594  * @cfg {Object} disable List of elements to disable..
23595  * @cfg {Array} btns List of additional buttons.
23596  * 
23597  * 
23598  * NEEDS Extra CSS? 
23599  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23600  */
23601  
23602 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23603 {
23604     
23605     Roo.apply(this, config);
23606     
23607     // default disabled, based on 'good practice'..
23608     this.disable = this.disable || {};
23609     Roo.applyIf(this.disable, {
23610         fontSize : true,
23611         colors : true,
23612         specialElements : true
23613     });
23614     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23615     
23616     this.editor = config.editor;
23617     this.editorcore = config.editor.editorcore;
23618     
23619     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23620     
23621     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23622     // dont call parent... till later.
23623 }
23624 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23625      
23626     bar : true,
23627     
23628     editor : false,
23629     editorcore : false,
23630     
23631     
23632     formats : [
23633         "p" ,  
23634         "h1","h2","h3","h4","h5","h6", 
23635         "pre", "code", 
23636         "abbr", "acronym", "address", "cite", "samp", "var",
23637         'div','span'
23638     ],
23639     
23640     onRender : function(ct, position)
23641     {
23642        // Roo.log("Call onRender: " + this.xtype);
23643         
23644        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23645        Roo.log(this.el);
23646        this.el.dom.style.marginBottom = '0';
23647        var _this = this;
23648        var editorcore = this.editorcore;
23649        var editor= this.editor;
23650        
23651        var children = [];
23652        var btn = function(id,cmd , toggle, handler, html){
23653        
23654             var  event = toggle ? 'toggle' : 'click';
23655        
23656             var a = {
23657                 size : 'sm',
23658                 xtype: 'Button',
23659                 xns: Roo.bootstrap,
23660                 glyphicon : id,
23661                 cmd : id || cmd,
23662                 enableToggle:toggle !== false,
23663                 html : html || '',
23664                 pressed : toggle ? false : null,
23665                 listeners : {}
23666             };
23667             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23668                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23669             };
23670             children.push(a);
23671             return a;
23672        }
23673        
23674     //    var cb_box = function...
23675         
23676         var style = {
23677                 xtype: 'Button',
23678                 size : 'sm',
23679                 xns: Roo.bootstrap,
23680                 glyphicon : 'font',
23681                 //html : 'submit'
23682                 menu : {
23683                     xtype: 'Menu',
23684                     xns: Roo.bootstrap,
23685                     items:  []
23686                 }
23687         };
23688         Roo.each(this.formats, function(f) {
23689             style.menu.items.push({
23690                 xtype :'MenuItem',
23691                 xns: Roo.bootstrap,
23692                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23693                 tagname : f,
23694                 listeners : {
23695                     click : function()
23696                     {
23697                         editorcore.insertTag(this.tagname);
23698                         editor.focus();
23699                     }
23700                 }
23701                 
23702             });
23703         });
23704         children.push(style);   
23705         
23706         btn('bold',false,true);
23707         btn('italic',false,true);
23708         btn('align-left', 'justifyleft',true);
23709         btn('align-center', 'justifycenter',true);
23710         btn('align-right' , 'justifyright',true);
23711         btn('link', false, false, function(btn) {
23712             //Roo.log("create link?");
23713             var url = prompt(this.createLinkText, this.defaultLinkValue);
23714             if(url && url != 'http:/'+'/'){
23715                 this.editorcore.relayCmd('createlink', url);
23716             }
23717         }),
23718         btn('list','insertunorderedlist',true);
23719         btn('pencil', false,true, function(btn){
23720                 Roo.log(this);
23721                 this.toggleSourceEdit(btn.pressed);
23722         });
23723         
23724         if (this.editor.btns.length > 0) {
23725             for (var i = 0; i<this.editor.btns.length; i++) {
23726                 children.push(this.editor.btns[i]);
23727             }
23728         }
23729         
23730         /*
23731         var cog = {
23732                 xtype: 'Button',
23733                 size : 'sm',
23734                 xns: Roo.bootstrap,
23735                 glyphicon : 'cog',
23736                 //html : 'submit'
23737                 menu : {
23738                     xtype: 'Menu',
23739                     xns: Roo.bootstrap,
23740                     items:  []
23741                 }
23742         };
23743         
23744         cog.menu.items.push({
23745             xtype :'MenuItem',
23746             xns: Roo.bootstrap,
23747             html : Clean styles,
23748             tagname : f,
23749             listeners : {
23750                 click : function()
23751                 {
23752                     editorcore.insertTag(this.tagname);
23753                     editor.focus();
23754                 }
23755             }
23756             
23757         });
23758        */
23759         
23760          
23761        this.xtype = 'NavSimplebar';
23762         
23763         for(var i=0;i< children.length;i++) {
23764             
23765             this.buttons.add(this.addxtypeChild(children[i]));
23766             
23767         }
23768         
23769         editor.on('editorevent', this.updateToolbar, this);
23770     },
23771     onBtnClick : function(id)
23772     {
23773        this.editorcore.relayCmd(id);
23774        this.editorcore.focus();
23775     },
23776     
23777     /**
23778      * Protected method that will not generally be called directly. It triggers
23779      * a toolbar update by reading the markup state of the current selection in the editor.
23780      */
23781     updateToolbar: function(){
23782
23783         if(!this.editorcore.activated){
23784             this.editor.onFirstFocus(); // is this neeed?
23785             return;
23786         }
23787
23788         var btns = this.buttons; 
23789         var doc = this.editorcore.doc;
23790         btns.get('bold').setActive(doc.queryCommandState('bold'));
23791         btns.get('italic').setActive(doc.queryCommandState('italic'));
23792         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23793         
23794         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23795         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23796         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23797         
23798         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23799         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23800          /*
23801         
23802         var ans = this.editorcore.getAllAncestors();
23803         if (this.formatCombo) {
23804             
23805             
23806             var store = this.formatCombo.store;
23807             this.formatCombo.setValue("");
23808             for (var i =0; i < ans.length;i++) {
23809                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23810                     // select it..
23811                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23812                     break;
23813                 }
23814             }
23815         }
23816         
23817         
23818         
23819         // hides menus... - so this cant be on a menu...
23820         Roo.bootstrap.MenuMgr.hideAll();
23821         */
23822         Roo.bootstrap.MenuMgr.hideAll();
23823         //this.editorsyncValue();
23824     },
23825     onFirstFocus: function() {
23826         this.buttons.each(function(item){
23827            item.enable();
23828         });
23829     },
23830     toggleSourceEdit : function(sourceEditMode){
23831         
23832           
23833         if(sourceEditMode){
23834             Roo.log("disabling buttons");
23835            this.buttons.each( function(item){
23836                 if(item.cmd != 'pencil'){
23837                     item.disable();
23838                 }
23839             });
23840           
23841         }else{
23842             Roo.log("enabling buttons");
23843             if(this.editorcore.initialized){
23844                 this.buttons.each( function(item){
23845                     item.enable();
23846                 });
23847             }
23848             
23849         }
23850         Roo.log("calling toggole on editor");
23851         // tell the editor that it's been pressed..
23852         this.editor.toggleSourceEdit(sourceEditMode);
23853        
23854     }
23855 });
23856
23857
23858
23859
23860
23861 /**
23862  * @class Roo.bootstrap.Table.AbstractSelectionModel
23863  * @extends Roo.util.Observable
23864  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23865  * implemented by descendant classes.  This class should not be directly instantiated.
23866  * @constructor
23867  */
23868 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23869     this.locked = false;
23870     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23871 };
23872
23873
23874 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23875     /** @ignore Called by the grid automatically. Do not call directly. */
23876     init : function(grid){
23877         this.grid = grid;
23878         this.initEvents();
23879     },
23880
23881     /**
23882      * Locks the selections.
23883      */
23884     lock : function(){
23885         this.locked = true;
23886     },
23887
23888     /**
23889      * Unlocks the selections.
23890      */
23891     unlock : function(){
23892         this.locked = false;
23893     },
23894
23895     /**
23896      * Returns true if the selections are locked.
23897      * @return {Boolean}
23898      */
23899     isLocked : function(){
23900         return this.locked;
23901     }
23902 });
23903 /**
23904  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23905  * @class Roo.bootstrap.Table.RowSelectionModel
23906  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23907  * It supports multiple selections and keyboard selection/navigation. 
23908  * @constructor
23909  * @param {Object} config
23910  */
23911
23912 Roo.bootstrap.Table.RowSelectionModel = function(config){
23913     Roo.apply(this, config);
23914     this.selections = new Roo.util.MixedCollection(false, function(o){
23915         return o.id;
23916     });
23917
23918     this.last = false;
23919     this.lastActive = false;
23920
23921     this.addEvents({
23922         /**
23923              * @event selectionchange
23924              * Fires when the selection changes
23925              * @param {SelectionModel} this
23926              */
23927             "selectionchange" : true,
23928         /**
23929              * @event afterselectionchange
23930              * Fires after the selection changes (eg. by key press or clicking)
23931              * @param {SelectionModel} this
23932              */
23933             "afterselectionchange" : true,
23934         /**
23935              * @event beforerowselect
23936              * Fires when a row is selected being selected, return false to cancel.
23937              * @param {SelectionModel} this
23938              * @param {Number} rowIndex The selected index
23939              * @param {Boolean} keepExisting False if other selections will be cleared
23940              */
23941             "beforerowselect" : true,
23942         /**
23943              * @event rowselect
23944              * Fires when a row is selected.
23945              * @param {SelectionModel} this
23946              * @param {Number} rowIndex The selected index
23947              * @param {Roo.data.Record} r The record
23948              */
23949             "rowselect" : true,
23950         /**
23951              * @event rowdeselect
23952              * Fires when a row is deselected.
23953              * @param {SelectionModel} this
23954              * @param {Number} rowIndex The selected index
23955              */
23956         "rowdeselect" : true
23957     });
23958     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23959     this.locked = false;
23960  };
23961
23962 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23963     /**
23964      * @cfg {Boolean} singleSelect
23965      * True to allow selection of only one row at a time (defaults to false)
23966      */
23967     singleSelect : false,
23968
23969     // private
23970     initEvents : function()
23971     {
23972
23973         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23974         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23975         //}else{ // allow click to work like normal
23976          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23977         //}
23978         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23979         this.grid.on("rowclick", this.handleMouseDown, this);
23980         
23981         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23982             "up" : function(e){
23983                 if(!e.shiftKey){
23984                     this.selectPrevious(e.shiftKey);
23985                 }else if(this.last !== false && this.lastActive !== false){
23986                     var last = this.last;
23987                     this.selectRange(this.last,  this.lastActive-1);
23988                     this.grid.getView().focusRow(this.lastActive);
23989                     if(last !== false){
23990                         this.last = last;
23991                     }
23992                 }else{
23993                     this.selectFirstRow();
23994                 }
23995                 this.fireEvent("afterselectionchange", this);
23996             },
23997             "down" : function(e){
23998                 if(!e.shiftKey){
23999                     this.selectNext(e.shiftKey);
24000                 }else if(this.last !== false && this.lastActive !== false){
24001                     var last = this.last;
24002                     this.selectRange(this.last,  this.lastActive+1);
24003                     this.grid.getView().focusRow(this.lastActive);
24004                     if(last !== false){
24005                         this.last = last;
24006                     }
24007                 }else{
24008                     this.selectFirstRow();
24009                 }
24010                 this.fireEvent("afterselectionchange", this);
24011             },
24012             scope: this
24013         });
24014         this.grid.store.on('load', function(){
24015             this.selections.clear();
24016         },this);
24017         /*
24018         var view = this.grid.view;
24019         view.on("refresh", this.onRefresh, this);
24020         view.on("rowupdated", this.onRowUpdated, this);
24021         view.on("rowremoved", this.onRemove, this);
24022         */
24023     },
24024
24025     // private
24026     onRefresh : function()
24027     {
24028         var ds = this.grid.store, i, v = this.grid.view;
24029         var s = this.selections;
24030         s.each(function(r){
24031             if((i = ds.indexOfId(r.id)) != -1){
24032                 v.onRowSelect(i);
24033             }else{
24034                 s.remove(r);
24035             }
24036         });
24037     },
24038
24039     // private
24040     onRemove : function(v, index, r){
24041         this.selections.remove(r);
24042     },
24043
24044     // private
24045     onRowUpdated : function(v, index, r){
24046         if(this.isSelected(r)){
24047             v.onRowSelect(index);
24048         }
24049     },
24050
24051     /**
24052      * Select records.
24053      * @param {Array} records The records to select
24054      * @param {Boolean} keepExisting (optional) True to keep existing selections
24055      */
24056     selectRecords : function(records, keepExisting)
24057     {
24058         if(!keepExisting){
24059             this.clearSelections();
24060         }
24061             var ds = this.grid.store;
24062         for(var i = 0, len = records.length; i < len; i++){
24063             this.selectRow(ds.indexOf(records[i]), true);
24064         }
24065     },
24066
24067     /**
24068      * Gets the number of selected rows.
24069      * @return {Number}
24070      */
24071     getCount : function(){
24072         return this.selections.length;
24073     },
24074
24075     /**
24076      * Selects the first row in the grid.
24077      */
24078     selectFirstRow : function(){
24079         this.selectRow(0);
24080     },
24081
24082     /**
24083      * Select the last row.
24084      * @param {Boolean} keepExisting (optional) True to keep existing selections
24085      */
24086     selectLastRow : function(keepExisting){
24087         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24088         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24089     },
24090
24091     /**
24092      * Selects the row immediately following the last selected row.
24093      * @param {Boolean} keepExisting (optional) True to keep existing selections
24094      */
24095     selectNext : function(keepExisting)
24096     {
24097             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24098             this.selectRow(this.last+1, keepExisting);
24099             this.grid.getView().focusRow(this.last);
24100         }
24101     },
24102
24103     /**
24104      * Selects the row that precedes the last selected row.
24105      * @param {Boolean} keepExisting (optional) True to keep existing selections
24106      */
24107     selectPrevious : function(keepExisting){
24108         if(this.last){
24109             this.selectRow(this.last-1, keepExisting);
24110             this.grid.getView().focusRow(this.last);
24111         }
24112     },
24113
24114     /**
24115      * Returns the selected records
24116      * @return {Array} Array of selected records
24117      */
24118     getSelections : function(){
24119         return [].concat(this.selections.items);
24120     },
24121
24122     /**
24123      * Returns the first selected record.
24124      * @return {Record}
24125      */
24126     getSelected : function(){
24127         return this.selections.itemAt(0);
24128     },
24129
24130
24131     /**
24132      * Clears all selections.
24133      */
24134     clearSelections : function(fast)
24135     {
24136         if(this.locked) {
24137             return;
24138         }
24139         if(fast !== true){
24140                 var ds = this.grid.store;
24141             var s = this.selections;
24142             s.each(function(r){
24143                 this.deselectRow(ds.indexOfId(r.id));
24144             }, this);
24145             s.clear();
24146         }else{
24147             this.selections.clear();
24148         }
24149         this.last = false;
24150     },
24151
24152
24153     /**
24154      * Selects all rows.
24155      */
24156     selectAll : function(){
24157         if(this.locked) {
24158             return;
24159         }
24160         this.selections.clear();
24161         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24162             this.selectRow(i, true);
24163         }
24164     },
24165
24166     /**
24167      * Returns True if there is a selection.
24168      * @return {Boolean}
24169      */
24170     hasSelection : function(){
24171         return this.selections.length > 0;
24172     },
24173
24174     /**
24175      * Returns True if the specified row is selected.
24176      * @param {Number/Record} record The record or index of the record to check
24177      * @return {Boolean}
24178      */
24179     isSelected : function(index){
24180             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24181         return (r && this.selections.key(r.id) ? true : false);
24182     },
24183
24184     /**
24185      * Returns True if the specified record id is selected.
24186      * @param {String} id The id of record to check
24187      * @return {Boolean}
24188      */
24189     isIdSelected : function(id){
24190         return (this.selections.key(id) ? true : false);
24191     },
24192
24193
24194     // private
24195     handleMouseDBClick : function(e, t){
24196         
24197     },
24198     // private
24199     handleMouseDown : function(e, t)
24200     {
24201             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24202         if(this.isLocked() || rowIndex < 0 ){
24203             return;
24204         };
24205         if(e.shiftKey && this.last !== false){
24206             var last = this.last;
24207             this.selectRange(last, rowIndex, e.ctrlKey);
24208             this.last = last; // reset the last
24209             t.focus();
24210     
24211         }else{
24212             var isSelected = this.isSelected(rowIndex);
24213             //Roo.log("select row:" + rowIndex);
24214             if(isSelected){
24215                 this.deselectRow(rowIndex);
24216             } else {
24217                         this.selectRow(rowIndex, true);
24218             }
24219     
24220             /*
24221                 if(e.button !== 0 && isSelected){
24222                 alert('rowIndex 2: ' + rowIndex);
24223                     view.focusRow(rowIndex);
24224                 }else if(e.ctrlKey && isSelected){
24225                     this.deselectRow(rowIndex);
24226                 }else if(!isSelected){
24227                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24228                     view.focusRow(rowIndex);
24229                 }
24230             */
24231         }
24232         this.fireEvent("afterselectionchange", this);
24233     },
24234     // private
24235     handleDragableRowClick :  function(grid, rowIndex, e) 
24236     {
24237         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24238             this.selectRow(rowIndex, false);
24239             grid.view.focusRow(rowIndex);
24240              this.fireEvent("afterselectionchange", this);
24241         }
24242     },
24243     
24244     /**
24245      * Selects multiple rows.
24246      * @param {Array} rows Array of the indexes of the row to select
24247      * @param {Boolean} keepExisting (optional) True to keep existing selections
24248      */
24249     selectRows : function(rows, keepExisting){
24250         if(!keepExisting){
24251             this.clearSelections();
24252         }
24253         for(var i = 0, len = rows.length; i < len; i++){
24254             this.selectRow(rows[i], true);
24255         }
24256     },
24257
24258     /**
24259      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24260      * @param {Number} startRow The index of the first row in the range
24261      * @param {Number} endRow The index of the last row in the range
24262      * @param {Boolean} keepExisting (optional) True to retain existing selections
24263      */
24264     selectRange : function(startRow, endRow, keepExisting){
24265         if(this.locked) {
24266             return;
24267         }
24268         if(!keepExisting){
24269             this.clearSelections();
24270         }
24271         if(startRow <= endRow){
24272             for(var i = startRow; i <= endRow; i++){
24273                 this.selectRow(i, true);
24274             }
24275         }else{
24276             for(var i = startRow; i >= endRow; i--){
24277                 this.selectRow(i, true);
24278             }
24279         }
24280     },
24281
24282     /**
24283      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24284      * @param {Number} startRow The index of the first row in the range
24285      * @param {Number} endRow The index of the last row in the range
24286      */
24287     deselectRange : function(startRow, endRow, preventViewNotify){
24288         if(this.locked) {
24289             return;
24290         }
24291         for(var i = startRow; i <= endRow; i++){
24292             this.deselectRow(i, preventViewNotify);
24293         }
24294     },
24295
24296     /**
24297      * Selects a row.
24298      * @param {Number} row The index of the row to select
24299      * @param {Boolean} keepExisting (optional) True to keep existing selections
24300      */
24301     selectRow : function(index, keepExisting, preventViewNotify)
24302     {
24303             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24304             return;
24305         }
24306         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24307             if(!keepExisting || this.singleSelect){
24308                 this.clearSelections();
24309             }
24310             
24311             var r = this.grid.store.getAt(index);
24312             //console.log('selectRow - record id :' + r.id);
24313             
24314             this.selections.add(r);
24315             this.last = this.lastActive = index;
24316             if(!preventViewNotify){
24317                 var proxy = new Roo.Element(
24318                                 this.grid.getRowDom(index)
24319                 );
24320                 proxy.addClass('bg-info info');
24321             }
24322             this.fireEvent("rowselect", this, index, r);
24323             this.fireEvent("selectionchange", this);
24324         }
24325     },
24326
24327     /**
24328      * Deselects a row.
24329      * @param {Number} row The index of the row to deselect
24330      */
24331     deselectRow : function(index, preventViewNotify)
24332     {
24333         if(this.locked) {
24334             return;
24335         }
24336         if(this.last == index){
24337             this.last = false;
24338         }
24339         if(this.lastActive == index){
24340             this.lastActive = false;
24341         }
24342         
24343         var r = this.grid.store.getAt(index);
24344         if (!r) {
24345             return;
24346         }
24347         
24348         this.selections.remove(r);
24349         //.console.log('deselectRow - record id :' + r.id);
24350         if(!preventViewNotify){
24351         
24352             var proxy = new Roo.Element(
24353                 this.grid.getRowDom(index)
24354             );
24355             proxy.removeClass('bg-info info');
24356         }
24357         this.fireEvent("rowdeselect", this, index);
24358         this.fireEvent("selectionchange", this);
24359     },
24360
24361     // private
24362     restoreLast : function(){
24363         if(this._last){
24364             this.last = this._last;
24365         }
24366     },
24367
24368     // private
24369     acceptsNav : function(row, col, cm){
24370         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24371     },
24372
24373     // private
24374     onEditorKey : function(field, e){
24375         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24376         if(k == e.TAB){
24377             e.stopEvent();
24378             ed.completeEdit();
24379             if(e.shiftKey){
24380                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24381             }else{
24382                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24383             }
24384         }else if(k == e.ENTER && !e.ctrlKey){
24385             e.stopEvent();
24386             ed.completeEdit();
24387             if(e.shiftKey){
24388                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24389             }else{
24390                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24391             }
24392         }else if(k == e.ESC){
24393             ed.cancelEdit();
24394         }
24395         if(newCell){
24396             g.startEditing(newCell[0], newCell[1]);
24397         }
24398     }
24399 });
24400 /*
24401  * Based on:
24402  * Ext JS Library 1.1.1
24403  * Copyright(c) 2006-2007, Ext JS, LLC.
24404  *
24405  * Originally Released Under LGPL - original licence link has changed is not relivant.
24406  *
24407  * Fork - LGPL
24408  * <script type="text/javascript">
24409  */
24410  
24411 /**
24412  * @class Roo.bootstrap.PagingToolbar
24413  * @extends Roo.bootstrap.NavSimplebar
24414  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24415  * @constructor
24416  * Create a new PagingToolbar
24417  * @param {Object} config The config object
24418  * @param {Roo.data.Store} store
24419  */
24420 Roo.bootstrap.PagingToolbar = function(config)
24421 {
24422     // old args format still supported... - xtype is prefered..
24423         // created from xtype...
24424     
24425     this.ds = config.dataSource;
24426     
24427     if (config.store && !this.ds) {
24428         this.store= Roo.factory(config.store, Roo.data);
24429         this.ds = this.store;
24430         this.ds.xmodule = this.xmodule || false;
24431     }
24432     
24433     this.toolbarItems = [];
24434     if (config.items) {
24435         this.toolbarItems = config.items;
24436     }
24437     
24438     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24439     
24440     this.cursor = 0;
24441     
24442     if (this.ds) { 
24443         this.bind(this.ds);
24444     }
24445     
24446     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24447     
24448 };
24449
24450 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24451     /**
24452      * @cfg {Roo.data.Store} dataSource
24453      * The underlying data store providing the paged data
24454      */
24455     /**
24456      * @cfg {String/HTMLElement/Element} container
24457      * container The id or element that will contain the toolbar
24458      */
24459     /**
24460      * @cfg {Boolean} displayInfo
24461      * True to display the displayMsg (defaults to false)
24462      */
24463     /**
24464      * @cfg {Number} pageSize
24465      * The number of records to display per page (defaults to 20)
24466      */
24467     pageSize: 20,
24468     /**
24469      * @cfg {String} displayMsg
24470      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24471      */
24472     displayMsg : 'Displaying {0} - {1} of {2}',
24473     /**
24474      * @cfg {String} emptyMsg
24475      * The message to display when no records are found (defaults to "No data to display")
24476      */
24477     emptyMsg : 'No data to display',
24478     /**
24479      * Customizable piece of the default paging text (defaults to "Page")
24480      * @type String
24481      */
24482     beforePageText : "Page",
24483     /**
24484      * Customizable piece of the default paging text (defaults to "of %0")
24485      * @type String
24486      */
24487     afterPageText : "of {0}",
24488     /**
24489      * Customizable piece of the default paging text (defaults to "First Page")
24490      * @type String
24491      */
24492     firstText : "First Page",
24493     /**
24494      * Customizable piece of the default paging text (defaults to "Previous Page")
24495      * @type String
24496      */
24497     prevText : "Previous Page",
24498     /**
24499      * Customizable piece of the default paging text (defaults to "Next Page")
24500      * @type String
24501      */
24502     nextText : "Next Page",
24503     /**
24504      * Customizable piece of the default paging text (defaults to "Last Page")
24505      * @type String
24506      */
24507     lastText : "Last Page",
24508     /**
24509      * Customizable piece of the default paging text (defaults to "Refresh")
24510      * @type String
24511      */
24512     refreshText : "Refresh",
24513
24514     buttons : false,
24515     // private
24516     onRender : function(ct, position) 
24517     {
24518         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24519         this.navgroup.parentId = this.id;
24520         this.navgroup.onRender(this.el, null);
24521         // add the buttons to the navgroup
24522         
24523         if(this.displayInfo){
24524             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24525             this.displayEl = this.el.select('.x-paging-info', true).first();
24526 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24527 //            this.displayEl = navel.el.select('span',true).first();
24528         }
24529         
24530         var _this = this;
24531         
24532         if(this.buttons){
24533             Roo.each(_this.buttons, function(e){ // this might need to use render????
24534                Roo.factory(e).render(_this.el);
24535             });
24536         }
24537             
24538         Roo.each(_this.toolbarItems, function(e) {
24539             _this.navgroup.addItem(e);
24540         });
24541         
24542         
24543         this.first = this.navgroup.addItem({
24544             tooltip: this.firstText,
24545             cls: "prev",
24546             icon : 'fa fa-backward',
24547             disabled: true,
24548             preventDefault: true,
24549             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24550         });
24551         
24552         this.prev =  this.navgroup.addItem({
24553             tooltip: this.prevText,
24554             cls: "prev",
24555             icon : 'fa fa-step-backward',
24556             disabled: true,
24557             preventDefault: true,
24558             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24559         });
24560     //this.addSeparator();
24561         
24562         
24563         var field = this.navgroup.addItem( {
24564             tagtype : 'span',
24565             cls : 'x-paging-position',
24566             
24567             html : this.beforePageText  +
24568                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24569                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24570          } ); //?? escaped?
24571         
24572         this.field = field.el.select('input', true).first();
24573         this.field.on("keydown", this.onPagingKeydown, this);
24574         this.field.on("focus", function(){this.dom.select();});
24575     
24576     
24577         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24578         //this.field.setHeight(18);
24579         //this.addSeparator();
24580         this.next = this.navgroup.addItem({
24581             tooltip: this.nextText,
24582             cls: "next",
24583             html : ' <i class="fa fa-step-forward">',
24584             disabled: true,
24585             preventDefault: true,
24586             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24587         });
24588         this.last = this.navgroup.addItem({
24589             tooltip: this.lastText,
24590             icon : 'fa fa-forward',
24591             cls: "next",
24592             disabled: true,
24593             preventDefault: true,
24594             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24595         });
24596     //this.addSeparator();
24597         this.loading = this.navgroup.addItem({
24598             tooltip: this.refreshText,
24599             icon: 'fa fa-refresh',
24600             preventDefault: true,
24601             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24602         });
24603         
24604     },
24605
24606     // private
24607     updateInfo : function(){
24608         if(this.displayEl){
24609             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24610             var msg = count == 0 ?
24611                 this.emptyMsg :
24612                 String.format(
24613                     this.displayMsg,
24614                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24615                 );
24616             this.displayEl.update(msg);
24617         }
24618     },
24619
24620     // private
24621     onLoad : function(ds, r, o)
24622     {
24623         this.cursor = o.params.start ? o.params.start : 0;
24624         
24625         var d = this.getPageData(),
24626             ap = d.activePage,
24627             ps = d.pages;
24628         
24629         
24630         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24631         this.field.dom.value = ap;
24632         this.first.setDisabled(ap == 1);
24633         this.prev.setDisabled(ap == 1);
24634         this.next.setDisabled(ap == ps);
24635         this.last.setDisabled(ap == ps);
24636         this.loading.enable();
24637         this.updateInfo();
24638     },
24639
24640     // private
24641     getPageData : function(){
24642         var total = this.ds.getTotalCount();
24643         return {
24644             total : total,
24645             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24646             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24647         };
24648     },
24649
24650     // private
24651     onLoadError : function(){
24652         this.loading.enable();
24653     },
24654
24655     // private
24656     onPagingKeydown : function(e){
24657         var k = e.getKey();
24658         var d = this.getPageData();
24659         if(k == e.RETURN){
24660             var v = this.field.dom.value, pageNum;
24661             if(!v || isNaN(pageNum = parseInt(v, 10))){
24662                 this.field.dom.value = d.activePage;
24663                 return;
24664             }
24665             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24666             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24667             e.stopEvent();
24668         }
24669         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))
24670         {
24671           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24672           this.field.dom.value = pageNum;
24673           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24674           e.stopEvent();
24675         }
24676         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24677         {
24678           var v = this.field.dom.value, pageNum; 
24679           var increment = (e.shiftKey) ? 10 : 1;
24680           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24681                 increment *= -1;
24682           }
24683           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24684             this.field.dom.value = d.activePage;
24685             return;
24686           }
24687           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24688           {
24689             this.field.dom.value = parseInt(v, 10) + increment;
24690             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24691             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24692           }
24693           e.stopEvent();
24694         }
24695     },
24696
24697     // private
24698     beforeLoad : function(){
24699         if(this.loading){
24700             this.loading.disable();
24701         }
24702     },
24703
24704     // private
24705     onClick : function(which){
24706         
24707         var ds = this.ds;
24708         if (!ds) {
24709             return;
24710         }
24711         
24712         switch(which){
24713             case "first":
24714                 ds.load({params:{start: 0, limit: this.pageSize}});
24715             break;
24716             case "prev":
24717                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24718             break;
24719             case "next":
24720                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24721             break;
24722             case "last":
24723                 var total = ds.getTotalCount();
24724                 var extra = total % this.pageSize;
24725                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24726                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24727             break;
24728             case "refresh":
24729                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24730             break;
24731         }
24732     },
24733
24734     /**
24735      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24736      * @param {Roo.data.Store} store The data store to unbind
24737      */
24738     unbind : function(ds){
24739         ds.un("beforeload", this.beforeLoad, this);
24740         ds.un("load", this.onLoad, this);
24741         ds.un("loadexception", this.onLoadError, this);
24742         ds.un("remove", this.updateInfo, this);
24743         ds.un("add", this.updateInfo, this);
24744         this.ds = undefined;
24745     },
24746
24747     /**
24748      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24749      * @param {Roo.data.Store} store The data store to bind
24750      */
24751     bind : function(ds){
24752         ds.on("beforeload", this.beforeLoad, this);
24753         ds.on("load", this.onLoad, this);
24754         ds.on("loadexception", this.onLoadError, this);
24755         ds.on("remove", this.updateInfo, this);
24756         ds.on("add", this.updateInfo, this);
24757         this.ds = ds;
24758     }
24759 });/*
24760  * - LGPL
24761  *
24762  * element
24763  * 
24764  */
24765
24766 /**
24767  * @class Roo.bootstrap.MessageBar
24768  * @extends Roo.bootstrap.Component
24769  * Bootstrap MessageBar class
24770  * @cfg {String} html contents of the MessageBar
24771  * @cfg {String} weight (info | success | warning | danger) default info
24772  * @cfg {String} beforeClass insert the bar before the given class
24773  * @cfg {Boolean} closable (true | false) default false
24774  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24775  * 
24776  * @constructor
24777  * Create a new Element
24778  * @param {Object} config The config object
24779  */
24780
24781 Roo.bootstrap.MessageBar = function(config){
24782     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24783 };
24784
24785 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24786     
24787     html: '',
24788     weight: 'info',
24789     closable: false,
24790     fixed: false,
24791     beforeClass: 'bootstrap-sticky-wrap',
24792     
24793     getAutoCreate : function(){
24794         
24795         var cfg = {
24796             tag: 'div',
24797             cls: 'alert alert-dismissable alert-' + this.weight,
24798             cn: [
24799                 {
24800                     tag: 'span',
24801                     cls: 'message',
24802                     html: this.html || ''
24803                 }
24804             ]
24805         };
24806         
24807         if(this.fixed){
24808             cfg.cls += ' alert-messages-fixed';
24809         }
24810         
24811         if(this.closable){
24812             cfg.cn.push({
24813                 tag: 'button',
24814                 cls: 'close',
24815                 html: 'x'
24816             });
24817         }
24818         
24819         return cfg;
24820     },
24821     
24822     onRender : function(ct, position)
24823     {
24824         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24825         
24826         if(!this.el){
24827             var cfg = Roo.apply({},  this.getAutoCreate());
24828             cfg.id = Roo.id();
24829             
24830             if (this.cls) {
24831                 cfg.cls += ' ' + this.cls;
24832             }
24833             if (this.style) {
24834                 cfg.style = this.style;
24835             }
24836             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24837             
24838             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24839         }
24840         
24841         this.el.select('>button.close').on('click', this.hide, this);
24842         
24843     },
24844     
24845     show : function()
24846     {
24847         if (!this.rendered) {
24848             this.render();
24849         }
24850         
24851         this.el.show();
24852         
24853         this.fireEvent('show', this);
24854         
24855     },
24856     
24857     hide : function()
24858     {
24859         if (!this.rendered) {
24860             this.render();
24861         }
24862         
24863         this.el.hide();
24864         
24865         this.fireEvent('hide', this);
24866     },
24867     
24868     update : function()
24869     {
24870 //        var e = this.el.dom.firstChild;
24871 //        
24872 //        if(this.closable){
24873 //            e = e.nextSibling;
24874 //        }
24875 //        
24876 //        e.data = this.html || '';
24877
24878         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24879     }
24880    
24881 });
24882
24883  
24884
24885      /*
24886  * - LGPL
24887  *
24888  * Graph
24889  * 
24890  */
24891
24892
24893 /**
24894  * @class Roo.bootstrap.Graph
24895  * @extends Roo.bootstrap.Component
24896  * Bootstrap Graph class
24897 > Prameters
24898  -sm {number} sm 4
24899  -md {number} md 5
24900  @cfg {String} graphtype  bar | vbar | pie
24901  @cfg {number} g_x coodinator | centre x (pie)
24902  @cfg {number} g_y coodinator | centre y (pie)
24903  @cfg {number} g_r radius (pie)
24904  @cfg {number} g_height height of the chart (respected by all elements in the set)
24905  @cfg {number} g_width width of the chart (respected by all elements in the set)
24906  @cfg {Object} title The title of the chart
24907     
24908  -{Array}  values
24909  -opts (object) options for the chart 
24910      o {
24911      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24912      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24913      o vgutter (number)
24914      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.
24915      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24916      o to
24917      o stretch (boolean)
24918      o }
24919  -opts (object) options for the pie
24920      o{
24921      o cut
24922      o startAngle (number)
24923      o endAngle (number)
24924      } 
24925  *
24926  * @constructor
24927  * Create a new Input
24928  * @param {Object} config The config object
24929  */
24930
24931 Roo.bootstrap.Graph = function(config){
24932     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24933     
24934     this.addEvents({
24935         // img events
24936         /**
24937          * @event click
24938          * The img click event for the img.
24939          * @param {Roo.EventObject} e
24940          */
24941         "click" : true
24942     });
24943 };
24944
24945 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24946     
24947     sm: 4,
24948     md: 5,
24949     graphtype: 'bar',
24950     g_height: 250,
24951     g_width: 400,
24952     g_x: 50,
24953     g_y: 50,
24954     g_r: 30,
24955     opts:{
24956         //g_colors: this.colors,
24957         g_type: 'soft',
24958         g_gutter: '20%'
24959
24960     },
24961     title : false,
24962
24963     getAutoCreate : function(){
24964         
24965         var cfg = {
24966             tag: 'div',
24967             html : null
24968         };
24969         
24970         
24971         return  cfg;
24972     },
24973
24974     onRender : function(ct,position){
24975         
24976         
24977         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24978         
24979         if (typeof(Raphael) == 'undefined') {
24980             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24981             return;
24982         }
24983         
24984         this.raphael = Raphael(this.el.dom);
24985         
24986                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24987                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24988                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24989                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24990                 /*
24991                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24992                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24993                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24994                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24995                 
24996                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24997                 r.barchart(330, 10, 300, 220, data1);
24998                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24999                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25000                 */
25001                 
25002                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25003                 // r.barchart(30, 30, 560, 250,  xdata, {
25004                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25005                 //     axis : "0 0 1 1",
25006                 //     axisxlabels :  xdata
25007                 //     //yvalues : cols,
25008                    
25009                 // });
25010 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25011 //        
25012 //        this.load(null,xdata,{
25013 //                axis : "0 0 1 1",
25014 //                axisxlabels :  xdata
25015 //                });
25016
25017     },
25018
25019     load : function(graphtype,xdata,opts)
25020     {
25021         this.raphael.clear();
25022         if(!graphtype) {
25023             graphtype = this.graphtype;
25024         }
25025         if(!opts){
25026             opts = this.opts;
25027         }
25028         var r = this.raphael,
25029             fin = function () {
25030                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25031             },
25032             fout = function () {
25033                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25034             },
25035             pfin = function() {
25036                 this.sector.stop();
25037                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25038
25039                 if (this.label) {
25040                     this.label[0].stop();
25041                     this.label[0].attr({ r: 7.5 });
25042                     this.label[1].attr({ "font-weight": 800 });
25043                 }
25044             },
25045             pfout = function() {
25046                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25047
25048                 if (this.label) {
25049                     this.label[0].animate({ r: 5 }, 500, "bounce");
25050                     this.label[1].attr({ "font-weight": 400 });
25051                 }
25052             };
25053
25054         switch(graphtype){
25055             case 'bar':
25056                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25057                 break;
25058             case 'hbar':
25059                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25060                 break;
25061             case 'pie':
25062 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25063 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25064 //            
25065                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25066                 
25067                 break;
25068
25069         }
25070         
25071         if(this.title){
25072             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25073         }
25074         
25075     },
25076     
25077     setTitle: function(o)
25078     {
25079         this.title = o;
25080     },
25081     
25082     initEvents: function() {
25083         
25084         if(!this.href){
25085             this.el.on('click', this.onClick, this);
25086         }
25087     },
25088     
25089     onClick : function(e)
25090     {
25091         Roo.log('img onclick');
25092         this.fireEvent('click', this, e);
25093     }
25094    
25095 });
25096
25097  
25098 /*
25099  * - LGPL
25100  *
25101  * numberBox
25102  * 
25103  */
25104 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25105
25106 /**
25107  * @class Roo.bootstrap.dash.NumberBox
25108  * @extends Roo.bootstrap.Component
25109  * Bootstrap NumberBox class
25110  * @cfg {String} headline Box headline
25111  * @cfg {String} content Box content
25112  * @cfg {String} icon Box icon
25113  * @cfg {String} footer Footer text
25114  * @cfg {String} fhref Footer href
25115  * 
25116  * @constructor
25117  * Create a new NumberBox
25118  * @param {Object} config The config object
25119  */
25120
25121
25122 Roo.bootstrap.dash.NumberBox = function(config){
25123     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25124     
25125 };
25126
25127 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25128     
25129     headline : '',
25130     content : '',
25131     icon : '',
25132     footer : '',
25133     fhref : '',
25134     ficon : '',
25135     
25136     getAutoCreate : function(){
25137         
25138         var cfg = {
25139             tag : 'div',
25140             cls : 'small-box ',
25141             cn : [
25142                 {
25143                     tag : 'div',
25144                     cls : 'inner',
25145                     cn :[
25146                         {
25147                             tag : 'h3',
25148                             cls : 'roo-headline',
25149                             html : this.headline
25150                         },
25151                         {
25152                             tag : 'p',
25153                             cls : 'roo-content',
25154                             html : this.content
25155                         }
25156                     ]
25157                 }
25158             ]
25159         };
25160         
25161         if(this.icon){
25162             cfg.cn.push({
25163                 tag : 'div',
25164                 cls : 'icon',
25165                 cn :[
25166                     {
25167                         tag : 'i',
25168                         cls : 'ion ' + this.icon
25169                     }
25170                 ]
25171             });
25172         }
25173         
25174         if(this.footer){
25175             var footer = {
25176                 tag : 'a',
25177                 cls : 'small-box-footer',
25178                 href : this.fhref || '#',
25179                 html : this.footer
25180             };
25181             
25182             cfg.cn.push(footer);
25183             
25184         }
25185         
25186         return  cfg;
25187     },
25188
25189     onRender : function(ct,position){
25190         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25191
25192
25193        
25194                 
25195     },
25196
25197     setHeadline: function (value)
25198     {
25199         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25200     },
25201     
25202     setFooter: function (value, href)
25203     {
25204         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25205         
25206         if(href){
25207             this.el.select('a.small-box-footer',true).first().attr('href', href);
25208         }
25209         
25210     },
25211
25212     setContent: function (value)
25213     {
25214         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25215     },
25216
25217     initEvents: function() 
25218     {   
25219         
25220     }
25221     
25222 });
25223
25224  
25225 /*
25226  * - LGPL
25227  *
25228  * TabBox
25229  * 
25230  */
25231 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25232
25233 /**
25234  * @class Roo.bootstrap.dash.TabBox
25235  * @extends Roo.bootstrap.Component
25236  * Bootstrap TabBox class
25237  * @cfg {String} title Title of the TabBox
25238  * @cfg {String} icon Icon of the TabBox
25239  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25240  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25241  * 
25242  * @constructor
25243  * Create a new TabBox
25244  * @param {Object} config The config object
25245  */
25246
25247
25248 Roo.bootstrap.dash.TabBox = function(config){
25249     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25250     this.addEvents({
25251         // raw events
25252         /**
25253          * @event addpane
25254          * When a pane is added
25255          * @param {Roo.bootstrap.dash.TabPane} pane
25256          */
25257         "addpane" : true,
25258         /**
25259          * @event activatepane
25260          * When a pane is activated
25261          * @param {Roo.bootstrap.dash.TabPane} pane
25262          */
25263         "activatepane" : true
25264         
25265          
25266     });
25267     
25268     this.panes = [];
25269 };
25270
25271 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25272
25273     title : '',
25274     icon : false,
25275     showtabs : true,
25276     tabScrollable : false,
25277     
25278     getChildContainer : function()
25279     {
25280         return this.el.select('.tab-content', true).first();
25281     },
25282     
25283     getAutoCreate : function(){
25284         
25285         var header = {
25286             tag: 'li',
25287             cls: 'pull-left header',
25288             html: this.title,
25289             cn : []
25290         };
25291         
25292         if(this.icon){
25293             header.cn.push({
25294                 tag: 'i',
25295                 cls: 'fa ' + this.icon
25296             });
25297         }
25298         
25299         var h = {
25300             tag: 'ul',
25301             cls: 'nav nav-tabs pull-right',
25302             cn: [
25303                 header
25304             ]
25305         };
25306         
25307         if(this.tabScrollable){
25308             h = {
25309                 tag: 'div',
25310                 cls: 'tab-header',
25311                 cn: [
25312                     {
25313                         tag: 'ul',
25314                         cls: 'nav nav-tabs pull-right',
25315                         cn: [
25316                             header
25317                         ]
25318                     }
25319                 ]
25320             };
25321         }
25322         
25323         var cfg = {
25324             tag: 'div',
25325             cls: 'nav-tabs-custom',
25326             cn: [
25327                 h,
25328                 {
25329                     tag: 'div',
25330                     cls: 'tab-content no-padding',
25331                     cn: []
25332                 }
25333             ]
25334         };
25335
25336         return  cfg;
25337     },
25338     initEvents : function()
25339     {
25340         //Roo.log('add add pane handler');
25341         this.on('addpane', this.onAddPane, this);
25342     },
25343      /**
25344      * Updates the box title
25345      * @param {String} html to set the title to.
25346      */
25347     setTitle : function(value)
25348     {
25349         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25350     },
25351     onAddPane : function(pane)
25352     {
25353         this.panes.push(pane);
25354         //Roo.log('addpane');
25355         //Roo.log(pane);
25356         // tabs are rendere left to right..
25357         if(!this.showtabs){
25358             return;
25359         }
25360         
25361         var ctr = this.el.select('.nav-tabs', true).first();
25362          
25363          
25364         var existing = ctr.select('.nav-tab',true);
25365         var qty = existing.getCount();;
25366         
25367         
25368         var tab = ctr.createChild({
25369             tag : 'li',
25370             cls : 'nav-tab' + (qty ? '' : ' active'),
25371             cn : [
25372                 {
25373                     tag : 'a',
25374                     href:'#',
25375                     html : pane.title
25376                 }
25377             ]
25378         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25379         pane.tab = tab;
25380         
25381         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25382         if (!qty) {
25383             pane.el.addClass('active');
25384         }
25385         
25386                 
25387     },
25388     onTabClick : function(ev,un,ob,pane)
25389     {
25390         //Roo.log('tab - prev default');
25391         ev.preventDefault();
25392         
25393         
25394         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25395         pane.tab.addClass('active');
25396         //Roo.log(pane.title);
25397         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25398         // technically we should have a deactivate event.. but maybe add later.
25399         // and it should not de-activate the selected tab...
25400         this.fireEvent('activatepane', pane);
25401         pane.el.addClass('active');
25402         pane.fireEvent('activate');
25403         
25404         
25405     },
25406     
25407     getActivePane : function()
25408     {
25409         var r = false;
25410         Roo.each(this.panes, function(p) {
25411             if(p.el.hasClass('active')){
25412                 r = p;
25413                 return false;
25414             }
25415             
25416             return;
25417         });
25418         
25419         return r;
25420     }
25421     
25422     
25423 });
25424
25425  
25426 /*
25427  * - LGPL
25428  *
25429  * Tab pane
25430  * 
25431  */
25432 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25433 /**
25434  * @class Roo.bootstrap.TabPane
25435  * @extends Roo.bootstrap.Component
25436  * Bootstrap TabPane class
25437  * @cfg {Boolean} active (false | true) Default false
25438  * @cfg {String} title title of panel
25439
25440  * 
25441  * @constructor
25442  * Create a new TabPane
25443  * @param {Object} config The config object
25444  */
25445
25446 Roo.bootstrap.dash.TabPane = function(config){
25447     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25448     
25449     this.addEvents({
25450         // raw events
25451         /**
25452          * @event activate
25453          * When a pane is activated
25454          * @param {Roo.bootstrap.dash.TabPane} pane
25455          */
25456         "activate" : true
25457          
25458     });
25459 };
25460
25461 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25462     
25463     active : false,
25464     title : '',
25465     
25466     // the tabBox that this is attached to.
25467     tab : false,
25468      
25469     getAutoCreate : function() 
25470     {
25471         var cfg = {
25472             tag: 'div',
25473             cls: 'tab-pane'
25474         };
25475         
25476         if(this.active){
25477             cfg.cls += ' active';
25478         }
25479         
25480         return cfg;
25481     },
25482     initEvents  : function()
25483     {
25484         //Roo.log('trigger add pane handler');
25485         this.parent().fireEvent('addpane', this)
25486     },
25487     
25488      /**
25489      * Updates the tab title 
25490      * @param {String} html to set the title to.
25491      */
25492     setTitle: function(str)
25493     {
25494         if (!this.tab) {
25495             return;
25496         }
25497         this.title = str;
25498         this.tab.select('a', true).first().dom.innerHTML = str;
25499         
25500     }
25501     
25502     
25503     
25504 });
25505
25506  
25507
25508
25509  /*
25510  * - LGPL
25511  *
25512  * menu
25513  * 
25514  */
25515 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25516
25517 /**
25518  * @class Roo.bootstrap.menu.Menu
25519  * @extends Roo.bootstrap.Component
25520  * Bootstrap Menu class - container for Menu
25521  * @cfg {String} html Text of the menu
25522  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25523  * @cfg {String} icon Font awesome icon
25524  * @cfg {String} pos Menu align to (top | bottom) default bottom
25525  * 
25526  * 
25527  * @constructor
25528  * Create a new Menu
25529  * @param {Object} config The config object
25530  */
25531
25532
25533 Roo.bootstrap.menu.Menu = function(config){
25534     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25535     
25536     this.addEvents({
25537         /**
25538          * @event beforeshow
25539          * Fires before this menu is displayed
25540          * @param {Roo.bootstrap.menu.Menu} this
25541          */
25542         beforeshow : true,
25543         /**
25544          * @event beforehide
25545          * Fires before this menu is hidden
25546          * @param {Roo.bootstrap.menu.Menu} this
25547          */
25548         beforehide : true,
25549         /**
25550          * @event show
25551          * Fires after this menu is displayed
25552          * @param {Roo.bootstrap.menu.Menu} this
25553          */
25554         show : true,
25555         /**
25556          * @event hide
25557          * Fires after this menu is hidden
25558          * @param {Roo.bootstrap.menu.Menu} this
25559          */
25560         hide : true,
25561         /**
25562          * @event click
25563          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25564          * @param {Roo.bootstrap.menu.Menu} this
25565          * @param {Roo.EventObject} e
25566          */
25567         click : true
25568     });
25569     
25570 };
25571
25572 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25573     
25574     submenu : false,
25575     html : '',
25576     weight : 'default',
25577     icon : false,
25578     pos : 'bottom',
25579     
25580     
25581     getChildContainer : function() {
25582         if(this.isSubMenu){
25583             return this.el;
25584         }
25585         
25586         return this.el.select('ul.dropdown-menu', true).first();  
25587     },
25588     
25589     getAutoCreate : function()
25590     {
25591         var text = [
25592             {
25593                 tag : 'span',
25594                 cls : 'roo-menu-text',
25595                 html : this.html
25596             }
25597         ];
25598         
25599         if(this.icon){
25600             text.unshift({
25601                 tag : 'i',
25602                 cls : 'fa ' + this.icon
25603             })
25604         }
25605         
25606         
25607         var cfg = {
25608             tag : 'div',
25609             cls : 'btn-group',
25610             cn : [
25611                 {
25612                     tag : 'button',
25613                     cls : 'dropdown-button btn btn-' + this.weight,
25614                     cn : text
25615                 },
25616                 {
25617                     tag : 'button',
25618                     cls : 'dropdown-toggle btn btn-' + this.weight,
25619                     cn : [
25620                         {
25621                             tag : 'span',
25622                             cls : 'caret'
25623                         }
25624                     ]
25625                 },
25626                 {
25627                     tag : 'ul',
25628                     cls : 'dropdown-menu'
25629                 }
25630             ]
25631             
25632         };
25633         
25634         if(this.pos == 'top'){
25635             cfg.cls += ' dropup';
25636         }
25637         
25638         if(this.isSubMenu){
25639             cfg = {
25640                 tag : 'ul',
25641                 cls : 'dropdown-menu'
25642             }
25643         }
25644         
25645         return cfg;
25646     },
25647     
25648     onRender : function(ct, position)
25649     {
25650         this.isSubMenu = ct.hasClass('dropdown-submenu');
25651         
25652         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25653     },
25654     
25655     initEvents : function() 
25656     {
25657         if(this.isSubMenu){
25658             return;
25659         }
25660         
25661         this.hidden = true;
25662         
25663         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25664         this.triggerEl.on('click', this.onTriggerPress, this);
25665         
25666         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25667         this.buttonEl.on('click', this.onClick, this);
25668         
25669     },
25670     
25671     list : function()
25672     {
25673         if(this.isSubMenu){
25674             return this.el;
25675         }
25676         
25677         return this.el.select('ul.dropdown-menu', true).first();
25678     },
25679     
25680     onClick : function(e)
25681     {
25682         this.fireEvent("click", this, e);
25683     },
25684     
25685     onTriggerPress  : function(e)
25686     {   
25687         if (this.isVisible()) {
25688             this.hide();
25689         } else {
25690             this.show();
25691         }
25692     },
25693     
25694     isVisible : function(){
25695         return !this.hidden;
25696     },
25697     
25698     show : function()
25699     {
25700         this.fireEvent("beforeshow", this);
25701         
25702         this.hidden = false;
25703         this.el.addClass('open');
25704         
25705         Roo.get(document).on("mouseup", this.onMouseUp, this);
25706         
25707         this.fireEvent("show", this);
25708         
25709         
25710     },
25711     
25712     hide : function()
25713     {
25714         this.fireEvent("beforehide", this);
25715         
25716         this.hidden = true;
25717         this.el.removeClass('open');
25718         
25719         Roo.get(document).un("mouseup", this.onMouseUp);
25720         
25721         this.fireEvent("hide", this);
25722     },
25723     
25724     onMouseUp : function()
25725     {
25726         this.hide();
25727     }
25728     
25729 });
25730
25731  
25732  /*
25733  * - LGPL
25734  *
25735  * menu item
25736  * 
25737  */
25738 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25739
25740 /**
25741  * @class Roo.bootstrap.menu.Item
25742  * @extends Roo.bootstrap.Component
25743  * Bootstrap MenuItem class
25744  * @cfg {Boolean} submenu (true | false) default false
25745  * @cfg {String} html text of the item
25746  * @cfg {String} href the link
25747  * @cfg {Boolean} disable (true | false) default false
25748  * @cfg {Boolean} preventDefault (true | false) default true
25749  * @cfg {String} icon Font awesome icon
25750  * @cfg {String} pos Submenu align to (left | right) default right 
25751  * 
25752  * 
25753  * @constructor
25754  * Create a new Item
25755  * @param {Object} config The config object
25756  */
25757
25758
25759 Roo.bootstrap.menu.Item = function(config){
25760     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25761     this.addEvents({
25762         /**
25763          * @event mouseover
25764          * Fires when the mouse is hovering over this menu
25765          * @param {Roo.bootstrap.menu.Item} this
25766          * @param {Roo.EventObject} e
25767          */
25768         mouseover : true,
25769         /**
25770          * @event mouseout
25771          * Fires when the mouse exits this menu
25772          * @param {Roo.bootstrap.menu.Item} this
25773          * @param {Roo.EventObject} e
25774          */
25775         mouseout : true,
25776         // raw events
25777         /**
25778          * @event click
25779          * The raw click event for the entire grid.
25780          * @param {Roo.EventObject} e
25781          */
25782         click : true
25783     });
25784 };
25785
25786 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25787     
25788     submenu : false,
25789     href : '',
25790     html : '',
25791     preventDefault: true,
25792     disable : false,
25793     icon : false,
25794     pos : 'right',
25795     
25796     getAutoCreate : function()
25797     {
25798         var text = [
25799             {
25800                 tag : 'span',
25801                 cls : 'roo-menu-item-text',
25802                 html : this.html
25803             }
25804         ];
25805         
25806         if(this.icon){
25807             text.unshift({
25808                 tag : 'i',
25809                 cls : 'fa ' + this.icon
25810             })
25811         }
25812         
25813         var cfg = {
25814             tag : 'li',
25815             cn : [
25816                 {
25817                     tag : 'a',
25818                     href : this.href || '#',
25819                     cn : text
25820                 }
25821             ]
25822         };
25823         
25824         if(this.disable){
25825             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25826         }
25827         
25828         if(this.submenu){
25829             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25830             
25831             if(this.pos == 'left'){
25832                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25833             }
25834         }
25835         
25836         return cfg;
25837     },
25838     
25839     initEvents : function() 
25840     {
25841         this.el.on('mouseover', this.onMouseOver, this);
25842         this.el.on('mouseout', this.onMouseOut, this);
25843         
25844         this.el.select('a', true).first().on('click', this.onClick, this);
25845         
25846     },
25847     
25848     onClick : function(e)
25849     {
25850         if(this.preventDefault){
25851             e.preventDefault();
25852         }
25853         
25854         this.fireEvent("click", this, e);
25855     },
25856     
25857     onMouseOver : function(e)
25858     {
25859         if(this.submenu && this.pos == 'left'){
25860             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25861         }
25862         
25863         this.fireEvent("mouseover", this, e);
25864     },
25865     
25866     onMouseOut : function(e)
25867     {
25868         this.fireEvent("mouseout", this, e);
25869     }
25870 });
25871
25872  
25873
25874  /*
25875  * - LGPL
25876  *
25877  * menu separator
25878  * 
25879  */
25880 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25881
25882 /**
25883  * @class Roo.bootstrap.menu.Separator
25884  * @extends Roo.bootstrap.Component
25885  * Bootstrap Separator class
25886  * 
25887  * @constructor
25888  * Create a new Separator
25889  * @param {Object} config The config object
25890  */
25891
25892
25893 Roo.bootstrap.menu.Separator = function(config){
25894     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25895 };
25896
25897 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25898     
25899     getAutoCreate : function(){
25900         var cfg = {
25901             tag : 'li',
25902             cls: 'divider'
25903         };
25904         
25905         return cfg;
25906     }
25907    
25908 });
25909
25910  
25911
25912  /*
25913  * - LGPL
25914  *
25915  * Tooltip
25916  * 
25917  */
25918
25919 /**
25920  * @class Roo.bootstrap.Tooltip
25921  * Bootstrap Tooltip class
25922  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25923  * to determine which dom element triggers the tooltip.
25924  * 
25925  * It needs to add support for additional attributes like tooltip-position
25926  * 
25927  * @constructor
25928  * Create a new Toolti
25929  * @param {Object} config The config object
25930  */
25931
25932 Roo.bootstrap.Tooltip = function(config){
25933     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25934     
25935     this.alignment = Roo.bootstrap.Tooltip.alignment;
25936     
25937     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25938         this.alignment = config.alignment;
25939     }
25940     
25941 };
25942
25943 Roo.apply(Roo.bootstrap.Tooltip, {
25944     /**
25945      * @function init initialize tooltip monitoring.
25946      * @static
25947      */
25948     currentEl : false,
25949     currentTip : false,
25950     currentRegion : false,
25951     
25952     //  init : delay?
25953     
25954     init : function()
25955     {
25956         Roo.get(document).on('mouseover', this.enter ,this);
25957         Roo.get(document).on('mouseout', this.leave, this);
25958          
25959         
25960         this.currentTip = new Roo.bootstrap.Tooltip();
25961     },
25962     
25963     enter : function(ev)
25964     {
25965         var dom = ev.getTarget();
25966         
25967         //Roo.log(['enter',dom]);
25968         var el = Roo.fly(dom);
25969         if (this.currentEl) {
25970             //Roo.log(dom);
25971             //Roo.log(this.currentEl);
25972             //Roo.log(this.currentEl.contains(dom));
25973             if (this.currentEl == el) {
25974                 return;
25975             }
25976             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25977                 return;
25978             }
25979
25980         }
25981         
25982         if (this.currentTip.el) {
25983             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25984         }    
25985         //Roo.log(ev);
25986         
25987         if(!el || el.dom == document){
25988             return;
25989         }
25990         
25991         var bindEl = el;
25992         
25993         // you can not look for children, as if el is the body.. then everythign is the child..
25994         if (!el.attr('tooltip')) { //
25995             if (!el.select("[tooltip]").elements.length) {
25996                 return;
25997             }
25998             // is the mouse over this child...?
25999             bindEl = el.select("[tooltip]").first();
26000             var xy = ev.getXY();
26001             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26002                 //Roo.log("not in region.");
26003                 return;
26004             }
26005             //Roo.log("child element over..");
26006             
26007         }
26008         this.currentEl = bindEl;
26009         this.currentTip.bind(bindEl);
26010         this.currentRegion = Roo.lib.Region.getRegion(dom);
26011         this.currentTip.enter();
26012         
26013     },
26014     leave : function(ev)
26015     {
26016         var dom = ev.getTarget();
26017         //Roo.log(['leave',dom]);
26018         if (!this.currentEl) {
26019             return;
26020         }
26021         
26022         
26023         if (dom != this.currentEl.dom) {
26024             return;
26025         }
26026         var xy = ev.getXY();
26027         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26028             return;
26029         }
26030         // only activate leave if mouse cursor is outside... bounding box..
26031         
26032         
26033         
26034         
26035         if (this.currentTip) {
26036             this.currentTip.leave();
26037         }
26038         //Roo.log('clear currentEl');
26039         this.currentEl = false;
26040         
26041         
26042     },
26043     alignment : {
26044         'left' : ['r-l', [-2,0], 'right'],
26045         'right' : ['l-r', [2,0], 'left'],
26046         'bottom' : ['t-b', [0,2], 'top'],
26047         'top' : [ 'b-t', [0,-2], 'bottom']
26048     }
26049     
26050 });
26051
26052
26053 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26054     
26055     
26056     bindEl : false,
26057     
26058     delay : null, // can be { show : 300 , hide: 500}
26059     
26060     timeout : null,
26061     
26062     hoverState : null, //???
26063     
26064     placement : 'bottom', 
26065     
26066     alignment : false,
26067     
26068     getAutoCreate : function(){
26069     
26070         var cfg = {
26071            cls : 'tooltip',
26072            role : 'tooltip',
26073            cn : [
26074                 {
26075                     cls : 'tooltip-arrow'
26076                 },
26077                 {
26078                     cls : 'tooltip-inner'
26079                 }
26080            ]
26081         };
26082         
26083         return cfg;
26084     },
26085     bind : function(el)
26086     {
26087         this.bindEl = el;
26088     },
26089       
26090     
26091     enter : function () {
26092        
26093         if (this.timeout != null) {
26094             clearTimeout(this.timeout);
26095         }
26096         
26097         this.hoverState = 'in';
26098          //Roo.log("enter - show");
26099         if (!this.delay || !this.delay.show) {
26100             this.show();
26101             return;
26102         }
26103         var _t = this;
26104         this.timeout = setTimeout(function () {
26105             if (_t.hoverState == 'in') {
26106                 _t.show();
26107             }
26108         }, this.delay.show);
26109     },
26110     leave : function()
26111     {
26112         clearTimeout(this.timeout);
26113     
26114         this.hoverState = 'out';
26115          if (!this.delay || !this.delay.hide) {
26116             this.hide();
26117             return;
26118         }
26119        
26120         var _t = this;
26121         this.timeout = setTimeout(function () {
26122             //Roo.log("leave - timeout");
26123             
26124             if (_t.hoverState == 'out') {
26125                 _t.hide();
26126                 Roo.bootstrap.Tooltip.currentEl = false;
26127             }
26128         }, delay);
26129     },
26130     
26131     show : function (msg)
26132     {
26133         if (!this.el) {
26134             this.render(document.body);
26135         }
26136         // set content.
26137         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26138         
26139         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26140         
26141         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26142         
26143         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26144         
26145         var placement = typeof this.placement == 'function' ?
26146             this.placement.call(this, this.el, on_el) :
26147             this.placement;
26148             
26149         var autoToken = /\s?auto?\s?/i;
26150         var autoPlace = autoToken.test(placement);
26151         if (autoPlace) {
26152             placement = placement.replace(autoToken, '') || 'top';
26153         }
26154         
26155         //this.el.detach()
26156         //this.el.setXY([0,0]);
26157         this.el.show();
26158         //this.el.dom.style.display='block';
26159         
26160         //this.el.appendTo(on_el);
26161         
26162         var p = this.getPosition();
26163         var box = this.el.getBox();
26164         
26165         if (autoPlace) {
26166             // fixme..
26167         }
26168         
26169         var align = this.alignment[placement];
26170         
26171         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26172         
26173         if(placement == 'top' || placement == 'bottom'){
26174             if(xy[0] < 0){
26175                 placement = 'right';
26176             }
26177             
26178             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26179                 placement = 'left';
26180             }
26181             
26182             var scroll = Roo.select('body', true).first().getScroll();
26183             
26184             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26185                 placement = 'top';
26186             }
26187             
26188         }
26189         
26190         this.el.alignTo(this.bindEl, align[0],align[1]);
26191         //var arrow = this.el.select('.arrow',true).first();
26192         //arrow.set(align[2], 
26193         
26194         this.el.addClass(placement);
26195         
26196         this.el.addClass('in fade');
26197         
26198         this.hoverState = null;
26199         
26200         if (this.el.hasClass('fade')) {
26201             // fade it?
26202         }
26203         
26204     },
26205     hide : function()
26206     {
26207          
26208         if (!this.el) {
26209             return;
26210         }
26211         //this.el.setXY([0,0]);
26212         this.el.removeClass('in');
26213         //this.el.hide();
26214         
26215     }
26216     
26217 });
26218  
26219
26220  /*
26221  * - LGPL
26222  *
26223  * Location Picker
26224  * 
26225  */
26226
26227 /**
26228  * @class Roo.bootstrap.LocationPicker
26229  * @extends Roo.bootstrap.Component
26230  * Bootstrap LocationPicker class
26231  * @cfg {Number} latitude Position when init default 0
26232  * @cfg {Number} longitude Position when init default 0
26233  * @cfg {Number} zoom default 15
26234  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26235  * @cfg {Boolean} mapTypeControl default false
26236  * @cfg {Boolean} disableDoubleClickZoom default false
26237  * @cfg {Boolean} scrollwheel default true
26238  * @cfg {Boolean} streetViewControl default false
26239  * @cfg {Number} radius default 0
26240  * @cfg {String} locationName
26241  * @cfg {Boolean} draggable default true
26242  * @cfg {Boolean} enableAutocomplete default false
26243  * @cfg {Boolean} enableReverseGeocode default true
26244  * @cfg {String} markerTitle
26245  * 
26246  * @constructor
26247  * Create a new LocationPicker
26248  * @param {Object} config The config object
26249  */
26250
26251
26252 Roo.bootstrap.LocationPicker = function(config){
26253     
26254     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26255     
26256     this.addEvents({
26257         /**
26258          * @event initial
26259          * Fires when the picker initialized.
26260          * @param {Roo.bootstrap.LocationPicker} this
26261          * @param {Google Location} location
26262          */
26263         initial : true,
26264         /**
26265          * @event positionchanged
26266          * Fires when the picker position changed.
26267          * @param {Roo.bootstrap.LocationPicker} this
26268          * @param {Google Location} location
26269          */
26270         positionchanged : true,
26271         /**
26272          * @event resize
26273          * Fires when the map resize.
26274          * @param {Roo.bootstrap.LocationPicker} this
26275          */
26276         resize : true,
26277         /**
26278          * @event show
26279          * Fires when the map show.
26280          * @param {Roo.bootstrap.LocationPicker} this
26281          */
26282         show : true,
26283         /**
26284          * @event hide
26285          * Fires when the map hide.
26286          * @param {Roo.bootstrap.LocationPicker} this
26287          */
26288         hide : true,
26289         /**
26290          * @event mapClick
26291          * Fires when click the map.
26292          * @param {Roo.bootstrap.LocationPicker} this
26293          * @param {Map event} e
26294          */
26295         mapClick : true,
26296         /**
26297          * @event mapRightClick
26298          * Fires when right click the map.
26299          * @param {Roo.bootstrap.LocationPicker} this
26300          * @param {Map event} e
26301          */
26302         mapRightClick : true,
26303         /**
26304          * @event markerClick
26305          * Fires when click the marker.
26306          * @param {Roo.bootstrap.LocationPicker} this
26307          * @param {Map event} e
26308          */
26309         markerClick : true,
26310         /**
26311          * @event markerRightClick
26312          * Fires when right click the marker.
26313          * @param {Roo.bootstrap.LocationPicker} this
26314          * @param {Map event} e
26315          */
26316         markerRightClick : true,
26317         /**
26318          * @event OverlayViewDraw
26319          * Fires when OverlayView Draw
26320          * @param {Roo.bootstrap.LocationPicker} this
26321          */
26322         OverlayViewDraw : true,
26323         /**
26324          * @event OverlayViewOnAdd
26325          * Fires when OverlayView Draw
26326          * @param {Roo.bootstrap.LocationPicker} this
26327          */
26328         OverlayViewOnAdd : true,
26329         /**
26330          * @event OverlayViewOnRemove
26331          * Fires when OverlayView Draw
26332          * @param {Roo.bootstrap.LocationPicker} this
26333          */
26334         OverlayViewOnRemove : true,
26335         /**
26336          * @event OverlayViewShow
26337          * Fires when OverlayView Draw
26338          * @param {Roo.bootstrap.LocationPicker} this
26339          * @param {Pixel} cpx
26340          */
26341         OverlayViewShow : true,
26342         /**
26343          * @event OverlayViewHide
26344          * Fires when OverlayView Draw
26345          * @param {Roo.bootstrap.LocationPicker} this
26346          */
26347         OverlayViewHide : true,
26348         /**
26349          * @event loadexception
26350          * Fires when load google lib failed.
26351          * @param {Roo.bootstrap.LocationPicker} this
26352          */
26353         loadexception : true
26354     });
26355         
26356 };
26357
26358 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26359     
26360     gMapContext: false,
26361     
26362     latitude: 0,
26363     longitude: 0,
26364     zoom: 15,
26365     mapTypeId: false,
26366     mapTypeControl: false,
26367     disableDoubleClickZoom: false,
26368     scrollwheel: true,
26369     streetViewControl: false,
26370     radius: 0,
26371     locationName: '',
26372     draggable: true,
26373     enableAutocomplete: false,
26374     enableReverseGeocode: true,
26375     markerTitle: '',
26376     
26377     getAutoCreate: function()
26378     {
26379
26380         var cfg = {
26381             tag: 'div',
26382             cls: 'roo-location-picker'
26383         };
26384         
26385         return cfg
26386     },
26387     
26388     initEvents: function(ct, position)
26389     {       
26390         if(!this.el.getWidth() || this.isApplied()){
26391             return;
26392         }
26393         
26394         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26395         
26396         this.initial();
26397     },
26398     
26399     initial: function()
26400     {
26401         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26402             this.fireEvent('loadexception', this);
26403             return;
26404         }
26405         
26406         if(!this.mapTypeId){
26407             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26408         }
26409         
26410         this.gMapContext = this.GMapContext();
26411         
26412         this.initOverlayView();
26413         
26414         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26415         
26416         var _this = this;
26417                 
26418         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26419             _this.setPosition(_this.gMapContext.marker.position);
26420         });
26421         
26422         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26423             _this.fireEvent('mapClick', this, event);
26424             
26425         });
26426
26427         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26428             _this.fireEvent('mapRightClick', this, event);
26429             
26430         });
26431         
26432         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26433             _this.fireEvent('markerClick', this, event);
26434             
26435         });
26436
26437         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26438             _this.fireEvent('markerRightClick', this, event);
26439             
26440         });
26441         
26442         this.setPosition(this.gMapContext.location);
26443         
26444         this.fireEvent('initial', this, this.gMapContext.location);
26445     },
26446     
26447     initOverlayView: function()
26448     {
26449         var _this = this;
26450         
26451         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26452             
26453             draw: function()
26454             {
26455                 _this.fireEvent('OverlayViewDraw', _this);
26456             },
26457             
26458             onAdd: function()
26459             {
26460                 _this.fireEvent('OverlayViewOnAdd', _this);
26461             },
26462             
26463             onRemove: function()
26464             {
26465                 _this.fireEvent('OverlayViewOnRemove', _this);
26466             },
26467             
26468             show: function(cpx)
26469             {
26470                 _this.fireEvent('OverlayViewShow', _this, cpx);
26471             },
26472             
26473             hide: function()
26474             {
26475                 _this.fireEvent('OverlayViewHide', _this);
26476             }
26477             
26478         });
26479     },
26480     
26481     fromLatLngToContainerPixel: function(event)
26482     {
26483         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26484     },
26485     
26486     isApplied: function() 
26487     {
26488         return this.getGmapContext() == false ? false : true;
26489     },
26490     
26491     getGmapContext: function() 
26492     {
26493         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26494     },
26495     
26496     GMapContext: function() 
26497     {
26498         var position = new google.maps.LatLng(this.latitude, this.longitude);
26499         
26500         var _map = new google.maps.Map(this.el.dom, {
26501             center: position,
26502             zoom: this.zoom,
26503             mapTypeId: this.mapTypeId,
26504             mapTypeControl: this.mapTypeControl,
26505             disableDoubleClickZoom: this.disableDoubleClickZoom,
26506             scrollwheel: this.scrollwheel,
26507             streetViewControl: this.streetViewControl,
26508             locationName: this.locationName,
26509             draggable: this.draggable,
26510             enableAutocomplete: this.enableAutocomplete,
26511             enableReverseGeocode: this.enableReverseGeocode
26512         });
26513         
26514         var _marker = new google.maps.Marker({
26515             position: position,
26516             map: _map,
26517             title: this.markerTitle,
26518             draggable: this.draggable
26519         });
26520         
26521         return {
26522             map: _map,
26523             marker: _marker,
26524             circle: null,
26525             location: position,
26526             radius: this.radius,
26527             locationName: this.locationName,
26528             addressComponents: {
26529                 formatted_address: null,
26530                 addressLine1: null,
26531                 addressLine2: null,
26532                 streetName: null,
26533                 streetNumber: null,
26534                 city: null,
26535                 district: null,
26536                 state: null,
26537                 stateOrProvince: null
26538             },
26539             settings: this,
26540             domContainer: this.el.dom,
26541             geodecoder: new google.maps.Geocoder()
26542         };
26543     },
26544     
26545     drawCircle: function(center, radius, options) 
26546     {
26547         if (this.gMapContext.circle != null) {
26548             this.gMapContext.circle.setMap(null);
26549         }
26550         if (radius > 0) {
26551             radius *= 1;
26552             options = Roo.apply({}, options, {
26553                 strokeColor: "#0000FF",
26554                 strokeOpacity: .35,
26555                 strokeWeight: 2,
26556                 fillColor: "#0000FF",
26557                 fillOpacity: .2
26558             });
26559             
26560             options.map = this.gMapContext.map;
26561             options.radius = radius;
26562             options.center = center;
26563             this.gMapContext.circle = new google.maps.Circle(options);
26564             return this.gMapContext.circle;
26565         }
26566         
26567         return null;
26568     },
26569     
26570     setPosition: function(location) 
26571     {
26572         this.gMapContext.location = location;
26573         this.gMapContext.marker.setPosition(location);
26574         this.gMapContext.map.panTo(location);
26575         this.drawCircle(location, this.gMapContext.radius, {});
26576         
26577         var _this = this;
26578         
26579         if (this.gMapContext.settings.enableReverseGeocode) {
26580             this.gMapContext.geodecoder.geocode({
26581                 latLng: this.gMapContext.location
26582             }, function(results, status) {
26583                 
26584                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26585                     _this.gMapContext.locationName = results[0].formatted_address;
26586                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26587                     
26588                     _this.fireEvent('positionchanged', this, location);
26589                 }
26590             });
26591             
26592             return;
26593         }
26594         
26595         this.fireEvent('positionchanged', this, location);
26596     },
26597     
26598     resize: function()
26599     {
26600         google.maps.event.trigger(this.gMapContext.map, "resize");
26601         
26602         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26603         
26604         this.fireEvent('resize', this);
26605     },
26606     
26607     setPositionByLatLng: function(latitude, longitude)
26608     {
26609         this.setPosition(new google.maps.LatLng(latitude, longitude));
26610     },
26611     
26612     getCurrentPosition: function() 
26613     {
26614         return {
26615             latitude: this.gMapContext.location.lat(),
26616             longitude: this.gMapContext.location.lng()
26617         };
26618     },
26619     
26620     getAddressName: function() 
26621     {
26622         return this.gMapContext.locationName;
26623     },
26624     
26625     getAddressComponents: function() 
26626     {
26627         return this.gMapContext.addressComponents;
26628     },
26629     
26630     address_component_from_google_geocode: function(address_components) 
26631     {
26632         var result = {};
26633         
26634         for (var i = 0; i < address_components.length; i++) {
26635             var component = address_components[i];
26636             if (component.types.indexOf("postal_code") >= 0) {
26637                 result.postalCode = component.short_name;
26638             } else if (component.types.indexOf("street_number") >= 0) {
26639                 result.streetNumber = component.short_name;
26640             } else if (component.types.indexOf("route") >= 0) {
26641                 result.streetName = component.short_name;
26642             } else if (component.types.indexOf("neighborhood") >= 0) {
26643                 result.city = component.short_name;
26644             } else if (component.types.indexOf("locality") >= 0) {
26645                 result.city = component.short_name;
26646             } else if (component.types.indexOf("sublocality") >= 0) {
26647                 result.district = component.short_name;
26648             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26649                 result.stateOrProvince = component.short_name;
26650             } else if (component.types.indexOf("country") >= 0) {
26651                 result.country = component.short_name;
26652             }
26653         }
26654         
26655         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26656         result.addressLine2 = "";
26657         return result;
26658     },
26659     
26660     setZoomLevel: function(zoom)
26661     {
26662         this.gMapContext.map.setZoom(zoom);
26663     },
26664     
26665     show: function()
26666     {
26667         if(!this.el){
26668             return;
26669         }
26670         
26671         this.el.show();
26672         
26673         this.resize();
26674         
26675         this.fireEvent('show', this);
26676     },
26677     
26678     hide: function()
26679     {
26680         if(!this.el){
26681             return;
26682         }
26683         
26684         this.el.hide();
26685         
26686         this.fireEvent('hide', this);
26687     }
26688     
26689 });
26690
26691 Roo.apply(Roo.bootstrap.LocationPicker, {
26692     
26693     OverlayView : function(map, options)
26694     {
26695         options = options || {};
26696         
26697         this.setMap(map);
26698     }
26699     
26700     
26701 });/*
26702  * - LGPL
26703  *
26704  * Alert
26705  * 
26706  */
26707
26708 /**
26709  * @class Roo.bootstrap.Alert
26710  * @extends Roo.bootstrap.Component
26711  * Bootstrap Alert class
26712  * @cfg {String} title The title of alert
26713  * @cfg {String} html The content of alert
26714  * @cfg {String} weight (  success | info | warning | danger )
26715  * @cfg {String} faicon font-awesomeicon
26716  * 
26717  * @constructor
26718  * Create a new alert
26719  * @param {Object} config The config object
26720  */
26721
26722
26723 Roo.bootstrap.Alert = function(config){
26724     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26725     
26726 };
26727
26728 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26729     
26730     title: '',
26731     html: '',
26732     weight: false,
26733     faicon: false,
26734     
26735     getAutoCreate : function()
26736     {
26737         
26738         var cfg = {
26739             tag : 'div',
26740             cls : 'alert',
26741             cn : [
26742                 {
26743                     tag : 'i',
26744                     cls : 'roo-alert-icon'
26745                     
26746                 },
26747                 {
26748                     tag : 'b',
26749                     cls : 'roo-alert-title',
26750                     html : this.title
26751                 },
26752                 {
26753                     tag : 'span',
26754                     cls : 'roo-alert-text',
26755                     html : this.html
26756                 }
26757             ]
26758         };
26759         
26760         if(this.faicon){
26761             cfg.cn[0].cls += ' fa ' + this.faicon;
26762         }
26763         
26764         if(this.weight){
26765             cfg.cls += ' alert-' + this.weight;
26766         }
26767         
26768         return cfg;
26769     },
26770     
26771     initEvents: function() 
26772     {
26773         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26774     },
26775     
26776     setTitle : function(str)
26777     {
26778         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26779     },
26780     
26781     setText : function(str)
26782     {
26783         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26784     },
26785     
26786     setWeight : function(weight)
26787     {
26788         if(this.weight){
26789             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26790         }
26791         
26792         this.weight = weight;
26793         
26794         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26795     },
26796     
26797     setIcon : function(icon)
26798     {
26799         if(this.faicon){
26800             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26801         }
26802         
26803         this.faicon = icon;
26804         
26805         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26806     },
26807     
26808     hide: function() 
26809     {
26810         this.el.hide();   
26811     },
26812     
26813     show: function() 
26814     {  
26815         this.el.show();   
26816     }
26817     
26818 });
26819
26820  
26821 /*
26822 * Licence: LGPL
26823 */
26824
26825 /**
26826  * @class Roo.bootstrap.UploadCropbox
26827  * @extends Roo.bootstrap.Component
26828  * Bootstrap UploadCropbox class
26829  * @cfg {String} emptyText show when image has been loaded
26830  * @cfg {String} rotateNotify show when image too small to rotate
26831  * @cfg {Number} errorTimeout default 3000
26832  * @cfg {Number} minWidth default 300
26833  * @cfg {Number} minHeight default 300
26834  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26835  * @cfg {Boolean} isDocument (true|false) default false
26836  * @cfg {String} url action url
26837  * @cfg {String} paramName default 'imageUpload'
26838  * @cfg {String} method default POST
26839  * @cfg {Boolean} loadMask (true|false) default true
26840  * @cfg {Boolean} loadingText default 'Loading...'
26841  * 
26842  * @constructor
26843  * Create a new UploadCropbox
26844  * @param {Object} config The config object
26845  */
26846
26847 Roo.bootstrap.UploadCropbox = function(config){
26848     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26849     
26850     this.addEvents({
26851         /**
26852          * @event beforeselectfile
26853          * Fire before select file
26854          * @param {Roo.bootstrap.UploadCropbox} this
26855          */
26856         "beforeselectfile" : true,
26857         /**
26858          * @event initial
26859          * Fire after initEvent
26860          * @param {Roo.bootstrap.UploadCropbox} this
26861          */
26862         "initial" : true,
26863         /**
26864          * @event crop
26865          * Fire after initEvent
26866          * @param {Roo.bootstrap.UploadCropbox} this
26867          * @param {String} data
26868          */
26869         "crop" : true,
26870         /**
26871          * @event prepare
26872          * Fire when preparing the file data
26873          * @param {Roo.bootstrap.UploadCropbox} this
26874          * @param {Object} file
26875          */
26876         "prepare" : true,
26877         /**
26878          * @event exception
26879          * Fire when get exception
26880          * @param {Roo.bootstrap.UploadCropbox} this
26881          * @param {XMLHttpRequest} xhr
26882          */
26883         "exception" : true,
26884         /**
26885          * @event beforeloadcanvas
26886          * Fire before load the canvas
26887          * @param {Roo.bootstrap.UploadCropbox} this
26888          * @param {String} src
26889          */
26890         "beforeloadcanvas" : true,
26891         /**
26892          * @event trash
26893          * Fire when trash image
26894          * @param {Roo.bootstrap.UploadCropbox} this
26895          */
26896         "trash" : true,
26897         /**
26898          * @event download
26899          * Fire when download the image
26900          * @param {Roo.bootstrap.UploadCropbox} this
26901          */
26902         "download" : true,
26903         /**
26904          * @event footerbuttonclick
26905          * Fire when footerbuttonclick
26906          * @param {Roo.bootstrap.UploadCropbox} this
26907          * @param {String} type
26908          */
26909         "footerbuttonclick" : true,
26910         /**
26911          * @event resize
26912          * Fire when resize
26913          * @param {Roo.bootstrap.UploadCropbox} this
26914          */
26915         "resize" : true,
26916         /**
26917          * @event rotate
26918          * Fire when rotate the image
26919          * @param {Roo.bootstrap.UploadCropbox} this
26920          * @param {String} pos
26921          */
26922         "rotate" : true,
26923         /**
26924          * @event inspect
26925          * Fire when inspect the file
26926          * @param {Roo.bootstrap.UploadCropbox} this
26927          * @param {Object} file
26928          */
26929         "inspect" : true,
26930         /**
26931          * @event upload
26932          * Fire when xhr upload the file
26933          * @param {Roo.bootstrap.UploadCropbox} this
26934          * @param {Object} data
26935          */
26936         "upload" : true,
26937         /**
26938          * @event arrange
26939          * Fire when arrange the file data
26940          * @param {Roo.bootstrap.UploadCropbox} this
26941          * @param {Object} formData
26942          */
26943         "arrange" : true
26944     });
26945     
26946     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26947 };
26948
26949 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26950     
26951     emptyText : 'Click to upload image',
26952     rotateNotify : 'Image is too small to rotate',
26953     errorTimeout : 3000,
26954     scale : 0,
26955     baseScale : 1,
26956     rotate : 0,
26957     dragable : false,
26958     pinching : false,
26959     mouseX : 0,
26960     mouseY : 0,
26961     cropData : false,
26962     minWidth : 300,
26963     minHeight : 300,
26964     file : false,
26965     exif : {},
26966     baseRotate : 1,
26967     cropType : 'image/jpeg',
26968     buttons : false,
26969     canvasLoaded : false,
26970     isDocument : false,
26971     method : 'POST',
26972     paramName : 'imageUpload',
26973     loadMask : true,
26974     loadingText : 'Loading...',
26975     maskEl : false,
26976     
26977     getAutoCreate : function()
26978     {
26979         var cfg = {
26980             tag : 'div',
26981             cls : 'roo-upload-cropbox',
26982             cn : [
26983                 {
26984                     tag : 'input',
26985                     cls : 'roo-upload-cropbox-selector',
26986                     type : 'file'
26987                 },
26988                 {
26989                     tag : 'div',
26990                     cls : 'roo-upload-cropbox-body',
26991                     style : 'cursor:pointer',
26992                     cn : [
26993                         {
26994                             tag : 'div',
26995                             cls : 'roo-upload-cropbox-preview'
26996                         },
26997                         {
26998                             tag : 'div',
26999                             cls : 'roo-upload-cropbox-thumb'
27000                         },
27001                         {
27002                             tag : 'div',
27003                             cls : 'roo-upload-cropbox-empty-notify',
27004                             html : this.emptyText
27005                         },
27006                         {
27007                             tag : 'div',
27008                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27009                             html : this.rotateNotify
27010                         }
27011                     ]
27012                 },
27013                 {
27014                     tag : 'div',
27015                     cls : 'roo-upload-cropbox-footer',
27016                     cn : {
27017                         tag : 'div',
27018                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27019                         cn : []
27020                     }
27021                 }
27022             ]
27023         };
27024         
27025         return cfg;
27026     },
27027     
27028     onRender : function(ct, position)
27029     {
27030         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27031         
27032         if (this.buttons.length) {
27033             
27034             Roo.each(this.buttons, function(bb) {
27035                 
27036                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27037                 
27038                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27039                 
27040             }, this);
27041         }
27042         
27043         if(this.loadMask){
27044             this.maskEl = this.el;
27045         }
27046     },
27047     
27048     initEvents : function()
27049     {
27050         this.urlAPI = (window.createObjectURL && window) || 
27051                                 (window.URL && URL.revokeObjectURL && URL) || 
27052                                 (window.webkitURL && webkitURL);
27053                         
27054         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27055         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27056         
27057         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27058         this.selectorEl.hide();
27059         
27060         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27061         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27062         
27063         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27064         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27065         this.thumbEl.hide();
27066         
27067         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27068         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069         
27070         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27071         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072         this.errorEl.hide();
27073         
27074         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27075         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076         this.footerEl.hide();
27077         
27078         this.setThumbBoxSize();
27079         
27080         this.bind();
27081         
27082         this.resize();
27083         
27084         this.fireEvent('initial', this);
27085     },
27086
27087     bind : function()
27088     {
27089         var _this = this;
27090         
27091         window.addEventListener("resize", function() { _this.resize(); } );
27092         
27093         this.bodyEl.on('click', this.beforeSelectFile, this);
27094         
27095         if(Roo.isTouch){
27096             this.bodyEl.on('touchstart', this.onTouchStart, this);
27097             this.bodyEl.on('touchmove', this.onTouchMove, this);
27098             this.bodyEl.on('touchend', this.onTouchEnd, this);
27099         }
27100         
27101         if(!Roo.isTouch){
27102             this.bodyEl.on('mousedown', this.onMouseDown, this);
27103             this.bodyEl.on('mousemove', this.onMouseMove, this);
27104             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27105             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27106             Roo.get(document).on('mouseup', this.onMouseUp, this);
27107         }
27108         
27109         this.selectorEl.on('change', this.onFileSelected, this);
27110     },
27111     
27112     reset : function()
27113     {    
27114         this.scale = 0;
27115         this.baseScale = 1;
27116         this.rotate = 0;
27117         this.baseRotate = 1;
27118         this.dragable = false;
27119         this.pinching = false;
27120         this.mouseX = 0;
27121         this.mouseY = 0;
27122         this.cropData = false;
27123         this.notifyEl.dom.innerHTML = this.emptyText;
27124         
27125         this.selectorEl.dom.value = '';
27126         
27127     },
27128     
27129     resize : function()
27130     {
27131         if(this.fireEvent('resize', this) != false){
27132             this.setThumbBoxPosition();
27133             this.setCanvasPosition();
27134         }
27135     },
27136     
27137     onFooterButtonClick : function(e, el, o, type)
27138     {
27139         switch (type) {
27140             case 'rotate-left' :
27141                 this.onRotateLeft(e);
27142                 break;
27143             case 'rotate-right' :
27144                 this.onRotateRight(e);
27145                 break;
27146             case 'picture' :
27147                 this.beforeSelectFile(e);
27148                 break;
27149             case 'trash' :
27150                 this.trash(e);
27151                 break;
27152             case 'crop' :
27153                 this.crop(e);
27154                 break;
27155             case 'download' :
27156                 this.download(e);
27157                 break;
27158             default :
27159                 break;
27160         }
27161         
27162         this.fireEvent('footerbuttonclick', this, type);
27163     },
27164     
27165     beforeSelectFile : function(e)
27166     {
27167         e.preventDefault();
27168         
27169         if(this.fireEvent('beforeselectfile', this) != false){
27170             this.selectorEl.dom.click();
27171         }
27172     },
27173     
27174     onFileSelected : function(e)
27175     {
27176         e.preventDefault();
27177         
27178         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27179             return;
27180         }
27181         
27182         var file = this.selectorEl.dom.files[0];
27183         
27184         if(this.fireEvent('inspect', this, file) != false){
27185             this.prepare(file);
27186         }
27187         
27188     },
27189     
27190     trash : function(e)
27191     {
27192         this.fireEvent('trash', this);
27193     },
27194     
27195     download : function(e)
27196     {
27197         this.fireEvent('download', this);
27198     },
27199     
27200     loadCanvas : function(src)
27201     {   
27202         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27203             
27204             this.reset();
27205             
27206             this.imageEl = document.createElement('img');
27207             
27208             var _this = this;
27209             
27210             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27211             
27212             this.imageEl.src = src;
27213         }
27214     },
27215     
27216     onLoadCanvas : function()
27217     {   
27218         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27219         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27220         
27221         this.bodyEl.un('click', this.beforeSelectFile, this);
27222         
27223         this.notifyEl.hide();
27224         this.thumbEl.show();
27225         this.footerEl.show();
27226         
27227         this.baseRotateLevel();
27228         
27229         if(this.isDocument){
27230             this.setThumbBoxSize();
27231         }
27232         
27233         this.setThumbBoxPosition();
27234         
27235         this.baseScaleLevel();
27236         
27237         this.draw();
27238         
27239         this.resize();
27240         
27241         this.canvasLoaded = true;
27242         
27243         if(this.loadMask){
27244             this.maskEl.unmask();
27245         }
27246         
27247     },
27248     
27249     setCanvasPosition : function()
27250     {   
27251         if(!this.canvasEl){
27252             return;
27253         }
27254         
27255         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27256         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27257         
27258         this.previewEl.setLeft(pw);
27259         this.previewEl.setTop(ph);
27260         
27261     },
27262     
27263     onMouseDown : function(e)
27264     {   
27265         e.stopEvent();
27266         
27267         this.dragable = true;
27268         this.pinching = false;
27269         
27270         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27271             this.dragable = false;
27272             return;
27273         }
27274         
27275         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27276         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27277         
27278     },
27279     
27280     onMouseMove : function(e)
27281     {   
27282         e.stopEvent();
27283         
27284         if(!this.canvasLoaded){
27285             return;
27286         }
27287         
27288         if (!this.dragable){
27289             return;
27290         }
27291         
27292         var minX = Math.ceil(this.thumbEl.getLeft(true));
27293         var minY = Math.ceil(this.thumbEl.getTop(true));
27294         
27295         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27296         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27297         
27298         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27299         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27300         
27301         x = x - this.mouseX;
27302         y = y - this.mouseY;
27303         
27304         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27305         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27306         
27307         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27308         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27309         
27310         this.previewEl.setLeft(bgX);
27311         this.previewEl.setTop(bgY);
27312         
27313         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27314         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27315     },
27316     
27317     onMouseUp : function(e)
27318     {   
27319         e.stopEvent();
27320         
27321         this.dragable = false;
27322     },
27323     
27324     onMouseWheel : function(e)
27325     {   
27326         e.stopEvent();
27327         
27328         this.startScale = this.scale;
27329         
27330         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27331         
27332         if(!this.zoomable()){
27333             this.scale = this.startScale;
27334             return;
27335         }
27336         
27337         this.draw();
27338         
27339         return;
27340     },
27341     
27342     zoomable : function()
27343     {
27344         var minScale = this.thumbEl.getWidth() / this.minWidth;
27345         
27346         if(this.minWidth < this.minHeight){
27347             minScale = this.thumbEl.getHeight() / this.minHeight;
27348         }
27349         
27350         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27351         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27352         
27353         if(
27354                 this.isDocument &&
27355                 (this.rotate == 0 || this.rotate == 180) && 
27356                 (
27357                     width > this.imageEl.OriginWidth || 
27358                     height > this.imageEl.OriginHeight ||
27359                     (width < this.minWidth && height < this.minHeight)
27360                 )
27361         ){
27362             return false;
27363         }
27364         
27365         if(
27366                 this.isDocument &&
27367                 (this.rotate == 90 || this.rotate == 270) && 
27368                 (
27369                     width > this.imageEl.OriginWidth || 
27370                     height > this.imageEl.OriginHeight ||
27371                     (width < this.minHeight && height < this.minWidth)
27372                 )
27373         ){
27374             return false;
27375         }
27376         
27377         if(
27378                 !this.isDocument &&
27379                 (this.rotate == 0 || this.rotate == 180) && 
27380                 (
27381                     width < this.minWidth || 
27382                     width > this.imageEl.OriginWidth || 
27383                     height < this.minHeight || 
27384                     height > this.imageEl.OriginHeight
27385                 )
27386         ){
27387             return false;
27388         }
27389         
27390         if(
27391                 !this.isDocument &&
27392                 (this.rotate == 90 || this.rotate == 270) && 
27393                 (
27394                     width < this.minHeight || 
27395                     width > this.imageEl.OriginWidth || 
27396                     height < this.minWidth || 
27397                     height > this.imageEl.OriginHeight
27398                 )
27399         ){
27400             return false;
27401         }
27402         
27403         return true;
27404         
27405     },
27406     
27407     onRotateLeft : function(e)
27408     {   
27409         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27410             
27411             var minScale = this.thumbEl.getWidth() / this.minWidth;
27412             
27413             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27414             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27415             
27416             this.startScale = this.scale;
27417             
27418             while (this.getScaleLevel() < minScale){
27419             
27420                 this.scale = this.scale + 1;
27421                 
27422                 if(!this.zoomable()){
27423                     break;
27424                 }
27425                 
27426                 if(
27427                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27428                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27429                 ){
27430                     continue;
27431                 }
27432                 
27433                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27434
27435                 this.draw();
27436                 
27437                 return;
27438             }
27439             
27440             this.scale = this.startScale;
27441             
27442             this.onRotateFail();
27443             
27444             return false;
27445         }
27446         
27447         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27448
27449         if(this.isDocument){
27450             this.setThumbBoxSize();
27451             this.setThumbBoxPosition();
27452             this.setCanvasPosition();
27453         }
27454         
27455         this.draw();
27456         
27457         this.fireEvent('rotate', this, 'left');
27458         
27459     },
27460     
27461     onRotateRight : function(e)
27462     {
27463         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27464             
27465             var minScale = this.thumbEl.getWidth() / this.minWidth;
27466         
27467             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27468             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27469             
27470             this.startScale = this.scale;
27471             
27472             while (this.getScaleLevel() < minScale){
27473             
27474                 this.scale = this.scale + 1;
27475                 
27476                 if(!this.zoomable()){
27477                     break;
27478                 }
27479                 
27480                 if(
27481                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27482                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27483                 ){
27484                     continue;
27485                 }
27486                 
27487                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27488
27489                 this.draw();
27490                 
27491                 return;
27492             }
27493             
27494             this.scale = this.startScale;
27495             
27496             this.onRotateFail();
27497             
27498             return false;
27499         }
27500         
27501         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27502
27503         if(this.isDocument){
27504             this.setThumbBoxSize();
27505             this.setThumbBoxPosition();
27506             this.setCanvasPosition();
27507         }
27508         
27509         this.draw();
27510         
27511         this.fireEvent('rotate', this, 'right');
27512     },
27513     
27514     onRotateFail : function()
27515     {
27516         this.errorEl.show(true);
27517         
27518         var _this = this;
27519         
27520         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27521     },
27522     
27523     draw : function()
27524     {
27525         this.previewEl.dom.innerHTML = '';
27526         
27527         var canvasEl = document.createElement("canvas");
27528         
27529         var contextEl = canvasEl.getContext("2d");
27530         
27531         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27532         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27533         var center = this.imageEl.OriginWidth / 2;
27534         
27535         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27536             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27537             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27538             center = this.imageEl.OriginHeight / 2;
27539         }
27540         
27541         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27542         
27543         contextEl.translate(center, center);
27544         contextEl.rotate(this.rotate * Math.PI / 180);
27545
27546         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27547         
27548         this.canvasEl = document.createElement("canvas");
27549         
27550         this.contextEl = this.canvasEl.getContext("2d");
27551         
27552         switch (this.rotate) {
27553             case 0 :
27554                 
27555                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27556                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27557                 
27558                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27559                 
27560                 break;
27561             case 90 : 
27562                 
27563                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27564                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27565                 
27566                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27567                     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);
27568                     break;
27569                 }
27570                 
27571                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27572                 
27573                 break;
27574             case 180 :
27575                 
27576                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27577                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27578                 
27579                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27580                     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);
27581                     break;
27582                 }
27583                 
27584                 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);
27585                 
27586                 break;
27587             case 270 :
27588                 
27589                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27590                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27591         
27592                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27593                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27594                     break;
27595                 }
27596                 
27597                 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);
27598                 
27599                 break;
27600             default : 
27601                 break;
27602         }
27603         
27604         this.previewEl.appendChild(this.canvasEl);
27605         
27606         this.setCanvasPosition();
27607     },
27608     
27609     crop : function()
27610     {
27611         if(!this.canvasLoaded){
27612             return;
27613         }
27614         
27615         var imageCanvas = document.createElement("canvas");
27616         
27617         var imageContext = imageCanvas.getContext("2d");
27618         
27619         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27620         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27621         
27622         var center = imageCanvas.width / 2;
27623         
27624         imageContext.translate(center, center);
27625         
27626         imageContext.rotate(this.rotate * Math.PI / 180);
27627         
27628         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27629         
27630         var canvas = document.createElement("canvas");
27631         
27632         var context = canvas.getContext("2d");
27633                 
27634         canvas.width = this.minWidth;
27635         canvas.height = this.minHeight;
27636
27637         switch (this.rotate) {
27638             case 0 :
27639                 
27640                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27641                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27642                 
27643                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27644                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27645                 
27646                 var targetWidth = this.minWidth - 2 * x;
27647                 var targetHeight = this.minHeight - 2 * y;
27648                 
27649                 var scale = 1;
27650                 
27651                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27652                     scale = targetWidth / width;
27653                 }
27654                 
27655                 if(x > 0 && y == 0){
27656                     scale = targetHeight / height;
27657                 }
27658                 
27659                 if(x > 0 && y > 0){
27660                     scale = targetWidth / width;
27661                     
27662                     if(width < height){
27663                         scale = targetHeight / height;
27664                     }
27665                 }
27666                 
27667                 context.scale(scale, scale);
27668                 
27669                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27670                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27671
27672                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27673                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27674
27675                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27676                 
27677                 break;
27678             case 90 : 
27679                 
27680                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27681                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27682                 
27683                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27684                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27685                 
27686                 var targetWidth = this.minWidth - 2 * x;
27687                 var targetHeight = this.minHeight - 2 * y;
27688                 
27689                 var scale = 1;
27690                 
27691                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27692                     scale = targetWidth / width;
27693                 }
27694                 
27695                 if(x > 0 && y == 0){
27696                     scale = targetHeight / height;
27697                 }
27698                 
27699                 if(x > 0 && y > 0){
27700                     scale = targetWidth / width;
27701                     
27702                     if(width < height){
27703                         scale = targetHeight / height;
27704                     }
27705                 }
27706                 
27707                 context.scale(scale, scale);
27708                 
27709                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27710                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27711
27712                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27713                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27714                 
27715                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27716                 
27717                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27718                 
27719                 break;
27720             case 180 :
27721                 
27722                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27723                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27724                 
27725                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27726                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27727                 
27728                 var targetWidth = this.minWidth - 2 * x;
27729                 var targetHeight = this.minHeight - 2 * y;
27730                 
27731                 var scale = 1;
27732                 
27733                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27734                     scale = targetWidth / width;
27735                 }
27736                 
27737                 if(x > 0 && y == 0){
27738                     scale = targetHeight / height;
27739                 }
27740                 
27741                 if(x > 0 && y > 0){
27742                     scale = targetWidth / width;
27743                     
27744                     if(width < height){
27745                         scale = targetHeight / height;
27746                     }
27747                 }
27748                 
27749                 context.scale(scale, scale);
27750                 
27751                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27752                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27753
27754                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27755                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27756
27757                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27758                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27759                 
27760                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27761                 
27762                 break;
27763             case 270 :
27764                 
27765                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27766                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27767                 
27768                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27769                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27770                 
27771                 var targetWidth = this.minWidth - 2 * x;
27772                 var targetHeight = this.minHeight - 2 * y;
27773                 
27774                 var scale = 1;
27775                 
27776                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27777                     scale = targetWidth / width;
27778                 }
27779                 
27780                 if(x > 0 && y == 0){
27781                     scale = targetHeight / height;
27782                 }
27783                 
27784                 if(x > 0 && y > 0){
27785                     scale = targetWidth / width;
27786                     
27787                     if(width < height){
27788                         scale = targetHeight / height;
27789                     }
27790                 }
27791                 
27792                 context.scale(scale, scale);
27793                 
27794                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27795                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27796
27797                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27798                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27799                 
27800                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27801                 
27802                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27803                 
27804                 break;
27805             default : 
27806                 break;
27807         }
27808         
27809         this.cropData = canvas.toDataURL(this.cropType);
27810         
27811         if(this.fireEvent('crop', this, this.cropData) !== false){
27812             this.process(this.file, this.cropData);
27813         }
27814         
27815         return;
27816         
27817     },
27818     
27819     setThumbBoxSize : function()
27820     {
27821         var width, height;
27822         
27823         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27824             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27825             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27826             
27827             this.minWidth = width;
27828             this.minHeight = height;
27829             
27830             if(this.rotate == 90 || this.rotate == 270){
27831                 this.minWidth = height;
27832                 this.minHeight = width;
27833             }
27834         }
27835         
27836         height = 300;
27837         width = Math.ceil(this.minWidth * height / this.minHeight);
27838         
27839         if(this.minWidth > this.minHeight){
27840             width = 300;
27841             height = Math.ceil(this.minHeight * width / this.minWidth);
27842         }
27843         
27844         this.thumbEl.setStyle({
27845             width : width + 'px',
27846             height : height + 'px'
27847         });
27848
27849         return;
27850             
27851     },
27852     
27853     setThumbBoxPosition : function()
27854     {
27855         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27856         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27857         
27858         this.thumbEl.setLeft(x);
27859         this.thumbEl.setTop(y);
27860         
27861     },
27862     
27863     baseRotateLevel : function()
27864     {
27865         this.baseRotate = 1;
27866         
27867         if(
27868                 typeof(this.exif) != 'undefined' &&
27869                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27870                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27871         ){
27872             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27873         }
27874         
27875         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27876         
27877     },
27878     
27879     baseScaleLevel : function()
27880     {
27881         var width, height;
27882         
27883         if(this.isDocument){
27884             
27885             if(this.baseRotate == 6 || this.baseRotate == 8){
27886             
27887                 height = this.thumbEl.getHeight();
27888                 this.baseScale = height / this.imageEl.OriginWidth;
27889
27890                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27891                     width = this.thumbEl.getWidth();
27892                     this.baseScale = width / this.imageEl.OriginHeight;
27893                 }
27894
27895                 return;
27896             }
27897
27898             height = this.thumbEl.getHeight();
27899             this.baseScale = height / this.imageEl.OriginHeight;
27900
27901             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27902                 width = this.thumbEl.getWidth();
27903                 this.baseScale = width / this.imageEl.OriginWidth;
27904             }
27905
27906             return;
27907         }
27908         
27909         if(this.baseRotate == 6 || this.baseRotate == 8){
27910             
27911             width = this.thumbEl.getHeight();
27912             this.baseScale = width / this.imageEl.OriginHeight;
27913             
27914             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27915                 height = this.thumbEl.getWidth();
27916                 this.baseScale = height / this.imageEl.OriginHeight;
27917             }
27918             
27919             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27920                 height = this.thumbEl.getWidth();
27921                 this.baseScale = height / this.imageEl.OriginHeight;
27922                 
27923                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27924                     width = this.thumbEl.getHeight();
27925                     this.baseScale = width / this.imageEl.OriginWidth;
27926                 }
27927             }
27928             
27929             return;
27930         }
27931         
27932         width = this.thumbEl.getWidth();
27933         this.baseScale = width / this.imageEl.OriginWidth;
27934         
27935         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27936             height = this.thumbEl.getHeight();
27937             this.baseScale = height / this.imageEl.OriginHeight;
27938         }
27939         
27940         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27941             
27942             height = this.thumbEl.getHeight();
27943             this.baseScale = height / this.imageEl.OriginHeight;
27944             
27945             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27946                 width = this.thumbEl.getWidth();
27947                 this.baseScale = width / this.imageEl.OriginWidth;
27948             }
27949             
27950         }
27951         
27952         return;
27953     },
27954     
27955     getScaleLevel : function()
27956     {
27957         return this.baseScale * Math.pow(1.1, this.scale);
27958     },
27959     
27960     onTouchStart : function(e)
27961     {
27962         if(!this.canvasLoaded){
27963             this.beforeSelectFile(e);
27964             return;
27965         }
27966         
27967         var touches = e.browserEvent.touches;
27968         
27969         if(!touches){
27970             return;
27971         }
27972         
27973         if(touches.length == 1){
27974             this.onMouseDown(e);
27975             return;
27976         }
27977         
27978         if(touches.length != 2){
27979             return;
27980         }
27981         
27982         var coords = [];
27983         
27984         for(var i = 0, finger; finger = touches[i]; i++){
27985             coords.push(finger.pageX, finger.pageY);
27986         }
27987         
27988         var x = Math.pow(coords[0] - coords[2], 2);
27989         var y = Math.pow(coords[1] - coords[3], 2);
27990         
27991         this.startDistance = Math.sqrt(x + y);
27992         
27993         this.startScale = this.scale;
27994         
27995         this.pinching = true;
27996         this.dragable = false;
27997         
27998     },
27999     
28000     onTouchMove : function(e)
28001     {
28002         if(!this.pinching && !this.dragable){
28003             return;
28004         }
28005         
28006         var touches = e.browserEvent.touches;
28007         
28008         if(!touches){
28009             return;
28010         }
28011         
28012         if(this.dragable){
28013             this.onMouseMove(e);
28014             return;
28015         }
28016         
28017         var coords = [];
28018         
28019         for(var i = 0, finger; finger = touches[i]; i++){
28020             coords.push(finger.pageX, finger.pageY);
28021         }
28022         
28023         var x = Math.pow(coords[0] - coords[2], 2);
28024         var y = Math.pow(coords[1] - coords[3], 2);
28025         
28026         this.endDistance = Math.sqrt(x + y);
28027         
28028         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28029         
28030         if(!this.zoomable()){
28031             this.scale = this.startScale;
28032             return;
28033         }
28034         
28035         this.draw();
28036         
28037     },
28038     
28039     onTouchEnd : function(e)
28040     {
28041         this.pinching = false;
28042         this.dragable = false;
28043         
28044     },
28045     
28046     process : function(file, crop)
28047     {
28048         if(this.loadMask){
28049             this.maskEl.mask(this.loadingText);
28050         }
28051         
28052         this.xhr = new XMLHttpRequest();
28053         
28054         file.xhr = this.xhr;
28055
28056         this.xhr.open(this.method, this.url, true);
28057         
28058         var headers = {
28059             "Accept": "application/json",
28060             "Cache-Control": "no-cache",
28061             "X-Requested-With": "XMLHttpRequest"
28062         };
28063         
28064         for (var headerName in headers) {
28065             var headerValue = headers[headerName];
28066             if (headerValue) {
28067                 this.xhr.setRequestHeader(headerName, headerValue);
28068             }
28069         }
28070         
28071         var _this = this;
28072         
28073         this.xhr.onload = function()
28074         {
28075             _this.xhrOnLoad(_this.xhr);
28076         }
28077         
28078         this.xhr.onerror = function()
28079         {
28080             _this.xhrOnError(_this.xhr);
28081         }
28082         
28083         var formData = new FormData();
28084
28085         formData.append('returnHTML', 'NO');
28086         
28087         if(crop){
28088             formData.append('crop', crop);
28089         }
28090         
28091         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28092             formData.append(this.paramName, file, file.name);
28093         }
28094         
28095         if(typeof(file.filename) != 'undefined'){
28096             formData.append('filename', file.filename);
28097         }
28098         
28099         if(typeof(file.mimetype) != 'undefined'){
28100             formData.append('mimetype', file.mimetype);
28101         }
28102         
28103         if(this.fireEvent('arrange', this, formData) != false){
28104             this.xhr.send(formData);
28105         };
28106     },
28107     
28108     xhrOnLoad : function(xhr)
28109     {
28110         if(this.loadMask){
28111             this.maskEl.unmask();
28112         }
28113         
28114         if (xhr.readyState !== 4) {
28115             this.fireEvent('exception', this, xhr);
28116             return;
28117         }
28118
28119         var response = Roo.decode(xhr.responseText);
28120         
28121         if(!response.success){
28122             this.fireEvent('exception', this, xhr);
28123             return;
28124         }
28125         
28126         var response = Roo.decode(xhr.responseText);
28127         
28128         this.fireEvent('upload', this, response);
28129         
28130     },
28131     
28132     xhrOnError : function()
28133     {
28134         if(this.loadMask){
28135             this.maskEl.unmask();
28136         }
28137         
28138         Roo.log('xhr on error');
28139         
28140         var response = Roo.decode(xhr.responseText);
28141           
28142         Roo.log(response);
28143         
28144     },
28145     
28146     prepare : function(file)
28147     {   
28148         if(this.loadMask){
28149             this.maskEl.mask(this.loadingText);
28150         }
28151         
28152         this.file = false;
28153         this.exif = {};
28154         
28155         if(typeof(file) === 'string'){
28156             this.loadCanvas(file);
28157             return;
28158         }
28159         
28160         if(!file || !this.urlAPI){
28161             return;
28162         }
28163         
28164         this.file = file;
28165         this.cropType = file.type;
28166         
28167         var _this = this;
28168         
28169         if(this.fireEvent('prepare', this, this.file) != false){
28170             
28171             var reader = new FileReader();
28172             
28173             reader.onload = function (e) {
28174                 if (e.target.error) {
28175                     Roo.log(e.target.error);
28176                     return;
28177                 }
28178                 
28179                 var buffer = e.target.result,
28180                     dataView = new DataView(buffer),
28181                     offset = 2,
28182                     maxOffset = dataView.byteLength - 4,
28183                     markerBytes,
28184                     markerLength;
28185                 
28186                 if (dataView.getUint16(0) === 0xffd8) {
28187                     while (offset < maxOffset) {
28188                         markerBytes = dataView.getUint16(offset);
28189                         
28190                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28191                             markerLength = dataView.getUint16(offset + 2) + 2;
28192                             if (offset + markerLength > dataView.byteLength) {
28193                                 Roo.log('Invalid meta data: Invalid segment size.');
28194                                 break;
28195                             }
28196                             
28197                             if(markerBytes == 0xffe1){
28198                                 _this.parseExifData(
28199                                     dataView,
28200                                     offset,
28201                                     markerLength
28202                                 );
28203                             }
28204                             
28205                             offset += markerLength;
28206                             
28207                             continue;
28208                         }
28209                         
28210                         break;
28211                     }
28212                     
28213                 }
28214                 
28215                 var url = _this.urlAPI.createObjectURL(_this.file);
28216                 
28217                 _this.loadCanvas(url);
28218                 
28219                 return;
28220             }
28221             
28222             reader.readAsArrayBuffer(this.file);
28223             
28224         }
28225         
28226     },
28227     
28228     parseExifData : function(dataView, offset, length)
28229     {
28230         var tiffOffset = offset + 10,
28231             littleEndian,
28232             dirOffset;
28233     
28234         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28235             // No Exif data, might be XMP data instead
28236             return;
28237         }
28238         
28239         // Check for the ASCII code for "Exif" (0x45786966):
28240         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28241             // No Exif data, might be XMP data instead
28242             return;
28243         }
28244         if (tiffOffset + 8 > dataView.byteLength) {
28245             Roo.log('Invalid Exif data: Invalid segment size.');
28246             return;
28247         }
28248         // Check for the two null bytes:
28249         if (dataView.getUint16(offset + 8) !== 0x0000) {
28250             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28251             return;
28252         }
28253         // Check the byte alignment:
28254         switch (dataView.getUint16(tiffOffset)) {
28255         case 0x4949:
28256             littleEndian = true;
28257             break;
28258         case 0x4D4D:
28259             littleEndian = false;
28260             break;
28261         default:
28262             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28263             return;
28264         }
28265         // Check for the TIFF tag marker (0x002A):
28266         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28267             Roo.log('Invalid Exif data: Missing TIFF marker.');
28268             return;
28269         }
28270         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28271         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28272         
28273         this.parseExifTags(
28274             dataView,
28275             tiffOffset,
28276             tiffOffset + dirOffset,
28277             littleEndian
28278         );
28279     },
28280     
28281     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28282     {
28283         var tagsNumber,
28284             dirEndOffset,
28285             i;
28286         if (dirOffset + 6 > dataView.byteLength) {
28287             Roo.log('Invalid Exif data: Invalid directory offset.');
28288             return;
28289         }
28290         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28291         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28292         if (dirEndOffset + 4 > dataView.byteLength) {
28293             Roo.log('Invalid Exif data: Invalid directory size.');
28294             return;
28295         }
28296         for (i = 0; i < tagsNumber; i += 1) {
28297             this.parseExifTag(
28298                 dataView,
28299                 tiffOffset,
28300                 dirOffset + 2 + 12 * i, // tag offset
28301                 littleEndian
28302             );
28303         }
28304         // Return the offset to the next directory:
28305         return dataView.getUint32(dirEndOffset, littleEndian);
28306     },
28307     
28308     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28309     {
28310         var tag = dataView.getUint16(offset, littleEndian);
28311         
28312         this.exif[tag] = this.getExifValue(
28313             dataView,
28314             tiffOffset,
28315             offset,
28316             dataView.getUint16(offset + 2, littleEndian), // tag type
28317             dataView.getUint32(offset + 4, littleEndian), // tag length
28318             littleEndian
28319         );
28320     },
28321     
28322     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28323     {
28324         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28325             tagSize,
28326             dataOffset,
28327             values,
28328             i,
28329             str,
28330             c;
28331     
28332         if (!tagType) {
28333             Roo.log('Invalid Exif data: Invalid tag type.');
28334             return;
28335         }
28336         
28337         tagSize = tagType.size * length;
28338         // Determine if the value is contained in the dataOffset bytes,
28339         // or if the value at the dataOffset is a pointer to the actual data:
28340         dataOffset = tagSize > 4 ?
28341                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28342         if (dataOffset + tagSize > dataView.byteLength) {
28343             Roo.log('Invalid Exif data: Invalid data offset.');
28344             return;
28345         }
28346         if (length === 1) {
28347             return tagType.getValue(dataView, dataOffset, littleEndian);
28348         }
28349         values = [];
28350         for (i = 0; i < length; i += 1) {
28351             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28352         }
28353         
28354         if (tagType.ascii) {
28355             str = '';
28356             // Concatenate the chars:
28357             for (i = 0; i < values.length; i += 1) {
28358                 c = values[i];
28359                 // Ignore the terminating NULL byte(s):
28360                 if (c === '\u0000') {
28361                     break;
28362                 }
28363                 str += c;
28364             }
28365             return str;
28366         }
28367         return values;
28368     }
28369     
28370 });
28371
28372 Roo.apply(Roo.bootstrap.UploadCropbox, {
28373     tags : {
28374         'Orientation': 0x0112
28375     },
28376     
28377     Orientation: {
28378             1: 0, //'top-left',
28379 //            2: 'top-right',
28380             3: 180, //'bottom-right',
28381 //            4: 'bottom-left',
28382 //            5: 'left-top',
28383             6: 90, //'right-top',
28384 //            7: 'right-bottom',
28385             8: 270 //'left-bottom'
28386     },
28387     
28388     exifTagTypes : {
28389         // byte, 8-bit unsigned int:
28390         1: {
28391             getValue: function (dataView, dataOffset) {
28392                 return dataView.getUint8(dataOffset);
28393             },
28394             size: 1
28395         },
28396         // ascii, 8-bit byte:
28397         2: {
28398             getValue: function (dataView, dataOffset) {
28399                 return String.fromCharCode(dataView.getUint8(dataOffset));
28400             },
28401             size: 1,
28402             ascii: true
28403         },
28404         // short, 16 bit int:
28405         3: {
28406             getValue: function (dataView, dataOffset, littleEndian) {
28407                 return dataView.getUint16(dataOffset, littleEndian);
28408             },
28409             size: 2
28410         },
28411         // long, 32 bit int:
28412         4: {
28413             getValue: function (dataView, dataOffset, littleEndian) {
28414                 return dataView.getUint32(dataOffset, littleEndian);
28415             },
28416             size: 4
28417         },
28418         // rational = two long values, first is numerator, second is denominator:
28419         5: {
28420             getValue: function (dataView, dataOffset, littleEndian) {
28421                 return dataView.getUint32(dataOffset, littleEndian) /
28422                     dataView.getUint32(dataOffset + 4, littleEndian);
28423             },
28424             size: 8
28425         },
28426         // slong, 32 bit signed int:
28427         9: {
28428             getValue: function (dataView, dataOffset, littleEndian) {
28429                 return dataView.getInt32(dataOffset, littleEndian);
28430             },
28431             size: 4
28432         },
28433         // srational, two slongs, first is numerator, second is denominator:
28434         10: {
28435             getValue: function (dataView, dataOffset, littleEndian) {
28436                 return dataView.getInt32(dataOffset, littleEndian) /
28437                     dataView.getInt32(dataOffset + 4, littleEndian);
28438             },
28439             size: 8
28440         }
28441     },
28442     
28443     footer : {
28444         STANDARD : [
28445             {
28446                 tag : 'div',
28447                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28448                 action : 'rotate-left',
28449                 cn : [
28450                     {
28451                         tag : 'button',
28452                         cls : 'btn btn-default',
28453                         html : '<i class="fa fa-undo"></i>'
28454                     }
28455                 ]
28456             },
28457             {
28458                 tag : 'div',
28459                 cls : 'btn-group roo-upload-cropbox-picture',
28460                 action : 'picture',
28461                 cn : [
28462                     {
28463                         tag : 'button',
28464                         cls : 'btn btn-default',
28465                         html : '<i class="fa fa-picture-o"></i>'
28466                     }
28467                 ]
28468             },
28469             {
28470                 tag : 'div',
28471                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28472                 action : 'rotate-right',
28473                 cn : [
28474                     {
28475                         tag : 'button',
28476                         cls : 'btn btn-default',
28477                         html : '<i class="fa fa-repeat"></i>'
28478                     }
28479                 ]
28480             }
28481         ],
28482         DOCUMENT : [
28483             {
28484                 tag : 'div',
28485                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28486                 action : 'rotate-left',
28487                 cn : [
28488                     {
28489                         tag : 'button',
28490                         cls : 'btn btn-default',
28491                         html : '<i class="fa fa-undo"></i>'
28492                     }
28493                 ]
28494             },
28495             {
28496                 tag : 'div',
28497                 cls : 'btn-group roo-upload-cropbox-download',
28498                 action : 'download',
28499                 cn : [
28500                     {
28501                         tag : 'button',
28502                         cls : 'btn btn-default',
28503                         html : '<i class="fa fa-download"></i>'
28504                     }
28505                 ]
28506             },
28507             {
28508                 tag : 'div',
28509                 cls : 'btn-group roo-upload-cropbox-crop',
28510                 action : 'crop',
28511                 cn : [
28512                     {
28513                         tag : 'button',
28514                         cls : 'btn btn-default',
28515                         html : '<i class="fa fa-crop"></i>'
28516                     }
28517                 ]
28518             },
28519             {
28520                 tag : 'div',
28521                 cls : 'btn-group roo-upload-cropbox-trash',
28522                 action : 'trash',
28523                 cn : [
28524                     {
28525                         tag : 'button',
28526                         cls : 'btn btn-default',
28527                         html : '<i class="fa fa-trash"></i>'
28528                     }
28529                 ]
28530             },
28531             {
28532                 tag : 'div',
28533                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28534                 action : 'rotate-right',
28535                 cn : [
28536                     {
28537                         tag : 'button',
28538                         cls : 'btn btn-default',
28539                         html : '<i class="fa fa-repeat"></i>'
28540                     }
28541                 ]
28542             }
28543         ],
28544         ROTATOR : [
28545             {
28546                 tag : 'div',
28547                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28548                 action : 'rotate-left',
28549                 cn : [
28550                     {
28551                         tag : 'button',
28552                         cls : 'btn btn-default',
28553                         html : '<i class="fa fa-undo"></i>'
28554                     }
28555                 ]
28556             },
28557             {
28558                 tag : 'div',
28559                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28560                 action : 'rotate-right',
28561                 cn : [
28562                     {
28563                         tag : 'button',
28564                         cls : 'btn btn-default',
28565                         html : '<i class="fa fa-repeat"></i>'
28566                     }
28567                 ]
28568             }
28569         ]
28570     }
28571 });
28572
28573 /*
28574 * Licence: LGPL
28575 */
28576
28577 /**
28578  * @class Roo.bootstrap.DocumentManager
28579  * @extends Roo.bootstrap.Component
28580  * Bootstrap DocumentManager class
28581  * @cfg {String} paramName default 'imageUpload'
28582  * @cfg {String} toolTipName default 'filename'
28583  * @cfg {String} method default POST
28584  * @cfg {String} url action url
28585  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28586  * @cfg {Boolean} multiple multiple upload default true
28587  * @cfg {Number} thumbSize default 300
28588  * @cfg {String} fieldLabel
28589  * @cfg {Number} labelWidth default 4
28590  * @cfg {String} labelAlign (left|top) default left
28591  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28592 * @cfg {Number} labellg set the width of label (1-12)
28593  * @cfg {Number} labelmd set the width of label (1-12)
28594  * @cfg {Number} labelsm set the width of label (1-12)
28595  * @cfg {Number} labelxs set the width of label (1-12)
28596  * 
28597  * @constructor
28598  * Create a new DocumentManager
28599  * @param {Object} config The config object
28600  */
28601
28602 Roo.bootstrap.DocumentManager = function(config){
28603     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28604     
28605     this.files = [];
28606     this.delegates = [];
28607     
28608     this.addEvents({
28609         /**
28610          * @event initial
28611          * Fire when initial the DocumentManager
28612          * @param {Roo.bootstrap.DocumentManager} this
28613          */
28614         "initial" : true,
28615         /**
28616          * @event inspect
28617          * inspect selected file
28618          * @param {Roo.bootstrap.DocumentManager} this
28619          * @param {File} file
28620          */
28621         "inspect" : true,
28622         /**
28623          * @event exception
28624          * Fire when xhr load exception
28625          * @param {Roo.bootstrap.DocumentManager} this
28626          * @param {XMLHttpRequest} xhr
28627          */
28628         "exception" : true,
28629         /**
28630          * @event afterupload
28631          * Fire when xhr load exception
28632          * @param {Roo.bootstrap.DocumentManager} this
28633          * @param {XMLHttpRequest} xhr
28634          */
28635         "afterupload" : true,
28636         /**
28637          * @event prepare
28638          * prepare the form data
28639          * @param {Roo.bootstrap.DocumentManager} this
28640          * @param {Object} formData
28641          */
28642         "prepare" : true,
28643         /**
28644          * @event remove
28645          * Fire when remove the file
28646          * @param {Roo.bootstrap.DocumentManager} this
28647          * @param {Object} file
28648          */
28649         "remove" : true,
28650         /**
28651          * @event refresh
28652          * Fire after refresh the file
28653          * @param {Roo.bootstrap.DocumentManager} this
28654          */
28655         "refresh" : true,
28656         /**
28657          * @event click
28658          * Fire after click the image
28659          * @param {Roo.bootstrap.DocumentManager} this
28660          * @param {Object} file
28661          */
28662         "click" : true,
28663         /**
28664          * @event edit
28665          * Fire when upload a image and editable set to true
28666          * @param {Roo.bootstrap.DocumentManager} this
28667          * @param {Object} file
28668          */
28669         "edit" : true,
28670         /**
28671          * @event beforeselectfile
28672          * Fire before select file
28673          * @param {Roo.bootstrap.DocumentManager} this
28674          */
28675         "beforeselectfile" : true,
28676         /**
28677          * @event process
28678          * Fire before process file
28679          * @param {Roo.bootstrap.DocumentManager} this
28680          * @param {Object} file
28681          */
28682         "process" : true,
28683         /**
28684          * @event previewrendered
28685          * Fire when preview rendered
28686          * @param {Roo.bootstrap.DocumentManager} this
28687          * @param {Object} file
28688          */
28689         "previewrendered" : true
28690         
28691     });
28692 };
28693
28694 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28695     
28696     boxes : 0,
28697     inputName : '',
28698     thumbSize : 300,
28699     multiple : true,
28700     files : false,
28701     method : 'POST',
28702     url : '',
28703     paramName : 'imageUpload',
28704     toolTipName : 'filename',
28705     fieldLabel : '',
28706     labelWidth : 4,
28707     labelAlign : 'left',
28708     editable : true,
28709     delegates : false,
28710     xhr : false, 
28711     
28712     labellg : 0,
28713     labelmd : 0,
28714     labelsm : 0,
28715     labelxs : 0,
28716     
28717     getAutoCreate : function()
28718     {   
28719         var managerWidget = {
28720             tag : 'div',
28721             cls : 'roo-document-manager',
28722             cn : [
28723                 {
28724                     tag : 'input',
28725                     cls : 'roo-document-manager-selector',
28726                     type : 'file'
28727                 },
28728                 {
28729                     tag : 'div',
28730                     cls : 'roo-document-manager-uploader',
28731                     cn : [
28732                         {
28733                             tag : 'div',
28734                             cls : 'roo-document-manager-upload-btn',
28735                             html : '<i class="fa fa-plus"></i>'
28736                         }
28737                     ]
28738                     
28739                 }
28740             ]
28741         };
28742         
28743         var content = [
28744             {
28745                 tag : 'div',
28746                 cls : 'column col-md-12',
28747                 cn : managerWidget
28748             }
28749         ];
28750         
28751         if(this.fieldLabel.length){
28752             
28753             content = [
28754                 {
28755                     tag : 'div',
28756                     cls : 'column col-md-12',
28757                     html : this.fieldLabel
28758                 },
28759                 {
28760                     tag : 'div',
28761                     cls : 'column col-md-12',
28762                     cn : managerWidget
28763                 }
28764             ];
28765
28766             if(this.labelAlign == 'left'){
28767                 content = [
28768                     {
28769                         tag : 'div',
28770                         cls : 'column',
28771                         html : this.fieldLabel
28772                     },
28773                     {
28774                         tag : 'div',
28775                         cls : 'column',
28776                         cn : managerWidget
28777                     }
28778                 ];
28779                 
28780                 if(this.labelWidth > 12){
28781                     content[0].style = "width: " + this.labelWidth + 'px';
28782                 }
28783
28784                 if(this.labelWidth < 13 && this.labelmd == 0){
28785                     this.labelmd = this.labelWidth;
28786                 }
28787
28788                 if(this.labellg > 0){
28789                     content[0].cls += ' col-lg-' + this.labellg;
28790                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28791                 }
28792
28793                 if(this.labelmd > 0){
28794                     content[0].cls += ' col-md-' + this.labelmd;
28795                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28796                 }
28797
28798                 if(this.labelsm > 0){
28799                     content[0].cls += ' col-sm-' + this.labelsm;
28800                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28801                 }
28802
28803                 if(this.labelxs > 0){
28804                     content[0].cls += ' col-xs-' + this.labelxs;
28805                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28806                 }
28807                 
28808             }
28809         }
28810         
28811         var cfg = {
28812             tag : 'div',
28813             cls : 'row clearfix',
28814             cn : content
28815         };
28816         
28817         return cfg;
28818         
28819     },
28820     
28821     initEvents : function()
28822     {
28823         this.managerEl = this.el.select('.roo-document-manager', true).first();
28824         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28825         
28826         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28827         this.selectorEl.hide();
28828         
28829         if(this.multiple){
28830             this.selectorEl.attr('multiple', 'multiple');
28831         }
28832         
28833         this.selectorEl.on('change', this.onFileSelected, this);
28834         
28835         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28836         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28837         
28838         this.uploader.on('click', this.onUploaderClick, this);
28839         
28840         this.renderProgressDialog();
28841         
28842         var _this = this;
28843         
28844         window.addEventListener("resize", function() { _this.refresh(); } );
28845         
28846         this.fireEvent('initial', this);
28847     },
28848     
28849     renderProgressDialog : function()
28850     {
28851         var _this = this;
28852         
28853         this.progressDialog = new Roo.bootstrap.Modal({
28854             cls : 'roo-document-manager-progress-dialog',
28855             allow_close : false,
28856             title : '',
28857             buttons : [
28858                 {
28859                     name  :'cancel',
28860                     weight : 'danger',
28861                     html : 'Cancel'
28862                 }
28863             ], 
28864             listeners : { 
28865                 btnclick : function() {
28866                     _this.uploadCancel();
28867                     this.hide();
28868                 }
28869             }
28870         });
28871          
28872         this.progressDialog.render(Roo.get(document.body));
28873          
28874         this.progress = new Roo.bootstrap.Progress({
28875             cls : 'roo-document-manager-progress',
28876             active : true,
28877             striped : true
28878         });
28879         
28880         this.progress.render(this.progressDialog.getChildContainer());
28881         
28882         this.progressBar = new Roo.bootstrap.ProgressBar({
28883             cls : 'roo-document-manager-progress-bar',
28884             aria_valuenow : 0,
28885             aria_valuemin : 0,
28886             aria_valuemax : 12,
28887             panel : 'success'
28888         });
28889         
28890         this.progressBar.render(this.progress.getChildContainer());
28891     },
28892     
28893     onUploaderClick : function(e)
28894     {
28895         e.preventDefault();
28896      
28897         if(this.fireEvent('beforeselectfile', this) != false){
28898             this.selectorEl.dom.click();
28899         }
28900         
28901     },
28902     
28903     onFileSelected : function(e)
28904     {
28905         e.preventDefault();
28906         
28907         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28908             return;
28909         }
28910         
28911         Roo.each(this.selectorEl.dom.files, function(file){
28912             if(this.fireEvent('inspect', this, file) != false){
28913                 this.files.push(file);
28914             }
28915         }, this);
28916         
28917         this.queue();
28918         
28919     },
28920     
28921     queue : function()
28922     {
28923         this.selectorEl.dom.value = '';
28924         
28925         if(!this.files || !this.files.length){
28926             return;
28927         }
28928         
28929         if(this.boxes > 0 && this.files.length > this.boxes){
28930             this.files = this.files.slice(0, this.boxes);
28931         }
28932         
28933         this.uploader.show();
28934         
28935         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28936             this.uploader.hide();
28937         }
28938         
28939         var _this = this;
28940         
28941         var files = [];
28942         
28943         var docs = [];
28944         
28945         Roo.each(this.files, function(file){
28946             
28947             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28948                 var f = this.renderPreview(file);
28949                 files.push(f);
28950                 return;
28951             }
28952             
28953             if(file.type.indexOf('image') != -1){
28954                 this.delegates.push(
28955                     (function(){
28956                         _this.process(file);
28957                     }).createDelegate(this)
28958                 );
28959         
28960                 return;
28961             }
28962             
28963             docs.push(
28964                 (function(){
28965                     _this.process(file);
28966                 }).createDelegate(this)
28967             );
28968             
28969         }, this);
28970         
28971         this.files = files;
28972         
28973         this.delegates = this.delegates.concat(docs);
28974         
28975         if(!this.delegates.length){
28976             this.refresh();
28977             return;
28978         }
28979         
28980         this.progressBar.aria_valuemax = this.delegates.length;
28981         
28982         this.arrange();
28983         
28984         return;
28985     },
28986     
28987     arrange : function()
28988     {
28989         if(!this.delegates.length){
28990             this.progressDialog.hide();
28991             this.refresh();
28992             return;
28993         }
28994         
28995         var delegate = this.delegates.shift();
28996         
28997         this.progressDialog.show();
28998         
28999         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29000         
29001         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29002         
29003         delegate();
29004     },
29005     
29006     refresh : function()
29007     {
29008         this.uploader.show();
29009         
29010         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29011             this.uploader.hide();
29012         }
29013         
29014         Roo.isTouch ? this.closable(false) : this.closable(true);
29015         
29016         this.fireEvent('refresh', this);
29017     },
29018     
29019     onRemove : function(e, el, o)
29020     {
29021         e.preventDefault();
29022         
29023         this.fireEvent('remove', this, o);
29024         
29025     },
29026     
29027     remove : function(o)
29028     {
29029         var files = [];
29030         
29031         Roo.each(this.files, function(file){
29032             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29033                 files.push(file);
29034                 return;
29035             }
29036
29037             o.target.remove();
29038
29039         }, this);
29040         
29041         this.files = files;
29042         
29043         this.refresh();
29044     },
29045     
29046     clear : function()
29047     {
29048         Roo.each(this.files, function(file){
29049             if(!file.target){
29050                 return;
29051             }
29052             
29053             file.target.remove();
29054
29055         }, this);
29056         
29057         this.files = [];
29058         
29059         this.refresh();
29060     },
29061     
29062     onClick : function(e, el, o)
29063     {
29064         e.preventDefault();
29065         
29066         this.fireEvent('click', this, o);
29067         
29068     },
29069     
29070     closable : function(closable)
29071     {
29072         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29073             
29074             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29075             
29076             if(closable){
29077                 el.show();
29078                 return;
29079             }
29080             
29081             el.hide();
29082             
29083         }, this);
29084     },
29085     
29086     xhrOnLoad : function(xhr)
29087     {
29088         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29089             el.remove();
29090         }, this);
29091         
29092         if (xhr.readyState !== 4) {
29093             this.arrange();
29094             this.fireEvent('exception', this, xhr);
29095             return;
29096         }
29097
29098         var response = Roo.decode(xhr.responseText);
29099         
29100         if(!response.success){
29101             this.arrange();
29102             this.fireEvent('exception', this, xhr);
29103             return;
29104         }
29105         
29106         var file = this.renderPreview(response.data);
29107         
29108         this.files.push(file);
29109         
29110         this.arrange();
29111         
29112         this.fireEvent('afterupload', this, xhr);
29113         
29114     },
29115     
29116     xhrOnError : function(xhr)
29117     {
29118         Roo.log('xhr on error');
29119         
29120         var response = Roo.decode(xhr.responseText);
29121           
29122         Roo.log(response);
29123         
29124         this.arrange();
29125     },
29126     
29127     process : function(file)
29128     {
29129         if(this.fireEvent('process', this, file) !== false){
29130             if(this.editable && file.type.indexOf('image') != -1){
29131                 this.fireEvent('edit', this, file);
29132                 return;
29133             }
29134
29135             this.uploadStart(file, false);
29136
29137             return;
29138         }
29139         
29140     },
29141     
29142     uploadStart : function(file, crop)
29143     {
29144         this.xhr = new XMLHttpRequest();
29145         
29146         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29147             this.arrange();
29148             return;
29149         }
29150         
29151         file.xhr = this.xhr;
29152             
29153         this.managerEl.createChild({
29154             tag : 'div',
29155             cls : 'roo-document-manager-loading',
29156             cn : [
29157                 {
29158                     tag : 'div',
29159                     tooltip : file.name,
29160                     cls : 'roo-document-manager-thumb',
29161                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29162                 }
29163             ]
29164
29165         });
29166
29167         this.xhr.open(this.method, this.url, true);
29168         
29169         var headers = {
29170             "Accept": "application/json",
29171             "Cache-Control": "no-cache",
29172             "X-Requested-With": "XMLHttpRequest"
29173         };
29174         
29175         for (var headerName in headers) {
29176             var headerValue = headers[headerName];
29177             if (headerValue) {
29178                 this.xhr.setRequestHeader(headerName, headerValue);
29179             }
29180         }
29181         
29182         var _this = this;
29183         
29184         this.xhr.onload = function()
29185         {
29186             _this.xhrOnLoad(_this.xhr);
29187         }
29188         
29189         this.xhr.onerror = function()
29190         {
29191             _this.xhrOnError(_this.xhr);
29192         }
29193         
29194         var formData = new FormData();
29195
29196         formData.append('returnHTML', 'NO');
29197         
29198         if(crop){
29199             formData.append('crop', crop);
29200         }
29201         
29202         formData.append(this.paramName, file, file.name);
29203         
29204         var options = {
29205             file : file, 
29206             manually : false
29207         };
29208         
29209         if(this.fireEvent('prepare', this, formData, options) != false){
29210             
29211             if(options.manually){
29212                 return;
29213             }
29214             
29215             this.xhr.send(formData);
29216             return;
29217         };
29218         
29219         this.uploadCancel();
29220     },
29221     
29222     uploadCancel : function()
29223     {
29224         if (this.xhr) {
29225             this.xhr.abort();
29226         }
29227         
29228         this.delegates = [];
29229         
29230         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29231             el.remove();
29232         }, this);
29233         
29234         this.arrange();
29235     },
29236     
29237     renderPreview : function(file)
29238     {
29239         if(typeof(file.target) != 'undefined' && file.target){
29240             return file;
29241         }
29242         
29243         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29244         
29245         var previewEl = this.managerEl.createChild({
29246             tag : 'div',
29247             cls : 'roo-document-manager-preview',
29248             cn : [
29249                 {
29250                     tag : 'div',
29251                     tooltip : file[this.toolTipName],
29252                     cls : 'roo-document-manager-thumb',
29253                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29254                 },
29255                 {
29256                     tag : 'button',
29257                     cls : 'close',
29258                     html : '<i class="fa fa-times-circle"></i>'
29259                 }
29260             ]
29261         });
29262
29263         var close = previewEl.select('button.close', true).first();
29264
29265         close.on('click', this.onRemove, this, file);
29266
29267         file.target = previewEl;
29268
29269         var image = previewEl.select('img', true).first();
29270         
29271         var _this = this;
29272         
29273         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29274         
29275         image.on('click', this.onClick, this, file);
29276         
29277         this.fireEvent('previewrendered', this, file);
29278         
29279         return file;
29280         
29281     },
29282     
29283     onPreviewLoad : function(file, image)
29284     {
29285         if(typeof(file.target) == 'undefined' || !file.target){
29286             return;
29287         }
29288         
29289         var width = image.dom.naturalWidth || image.dom.width;
29290         var height = image.dom.naturalHeight || image.dom.height;
29291         
29292         if(width > height){
29293             file.target.addClass('wide');
29294             return;
29295         }
29296         
29297         file.target.addClass('tall');
29298         return;
29299         
29300     },
29301     
29302     uploadFromSource : function(file, crop)
29303     {
29304         this.xhr = new XMLHttpRequest();
29305         
29306         this.managerEl.createChild({
29307             tag : 'div',
29308             cls : 'roo-document-manager-loading',
29309             cn : [
29310                 {
29311                     tag : 'div',
29312                     tooltip : file.name,
29313                     cls : 'roo-document-manager-thumb',
29314                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29315                 }
29316             ]
29317
29318         });
29319
29320         this.xhr.open(this.method, this.url, true);
29321         
29322         var headers = {
29323             "Accept": "application/json",
29324             "Cache-Control": "no-cache",
29325             "X-Requested-With": "XMLHttpRequest"
29326         };
29327         
29328         for (var headerName in headers) {
29329             var headerValue = headers[headerName];
29330             if (headerValue) {
29331                 this.xhr.setRequestHeader(headerName, headerValue);
29332             }
29333         }
29334         
29335         var _this = this;
29336         
29337         this.xhr.onload = function()
29338         {
29339             _this.xhrOnLoad(_this.xhr);
29340         }
29341         
29342         this.xhr.onerror = function()
29343         {
29344             _this.xhrOnError(_this.xhr);
29345         }
29346         
29347         var formData = new FormData();
29348
29349         formData.append('returnHTML', 'NO');
29350         
29351         formData.append('crop', crop);
29352         
29353         if(typeof(file.filename) != 'undefined'){
29354             formData.append('filename', file.filename);
29355         }
29356         
29357         if(typeof(file.mimetype) != 'undefined'){
29358             formData.append('mimetype', file.mimetype);
29359         }
29360         
29361         Roo.log(formData);
29362         
29363         if(this.fireEvent('prepare', this, formData) != false){
29364             this.xhr.send(formData);
29365         };
29366     }
29367 });
29368
29369 /*
29370 * Licence: LGPL
29371 */
29372
29373 /**
29374  * @class Roo.bootstrap.DocumentViewer
29375  * @extends Roo.bootstrap.Component
29376  * Bootstrap DocumentViewer class
29377  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29378  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29379  * 
29380  * @constructor
29381  * Create a new DocumentViewer
29382  * @param {Object} config The config object
29383  */
29384
29385 Roo.bootstrap.DocumentViewer = function(config){
29386     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29387     
29388     this.addEvents({
29389         /**
29390          * @event initial
29391          * Fire after initEvent
29392          * @param {Roo.bootstrap.DocumentViewer} this
29393          */
29394         "initial" : true,
29395         /**
29396          * @event click
29397          * Fire after click
29398          * @param {Roo.bootstrap.DocumentViewer} this
29399          */
29400         "click" : true,
29401         /**
29402          * @event download
29403          * Fire after download button
29404          * @param {Roo.bootstrap.DocumentViewer} this
29405          */
29406         "download" : true,
29407         /**
29408          * @event trash
29409          * Fire after trash button
29410          * @param {Roo.bootstrap.DocumentViewer} this
29411          */
29412         "trash" : true
29413         
29414     });
29415 };
29416
29417 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29418     
29419     showDownload : true,
29420     
29421     showTrash : true,
29422     
29423     getAutoCreate : function()
29424     {
29425         var cfg = {
29426             tag : 'div',
29427             cls : 'roo-document-viewer',
29428             cn : [
29429                 {
29430                     tag : 'div',
29431                     cls : 'roo-document-viewer-body',
29432                     cn : [
29433                         {
29434                             tag : 'div',
29435                             cls : 'roo-document-viewer-thumb',
29436                             cn : [
29437                                 {
29438                                     tag : 'img',
29439                                     cls : 'roo-document-viewer-image'
29440                                 }
29441                             ]
29442                         }
29443                     ]
29444                 },
29445                 {
29446                     tag : 'div',
29447                     cls : 'roo-document-viewer-footer',
29448                     cn : {
29449                         tag : 'div',
29450                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29451                         cn : [
29452                             {
29453                                 tag : 'div',
29454                                 cls : 'btn-group roo-document-viewer-download',
29455                                 cn : [
29456                                     {
29457                                         tag : 'button',
29458                                         cls : 'btn btn-default',
29459                                         html : '<i class="fa fa-download"></i>'
29460                                     }
29461                                 ]
29462                             },
29463                             {
29464                                 tag : 'div',
29465                                 cls : 'btn-group roo-document-viewer-trash',
29466                                 cn : [
29467                                     {
29468                                         tag : 'button',
29469                                         cls : 'btn btn-default',
29470                                         html : '<i class="fa fa-trash"></i>'
29471                                     }
29472                                 ]
29473                             }
29474                         ]
29475                     }
29476                 }
29477             ]
29478         };
29479         
29480         return cfg;
29481     },
29482     
29483     initEvents : function()
29484     {
29485         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29486         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29487         
29488         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29489         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29490         
29491         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29492         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29493         
29494         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29495         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29496         
29497         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29498         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29499         
29500         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29501         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29502         
29503         this.bodyEl.on('click', this.onClick, this);
29504         this.downloadBtn.on('click', this.onDownload, this);
29505         this.trashBtn.on('click', this.onTrash, this);
29506         
29507         this.downloadBtn.hide();
29508         this.trashBtn.hide();
29509         
29510         if(this.showDownload){
29511             this.downloadBtn.show();
29512         }
29513         
29514         if(this.showTrash){
29515             this.trashBtn.show();
29516         }
29517         
29518         if(!this.showDownload && !this.showTrash) {
29519             this.footerEl.hide();
29520         }
29521         
29522     },
29523     
29524     initial : function()
29525     {
29526         this.fireEvent('initial', this);
29527         
29528     },
29529     
29530     onClick : function(e)
29531     {
29532         e.preventDefault();
29533         
29534         this.fireEvent('click', this);
29535     },
29536     
29537     onDownload : function(e)
29538     {
29539         e.preventDefault();
29540         
29541         this.fireEvent('download', this);
29542     },
29543     
29544     onTrash : function(e)
29545     {
29546         e.preventDefault();
29547         
29548         this.fireEvent('trash', this);
29549     }
29550     
29551 });
29552 /*
29553  * - LGPL
29554  *
29555  * nav progress bar
29556  * 
29557  */
29558
29559 /**
29560  * @class Roo.bootstrap.NavProgressBar
29561  * @extends Roo.bootstrap.Component
29562  * Bootstrap NavProgressBar class
29563  * 
29564  * @constructor
29565  * Create a new nav progress bar
29566  * @param {Object} config The config object
29567  */
29568
29569 Roo.bootstrap.NavProgressBar = function(config){
29570     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29571
29572     this.bullets = this.bullets || [];
29573    
29574 //    Roo.bootstrap.NavProgressBar.register(this);
29575      this.addEvents({
29576         /**
29577              * @event changed
29578              * Fires when the active item changes
29579              * @param {Roo.bootstrap.NavProgressBar} this
29580              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29581              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29582          */
29583         'changed': true
29584      });
29585     
29586 };
29587
29588 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29589     
29590     bullets : [],
29591     barItems : [],
29592     
29593     getAutoCreate : function()
29594     {
29595         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29596         
29597         cfg = {
29598             tag : 'div',
29599             cls : 'roo-navigation-bar-group',
29600             cn : [
29601                 {
29602                     tag : 'div',
29603                     cls : 'roo-navigation-top-bar'
29604                 },
29605                 {
29606                     tag : 'div',
29607                     cls : 'roo-navigation-bullets-bar',
29608                     cn : [
29609                         {
29610                             tag : 'ul',
29611                             cls : 'roo-navigation-bar'
29612                         }
29613                     ]
29614                 },
29615                 
29616                 {
29617                     tag : 'div',
29618                     cls : 'roo-navigation-bottom-bar'
29619                 }
29620             ]
29621             
29622         };
29623         
29624         return cfg;
29625         
29626     },
29627     
29628     initEvents: function() 
29629     {
29630         
29631     },
29632     
29633     onRender : function(ct, position) 
29634     {
29635         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29636         
29637         if(this.bullets.length){
29638             Roo.each(this.bullets, function(b){
29639                this.addItem(b);
29640             }, this);
29641         }
29642         
29643         this.format();
29644         
29645     },
29646     
29647     addItem : function(cfg)
29648     {
29649         var item = new Roo.bootstrap.NavProgressItem(cfg);
29650         
29651         item.parentId = this.id;
29652         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29653         
29654         if(cfg.html){
29655             var top = new Roo.bootstrap.Element({
29656                 tag : 'div',
29657                 cls : 'roo-navigation-bar-text'
29658             });
29659             
29660             var bottom = new Roo.bootstrap.Element({
29661                 tag : 'div',
29662                 cls : 'roo-navigation-bar-text'
29663             });
29664             
29665             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29666             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29667             
29668             var topText = new Roo.bootstrap.Element({
29669                 tag : 'span',
29670                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29671             });
29672             
29673             var bottomText = new Roo.bootstrap.Element({
29674                 tag : 'span',
29675                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29676             });
29677             
29678             topText.onRender(top.el, null);
29679             bottomText.onRender(bottom.el, null);
29680             
29681             item.topEl = top;
29682             item.bottomEl = bottom;
29683         }
29684         
29685         this.barItems.push(item);
29686         
29687         return item;
29688     },
29689     
29690     getActive : function()
29691     {
29692         var active = false;
29693         
29694         Roo.each(this.barItems, function(v){
29695             
29696             if (!v.isActive()) {
29697                 return;
29698             }
29699             
29700             active = v;
29701             return false;
29702             
29703         });
29704         
29705         return active;
29706     },
29707     
29708     setActiveItem : function(item)
29709     {
29710         var prev = false;
29711         
29712         Roo.each(this.barItems, function(v){
29713             if (v.rid == item.rid) {
29714                 return ;
29715             }
29716             
29717             if (v.isActive()) {
29718                 v.setActive(false);
29719                 prev = v;
29720             }
29721         });
29722
29723         item.setActive(true);
29724         
29725         this.fireEvent('changed', this, item, prev);
29726     },
29727     
29728     getBarItem: function(rid)
29729     {
29730         var ret = false;
29731         
29732         Roo.each(this.barItems, function(e) {
29733             if (e.rid != rid) {
29734                 return;
29735             }
29736             
29737             ret =  e;
29738             return false;
29739         });
29740         
29741         return ret;
29742     },
29743     
29744     indexOfItem : function(item)
29745     {
29746         var index = false;
29747         
29748         Roo.each(this.barItems, function(v, i){
29749             
29750             if (v.rid != item.rid) {
29751                 return;
29752             }
29753             
29754             index = i;
29755             return false
29756         });
29757         
29758         return index;
29759     },
29760     
29761     setActiveNext : function()
29762     {
29763         var i = this.indexOfItem(this.getActive());
29764         
29765         if (i > this.barItems.length) {
29766             return;
29767         }
29768         
29769         this.setActiveItem(this.barItems[i+1]);
29770     },
29771     
29772     setActivePrev : function()
29773     {
29774         var i = this.indexOfItem(this.getActive());
29775         
29776         if (i  < 1) {
29777             return;
29778         }
29779         
29780         this.setActiveItem(this.barItems[i-1]);
29781     },
29782     
29783     format : function()
29784     {
29785         if(!this.barItems.length){
29786             return;
29787         }
29788      
29789         var width = 100 / this.barItems.length;
29790         
29791         Roo.each(this.barItems, function(i){
29792             i.el.setStyle('width', width + '%');
29793             i.topEl.el.setStyle('width', width + '%');
29794             i.bottomEl.el.setStyle('width', width + '%');
29795         }, this);
29796         
29797     }
29798     
29799 });
29800 /*
29801  * - LGPL
29802  *
29803  * Nav Progress Item
29804  * 
29805  */
29806
29807 /**
29808  * @class Roo.bootstrap.NavProgressItem
29809  * @extends Roo.bootstrap.Component
29810  * Bootstrap NavProgressItem class
29811  * @cfg {String} rid the reference id
29812  * @cfg {Boolean} active (true|false) Is item active default false
29813  * @cfg {Boolean} disabled (true|false) Is item active default false
29814  * @cfg {String} html
29815  * @cfg {String} position (top|bottom) text position default bottom
29816  * @cfg {String} icon show icon instead of number
29817  * 
29818  * @constructor
29819  * Create a new NavProgressItem
29820  * @param {Object} config The config object
29821  */
29822 Roo.bootstrap.NavProgressItem = function(config){
29823     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29824     this.addEvents({
29825         // raw events
29826         /**
29827          * @event click
29828          * The raw click event for the entire grid.
29829          * @param {Roo.bootstrap.NavProgressItem} this
29830          * @param {Roo.EventObject} e
29831          */
29832         "click" : true
29833     });
29834    
29835 };
29836
29837 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29838     
29839     rid : '',
29840     active : false,
29841     disabled : false,
29842     html : '',
29843     position : 'bottom',
29844     icon : false,
29845     
29846     getAutoCreate : function()
29847     {
29848         var iconCls = 'roo-navigation-bar-item-icon';
29849         
29850         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29851         
29852         var cfg = {
29853             tag: 'li',
29854             cls: 'roo-navigation-bar-item',
29855             cn : [
29856                 {
29857                     tag : 'i',
29858                     cls : iconCls
29859                 }
29860             ]
29861         };
29862         
29863         if(this.active){
29864             cfg.cls += ' active';
29865         }
29866         if(this.disabled){
29867             cfg.cls += ' disabled';
29868         }
29869         
29870         return cfg;
29871     },
29872     
29873     disable : function()
29874     {
29875         this.setDisabled(true);
29876     },
29877     
29878     enable : function()
29879     {
29880         this.setDisabled(false);
29881     },
29882     
29883     initEvents: function() 
29884     {
29885         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29886         
29887         this.iconEl.on('click', this.onClick, this);
29888     },
29889     
29890     onClick : function(e)
29891     {
29892         e.preventDefault();
29893         
29894         if(this.disabled){
29895             return;
29896         }
29897         
29898         if(this.fireEvent('click', this, e) === false){
29899             return;
29900         };
29901         
29902         this.parent().setActiveItem(this);
29903     },
29904     
29905     isActive: function () 
29906     {
29907         return this.active;
29908     },
29909     
29910     setActive : function(state)
29911     {
29912         if(this.active == state){
29913             return;
29914         }
29915         
29916         this.active = state;
29917         
29918         if (state) {
29919             this.el.addClass('active');
29920             return;
29921         }
29922         
29923         this.el.removeClass('active');
29924         
29925         return;
29926     },
29927     
29928     setDisabled : function(state)
29929     {
29930         if(this.disabled == state){
29931             return;
29932         }
29933         
29934         this.disabled = state;
29935         
29936         if (state) {
29937             this.el.addClass('disabled');
29938             return;
29939         }
29940         
29941         this.el.removeClass('disabled');
29942     },
29943     
29944     tooltipEl : function()
29945     {
29946         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29947     }
29948 });
29949  
29950
29951  /*
29952  * - LGPL
29953  *
29954  * FieldLabel
29955  * 
29956  */
29957
29958 /**
29959  * @class Roo.bootstrap.FieldLabel
29960  * @extends Roo.bootstrap.Component
29961  * Bootstrap FieldLabel class
29962  * @cfg {String} html contents of the element
29963  * @cfg {String} tag tag of the element default label
29964  * @cfg {String} cls class of the element
29965  * @cfg {String} target label target 
29966  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29967  * @cfg {String} invalidClass default "text-warning"
29968  * @cfg {String} validClass default "text-success"
29969  * @cfg {String} iconTooltip default "This field is required"
29970  * @cfg {String} indicatorpos (left|right) default left
29971  * 
29972  * @constructor
29973  * Create a new FieldLabel
29974  * @param {Object} config The config object
29975  */
29976
29977 Roo.bootstrap.FieldLabel = function(config){
29978     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29979     
29980     this.addEvents({
29981             /**
29982              * @event invalid
29983              * Fires after the field has been marked as invalid.
29984              * @param {Roo.form.FieldLabel} this
29985              * @param {String} msg The validation message
29986              */
29987             invalid : true,
29988             /**
29989              * @event valid
29990              * Fires after the field has been validated with no errors.
29991              * @param {Roo.form.FieldLabel} this
29992              */
29993             valid : true
29994         });
29995 };
29996
29997 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29998     
29999     tag: 'label',
30000     cls: '',
30001     html: '',
30002     target: '',
30003     allowBlank : true,
30004     invalidClass : 'has-warning',
30005     validClass : 'has-success',
30006     iconTooltip : 'This field is required',
30007     indicatorpos : 'left',
30008     
30009     getAutoCreate : function(){
30010         
30011         var cls = "";
30012         if (!this.allowBlank) {
30013             cls  = "visible";
30014         }
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 ' + cls,
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 '+ cls,
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 {String} thousandsDelimiter Symbol of thousandsDelimiter
33232      */
33233     thousandsDelimiter : false,
33234     /**
33235      * @cfg {String} valueAlign alignment of value
33236      */
33237     valueAlign : "left",
33238
33239     getAutoCreate : function()
33240     {
33241         var hiddenInput = {
33242             tag: 'input',
33243             type: 'hidden',
33244             id: Roo.id(),
33245             cls: 'hidden-number-input'
33246         };
33247         
33248         if (this.name) {
33249             hiddenInput.name = this.name;
33250         }
33251         
33252         this.name = '';
33253         
33254         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33255         
33256         this.name = hiddenInput.name;
33257         
33258         if(cfg.cn.length > 0) {
33259             cfg.cn.push(hiddenInput);
33260         }
33261         
33262         return cfg;
33263     },
33264
33265     // private
33266     initEvents : function()
33267     {   
33268         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33269         
33270         var allowed = "0123456789";
33271         
33272         if(this.allowDecimals){
33273             allowed += this.decimalSeparator;
33274         }
33275         
33276         if(this.allowNegative){
33277             allowed += "-";
33278         }
33279         
33280         if(this.thousandsDelimiter) {
33281             allowed += ",";
33282         }
33283         
33284         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33285         
33286         var keyPress = function(e){
33287             
33288             var k = e.getKey();
33289             
33290             var c = e.getCharCode();
33291             
33292             if(
33293                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33294                     allowed.indexOf(String.fromCharCode(c)) === -1
33295             ){
33296                 e.stopEvent();
33297                 return;
33298             }
33299             
33300             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33301                 return;
33302             }
33303             
33304             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33305                 e.stopEvent();
33306             }
33307         };
33308         
33309         this.el.on("keypress", keyPress, this);
33310     },
33311     
33312     validateValue : function(value)
33313     {
33314         
33315         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33316             return false;
33317         }
33318         
33319         var num = this.parseValue(value);
33320         
33321         if(isNaN(num)){
33322             this.markInvalid(String.format(this.nanText, value));
33323             return false;
33324         }
33325         
33326         if(num < this.minValue){
33327             this.markInvalid(String.format(this.minText, this.minValue));
33328             return false;
33329         }
33330         
33331         if(num > this.maxValue){
33332             this.markInvalid(String.format(this.maxText, this.maxValue));
33333             return false;
33334         }
33335         
33336         return true;
33337     },
33338
33339     getValue : function()
33340     {
33341         var v = this.hiddenEl().getValue();
33342         
33343         return this.fixPrecision(this.parseValue(v));
33344     },
33345
33346     parseValue : function(value)
33347     {
33348         if(this.thousandsDelimiter) {
33349             value += "";
33350             r = new RegExp(",", "g");
33351             value = value.replace(r, "");
33352         }
33353         
33354         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33355         return isNaN(value) ? '' : value;
33356     },
33357
33358     fixPrecision : function(value)
33359     {
33360         if(this.thousandsDelimiter) {
33361             value += "";
33362             r = new RegExp(",", "g");
33363             value = value.replace(r, "");
33364         }
33365         
33366         var nan = isNaN(value);
33367         
33368         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33369             return nan ? '' : value;
33370         }
33371         return parseFloat(value).toFixed(this.decimalPrecision);
33372     },
33373
33374     setValue : function(v)
33375     {
33376         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33377         
33378         this.value = v;
33379         
33380         if(this.rendered){
33381             
33382             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33383             
33384             this.inputEl().dom.value = (v == '') ? '' :
33385                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33386             
33387             if(!this.allowZero && v === '0') {
33388                 this.hiddenEl().dom.value = '';
33389                 this.inputEl().dom.value = '';
33390             }
33391             
33392             this.validate();
33393         }
33394     },
33395
33396     decimalPrecisionFcn : function(v)
33397     {
33398         return Math.floor(v);
33399     },
33400
33401     beforeBlur : function()
33402     {
33403         var v = this.parseValue(this.getRawValue());
33404         
33405         if(v || v === 0 || v === ''){
33406             this.setValue(v);
33407         }
33408     },
33409     
33410     hiddenEl : function()
33411     {
33412         return this.el.select('input.hidden-number-input',true).first();
33413     }
33414     
33415 });
33416
33417  
33418
33419 /*
33420 * Licence: LGPL
33421 */
33422
33423 /**
33424  * @class Roo.bootstrap.DocumentSlider
33425  * @extends Roo.bootstrap.Component
33426  * Bootstrap DocumentSlider class
33427  * 
33428  * @constructor
33429  * Create a new DocumentViewer
33430  * @param {Object} config The config object
33431  */
33432
33433 Roo.bootstrap.DocumentSlider = function(config){
33434     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33435     
33436     this.files = [];
33437     
33438     this.addEvents({
33439         /**
33440          * @event initial
33441          * Fire after initEvent
33442          * @param {Roo.bootstrap.DocumentSlider} this
33443          */
33444         "initial" : true,
33445         /**
33446          * @event update
33447          * Fire after update
33448          * @param {Roo.bootstrap.DocumentSlider} this
33449          */
33450         "update" : true,
33451         /**
33452          * @event click
33453          * Fire after click
33454          * @param {Roo.bootstrap.DocumentSlider} this
33455          */
33456         "click" : true
33457     });
33458 };
33459
33460 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33461     
33462     files : false,
33463     
33464     indicator : 0,
33465     
33466     getAutoCreate : function()
33467     {
33468         var cfg = {
33469             tag : 'div',
33470             cls : 'roo-document-slider',
33471             cn : [
33472                 {
33473                     tag : 'div',
33474                     cls : 'roo-document-slider-header',
33475                     cn : [
33476                         {
33477                             tag : 'div',
33478                             cls : 'roo-document-slider-header-title'
33479                         }
33480                     ]
33481                 },
33482                 {
33483                     tag : 'div',
33484                     cls : 'roo-document-slider-body',
33485                     cn : [
33486                         {
33487                             tag : 'div',
33488                             cls : 'roo-document-slider-prev',
33489                             cn : [
33490                                 {
33491                                     tag : 'i',
33492                                     cls : 'fa fa-chevron-left'
33493                                 }
33494                             ]
33495                         },
33496                         {
33497                             tag : 'div',
33498                             cls : 'roo-document-slider-thumb',
33499                             cn : [
33500                                 {
33501                                     tag : 'img',
33502                                     cls : 'roo-document-slider-image'
33503                                 }
33504                             ]
33505                         },
33506                         {
33507                             tag : 'div',
33508                             cls : 'roo-document-slider-next',
33509                             cn : [
33510                                 {
33511                                     tag : 'i',
33512                                     cls : 'fa fa-chevron-right'
33513                                 }
33514                             ]
33515                         }
33516                     ]
33517                 }
33518             ]
33519         };
33520         
33521         return cfg;
33522     },
33523     
33524     initEvents : function()
33525     {
33526         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33527         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33528         
33529         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33530         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33531         
33532         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33533         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33534         
33535         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33536         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33537         
33538         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33539         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33540         
33541         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33542         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33543         
33544         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33545         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33546         
33547         this.thumbEl.on('click', this.onClick, this);
33548         
33549         this.prevIndicator.on('click', this.prev, this);
33550         
33551         this.nextIndicator.on('click', this.next, this);
33552         
33553     },
33554     
33555     initial : function()
33556     {
33557         if(this.files.length){
33558             this.indicator = 1;
33559             this.update()
33560         }
33561         
33562         this.fireEvent('initial', this);
33563     },
33564     
33565     update : function()
33566     {
33567         this.imageEl.attr('src', this.files[this.indicator - 1]);
33568         
33569         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33570         
33571         this.prevIndicator.show();
33572         
33573         if(this.indicator == 1){
33574             this.prevIndicator.hide();
33575         }
33576         
33577         this.nextIndicator.show();
33578         
33579         if(this.indicator == this.files.length){
33580             this.nextIndicator.hide();
33581         }
33582         
33583         this.thumbEl.scrollTo('top');
33584         
33585         this.fireEvent('update', this);
33586     },
33587     
33588     onClick : function(e)
33589     {
33590         e.preventDefault();
33591         
33592         this.fireEvent('click', this);
33593     },
33594     
33595     prev : function(e)
33596     {
33597         e.preventDefault();
33598         
33599         this.indicator = Math.max(1, this.indicator - 1);
33600         
33601         this.update();
33602     },
33603     
33604     next : function(e)
33605     {
33606         e.preventDefault();
33607         
33608         this.indicator = Math.min(this.files.length, this.indicator + 1);
33609         
33610         this.update();
33611     }
33612 });
33613 /*
33614  * - LGPL
33615  *
33616  * RadioSet
33617  *
33618  *
33619  */
33620
33621 /**
33622  * @class Roo.bootstrap.RadioSet
33623  * @extends Roo.bootstrap.Input
33624  * Bootstrap RadioSet class
33625  * @cfg {String} indicatorpos (left|right) default left
33626  * @cfg {Boolean} inline (true|false) inline the element (default true)
33627  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33628  * @constructor
33629  * Create a new RadioSet
33630  * @param {Object} config The config object
33631  */
33632
33633 Roo.bootstrap.RadioSet = function(config){
33634     
33635     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33636     
33637     this.radioes = [];
33638     
33639     Roo.bootstrap.RadioSet.register(this);
33640     
33641     this.addEvents({
33642         /**
33643         * @event check
33644         * Fires when the element is checked or unchecked.
33645         * @param {Roo.bootstrap.RadioSet} this This radio
33646         * @param {Roo.bootstrap.Radio} item The checked item
33647         */
33648        check : true,
33649        /**
33650         * @event click
33651         * Fires when the element is click.
33652         * @param {Roo.bootstrap.RadioSet} this This radio set
33653         * @param {Roo.bootstrap.Radio} item The checked item
33654         * @param {Roo.EventObject} e The event object
33655         */
33656        click : true
33657     });
33658     
33659 };
33660
33661 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33662
33663     radioes : false,
33664     
33665     inline : true,
33666     
33667     weight : '',
33668     
33669     indicatorpos : 'left',
33670     
33671     getAutoCreate : function()
33672     {
33673         var label = {
33674             tag : 'label',
33675             cls : 'roo-radio-set-label',
33676             cn : [
33677                 {
33678                     tag : 'span',
33679                     html : this.fieldLabel
33680                 }
33681             ]
33682         };
33683         
33684         if(this.indicatorpos == 'left'){
33685             label.cn.unshift({
33686                 tag : 'i',
33687                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33688                 tooltip : 'This field is required'
33689             });
33690         } else {
33691             label.cn.push({
33692                 tag : 'i',
33693                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33694                 tooltip : 'This field is required'
33695             });
33696         }
33697         
33698         var items = {
33699             tag : 'div',
33700             cls : 'roo-radio-set-items'
33701         };
33702         
33703         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33704         
33705         if (align === 'left' && this.fieldLabel.length) {
33706             
33707             items = {
33708                 cls : "roo-radio-set-right", 
33709                 cn: [
33710                     items
33711                 ]
33712             };
33713             
33714             if(this.labelWidth > 12){
33715                 label.style = "width: " + this.labelWidth + 'px';
33716             }
33717             
33718             if(this.labelWidth < 13 && this.labelmd == 0){
33719                 this.labelmd = this.labelWidth;
33720             }
33721             
33722             if(this.labellg > 0){
33723                 label.cls += ' col-lg-' + this.labellg;
33724                 items.cls += ' col-lg-' + (12 - this.labellg);
33725             }
33726             
33727             if(this.labelmd > 0){
33728                 label.cls += ' col-md-' + this.labelmd;
33729                 items.cls += ' col-md-' + (12 - this.labelmd);
33730             }
33731             
33732             if(this.labelsm > 0){
33733                 label.cls += ' col-sm-' + this.labelsm;
33734                 items.cls += ' col-sm-' + (12 - this.labelsm);
33735             }
33736             
33737             if(this.labelxs > 0){
33738                 label.cls += ' col-xs-' + this.labelxs;
33739                 items.cls += ' col-xs-' + (12 - this.labelxs);
33740             }
33741         }
33742         
33743         var cfg = {
33744             tag : 'div',
33745             cls : 'roo-radio-set',
33746             cn : [
33747                 {
33748                     tag : 'input',
33749                     cls : 'roo-radio-set-input',
33750                     type : 'hidden',
33751                     name : this.name,
33752                     value : this.value ? this.value :  ''
33753                 },
33754                 label,
33755                 items
33756             ]
33757         };
33758         
33759         if(this.weight.length){
33760             cfg.cls += ' roo-radio-' + this.weight;
33761         }
33762         
33763         if(this.inline) {
33764             cfg.cls += ' roo-radio-set-inline';
33765         }
33766         
33767         var settings=this;
33768         ['xs','sm','md','lg'].map(function(size){
33769             if (settings[size]) {
33770                 cfg.cls += ' col-' + size + '-' + settings[size];
33771             }
33772         });
33773         
33774         return cfg;
33775         
33776     },
33777
33778     initEvents : function()
33779     {
33780         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33781         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33782         
33783         if(!this.fieldLabel.length){
33784             this.labelEl.hide();
33785         }
33786         
33787         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33788         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33789         
33790         this.indicator = this.indicatorEl();
33791         
33792         if(this.indicator){
33793             this.indicator.addClass('invisible');
33794         }
33795         
33796         this.originalValue = this.getValue();
33797         
33798     },
33799     
33800     inputEl: function ()
33801     {
33802         return this.el.select('.roo-radio-set-input', true).first();
33803     },
33804     
33805     getChildContainer : function()
33806     {
33807         return this.itemsEl;
33808     },
33809     
33810     register : function(item)
33811     {
33812         this.radioes.push(item);
33813         
33814     },
33815     
33816     validate : function()
33817     {   
33818         if(this.getVisibilityEl().hasClass('hidden')){
33819             return true;
33820         }
33821         
33822         var valid = false;
33823         
33824         Roo.each(this.radioes, function(i){
33825             if(!i.checked){
33826                 return;
33827             }
33828             
33829             valid = true;
33830             return false;
33831         });
33832         
33833         if(this.allowBlank) {
33834             return true;
33835         }
33836         
33837         if(this.disabled || valid){
33838             this.markValid();
33839             return true;
33840         }
33841         
33842         this.markInvalid();
33843         return false;
33844         
33845     },
33846     
33847     markValid : function()
33848     {
33849         if(this.labelEl.isVisible(true)){
33850             this.indicatorEl().removeClass('visible');
33851             this.indicatorEl().addClass('invisible');
33852         }
33853         
33854         this.el.removeClass([this.invalidClass, this.validClass]);
33855         this.el.addClass(this.validClass);
33856         
33857         this.fireEvent('valid', this);
33858     },
33859     
33860     markInvalid : function(msg)
33861     {
33862         if(this.allowBlank || this.disabled){
33863             return;
33864         }
33865         
33866         if(this.labelEl.isVisible(true)){
33867             this.indicatorEl().removeClass('invisible');
33868             this.indicatorEl().addClass('visible');
33869         }
33870         
33871         this.el.removeClass([this.invalidClass, this.validClass]);
33872         this.el.addClass(this.invalidClass);
33873         
33874         this.fireEvent('invalid', this, msg);
33875         
33876     },
33877     
33878     setValue : function(v, suppressEvent)
33879     {   
33880         if(this.value === v){
33881             return;
33882         }
33883         
33884         this.value = v;
33885         
33886         if(this.rendered){
33887             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33888         }
33889         
33890         Roo.each(this.radioes, function(i){
33891             i.checked = false;
33892             i.el.removeClass('checked');
33893         });
33894         
33895         Roo.each(this.radioes, function(i){
33896             
33897             if(i.value === v || i.value.toString() === v.toString()){
33898                 i.checked = true;
33899                 i.el.addClass('checked');
33900                 
33901                 if(suppressEvent !== true){
33902                     this.fireEvent('check', this, i);
33903                 }
33904                 
33905                 return false;
33906             }
33907             
33908         }, this);
33909         
33910         this.validate();
33911     },
33912     
33913     clearInvalid : function(){
33914         
33915         if(!this.el || this.preventMark){
33916             return;
33917         }
33918         
33919         this.el.removeClass([this.invalidClass]);
33920         
33921         this.fireEvent('valid', this);
33922     }
33923     
33924 });
33925
33926 Roo.apply(Roo.bootstrap.RadioSet, {
33927     
33928     groups: {},
33929     
33930     register : function(set)
33931     {
33932         this.groups[set.name] = set;
33933     },
33934     
33935     get: function(name) 
33936     {
33937         if (typeof(this.groups[name]) == 'undefined') {
33938             return false;
33939         }
33940         
33941         return this.groups[name] ;
33942     }
33943     
33944 });
33945 /*
33946  * Based on:
33947  * Ext JS Library 1.1.1
33948  * Copyright(c) 2006-2007, Ext JS, LLC.
33949  *
33950  * Originally Released Under LGPL - original licence link has changed is not relivant.
33951  *
33952  * Fork - LGPL
33953  * <script type="text/javascript">
33954  */
33955
33956
33957 /**
33958  * @class Roo.bootstrap.SplitBar
33959  * @extends Roo.util.Observable
33960  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33961  * <br><br>
33962  * Usage:
33963  * <pre><code>
33964 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33965                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33966 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33967 split.minSize = 100;
33968 split.maxSize = 600;
33969 split.animate = true;
33970 split.on('moved', splitterMoved);
33971 </code></pre>
33972  * @constructor
33973  * Create a new SplitBar
33974  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33975  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33976  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33977  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33978                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33979                         position of the SplitBar).
33980  */
33981 Roo.bootstrap.SplitBar = function(cfg){
33982     
33983     /** @private */
33984     
33985     //{
33986     //  dragElement : elm
33987     //  resizingElement: el,
33988         // optional..
33989     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33990     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33991         // existingProxy ???
33992     //}
33993     
33994     this.el = Roo.get(cfg.dragElement, true);
33995     this.el.dom.unselectable = "on";
33996     /** @private */
33997     this.resizingEl = Roo.get(cfg.resizingElement, true);
33998
33999     /**
34000      * @private
34001      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34002      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34003      * @type Number
34004      */
34005     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34006     
34007     /**
34008      * The minimum size of the resizing element. (Defaults to 0)
34009      * @type Number
34010      */
34011     this.minSize = 0;
34012     
34013     /**
34014      * The maximum size of the resizing element. (Defaults to 2000)
34015      * @type Number
34016      */
34017     this.maxSize = 2000;
34018     
34019     /**
34020      * Whether to animate the transition to the new size
34021      * @type Boolean
34022      */
34023     this.animate = false;
34024     
34025     /**
34026      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34027      * @type Boolean
34028      */
34029     this.useShim = false;
34030     
34031     /** @private */
34032     this.shim = null;
34033     
34034     if(!cfg.existingProxy){
34035         /** @private */
34036         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34037     }else{
34038         this.proxy = Roo.get(cfg.existingProxy).dom;
34039     }
34040     /** @private */
34041     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34042     
34043     /** @private */
34044     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34045     
34046     /** @private */
34047     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34048     
34049     /** @private */
34050     this.dragSpecs = {};
34051     
34052     /**
34053      * @private The adapter to use to positon and resize elements
34054      */
34055     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34056     this.adapter.init(this);
34057     
34058     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34059         /** @private */
34060         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34061         this.el.addClass("roo-splitbar-h");
34062     }else{
34063         /** @private */
34064         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34065         this.el.addClass("roo-splitbar-v");
34066     }
34067     
34068     this.addEvents({
34069         /**
34070          * @event resize
34071          * Fires when the splitter is moved (alias for {@link #event-moved})
34072          * @param {Roo.bootstrap.SplitBar} this
34073          * @param {Number} newSize the new width or height
34074          */
34075         "resize" : true,
34076         /**
34077          * @event moved
34078          * Fires when the splitter is moved
34079          * @param {Roo.bootstrap.SplitBar} this
34080          * @param {Number} newSize the new width or height
34081          */
34082         "moved" : true,
34083         /**
34084          * @event beforeresize
34085          * Fires before the splitter is dragged
34086          * @param {Roo.bootstrap.SplitBar} this
34087          */
34088         "beforeresize" : true,
34089
34090         "beforeapply" : true
34091     });
34092
34093     Roo.util.Observable.call(this);
34094 };
34095
34096 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34097     onStartProxyDrag : function(x, y){
34098         this.fireEvent("beforeresize", this);
34099         if(!this.overlay){
34100             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34101             o.unselectable();
34102             o.enableDisplayMode("block");
34103             // all splitbars share the same overlay
34104             Roo.bootstrap.SplitBar.prototype.overlay = o;
34105         }
34106         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34107         this.overlay.show();
34108         Roo.get(this.proxy).setDisplayed("block");
34109         var size = this.adapter.getElementSize(this);
34110         this.activeMinSize = this.getMinimumSize();;
34111         this.activeMaxSize = this.getMaximumSize();;
34112         var c1 = size - this.activeMinSize;
34113         var c2 = Math.max(this.activeMaxSize - size, 0);
34114         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34115             this.dd.resetConstraints();
34116             this.dd.setXConstraint(
34117                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34118                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34119             );
34120             this.dd.setYConstraint(0, 0);
34121         }else{
34122             this.dd.resetConstraints();
34123             this.dd.setXConstraint(0, 0);
34124             this.dd.setYConstraint(
34125                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34126                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34127             );
34128          }
34129         this.dragSpecs.startSize = size;
34130         this.dragSpecs.startPoint = [x, y];
34131         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34132     },
34133     
34134     /** 
34135      * @private Called after the drag operation by the DDProxy
34136      */
34137     onEndProxyDrag : function(e){
34138         Roo.get(this.proxy).setDisplayed(false);
34139         var endPoint = Roo.lib.Event.getXY(e);
34140         if(this.overlay){
34141             this.overlay.hide();
34142         }
34143         var newSize;
34144         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34145             newSize = this.dragSpecs.startSize + 
34146                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34147                     endPoint[0] - this.dragSpecs.startPoint[0] :
34148                     this.dragSpecs.startPoint[0] - endPoint[0]
34149                 );
34150         }else{
34151             newSize = this.dragSpecs.startSize + 
34152                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34153                     endPoint[1] - this.dragSpecs.startPoint[1] :
34154                     this.dragSpecs.startPoint[1] - endPoint[1]
34155                 );
34156         }
34157         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34158         if(newSize != this.dragSpecs.startSize){
34159             if(this.fireEvent('beforeapply', this, newSize) !== false){
34160                 this.adapter.setElementSize(this, newSize);
34161                 this.fireEvent("moved", this, newSize);
34162                 this.fireEvent("resize", this, newSize);
34163             }
34164         }
34165     },
34166     
34167     /**
34168      * Get the adapter this SplitBar uses
34169      * @return The adapter object
34170      */
34171     getAdapter : function(){
34172         return this.adapter;
34173     },
34174     
34175     /**
34176      * Set the adapter this SplitBar uses
34177      * @param {Object} adapter A SplitBar adapter object
34178      */
34179     setAdapter : function(adapter){
34180         this.adapter = adapter;
34181         this.adapter.init(this);
34182     },
34183     
34184     /**
34185      * Gets the minimum size for the resizing element
34186      * @return {Number} The minimum size
34187      */
34188     getMinimumSize : function(){
34189         return this.minSize;
34190     },
34191     
34192     /**
34193      * Sets the minimum size for the resizing element
34194      * @param {Number} minSize The minimum size
34195      */
34196     setMinimumSize : function(minSize){
34197         this.minSize = minSize;
34198     },
34199     
34200     /**
34201      * Gets the maximum size for the resizing element
34202      * @return {Number} The maximum size
34203      */
34204     getMaximumSize : function(){
34205         return this.maxSize;
34206     },
34207     
34208     /**
34209      * Sets the maximum size for the resizing element
34210      * @param {Number} maxSize The maximum size
34211      */
34212     setMaximumSize : function(maxSize){
34213         this.maxSize = maxSize;
34214     },
34215     
34216     /**
34217      * Sets the initialize size for the resizing element
34218      * @param {Number} size The initial size
34219      */
34220     setCurrentSize : function(size){
34221         var oldAnimate = this.animate;
34222         this.animate = false;
34223         this.adapter.setElementSize(this, size);
34224         this.animate = oldAnimate;
34225     },
34226     
34227     /**
34228      * Destroy this splitbar. 
34229      * @param {Boolean} removeEl True to remove the element
34230      */
34231     destroy : function(removeEl){
34232         if(this.shim){
34233             this.shim.remove();
34234         }
34235         this.dd.unreg();
34236         this.proxy.parentNode.removeChild(this.proxy);
34237         if(removeEl){
34238             this.el.remove();
34239         }
34240     }
34241 });
34242
34243 /**
34244  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
34245  */
34246 Roo.bootstrap.SplitBar.createProxy = function(dir){
34247     var proxy = new Roo.Element(document.createElement("div"));
34248     proxy.unselectable();
34249     var cls = 'roo-splitbar-proxy';
34250     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34251     document.body.appendChild(proxy.dom);
34252     return proxy.dom;
34253 };
34254
34255 /** 
34256  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34257  * Default Adapter. It assumes the splitter and resizing element are not positioned
34258  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34259  */
34260 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34261 };
34262
34263 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34264     // do nothing for now
34265     init : function(s){
34266     
34267     },
34268     /**
34269      * Called before drag operations to get the current size of the resizing element. 
34270      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34271      */
34272      getElementSize : function(s){
34273         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34274             return s.resizingEl.getWidth();
34275         }else{
34276             return s.resizingEl.getHeight();
34277         }
34278     },
34279     
34280     /**
34281      * Called after drag operations to set the size of the resizing element.
34282      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34283      * @param {Number} newSize The new size to set
34284      * @param {Function} onComplete A function to be invoked when resizing is complete
34285      */
34286     setElementSize : function(s, newSize, onComplete){
34287         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34288             if(!s.animate){
34289                 s.resizingEl.setWidth(newSize);
34290                 if(onComplete){
34291                     onComplete(s, newSize);
34292                 }
34293             }else{
34294                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34295             }
34296         }else{
34297             
34298             if(!s.animate){
34299                 s.resizingEl.setHeight(newSize);
34300                 if(onComplete){
34301                     onComplete(s, newSize);
34302                 }
34303             }else{
34304                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34305             }
34306         }
34307     }
34308 };
34309
34310 /** 
34311  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34312  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34313  * Adapter that  moves the splitter element to align with the resized sizing element. 
34314  * Used with an absolute positioned SplitBar.
34315  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34316  * document.body, make sure you assign an id to the body element.
34317  */
34318 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34319     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34320     this.container = Roo.get(container);
34321 };
34322
34323 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34324     init : function(s){
34325         this.basic.init(s);
34326     },
34327     
34328     getElementSize : function(s){
34329         return this.basic.getElementSize(s);
34330     },
34331     
34332     setElementSize : function(s, newSize, onComplete){
34333         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34334     },
34335     
34336     moveSplitter : function(s){
34337         var yes = Roo.bootstrap.SplitBar;
34338         switch(s.placement){
34339             case yes.LEFT:
34340                 s.el.setX(s.resizingEl.getRight());
34341                 break;
34342             case yes.RIGHT:
34343                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34344                 break;
34345             case yes.TOP:
34346                 s.el.setY(s.resizingEl.getBottom());
34347                 break;
34348             case yes.BOTTOM:
34349                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34350                 break;
34351         }
34352     }
34353 };
34354
34355 /**
34356  * Orientation constant - Create a vertical SplitBar
34357  * @static
34358  * @type Number
34359  */
34360 Roo.bootstrap.SplitBar.VERTICAL = 1;
34361
34362 /**
34363  * Orientation constant - Create a horizontal SplitBar
34364  * @static
34365  * @type Number
34366  */
34367 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34368
34369 /**
34370  * Placement constant - The resizing element is to the left of the splitter element
34371  * @static
34372  * @type Number
34373  */
34374 Roo.bootstrap.SplitBar.LEFT = 1;
34375
34376 /**
34377  * Placement constant - The resizing element is to the right of the splitter element
34378  * @static
34379  * @type Number
34380  */
34381 Roo.bootstrap.SplitBar.RIGHT = 2;
34382
34383 /**
34384  * Placement constant - The resizing element is positioned above the splitter element
34385  * @static
34386  * @type Number
34387  */
34388 Roo.bootstrap.SplitBar.TOP = 3;
34389
34390 /**
34391  * Placement constant - The resizing element is positioned under splitter element
34392  * @static
34393  * @type Number
34394  */
34395 Roo.bootstrap.SplitBar.BOTTOM = 4;
34396 Roo.namespace("Roo.bootstrap.layout");/*
34397  * Based on:
34398  * Ext JS Library 1.1.1
34399  * Copyright(c) 2006-2007, Ext JS, LLC.
34400  *
34401  * Originally Released Under LGPL - original licence link has changed is not relivant.
34402  *
34403  * Fork - LGPL
34404  * <script type="text/javascript">
34405  */
34406
34407 /**
34408  * @class Roo.bootstrap.layout.Manager
34409  * @extends Roo.bootstrap.Component
34410  * Base class for layout managers.
34411  */
34412 Roo.bootstrap.layout.Manager = function(config)
34413 {
34414     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34415
34416
34417
34418
34419
34420     /** false to disable window resize monitoring @type Boolean */
34421     this.monitorWindowResize = true;
34422     this.regions = {};
34423     this.addEvents({
34424         /**
34425          * @event layout
34426          * Fires when a layout is performed.
34427          * @param {Roo.LayoutManager} this
34428          */
34429         "layout" : true,
34430         /**
34431          * @event regionresized
34432          * Fires when the user resizes a region.
34433          * @param {Roo.LayoutRegion} region The resized region
34434          * @param {Number} newSize The new size (width for east/west, height for north/south)
34435          */
34436         "regionresized" : true,
34437         /**
34438          * @event regioncollapsed
34439          * Fires when a region is collapsed.
34440          * @param {Roo.LayoutRegion} region The collapsed region
34441          */
34442         "regioncollapsed" : true,
34443         /**
34444          * @event regionexpanded
34445          * Fires when a region is expanded.
34446          * @param {Roo.LayoutRegion} region The expanded region
34447          */
34448         "regionexpanded" : true
34449     });
34450     this.updating = false;
34451
34452     if (config.el) {
34453         this.el = Roo.get(config.el);
34454         this.initEvents();
34455     }
34456
34457 };
34458
34459 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34460
34461
34462     regions : null,
34463
34464     monitorWindowResize : true,
34465
34466
34467     updating : false,
34468
34469
34470     onRender : function(ct, position)
34471     {
34472         if(!this.el){
34473             this.el = Roo.get(ct);
34474             this.initEvents();
34475         }
34476         //this.fireEvent('render',this);
34477     },
34478
34479
34480     initEvents: function()
34481     {
34482
34483
34484         // ie scrollbar fix
34485         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34486             document.body.scroll = "no";
34487         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34488             this.el.position('relative');
34489         }
34490         this.id = this.el.id;
34491         this.el.addClass("roo-layout-container");
34492         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34493         if(this.el.dom != document.body ) {
34494             this.el.on('resize', this.layout,this);
34495             this.el.on('show', this.layout,this);
34496         }
34497
34498     },
34499
34500     /**
34501      * Returns true if this layout is currently being updated
34502      * @return {Boolean}
34503      */
34504     isUpdating : function(){
34505         return this.updating;
34506     },
34507
34508     /**
34509      * Suspend the LayoutManager from doing auto-layouts while
34510      * making multiple add or remove calls
34511      */
34512     beginUpdate : function(){
34513         this.updating = true;
34514     },
34515
34516     /**
34517      * Restore auto-layouts and optionally disable the manager from performing a layout
34518      * @param {Boolean} noLayout true to disable a layout update
34519      */
34520     endUpdate : function(noLayout){
34521         this.updating = false;
34522         if(!noLayout){
34523             this.layout();
34524         }
34525     },
34526
34527     layout: function(){
34528         // abstract...
34529     },
34530
34531     onRegionResized : function(region, newSize){
34532         this.fireEvent("regionresized", region, newSize);
34533         this.layout();
34534     },
34535
34536     onRegionCollapsed : function(region){
34537         this.fireEvent("regioncollapsed", region);
34538     },
34539
34540     onRegionExpanded : function(region){
34541         this.fireEvent("regionexpanded", region);
34542     },
34543
34544     /**
34545      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34546      * performs box-model adjustments.
34547      * @return {Object} The size as an object {width: (the width), height: (the height)}
34548      */
34549     getViewSize : function()
34550     {
34551         var size;
34552         if(this.el.dom != document.body){
34553             size = this.el.getSize();
34554         }else{
34555             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34556         }
34557         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34558         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34559         return size;
34560     },
34561
34562     /**
34563      * Returns the Element this layout is bound to.
34564      * @return {Roo.Element}
34565      */
34566     getEl : function(){
34567         return this.el;
34568     },
34569
34570     /**
34571      * Returns the specified region.
34572      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34573      * @return {Roo.LayoutRegion}
34574      */
34575     getRegion : function(target){
34576         return this.regions[target.toLowerCase()];
34577     },
34578
34579     onWindowResize : function(){
34580         if(this.monitorWindowResize){
34581             this.layout();
34582         }
34583     }
34584 });
34585 /*
34586  * Based on:
34587  * Ext JS Library 1.1.1
34588  * Copyright(c) 2006-2007, Ext JS, LLC.
34589  *
34590  * Originally Released Under LGPL - original licence link has changed is not relivant.
34591  *
34592  * Fork - LGPL
34593  * <script type="text/javascript">
34594  */
34595 /**
34596  * @class Roo.bootstrap.layout.Border
34597  * @extends Roo.bootstrap.layout.Manager
34598  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34599  * please see: examples/bootstrap/nested.html<br><br>
34600  
34601 <b>The container the layout is rendered into can be either the body element or any other element.
34602 If it is not the body element, the container needs to either be an absolute positioned element,
34603 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34604 the container size if it is not the body element.</b>
34605
34606 * @constructor
34607 * Create a new Border
34608 * @param {Object} config Configuration options
34609  */
34610 Roo.bootstrap.layout.Border = function(config){
34611     config = config || {};
34612     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34613     
34614     
34615     
34616     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34617         if(config[region]){
34618             config[region].region = region;
34619             this.addRegion(config[region]);
34620         }
34621     },this);
34622     
34623 };
34624
34625 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34626
34627 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34628     /**
34629      * Creates and adds a new region if it doesn't already exist.
34630      * @param {String} target The target region key (north, south, east, west or center).
34631      * @param {Object} config The regions config object
34632      * @return {BorderLayoutRegion} The new region
34633      */
34634     addRegion : function(config)
34635     {
34636         if(!this.regions[config.region]){
34637             var r = this.factory(config);
34638             this.bindRegion(r);
34639         }
34640         return this.regions[config.region];
34641     },
34642
34643     // private (kinda)
34644     bindRegion : function(r){
34645         this.regions[r.config.region] = r;
34646         
34647         r.on("visibilitychange",    this.layout, this);
34648         r.on("paneladded",          this.layout, this);
34649         r.on("panelremoved",        this.layout, this);
34650         r.on("invalidated",         this.layout, this);
34651         r.on("resized",             this.onRegionResized, this);
34652         r.on("collapsed",           this.onRegionCollapsed, this);
34653         r.on("expanded",            this.onRegionExpanded, this);
34654     },
34655
34656     /**
34657      * Performs a layout update.
34658      */
34659     layout : function()
34660     {
34661         if(this.updating) {
34662             return;
34663         }
34664         
34665         // render all the rebions if they have not been done alreayd?
34666         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34667             if(this.regions[region] && !this.regions[region].bodyEl){
34668                 this.regions[region].onRender(this.el)
34669             }
34670         },this);
34671         
34672         var size = this.getViewSize();
34673         var w = size.width;
34674         var h = size.height;
34675         var centerW = w;
34676         var centerH = h;
34677         var centerY = 0;
34678         var centerX = 0;
34679         //var x = 0, y = 0;
34680
34681         var rs = this.regions;
34682         var north = rs["north"];
34683         var south = rs["south"]; 
34684         var west = rs["west"];
34685         var east = rs["east"];
34686         var center = rs["center"];
34687         //if(this.hideOnLayout){ // not supported anymore
34688             //c.el.setStyle("display", "none");
34689         //}
34690         if(north && north.isVisible()){
34691             var b = north.getBox();
34692             var m = north.getMargins();
34693             b.width = w - (m.left+m.right);
34694             b.x = m.left;
34695             b.y = m.top;
34696             centerY = b.height + b.y + m.bottom;
34697             centerH -= centerY;
34698             north.updateBox(this.safeBox(b));
34699         }
34700         if(south && south.isVisible()){
34701             var b = south.getBox();
34702             var m = south.getMargins();
34703             b.width = w - (m.left+m.right);
34704             b.x = m.left;
34705             var totalHeight = (b.height + m.top + m.bottom);
34706             b.y = h - totalHeight + m.top;
34707             centerH -= totalHeight;
34708             south.updateBox(this.safeBox(b));
34709         }
34710         if(west && west.isVisible()){
34711             var b = west.getBox();
34712             var m = west.getMargins();
34713             b.height = centerH - (m.top+m.bottom);
34714             b.x = m.left;
34715             b.y = centerY + m.top;
34716             var totalWidth = (b.width + m.left + m.right);
34717             centerX += totalWidth;
34718             centerW -= totalWidth;
34719             west.updateBox(this.safeBox(b));
34720         }
34721         if(east && east.isVisible()){
34722             var b = east.getBox();
34723             var m = east.getMargins();
34724             b.height = centerH - (m.top+m.bottom);
34725             var totalWidth = (b.width + m.left + m.right);
34726             b.x = w - totalWidth + m.left;
34727             b.y = centerY + m.top;
34728             centerW -= totalWidth;
34729             east.updateBox(this.safeBox(b));
34730         }
34731         if(center){
34732             var m = center.getMargins();
34733             var centerBox = {
34734                 x: centerX + m.left,
34735                 y: centerY + m.top,
34736                 width: centerW - (m.left+m.right),
34737                 height: centerH - (m.top+m.bottom)
34738             };
34739             //if(this.hideOnLayout){
34740                 //center.el.setStyle("display", "block");
34741             //}
34742             center.updateBox(this.safeBox(centerBox));
34743         }
34744         this.el.repaint();
34745         this.fireEvent("layout", this);
34746     },
34747
34748     // private
34749     safeBox : function(box){
34750         box.width = Math.max(0, box.width);
34751         box.height = Math.max(0, box.height);
34752         return box;
34753     },
34754
34755     /**
34756      * Adds a ContentPanel (or subclass) to this layout.
34757      * @param {String} target The target region key (north, south, east, west or center).
34758      * @param {Roo.ContentPanel} panel The panel to add
34759      * @return {Roo.ContentPanel} The added panel
34760      */
34761     add : function(target, panel){
34762          
34763         target = target.toLowerCase();
34764         return this.regions[target].add(panel);
34765     },
34766
34767     /**
34768      * Remove a ContentPanel (or subclass) to this layout.
34769      * @param {String} target The target region key (north, south, east, west or center).
34770      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34771      * @return {Roo.ContentPanel} The removed panel
34772      */
34773     remove : function(target, panel){
34774         target = target.toLowerCase();
34775         return this.regions[target].remove(panel);
34776     },
34777
34778     /**
34779      * Searches all regions for a panel with the specified id
34780      * @param {String} panelId
34781      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34782      */
34783     findPanel : function(panelId){
34784         var rs = this.regions;
34785         for(var target in rs){
34786             if(typeof rs[target] != "function"){
34787                 var p = rs[target].getPanel(panelId);
34788                 if(p){
34789                     return p;
34790                 }
34791             }
34792         }
34793         return null;
34794     },
34795
34796     /**
34797      * Searches all regions for a panel with the specified id and activates (shows) it.
34798      * @param {String/ContentPanel} panelId The panels id or the panel itself
34799      * @return {Roo.ContentPanel} The shown panel or null
34800      */
34801     showPanel : function(panelId) {
34802       var rs = this.regions;
34803       for(var target in rs){
34804          var r = rs[target];
34805          if(typeof r != "function"){
34806             if(r.hasPanel(panelId)){
34807                return r.showPanel(panelId);
34808             }
34809          }
34810       }
34811       return null;
34812    },
34813
34814    /**
34815      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34816      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34817      */
34818    /*
34819     restoreState : function(provider){
34820         if(!provider){
34821             provider = Roo.state.Manager;
34822         }
34823         var sm = new Roo.LayoutStateManager();
34824         sm.init(this, provider);
34825     },
34826 */
34827  
34828  
34829     /**
34830      * Adds a xtype elements to the layout.
34831      * <pre><code>
34832
34833 layout.addxtype({
34834        xtype : 'ContentPanel',
34835        region: 'west',
34836        items: [ .... ]
34837    }
34838 );
34839
34840 layout.addxtype({
34841         xtype : 'NestedLayoutPanel',
34842         region: 'west',
34843         layout: {
34844            center: { },
34845            west: { }   
34846         },
34847         items : [ ... list of content panels or nested layout panels.. ]
34848    }
34849 );
34850 </code></pre>
34851      * @param {Object} cfg Xtype definition of item to add.
34852      */
34853     addxtype : function(cfg)
34854     {
34855         // basically accepts a pannel...
34856         // can accept a layout region..!?!?
34857         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34858         
34859         
34860         // theory?  children can only be panels??
34861         
34862         //if (!cfg.xtype.match(/Panel$/)) {
34863         //    return false;
34864         //}
34865         var ret = false;
34866         
34867         if (typeof(cfg.region) == 'undefined') {
34868             Roo.log("Failed to add Panel, region was not set");
34869             Roo.log(cfg);
34870             return false;
34871         }
34872         var region = cfg.region;
34873         delete cfg.region;
34874         
34875           
34876         var xitems = [];
34877         if (cfg.items) {
34878             xitems = cfg.items;
34879             delete cfg.items;
34880         }
34881         var nb = false;
34882         
34883         switch(cfg.xtype) 
34884         {
34885             case 'Content':  // ContentPanel (el, cfg)
34886             case 'Scroll':  // ContentPanel (el, cfg)
34887             case 'View': 
34888                 cfg.autoCreate = true;
34889                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34890                 //} else {
34891                 //    var el = this.el.createChild();
34892                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34893                 //}
34894                 
34895                 this.add(region, ret);
34896                 break;
34897             
34898             /*
34899             case 'TreePanel': // our new panel!
34900                 cfg.el = this.el.createChild();
34901                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34902                 this.add(region, ret);
34903                 break;
34904             */
34905             
34906             case 'Nest': 
34907                 // create a new Layout (which is  a Border Layout...
34908                 
34909                 var clayout = cfg.layout;
34910                 clayout.el  = this.el.createChild();
34911                 clayout.items   = clayout.items  || [];
34912                 
34913                 delete cfg.layout;
34914                 
34915                 // replace this exitems with the clayout ones..
34916                 xitems = clayout.items;
34917                  
34918                 // force background off if it's in center...
34919                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34920                     cfg.background = false;
34921                 }
34922                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34923                 
34924                 
34925                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34926                 //console.log('adding nested layout panel '  + cfg.toSource());
34927                 this.add(region, ret);
34928                 nb = {}; /// find first...
34929                 break;
34930             
34931             case 'Grid':
34932                 
34933                 // needs grid and region
34934                 
34935                 //var el = this.getRegion(region).el.createChild();
34936                 /*
34937                  *var el = this.el.createChild();
34938                 // create the grid first...
34939                 cfg.grid.container = el;
34940                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34941                 */
34942                 
34943                 if (region == 'center' && this.active ) {
34944                     cfg.background = false;
34945                 }
34946                 
34947                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34948                 
34949                 this.add(region, ret);
34950                 /*
34951                 if (cfg.background) {
34952                     // render grid on panel activation (if panel background)
34953                     ret.on('activate', function(gp) {
34954                         if (!gp.grid.rendered) {
34955                     //        gp.grid.render(el);
34956                         }
34957                     });
34958                 } else {
34959                   //  cfg.grid.render(el);
34960                 }
34961                 */
34962                 break;
34963            
34964            
34965             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34966                 // it was the old xcomponent building that caused this before.
34967                 // espeically if border is the top element in the tree.
34968                 ret = this;
34969                 break; 
34970                 
34971                     
34972                 
34973                 
34974                 
34975             default:
34976                 /*
34977                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34978                     
34979                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34980                     this.add(region, ret);
34981                 } else {
34982                 */
34983                     Roo.log(cfg);
34984                     throw "Can not add '" + cfg.xtype + "' to Border";
34985                     return null;
34986              
34987                                 
34988              
34989         }
34990         this.beginUpdate();
34991         // add children..
34992         var region = '';
34993         var abn = {};
34994         Roo.each(xitems, function(i)  {
34995             region = nb && i.region ? i.region : false;
34996             
34997             var add = ret.addxtype(i);
34998            
34999             if (region) {
35000                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35001                 if (!i.background) {
35002                     abn[region] = nb[region] ;
35003                 }
35004             }
35005             
35006         });
35007         this.endUpdate();
35008
35009         // make the last non-background panel active..
35010         //if (nb) { Roo.log(abn); }
35011         if (nb) {
35012             
35013             for(var r in abn) {
35014                 region = this.getRegion(r);
35015                 if (region) {
35016                     // tried using nb[r], but it does not work..
35017                      
35018                     region.showPanel(abn[r]);
35019                    
35020                 }
35021             }
35022         }
35023         return ret;
35024         
35025     },
35026     
35027     
35028 // private
35029     factory : function(cfg)
35030     {
35031         
35032         var validRegions = Roo.bootstrap.layout.Border.regions;
35033
35034         var target = cfg.region;
35035         cfg.mgr = this;
35036         
35037         var r = Roo.bootstrap.layout;
35038         Roo.log(target);
35039         switch(target){
35040             case "north":
35041                 return new r.North(cfg);
35042             case "south":
35043                 return new r.South(cfg);
35044             case "east":
35045                 return new r.East(cfg);
35046             case "west":
35047                 return new r.West(cfg);
35048             case "center":
35049                 return new r.Center(cfg);
35050         }
35051         throw 'Layout region "'+target+'" not supported.';
35052     }
35053     
35054     
35055 });
35056  /*
35057  * Based on:
35058  * Ext JS Library 1.1.1
35059  * Copyright(c) 2006-2007, Ext JS, LLC.
35060  *
35061  * Originally Released Under LGPL - original licence link has changed is not relivant.
35062  *
35063  * Fork - LGPL
35064  * <script type="text/javascript">
35065  */
35066  
35067 /**
35068  * @class Roo.bootstrap.layout.Basic
35069  * @extends Roo.util.Observable
35070  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35071  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35072  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35073  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35074  * @cfg {string}   region  the region that it inhabits..
35075  * @cfg {bool}   skipConfig skip config?
35076  * 
35077
35078  */
35079 Roo.bootstrap.layout.Basic = function(config){
35080     
35081     this.mgr = config.mgr;
35082     
35083     this.position = config.region;
35084     
35085     var skipConfig = config.skipConfig;
35086     
35087     this.events = {
35088         /**
35089          * @scope Roo.BasicLayoutRegion
35090          */
35091         
35092         /**
35093          * @event beforeremove
35094          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35095          * @param {Roo.LayoutRegion} this
35096          * @param {Roo.ContentPanel} panel The panel
35097          * @param {Object} e The cancel event object
35098          */
35099         "beforeremove" : true,
35100         /**
35101          * @event invalidated
35102          * Fires when the layout for this region is changed.
35103          * @param {Roo.LayoutRegion} this
35104          */
35105         "invalidated" : true,
35106         /**
35107          * @event visibilitychange
35108          * Fires when this region is shown or hidden 
35109          * @param {Roo.LayoutRegion} this
35110          * @param {Boolean} visibility true or false
35111          */
35112         "visibilitychange" : true,
35113         /**
35114          * @event paneladded
35115          * Fires when a panel is added. 
35116          * @param {Roo.LayoutRegion} this
35117          * @param {Roo.ContentPanel} panel The panel
35118          */
35119         "paneladded" : true,
35120         /**
35121          * @event panelremoved
35122          * Fires when a panel is removed. 
35123          * @param {Roo.LayoutRegion} this
35124          * @param {Roo.ContentPanel} panel The panel
35125          */
35126         "panelremoved" : true,
35127         /**
35128          * @event beforecollapse
35129          * Fires when this region before collapse.
35130          * @param {Roo.LayoutRegion} this
35131          */
35132         "beforecollapse" : true,
35133         /**
35134          * @event collapsed
35135          * Fires when this region is collapsed.
35136          * @param {Roo.LayoutRegion} this
35137          */
35138         "collapsed" : true,
35139         /**
35140          * @event expanded
35141          * Fires when this region is expanded.
35142          * @param {Roo.LayoutRegion} this
35143          */
35144         "expanded" : true,
35145         /**
35146          * @event slideshow
35147          * Fires when this region is slid into view.
35148          * @param {Roo.LayoutRegion} this
35149          */
35150         "slideshow" : true,
35151         /**
35152          * @event slidehide
35153          * Fires when this region slides out of view. 
35154          * @param {Roo.LayoutRegion} this
35155          */
35156         "slidehide" : true,
35157         /**
35158          * @event panelactivated
35159          * Fires when a panel is activated. 
35160          * @param {Roo.LayoutRegion} this
35161          * @param {Roo.ContentPanel} panel The activated panel
35162          */
35163         "panelactivated" : true,
35164         /**
35165          * @event resized
35166          * Fires when the user resizes this region. 
35167          * @param {Roo.LayoutRegion} this
35168          * @param {Number} newSize The new size (width for east/west, height for north/south)
35169          */
35170         "resized" : true
35171     };
35172     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35173     this.panels = new Roo.util.MixedCollection();
35174     this.panels.getKey = this.getPanelId.createDelegate(this);
35175     this.box = null;
35176     this.activePanel = null;
35177     // ensure listeners are added...
35178     
35179     if (config.listeners || config.events) {
35180         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35181             listeners : config.listeners || {},
35182             events : config.events || {}
35183         });
35184     }
35185     
35186     if(skipConfig !== true){
35187         this.applyConfig(config);
35188     }
35189 };
35190
35191 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35192 {
35193     getPanelId : function(p){
35194         return p.getId();
35195     },
35196     
35197     applyConfig : function(config){
35198         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35199         this.config = config;
35200         
35201     },
35202     
35203     /**
35204      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35205      * the width, for horizontal (north, south) the height.
35206      * @param {Number} newSize The new width or height
35207      */
35208     resizeTo : function(newSize){
35209         var el = this.el ? this.el :
35210                  (this.activePanel ? this.activePanel.getEl() : null);
35211         if(el){
35212             switch(this.position){
35213                 case "east":
35214                 case "west":
35215                     el.setWidth(newSize);
35216                     this.fireEvent("resized", this, newSize);
35217                 break;
35218                 case "north":
35219                 case "south":
35220                     el.setHeight(newSize);
35221                     this.fireEvent("resized", this, newSize);
35222                 break;                
35223             }
35224         }
35225     },
35226     
35227     getBox : function(){
35228         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35229     },
35230     
35231     getMargins : function(){
35232         return this.margins;
35233     },
35234     
35235     updateBox : function(box){
35236         this.box = box;
35237         var el = this.activePanel.getEl();
35238         el.dom.style.left = box.x + "px";
35239         el.dom.style.top = box.y + "px";
35240         this.activePanel.setSize(box.width, box.height);
35241     },
35242     
35243     /**
35244      * Returns the container element for this region.
35245      * @return {Roo.Element}
35246      */
35247     getEl : function(){
35248         return this.activePanel;
35249     },
35250     
35251     /**
35252      * Returns true if this region is currently visible.
35253      * @return {Boolean}
35254      */
35255     isVisible : function(){
35256         return this.activePanel ? true : false;
35257     },
35258     
35259     setActivePanel : function(panel){
35260         panel = this.getPanel(panel);
35261         if(this.activePanel && this.activePanel != panel){
35262             this.activePanel.setActiveState(false);
35263             this.activePanel.getEl().setLeftTop(-10000,-10000);
35264         }
35265         this.activePanel = panel;
35266         panel.setActiveState(true);
35267         if(this.box){
35268             panel.setSize(this.box.width, this.box.height);
35269         }
35270         this.fireEvent("panelactivated", this, panel);
35271         this.fireEvent("invalidated");
35272     },
35273     
35274     /**
35275      * Show the specified panel.
35276      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35277      * @return {Roo.ContentPanel} The shown panel or null
35278      */
35279     showPanel : function(panel){
35280         panel = this.getPanel(panel);
35281         if(panel){
35282             this.setActivePanel(panel);
35283         }
35284         return panel;
35285     },
35286     
35287     /**
35288      * Get the active panel for this region.
35289      * @return {Roo.ContentPanel} The active panel or null
35290      */
35291     getActivePanel : function(){
35292         return this.activePanel;
35293     },
35294     
35295     /**
35296      * Add the passed ContentPanel(s)
35297      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35298      * @return {Roo.ContentPanel} The panel added (if only one was added)
35299      */
35300     add : function(panel){
35301         if(arguments.length > 1){
35302             for(var i = 0, len = arguments.length; i < len; i++) {
35303                 this.add(arguments[i]);
35304             }
35305             return null;
35306         }
35307         if(this.hasPanel(panel)){
35308             this.showPanel(panel);
35309             return panel;
35310         }
35311         var el = panel.getEl();
35312         if(el.dom.parentNode != this.mgr.el.dom){
35313             this.mgr.el.dom.appendChild(el.dom);
35314         }
35315         if(panel.setRegion){
35316             panel.setRegion(this);
35317         }
35318         this.panels.add(panel);
35319         el.setStyle("position", "absolute");
35320         if(!panel.background){
35321             this.setActivePanel(panel);
35322             if(this.config.initialSize && this.panels.getCount()==1){
35323                 this.resizeTo(this.config.initialSize);
35324             }
35325         }
35326         this.fireEvent("paneladded", this, panel);
35327         return panel;
35328     },
35329     
35330     /**
35331      * Returns true if the panel is in this region.
35332      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35333      * @return {Boolean}
35334      */
35335     hasPanel : function(panel){
35336         if(typeof panel == "object"){ // must be panel obj
35337             panel = panel.getId();
35338         }
35339         return this.getPanel(panel) ? true : false;
35340     },
35341     
35342     /**
35343      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35344      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35345      * @param {Boolean} preservePanel Overrides the config preservePanel option
35346      * @return {Roo.ContentPanel} The panel that was removed
35347      */
35348     remove : function(panel, preservePanel){
35349         panel = this.getPanel(panel);
35350         if(!panel){
35351             return null;
35352         }
35353         var e = {};
35354         this.fireEvent("beforeremove", this, panel, e);
35355         if(e.cancel === true){
35356             return null;
35357         }
35358         var panelId = panel.getId();
35359         this.panels.removeKey(panelId);
35360         return panel;
35361     },
35362     
35363     /**
35364      * Returns the panel specified or null if it's not in this region.
35365      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35366      * @return {Roo.ContentPanel}
35367      */
35368     getPanel : function(id){
35369         if(typeof id == "object"){ // must be panel obj
35370             return id;
35371         }
35372         return this.panels.get(id);
35373     },
35374     
35375     /**
35376      * Returns this regions position (north/south/east/west/center).
35377      * @return {String} 
35378      */
35379     getPosition: function(){
35380         return this.position;    
35381     }
35382 });/*
35383  * Based on:
35384  * Ext JS Library 1.1.1
35385  * Copyright(c) 2006-2007, Ext JS, LLC.
35386  *
35387  * Originally Released Under LGPL - original licence link has changed is not relivant.
35388  *
35389  * Fork - LGPL
35390  * <script type="text/javascript">
35391  */
35392  
35393 /**
35394  * @class Roo.bootstrap.layout.Region
35395  * @extends Roo.bootstrap.layout.Basic
35396  * This class represents a region in a layout manager.
35397  
35398  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35399  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
35400  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35401  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35402  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35403  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35404  * @cfg {String}    title           The title for the region (overrides panel titles)
35405  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35406  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35407  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35408  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35409  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35410  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35411  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35412  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35413  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35414  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35415
35416  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35417  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35418  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35419  * @cfg {Number}    width           For East/West panels
35420  * @cfg {Number}    height          For North/South panels
35421  * @cfg {Boolean}   split           To show the splitter
35422  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35423  * 
35424  * @cfg {string}   cls             Extra CSS classes to add to region
35425  * 
35426  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35427  * @cfg {string}   region  the region that it inhabits..
35428  *
35429
35430  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35431  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35432
35433  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35434  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35435  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35436  */
35437 Roo.bootstrap.layout.Region = function(config)
35438 {
35439     this.applyConfig(config);
35440
35441     var mgr = config.mgr;
35442     var pos = config.region;
35443     config.skipConfig = true;
35444     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35445     
35446     if (mgr.el) {
35447         this.onRender(mgr.el);   
35448     }
35449      
35450     this.visible = true;
35451     this.collapsed = false;
35452     this.unrendered_panels = [];
35453 };
35454
35455 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35456
35457     position: '', // set by wrapper (eg. north/south etc..)
35458     unrendered_panels : null,  // unrendered panels.
35459     createBody : function(){
35460         /** This region's body element 
35461         * @type Roo.Element */
35462         this.bodyEl = this.el.createChild({
35463                 tag: "div",
35464                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35465         });
35466     },
35467
35468     onRender: function(ctr, pos)
35469     {
35470         var dh = Roo.DomHelper;
35471         /** This region's container element 
35472         * @type Roo.Element */
35473         this.el = dh.append(ctr.dom, {
35474                 tag: "div",
35475                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35476             }, true);
35477         /** This region's title element 
35478         * @type Roo.Element */
35479     
35480         this.titleEl = dh.append(this.el.dom,
35481             {
35482                     tag: "div",
35483                     unselectable: "on",
35484                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35485                     children:[
35486                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35487                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35488                     ]}, true);
35489         
35490         this.titleEl.enableDisplayMode();
35491         /** This region's title text element 
35492         * @type HTMLElement */
35493         this.titleTextEl = this.titleEl.dom.firstChild;
35494         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35495         /*
35496         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35497         this.closeBtn.enableDisplayMode();
35498         this.closeBtn.on("click", this.closeClicked, this);
35499         this.closeBtn.hide();
35500     */
35501         this.createBody(this.config);
35502         if(this.config.hideWhenEmpty){
35503             this.hide();
35504             this.on("paneladded", this.validateVisibility, this);
35505             this.on("panelremoved", this.validateVisibility, this);
35506         }
35507         if(this.autoScroll){
35508             this.bodyEl.setStyle("overflow", "auto");
35509         }else{
35510             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35511         }
35512         //if(c.titlebar !== false){
35513             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35514                 this.titleEl.hide();
35515             }else{
35516                 this.titleEl.show();
35517                 if(this.config.title){
35518                     this.titleTextEl.innerHTML = this.config.title;
35519                 }
35520             }
35521         //}
35522         if(this.config.collapsed){
35523             this.collapse(true);
35524         }
35525         if(this.config.hidden){
35526             this.hide();
35527         }
35528         
35529         if (this.unrendered_panels && this.unrendered_panels.length) {
35530             for (var i =0;i< this.unrendered_panels.length; i++) {
35531                 this.add(this.unrendered_panels[i]);
35532             }
35533             this.unrendered_panels = null;
35534             
35535         }
35536         
35537     },
35538     
35539     applyConfig : function(c)
35540     {
35541         /*
35542          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35543             var dh = Roo.DomHelper;
35544             if(c.titlebar !== false){
35545                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35546                 this.collapseBtn.on("click", this.collapse, this);
35547                 this.collapseBtn.enableDisplayMode();
35548                 /*
35549                 if(c.showPin === true || this.showPin){
35550                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35551                     this.stickBtn.enableDisplayMode();
35552                     this.stickBtn.on("click", this.expand, this);
35553                     this.stickBtn.hide();
35554                 }
35555                 
35556             }
35557             */
35558             /** This region's collapsed element
35559             * @type Roo.Element */
35560             /*
35561              *
35562             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35563                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35564             ]}, true);
35565             
35566             if(c.floatable !== false){
35567                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35568                this.collapsedEl.on("click", this.collapseClick, this);
35569             }
35570
35571             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35572                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35573                    id: "message", unselectable: "on", style:{"float":"left"}});
35574                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35575              }
35576             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35577             this.expandBtn.on("click", this.expand, this);
35578             
35579         }
35580         
35581         if(this.collapseBtn){
35582             this.collapseBtn.setVisible(c.collapsible == true);
35583         }
35584         
35585         this.cmargins = c.cmargins || this.cmargins ||
35586                          (this.position == "west" || this.position == "east" ?
35587                              {top: 0, left: 2, right:2, bottom: 0} :
35588                              {top: 2, left: 0, right:0, bottom: 2});
35589         */
35590         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35591         
35592         
35593         this.bottomTabs = c.tabPosition != "top";
35594         
35595         this.autoScroll = c.autoScroll || false;
35596         
35597         
35598        
35599         
35600         this.duration = c.duration || .30;
35601         this.slideDuration = c.slideDuration || .45;
35602         this.config = c;
35603        
35604     },
35605     /**
35606      * Returns true if this region is currently visible.
35607      * @return {Boolean}
35608      */
35609     isVisible : function(){
35610         return this.visible;
35611     },
35612
35613     /**
35614      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35615      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35616      */
35617     //setCollapsedTitle : function(title){
35618     //    title = title || "&#160;";
35619      //   if(this.collapsedTitleTextEl){
35620       //      this.collapsedTitleTextEl.innerHTML = title;
35621        // }
35622     //},
35623
35624     getBox : function(){
35625         var b;
35626       //  if(!this.collapsed){
35627             b = this.el.getBox(false, true);
35628        // }else{
35629           //  b = this.collapsedEl.getBox(false, true);
35630         //}
35631         return b;
35632     },
35633
35634     getMargins : function(){
35635         return this.margins;
35636         //return this.collapsed ? this.cmargins : this.margins;
35637     },
35638 /*
35639     highlight : function(){
35640         this.el.addClass("x-layout-panel-dragover");
35641     },
35642
35643     unhighlight : function(){
35644         this.el.removeClass("x-layout-panel-dragover");
35645     },
35646 */
35647     updateBox : function(box)
35648     {
35649         if (!this.bodyEl) {
35650             return; // not rendered yet..
35651         }
35652         
35653         this.box = box;
35654         if(!this.collapsed){
35655             this.el.dom.style.left = box.x + "px";
35656             this.el.dom.style.top = box.y + "px";
35657             this.updateBody(box.width, box.height);
35658         }else{
35659             this.collapsedEl.dom.style.left = box.x + "px";
35660             this.collapsedEl.dom.style.top = box.y + "px";
35661             this.collapsedEl.setSize(box.width, box.height);
35662         }
35663         if(this.tabs){
35664             this.tabs.autoSizeTabs();
35665         }
35666     },
35667
35668     updateBody : function(w, h)
35669     {
35670         if(w !== null){
35671             this.el.setWidth(w);
35672             w -= this.el.getBorderWidth("rl");
35673             if(this.config.adjustments){
35674                 w += this.config.adjustments[0];
35675             }
35676         }
35677         if(h !== null && h > 0){
35678             this.el.setHeight(h);
35679             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35680             h -= this.el.getBorderWidth("tb");
35681             if(this.config.adjustments){
35682                 h += this.config.adjustments[1];
35683             }
35684             this.bodyEl.setHeight(h);
35685             if(this.tabs){
35686                 h = this.tabs.syncHeight(h);
35687             }
35688         }
35689         if(this.panelSize){
35690             w = w !== null ? w : this.panelSize.width;
35691             h = h !== null ? h : this.panelSize.height;
35692         }
35693         if(this.activePanel){
35694             var el = this.activePanel.getEl();
35695             w = w !== null ? w : el.getWidth();
35696             h = h !== null ? h : el.getHeight();
35697             this.panelSize = {width: w, height: h};
35698             this.activePanel.setSize(w, h);
35699         }
35700         if(Roo.isIE && this.tabs){
35701             this.tabs.el.repaint();
35702         }
35703     },
35704
35705     /**
35706      * Returns the container element for this region.
35707      * @return {Roo.Element}
35708      */
35709     getEl : function(){
35710         return this.el;
35711     },
35712
35713     /**
35714      * Hides this region.
35715      */
35716     hide : function(){
35717         //if(!this.collapsed){
35718             this.el.dom.style.left = "-2000px";
35719             this.el.hide();
35720         //}else{
35721          //   this.collapsedEl.dom.style.left = "-2000px";
35722          //   this.collapsedEl.hide();
35723        // }
35724         this.visible = false;
35725         this.fireEvent("visibilitychange", this, false);
35726     },
35727
35728     /**
35729      * Shows this region if it was previously hidden.
35730      */
35731     show : function(){
35732         //if(!this.collapsed){
35733             this.el.show();
35734         //}else{
35735         //    this.collapsedEl.show();
35736        // }
35737         this.visible = true;
35738         this.fireEvent("visibilitychange", this, true);
35739     },
35740 /*
35741     closeClicked : function(){
35742         if(this.activePanel){
35743             this.remove(this.activePanel);
35744         }
35745     },
35746
35747     collapseClick : function(e){
35748         if(this.isSlid){
35749            e.stopPropagation();
35750            this.slideIn();
35751         }else{
35752            e.stopPropagation();
35753            this.slideOut();
35754         }
35755     },
35756 */
35757     /**
35758      * Collapses this region.
35759      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35760      */
35761     /*
35762     collapse : function(skipAnim, skipCheck = false){
35763         if(this.collapsed) {
35764             return;
35765         }
35766         
35767         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35768             
35769             this.collapsed = true;
35770             if(this.split){
35771                 this.split.el.hide();
35772             }
35773             if(this.config.animate && skipAnim !== true){
35774                 this.fireEvent("invalidated", this);
35775                 this.animateCollapse();
35776             }else{
35777                 this.el.setLocation(-20000,-20000);
35778                 this.el.hide();
35779                 this.collapsedEl.show();
35780                 this.fireEvent("collapsed", this);
35781                 this.fireEvent("invalidated", this);
35782             }
35783         }
35784         
35785     },
35786 */
35787     animateCollapse : function(){
35788         // overridden
35789     },
35790
35791     /**
35792      * Expands this region if it was previously collapsed.
35793      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35794      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35795      */
35796     /*
35797     expand : function(e, skipAnim){
35798         if(e) {
35799             e.stopPropagation();
35800         }
35801         if(!this.collapsed || this.el.hasActiveFx()) {
35802             return;
35803         }
35804         if(this.isSlid){
35805             this.afterSlideIn();
35806             skipAnim = true;
35807         }
35808         this.collapsed = false;
35809         if(this.config.animate && skipAnim !== true){
35810             this.animateExpand();
35811         }else{
35812             this.el.show();
35813             if(this.split){
35814                 this.split.el.show();
35815             }
35816             this.collapsedEl.setLocation(-2000,-2000);
35817             this.collapsedEl.hide();
35818             this.fireEvent("invalidated", this);
35819             this.fireEvent("expanded", this);
35820         }
35821     },
35822 */
35823     animateExpand : function(){
35824         // overridden
35825     },
35826
35827     initTabs : function()
35828     {
35829         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35830         
35831         var ts = new Roo.bootstrap.panel.Tabs({
35832                 el: this.bodyEl.dom,
35833                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35834                 disableTooltips: this.config.disableTabTips,
35835                 toolbar : this.config.toolbar
35836             });
35837         
35838         if(this.config.hideTabs){
35839             ts.stripWrap.setDisplayed(false);
35840         }
35841         this.tabs = ts;
35842         ts.resizeTabs = this.config.resizeTabs === true;
35843         ts.minTabWidth = this.config.minTabWidth || 40;
35844         ts.maxTabWidth = this.config.maxTabWidth || 250;
35845         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35846         ts.monitorResize = false;
35847         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35848         ts.bodyEl.addClass('roo-layout-tabs-body');
35849         this.panels.each(this.initPanelAsTab, this);
35850     },
35851
35852     initPanelAsTab : function(panel){
35853         var ti = this.tabs.addTab(
35854             panel.getEl().id,
35855             panel.getTitle(),
35856             null,
35857             this.config.closeOnTab && panel.isClosable(),
35858             panel.tpl
35859         );
35860         if(panel.tabTip !== undefined){
35861             ti.setTooltip(panel.tabTip);
35862         }
35863         ti.on("activate", function(){
35864               this.setActivePanel(panel);
35865         }, this);
35866         
35867         if(this.config.closeOnTab){
35868             ti.on("beforeclose", function(t, e){
35869                 e.cancel = true;
35870                 this.remove(panel);
35871             }, this);
35872         }
35873         
35874         panel.tabItem = ti;
35875         
35876         return ti;
35877     },
35878
35879     updatePanelTitle : function(panel, title)
35880     {
35881         if(this.activePanel == panel){
35882             this.updateTitle(title);
35883         }
35884         if(this.tabs){
35885             var ti = this.tabs.getTab(panel.getEl().id);
35886             ti.setText(title);
35887             if(panel.tabTip !== undefined){
35888                 ti.setTooltip(panel.tabTip);
35889             }
35890         }
35891     },
35892
35893     updateTitle : function(title){
35894         if(this.titleTextEl && !this.config.title){
35895             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35896         }
35897     },
35898
35899     setActivePanel : function(panel)
35900     {
35901         panel = this.getPanel(panel);
35902         if(this.activePanel && this.activePanel != panel){
35903             if(this.activePanel.setActiveState(false) === false){
35904                 return;
35905             }
35906         }
35907         this.activePanel = panel;
35908         panel.setActiveState(true);
35909         if(this.panelSize){
35910             panel.setSize(this.panelSize.width, this.panelSize.height);
35911         }
35912         if(this.closeBtn){
35913             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35914         }
35915         this.updateTitle(panel.getTitle());
35916         if(this.tabs){
35917             this.fireEvent("invalidated", this);
35918         }
35919         this.fireEvent("panelactivated", this, panel);
35920     },
35921
35922     /**
35923      * Shows the specified panel.
35924      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35925      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35926      */
35927     showPanel : function(panel)
35928     {
35929         panel = this.getPanel(panel);
35930         if(panel){
35931             if(this.tabs){
35932                 var tab = this.tabs.getTab(panel.getEl().id);
35933                 if(tab.isHidden()){
35934                     this.tabs.unhideTab(tab.id);
35935                 }
35936                 tab.activate();
35937             }else{
35938                 this.setActivePanel(panel);
35939             }
35940         }
35941         return panel;
35942     },
35943
35944     /**
35945      * Get the active panel for this region.
35946      * @return {Roo.ContentPanel} The active panel or null
35947      */
35948     getActivePanel : function(){
35949         return this.activePanel;
35950     },
35951
35952     validateVisibility : function(){
35953         if(this.panels.getCount() < 1){
35954             this.updateTitle("&#160;");
35955             this.closeBtn.hide();
35956             this.hide();
35957         }else{
35958             if(!this.isVisible()){
35959                 this.show();
35960             }
35961         }
35962     },
35963
35964     /**
35965      * Adds the passed ContentPanel(s) to this region.
35966      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35967      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35968      */
35969     add : function(panel)
35970     {
35971         if(arguments.length > 1){
35972             for(var i = 0, len = arguments.length; i < len; i++) {
35973                 this.add(arguments[i]);
35974             }
35975             return null;
35976         }
35977         
35978         // if we have not been rendered yet, then we can not really do much of this..
35979         if (!this.bodyEl) {
35980             this.unrendered_panels.push(panel);
35981             return panel;
35982         }
35983         
35984         
35985         
35986         
35987         if(this.hasPanel(panel)){
35988             this.showPanel(panel);
35989             return panel;
35990         }
35991         panel.setRegion(this);
35992         this.panels.add(panel);
35993        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35994             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35995             // and hide them... ???
35996             this.bodyEl.dom.appendChild(panel.getEl().dom);
35997             if(panel.background !== true){
35998                 this.setActivePanel(panel);
35999             }
36000             this.fireEvent("paneladded", this, panel);
36001             return panel;
36002         }
36003         */
36004         if(!this.tabs){
36005             this.initTabs();
36006         }else{
36007             this.initPanelAsTab(panel);
36008         }
36009         
36010         
36011         if(panel.background !== true){
36012             this.tabs.activate(panel.getEl().id);
36013         }
36014         this.fireEvent("paneladded", this, panel);
36015         return panel;
36016     },
36017
36018     /**
36019      * Hides the tab for the specified panel.
36020      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36021      */
36022     hidePanel : function(panel){
36023         if(this.tabs && (panel = this.getPanel(panel))){
36024             this.tabs.hideTab(panel.getEl().id);
36025         }
36026     },
36027
36028     /**
36029      * Unhides the tab for a previously hidden panel.
36030      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36031      */
36032     unhidePanel : function(panel){
36033         if(this.tabs && (panel = this.getPanel(panel))){
36034             this.tabs.unhideTab(panel.getEl().id);
36035         }
36036     },
36037
36038     clearPanels : function(){
36039         while(this.panels.getCount() > 0){
36040              this.remove(this.panels.first());
36041         }
36042     },
36043
36044     /**
36045      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36046      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36047      * @param {Boolean} preservePanel Overrides the config preservePanel option
36048      * @return {Roo.ContentPanel} The panel that was removed
36049      */
36050     remove : function(panel, preservePanel)
36051     {
36052         panel = this.getPanel(panel);
36053         if(!panel){
36054             return null;
36055         }
36056         var e = {};
36057         this.fireEvent("beforeremove", this, panel, e);
36058         if(e.cancel === true){
36059             return null;
36060         }
36061         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36062         var panelId = panel.getId();
36063         this.panels.removeKey(panelId);
36064         if(preservePanel){
36065             document.body.appendChild(panel.getEl().dom);
36066         }
36067         if(this.tabs){
36068             this.tabs.removeTab(panel.getEl().id);
36069         }else if (!preservePanel){
36070             this.bodyEl.dom.removeChild(panel.getEl().dom);
36071         }
36072         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36073             var p = this.panels.first();
36074             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36075             tempEl.appendChild(p.getEl().dom);
36076             this.bodyEl.update("");
36077             this.bodyEl.dom.appendChild(p.getEl().dom);
36078             tempEl = null;
36079             this.updateTitle(p.getTitle());
36080             this.tabs = null;
36081             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36082             this.setActivePanel(p);
36083         }
36084         panel.setRegion(null);
36085         if(this.activePanel == panel){
36086             this.activePanel = null;
36087         }
36088         if(this.config.autoDestroy !== false && preservePanel !== true){
36089             try{panel.destroy();}catch(e){}
36090         }
36091         this.fireEvent("panelremoved", this, panel);
36092         return panel;
36093     },
36094
36095     /**
36096      * Returns the TabPanel component used by this region
36097      * @return {Roo.TabPanel}
36098      */
36099     getTabs : function(){
36100         return this.tabs;
36101     },
36102
36103     createTool : function(parentEl, className){
36104         var btn = Roo.DomHelper.append(parentEl, {
36105             tag: "div",
36106             cls: "x-layout-tools-button",
36107             children: [ {
36108                 tag: "div",
36109                 cls: "roo-layout-tools-button-inner " + className,
36110                 html: "&#160;"
36111             }]
36112         }, true);
36113         btn.addClassOnOver("roo-layout-tools-button-over");
36114         return btn;
36115     }
36116 });/*
36117  * Based on:
36118  * Ext JS Library 1.1.1
36119  * Copyright(c) 2006-2007, Ext JS, LLC.
36120  *
36121  * Originally Released Under LGPL - original licence link has changed is not relivant.
36122  *
36123  * Fork - LGPL
36124  * <script type="text/javascript">
36125  */
36126  
36127
36128
36129 /**
36130  * @class Roo.SplitLayoutRegion
36131  * @extends Roo.LayoutRegion
36132  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36133  */
36134 Roo.bootstrap.layout.Split = function(config){
36135     this.cursor = config.cursor;
36136     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36137 };
36138
36139 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36140 {
36141     splitTip : "Drag to resize.",
36142     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36143     useSplitTips : false,
36144
36145     applyConfig : function(config){
36146         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36147     },
36148     
36149     onRender : function(ctr,pos) {
36150         
36151         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36152         if(!this.config.split){
36153             return;
36154         }
36155         if(!this.split){
36156             
36157             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36158                             tag: "div",
36159                             id: this.el.id + "-split",
36160                             cls: "roo-layout-split roo-layout-split-"+this.position,
36161                             html: "&#160;"
36162             });
36163             /** The SplitBar for this region 
36164             * @type Roo.SplitBar */
36165             // does not exist yet...
36166             Roo.log([this.position, this.orientation]);
36167             
36168             this.split = new Roo.bootstrap.SplitBar({
36169                 dragElement : splitEl,
36170                 resizingElement: this.el,
36171                 orientation : this.orientation
36172             });
36173             
36174             this.split.on("moved", this.onSplitMove, this);
36175             this.split.useShim = this.config.useShim === true;
36176             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36177             if(this.useSplitTips){
36178                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36179             }
36180             //if(config.collapsible){
36181             //    this.split.el.on("dblclick", this.collapse,  this);
36182             //}
36183         }
36184         if(typeof this.config.minSize != "undefined"){
36185             this.split.minSize = this.config.minSize;
36186         }
36187         if(typeof this.config.maxSize != "undefined"){
36188             this.split.maxSize = this.config.maxSize;
36189         }
36190         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36191             this.hideSplitter();
36192         }
36193         
36194     },
36195
36196     getHMaxSize : function(){
36197          var cmax = this.config.maxSize || 10000;
36198          var center = this.mgr.getRegion("center");
36199          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36200     },
36201
36202     getVMaxSize : function(){
36203          var cmax = this.config.maxSize || 10000;
36204          var center = this.mgr.getRegion("center");
36205          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36206     },
36207
36208     onSplitMove : function(split, newSize){
36209         this.fireEvent("resized", this, newSize);
36210     },
36211     
36212     /** 
36213      * Returns the {@link Roo.SplitBar} for this region.
36214      * @return {Roo.SplitBar}
36215      */
36216     getSplitBar : function(){
36217         return this.split;
36218     },
36219     
36220     hide : function(){
36221         this.hideSplitter();
36222         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36223     },
36224
36225     hideSplitter : function(){
36226         if(this.split){
36227             this.split.el.setLocation(-2000,-2000);
36228             this.split.el.hide();
36229         }
36230     },
36231
36232     show : function(){
36233         if(this.split){
36234             this.split.el.show();
36235         }
36236         Roo.bootstrap.layout.Split.superclass.show.call(this);
36237     },
36238     
36239     beforeSlide: function(){
36240         if(Roo.isGecko){// firefox overflow auto bug workaround
36241             this.bodyEl.clip();
36242             if(this.tabs) {
36243                 this.tabs.bodyEl.clip();
36244             }
36245             if(this.activePanel){
36246                 this.activePanel.getEl().clip();
36247                 
36248                 if(this.activePanel.beforeSlide){
36249                     this.activePanel.beforeSlide();
36250                 }
36251             }
36252         }
36253     },
36254     
36255     afterSlide : function(){
36256         if(Roo.isGecko){// firefox overflow auto bug workaround
36257             this.bodyEl.unclip();
36258             if(this.tabs) {
36259                 this.tabs.bodyEl.unclip();
36260             }
36261             if(this.activePanel){
36262                 this.activePanel.getEl().unclip();
36263                 if(this.activePanel.afterSlide){
36264                     this.activePanel.afterSlide();
36265                 }
36266             }
36267         }
36268     },
36269
36270     initAutoHide : function(){
36271         if(this.autoHide !== false){
36272             if(!this.autoHideHd){
36273                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36274                 this.autoHideHd = {
36275                     "mouseout": function(e){
36276                         if(!e.within(this.el, true)){
36277                             st.delay(500);
36278                         }
36279                     },
36280                     "mouseover" : function(e){
36281                         st.cancel();
36282                     },
36283                     scope : this
36284                 };
36285             }
36286             this.el.on(this.autoHideHd);
36287         }
36288     },
36289
36290     clearAutoHide : function(){
36291         if(this.autoHide !== false){
36292             this.el.un("mouseout", this.autoHideHd.mouseout);
36293             this.el.un("mouseover", this.autoHideHd.mouseover);
36294         }
36295     },
36296
36297     clearMonitor : function(){
36298         Roo.get(document).un("click", this.slideInIf, this);
36299     },
36300
36301     // these names are backwards but not changed for compat
36302     slideOut : function(){
36303         if(this.isSlid || this.el.hasActiveFx()){
36304             return;
36305         }
36306         this.isSlid = true;
36307         if(this.collapseBtn){
36308             this.collapseBtn.hide();
36309         }
36310         this.closeBtnState = this.closeBtn.getStyle('display');
36311         this.closeBtn.hide();
36312         if(this.stickBtn){
36313             this.stickBtn.show();
36314         }
36315         this.el.show();
36316         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36317         this.beforeSlide();
36318         this.el.setStyle("z-index", 10001);
36319         this.el.slideIn(this.getSlideAnchor(), {
36320             callback: function(){
36321                 this.afterSlide();
36322                 this.initAutoHide();
36323                 Roo.get(document).on("click", this.slideInIf, this);
36324                 this.fireEvent("slideshow", this);
36325             },
36326             scope: this,
36327             block: true
36328         });
36329     },
36330
36331     afterSlideIn : function(){
36332         this.clearAutoHide();
36333         this.isSlid = false;
36334         this.clearMonitor();
36335         this.el.setStyle("z-index", "");
36336         if(this.collapseBtn){
36337             this.collapseBtn.show();
36338         }
36339         this.closeBtn.setStyle('display', this.closeBtnState);
36340         if(this.stickBtn){
36341             this.stickBtn.hide();
36342         }
36343         this.fireEvent("slidehide", this);
36344     },
36345
36346     slideIn : function(cb){
36347         if(!this.isSlid || this.el.hasActiveFx()){
36348             Roo.callback(cb);
36349             return;
36350         }
36351         this.isSlid = false;
36352         this.beforeSlide();
36353         this.el.slideOut(this.getSlideAnchor(), {
36354             callback: function(){
36355                 this.el.setLeftTop(-10000, -10000);
36356                 this.afterSlide();
36357                 this.afterSlideIn();
36358                 Roo.callback(cb);
36359             },
36360             scope: this,
36361             block: true
36362         });
36363     },
36364     
36365     slideInIf : function(e){
36366         if(!e.within(this.el)){
36367             this.slideIn();
36368         }
36369     },
36370
36371     animateCollapse : function(){
36372         this.beforeSlide();
36373         this.el.setStyle("z-index", 20000);
36374         var anchor = this.getSlideAnchor();
36375         this.el.slideOut(anchor, {
36376             callback : function(){
36377                 this.el.setStyle("z-index", "");
36378                 this.collapsedEl.slideIn(anchor, {duration:.3});
36379                 this.afterSlide();
36380                 this.el.setLocation(-10000,-10000);
36381                 this.el.hide();
36382                 this.fireEvent("collapsed", this);
36383             },
36384             scope: this,
36385             block: true
36386         });
36387     },
36388
36389     animateExpand : function(){
36390         this.beforeSlide();
36391         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36392         this.el.setStyle("z-index", 20000);
36393         this.collapsedEl.hide({
36394             duration:.1
36395         });
36396         this.el.slideIn(this.getSlideAnchor(), {
36397             callback : function(){
36398                 this.el.setStyle("z-index", "");
36399                 this.afterSlide();
36400                 if(this.split){
36401                     this.split.el.show();
36402                 }
36403                 this.fireEvent("invalidated", this);
36404                 this.fireEvent("expanded", this);
36405             },
36406             scope: this,
36407             block: true
36408         });
36409     },
36410
36411     anchors : {
36412         "west" : "left",
36413         "east" : "right",
36414         "north" : "top",
36415         "south" : "bottom"
36416     },
36417
36418     sanchors : {
36419         "west" : "l",
36420         "east" : "r",
36421         "north" : "t",
36422         "south" : "b"
36423     },
36424
36425     canchors : {
36426         "west" : "tl-tr",
36427         "east" : "tr-tl",
36428         "north" : "tl-bl",
36429         "south" : "bl-tl"
36430     },
36431
36432     getAnchor : function(){
36433         return this.anchors[this.position];
36434     },
36435
36436     getCollapseAnchor : function(){
36437         return this.canchors[this.position];
36438     },
36439
36440     getSlideAnchor : function(){
36441         return this.sanchors[this.position];
36442     },
36443
36444     getAlignAdj : function(){
36445         var cm = this.cmargins;
36446         switch(this.position){
36447             case "west":
36448                 return [0, 0];
36449             break;
36450             case "east":
36451                 return [0, 0];
36452             break;
36453             case "north":
36454                 return [0, 0];
36455             break;
36456             case "south":
36457                 return [0, 0];
36458             break;
36459         }
36460     },
36461
36462     getExpandAdj : function(){
36463         var c = this.collapsedEl, cm = this.cmargins;
36464         switch(this.position){
36465             case "west":
36466                 return [-(cm.right+c.getWidth()+cm.left), 0];
36467             break;
36468             case "east":
36469                 return [cm.right+c.getWidth()+cm.left, 0];
36470             break;
36471             case "north":
36472                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36473             break;
36474             case "south":
36475                 return [0, cm.top+cm.bottom+c.getHeight()];
36476             break;
36477         }
36478     }
36479 });/*
36480  * Based on:
36481  * Ext JS Library 1.1.1
36482  * Copyright(c) 2006-2007, Ext JS, LLC.
36483  *
36484  * Originally Released Under LGPL - original licence link has changed is not relivant.
36485  *
36486  * Fork - LGPL
36487  * <script type="text/javascript">
36488  */
36489 /*
36490  * These classes are private internal classes
36491  */
36492 Roo.bootstrap.layout.Center = function(config){
36493     config.region = "center";
36494     Roo.bootstrap.layout.Region.call(this, config);
36495     this.visible = true;
36496     this.minWidth = config.minWidth || 20;
36497     this.minHeight = config.minHeight || 20;
36498 };
36499
36500 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36501     hide : function(){
36502         // center panel can't be hidden
36503     },
36504     
36505     show : function(){
36506         // center panel can't be hidden
36507     },
36508     
36509     getMinWidth: function(){
36510         return this.minWidth;
36511     },
36512     
36513     getMinHeight: function(){
36514         return this.minHeight;
36515     }
36516 });
36517
36518
36519
36520
36521  
36522
36523
36524
36525
36526
36527 Roo.bootstrap.layout.North = function(config)
36528 {
36529     config.region = 'north';
36530     config.cursor = 'n-resize';
36531     
36532     Roo.bootstrap.layout.Split.call(this, config);
36533     
36534     
36535     if(this.split){
36536         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36537         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36538         this.split.el.addClass("roo-layout-split-v");
36539     }
36540     var size = config.initialSize || config.height;
36541     if(typeof size != "undefined"){
36542         this.el.setHeight(size);
36543     }
36544 };
36545 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36546 {
36547     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36548     
36549     
36550     
36551     getBox : function(){
36552         if(this.collapsed){
36553             return this.collapsedEl.getBox();
36554         }
36555         var box = this.el.getBox();
36556         if(this.split){
36557             box.height += this.split.el.getHeight();
36558         }
36559         return box;
36560     },
36561     
36562     updateBox : function(box){
36563         if(this.split && !this.collapsed){
36564             box.height -= this.split.el.getHeight();
36565             this.split.el.setLeft(box.x);
36566             this.split.el.setTop(box.y+box.height);
36567             this.split.el.setWidth(box.width);
36568         }
36569         if(this.collapsed){
36570             this.updateBody(box.width, null);
36571         }
36572         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36573     }
36574 });
36575
36576
36577
36578
36579
36580 Roo.bootstrap.layout.South = function(config){
36581     config.region = 'south';
36582     config.cursor = 's-resize';
36583     Roo.bootstrap.layout.Split.call(this, config);
36584     if(this.split){
36585         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36586         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36587         this.split.el.addClass("roo-layout-split-v");
36588     }
36589     var size = config.initialSize || config.height;
36590     if(typeof size != "undefined"){
36591         this.el.setHeight(size);
36592     }
36593 };
36594
36595 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36596     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36597     getBox : function(){
36598         if(this.collapsed){
36599             return this.collapsedEl.getBox();
36600         }
36601         var box = this.el.getBox();
36602         if(this.split){
36603             var sh = this.split.el.getHeight();
36604             box.height += sh;
36605             box.y -= sh;
36606         }
36607         return box;
36608     },
36609     
36610     updateBox : function(box){
36611         if(this.split && !this.collapsed){
36612             var sh = this.split.el.getHeight();
36613             box.height -= sh;
36614             box.y += sh;
36615             this.split.el.setLeft(box.x);
36616             this.split.el.setTop(box.y-sh);
36617             this.split.el.setWidth(box.width);
36618         }
36619         if(this.collapsed){
36620             this.updateBody(box.width, null);
36621         }
36622         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36623     }
36624 });
36625
36626 Roo.bootstrap.layout.East = function(config){
36627     config.region = "east";
36628     config.cursor = "e-resize";
36629     Roo.bootstrap.layout.Split.call(this, config);
36630     if(this.split){
36631         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36632         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36633         this.split.el.addClass("roo-layout-split-h");
36634     }
36635     var size = config.initialSize || config.width;
36636     if(typeof size != "undefined"){
36637         this.el.setWidth(size);
36638     }
36639 };
36640 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36641     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36642     getBox : function(){
36643         if(this.collapsed){
36644             return this.collapsedEl.getBox();
36645         }
36646         var box = this.el.getBox();
36647         if(this.split){
36648             var sw = this.split.el.getWidth();
36649             box.width += sw;
36650             box.x -= sw;
36651         }
36652         return box;
36653     },
36654
36655     updateBox : function(box){
36656         if(this.split && !this.collapsed){
36657             var sw = this.split.el.getWidth();
36658             box.width -= sw;
36659             this.split.el.setLeft(box.x);
36660             this.split.el.setTop(box.y);
36661             this.split.el.setHeight(box.height);
36662             box.x += sw;
36663         }
36664         if(this.collapsed){
36665             this.updateBody(null, box.height);
36666         }
36667         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36668     }
36669 });
36670
36671 Roo.bootstrap.layout.West = function(config){
36672     config.region = "west";
36673     config.cursor = "w-resize";
36674     
36675     Roo.bootstrap.layout.Split.call(this, config);
36676     if(this.split){
36677         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36678         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36679         this.split.el.addClass("roo-layout-split-h");
36680     }
36681     
36682 };
36683 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36684     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36685     
36686     onRender: function(ctr, pos)
36687     {
36688         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36689         var size = this.config.initialSize || this.config.width;
36690         if(typeof size != "undefined"){
36691             this.el.setWidth(size);
36692         }
36693     },
36694     
36695     getBox : function(){
36696         if(this.collapsed){
36697             return this.collapsedEl.getBox();
36698         }
36699         var box = this.el.getBox();
36700         if(this.split){
36701             box.width += this.split.el.getWidth();
36702         }
36703         return box;
36704     },
36705     
36706     updateBox : function(box){
36707         if(this.split && !this.collapsed){
36708             var sw = this.split.el.getWidth();
36709             box.width -= sw;
36710             this.split.el.setLeft(box.x+box.width);
36711             this.split.el.setTop(box.y);
36712             this.split.el.setHeight(box.height);
36713         }
36714         if(this.collapsed){
36715             this.updateBody(null, box.height);
36716         }
36717         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36718     }
36719 });
36720 Roo.namespace("Roo.bootstrap.panel");/*
36721  * Based on:
36722  * Ext JS Library 1.1.1
36723  * Copyright(c) 2006-2007, Ext JS, LLC.
36724  *
36725  * Originally Released Under LGPL - original licence link has changed is not relivant.
36726  *
36727  * Fork - LGPL
36728  * <script type="text/javascript">
36729  */
36730 /**
36731  * @class Roo.ContentPanel
36732  * @extends Roo.util.Observable
36733  * A basic ContentPanel element.
36734  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36735  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36736  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
36737  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36738  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36739  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36740  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36741  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36742  * @cfg {String} title          The title for this panel
36743  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36744  * @cfg {String} url            Calls {@link #setUrl} with this value
36745  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36746  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36747  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36748  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36749  * @cfg {Boolean} badges render the badges
36750
36751  * @constructor
36752  * Create a new ContentPanel.
36753  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36754  * @param {String/Object} config A string to set only the title or a config object
36755  * @param {String} content (optional) Set the HTML content for this panel
36756  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36757  */
36758 Roo.bootstrap.panel.Content = function( config){
36759     
36760     this.tpl = config.tpl || false;
36761     
36762     var el = config.el;
36763     var content = config.content;
36764
36765     if(config.autoCreate){ // xtype is available if this is called from factory
36766         el = Roo.id();
36767     }
36768     this.el = Roo.get(el);
36769     if(!this.el && config && config.autoCreate){
36770         if(typeof config.autoCreate == "object"){
36771             if(!config.autoCreate.id){
36772                 config.autoCreate.id = config.id||el;
36773             }
36774             this.el = Roo.DomHelper.append(document.body,
36775                         config.autoCreate, true);
36776         }else{
36777             var elcfg =  {   tag: "div",
36778                             cls: "roo-layout-inactive-content",
36779                             id: config.id||el
36780                             };
36781             if (config.html) {
36782                 elcfg.html = config.html;
36783                 
36784             }
36785                         
36786             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36787         }
36788     } 
36789     this.closable = false;
36790     this.loaded = false;
36791     this.active = false;
36792    
36793       
36794     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36795         
36796         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36797         
36798         this.wrapEl = this.el; //this.el.wrap();
36799         var ti = [];
36800         if (config.toolbar.items) {
36801             ti = config.toolbar.items ;
36802             delete config.toolbar.items ;
36803         }
36804         
36805         var nitems = [];
36806         this.toolbar.render(this.wrapEl, 'before');
36807         for(var i =0;i < ti.length;i++) {
36808           //  Roo.log(['add child', items[i]]);
36809             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36810         }
36811         this.toolbar.items = nitems;
36812         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36813         delete config.toolbar;
36814         
36815     }
36816     /*
36817     // xtype created footer. - not sure if will work as we normally have to render first..
36818     if (this.footer && !this.footer.el && this.footer.xtype) {
36819         if (!this.wrapEl) {
36820             this.wrapEl = this.el.wrap();
36821         }
36822     
36823         this.footer.container = this.wrapEl.createChild();
36824          
36825         this.footer = Roo.factory(this.footer, Roo);
36826         
36827     }
36828     */
36829     
36830      if(typeof config == "string"){
36831         this.title = config;
36832     }else{
36833         Roo.apply(this, config);
36834     }
36835     
36836     if(this.resizeEl){
36837         this.resizeEl = Roo.get(this.resizeEl, true);
36838     }else{
36839         this.resizeEl = this.el;
36840     }
36841     // handle view.xtype
36842     
36843  
36844     
36845     
36846     this.addEvents({
36847         /**
36848          * @event activate
36849          * Fires when this panel is activated. 
36850          * @param {Roo.ContentPanel} this
36851          */
36852         "activate" : true,
36853         /**
36854          * @event deactivate
36855          * Fires when this panel is activated. 
36856          * @param {Roo.ContentPanel} this
36857          */
36858         "deactivate" : true,
36859
36860         /**
36861          * @event resize
36862          * Fires when this panel is resized if fitToFrame is true.
36863          * @param {Roo.ContentPanel} this
36864          * @param {Number} width The width after any component adjustments
36865          * @param {Number} height The height after any component adjustments
36866          */
36867         "resize" : true,
36868         
36869          /**
36870          * @event render
36871          * Fires when this tab is created
36872          * @param {Roo.ContentPanel} this
36873          */
36874         "render" : true
36875         
36876         
36877         
36878     });
36879     
36880
36881     
36882     
36883     if(this.autoScroll){
36884         this.resizeEl.setStyle("overflow", "auto");
36885     } else {
36886         // fix randome scrolling
36887         //this.el.on('scroll', function() {
36888         //    Roo.log('fix random scolling');
36889         //    this.scrollTo('top',0); 
36890         //});
36891     }
36892     content = content || this.content;
36893     if(content){
36894         this.setContent(content);
36895     }
36896     if(config && config.url){
36897         this.setUrl(this.url, this.params, this.loadOnce);
36898     }
36899     
36900     
36901     
36902     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36903     
36904     if (this.view && typeof(this.view.xtype) != 'undefined') {
36905         this.view.el = this.el.appendChild(document.createElement("div"));
36906         this.view = Roo.factory(this.view); 
36907         this.view.render  &&  this.view.render(false, '');  
36908     }
36909     
36910     
36911     this.fireEvent('render', this);
36912 };
36913
36914 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36915     
36916     tabTip : '',
36917     
36918     setRegion : function(region){
36919         this.region = region;
36920         this.setActiveClass(region && !this.background);
36921     },
36922     
36923     
36924     setActiveClass: function(state)
36925     {
36926         if(state){
36927            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36928            this.el.setStyle('position','relative');
36929         }else{
36930            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36931            this.el.setStyle('position', 'absolute');
36932         } 
36933     },
36934     
36935     /**
36936      * Returns the toolbar for this Panel if one was configured. 
36937      * @return {Roo.Toolbar} 
36938      */
36939     getToolbar : function(){
36940         return this.toolbar;
36941     },
36942     
36943     setActiveState : function(active)
36944     {
36945         this.active = active;
36946         this.setActiveClass(active);
36947         if(!active){
36948             if(this.fireEvent("deactivate", this) === false){
36949                 return false;
36950             }
36951             return true;
36952         }
36953         this.fireEvent("activate", this);
36954         return true;
36955     },
36956     /**
36957      * Updates this panel's element
36958      * @param {String} content The new content
36959      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36960     */
36961     setContent : function(content, loadScripts){
36962         this.el.update(content, loadScripts);
36963     },
36964
36965     ignoreResize : function(w, h){
36966         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36967             return true;
36968         }else{
36969             this.lastSize = {width: w, height: h};
36970             return false;
36971         }
36972     },
36973     /**
36974      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36975      * @return {Roo.UpdateManager} The UpdateManager
36976      */
36977     getUpdateManager : function(){
36978         return this.el.getUpdateManager();
36979     },
36980      /**
36981      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36982      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
36983 <pre><code>
36984 panel.load({
36985     url: "your-url.php",
36986     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36987     callback: yourFunction,
36988     scope: yourObject, //(optional scope)
36989     discardUrl: false,
36990     nocache: false,
36991     text: "Loading...",
36992     timeout: 30,
36993     scripts: false
36994 });
36995 </code></pre>
36996      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36997      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
36998      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
36999      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37000      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
37001      * @return {Roo.ContentPanel} this
37002      */
37003     load : function(){
37004         var um = this.el.getUpdateManager();
37005         um.update.apply(um, arguments);
37006         return this;
37007     },
37008
37009
37010     /**
37011      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
37012      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37013      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
37014      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
37015      * @return {Roo.UpdateManager} The UpdateManager
37016      */
37017     setUrl : function(url, params, loadOnce){
37018         if(this.refreshDelegate){
37019             this.removeListener("activate", this.refreshDelegate);
37020         }
37021         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37022         this.on("activate", this.refreshDelegate);
37023         return this.el.getUpdateManager();
37024     },
37025     
37026     _handleRefresh : function(url, params, loadOnce){
37027         if(!loadOnce || !this.loaded){
37028             var updater = this.el.getUpdateManager();
37029             updater.update(url, params, this._setLoaded.createDelegate(this));
37030         }
37031     },
37032     
37033     _setLoaded : function(){
37034         this.loaded = true;
37035     }, 
37036     
37037     /**
37038      * Returns this panel's id
37039      * @return {String} 
37040      */
37041     getId : function(){
37042         return this.el.id;
37043     },
37044     
37045     /** 
37046      * Returns this panel's element - used by regiosn to add.
37047      * @return {Roo.Element} 
37048      */
37049     getEl : function(){
37050         return this.wrapEl || this.el;
37051     },
37052     
37053    
37054     
37055     adjustForComponents : function(width, height)
37056     {
37057         //Roo.log('adjustForComponents ');
37058         if(this.resizeEl != this.el){
37059             width -= this.el.getFrameWidth('lr');
37060             height -= this.el.getFrameWidth('tb');
37061         }
37062         if(this.toolbar){
37063             var te = this.toolbar.getEl();
37064             te.setWidth(width);
37065             height -= te.getHeight();
37066         }
37067         if(this.footer){
37068             var te = this.footer.getEl();
37069             te.setWidth(width);
37070             height -= te.getHeight();
37071         }
37072         
37073         
37074         if(this.adjustments){
37075             width += this.adjustments[0];
37076             height += this.adjustments[1];
37077         }
37078         return {"width": width, "height": height};
37079     },
37080     
37081     setSize : function(width, height){
37082         if(this.fitToFrame && !this.ignoreResize(width, height)){
37083             if(this.fitContainer && this.resizeEl != this.el){
37084                 this.el.setSize(width, height);
37085             }
37086             var size = this.adjustForComponents(width, height);
37087             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37088             this.fireEvent('resize', this, size.width, size.height);
37089         }
37090     },
37091     
37092     /**
37093      * Returns this panel's title
37094      * @return {String} 
37095      */
37096     getTitle : function(){
37097         
37098         if (typeof(this.title) != 'object') {
37099             return this.title;
37100         }
37101         
37102         var t = '';
37103         for (var k in this.title) {
37104             if (!this.title.hasOwnProperty(k)) {
37105                 continue;
37106             }
37107             
37108             if (k.indexOf('-') >= 0) {
37109                 var s = k.split('-');
37110                 for (var i = 0; i<s.length; i++) {
37111                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37112                 }
37113             } else {
37114                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37115             }
37116         }
37117         return t;
37118     },
37119     
37120     /**
37121      * Set this panel's title
37122      * @param {String} title
37123      */
37124     setTitle : function(title){
37125         this.title = title;
37126         if(this.region){
37127             this.region.updatePanelTitle(this, title);
37128         }
37129     },
37130     
37131     /**
37132      * Returns true is this panel was configured to be closable
37133      * @return {Boolean} 
37134      */
37135     isClosable : function(){
37136         return this.closable;
37137     },
37138     
37139     beforeSlide : function(){
37140         this.el.clip();
37141         this.resizeEl.clip();
37142     },
37143     
37144     afterSlide : function(){
37145         this.el.unclip();
37146         this.resizeEl.unclip();
37147     },
37148     
37149     /**
37150      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37151      *   Will fail silently if the {@link #setUrl} method has not been called.
37152      *   This does not activate the panel, just updates its content.
37153      */
37154     refresh : function(){
37155         if(this.refreshDelegate){
37156            this.loaded = false;
37157            this.refreshDelegate();
37158         }
37159     },
37160     
37161     /**
37162      * Destroys this panel
37163      */
37164     destroy : function(){
37165         this.el.removeAllListeners();
37166         var tempEl = document.createElement("span");
37167         tempEl.appendChild(this.el.dom);
37168         tempEl.innerHTML = "";
37169         this.el.remove();
37170         this.el = null;
37171     },
37172     
37173     /**
37174      * form - if the content panel contains a form - this is a reference to it.
37175      * @type {Roo.form.Form}
37176      */
37177     form : false,
37178     /**
37179      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37180      *    This contains a reference to it.
37181      * @type {Roo.View}
37182      */
37183     view : false,
37184     
37185       /**
37186      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37187      * <pre><code>
37188
37189 layout.addxtype({
37190        xtype : 'Form',
37191        items: [ .... ]
37192    }
37193 );
37194
37195 </code></pre>
37196      * @param {Object} cfg Xtype definition of item to add.
37197      */
37198     
37199     
37200     getChildContainer: function () {
37201         return this.getEl();
37202     }
37203     
37204     
37205     /*
37206         var  ret = new Roo.factory(cfg);
37207         return ret;
37208         
37209         
37210         // add form..
37211         if (cfg.xtype.match(/^Form$/)) {
37212             
37213             var el;
37214             //if (this.footer) {
37215             //    el = this.footer.container.insertSibling(false, 'before');
37216             //} else {
37217                 el = this.el.createChild();
37218             //}
37219
37220             this.form = new  Roo.form.Form(cfg);
37221             
37222             
37223             if ( this.form.allItems.length) {
37224                 this.form.render(el.dom);
37225             }
37226             return this.form;
37227         }
37228         // should only have one of theses..
37229         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37230             // views.. should not be just added - used named prop 'view''
37231             
37232             cfg.el = this.el.appendChild(document.createElement("div"));
37233             // factory?
37234             
37235             var ret = new Roo.factory(cfg);
37236              
37237              ret.render && ret.render(false, ''); // render blank..
37238             this.view = ret;
37239             return ret;
37240         }
37241         return false;
37242     }
37243     \*/
37244 });
37245  
37246 /**
37247  * @class Roo.bootstrap.panel.Grid
37248  * @extends Roo.bootstrap.panel.Content
37249  * @constructor
37250  * Create a new GridPanel.
37251  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37252  * @param {Object} config A the config object
37253   
37254  */
37255
37256
37257
37258 Roo.bootstrap.panel.Grid = function(config)
37259 {
37260     
37261       
37262     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37263         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37264
37265     config.el = this.wrapper;
37266     //this.el = this.wrapper;
37267     
37268       if (config.container) {
37269         // ctor'ed from a Border/panel.grid
37270         
37271         
37272         this.wrapper.setStyle("overflow", "hidden");
37273         this.wrapper.addClass('roo-grid-container');
37274
37275     }
37276     
37277     
37278     if(config.toolbar){
37279         var tool_el = this.wrapper.createChild();    
37280         this.toolbar = Roo.factory(config.toolbar);
37281         var ti = [];
37282         if (config.toolbar.items) {
37283             ti = config.toolbar.items ;
37284             delete config.toolbar.items ;
37285         }
37286         
37287         var nitems = [];
37288         this.toolbar.render(tool_el);
37289         for(var i =0;i < ti.length;i++) {
37290           //  Roo.log(['add child', items[i]]);
37291             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37292         }
37293         this.toolbar.items = nitems;
37294         
37295         delete config.toolbar;
37296     }
37297     
37298     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37299     config.grid.scrollBody = true;;
37300     config.grid.monitorWindowResize = false; // turn off autosizing
37301     config.grid.autoHeight = false;
37302     config.grid.autoWidth = false;
37303     
37304     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37305     
37306     if (config.background) {
37307         // render grid on panel activation (if panel background)
37308         this.on('activate', function(gp) {
37309             if (!gp.grid.rendered) {
37310                 gp.grid.render(this.wrapper);
37311                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37312             }
37313         });
37314             
37315     } else {
37316         this.grid.render(this.wrapper);
37317         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37318
37319     }
37320     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37321     // ??? needed ??? config.el = this.wrapper;
37322     
37323     
37324     
37325   
37326     // xtype created footer. - not sure if will work as we normally have to render first..
37327     if (this.footer && !this.footer.el && this.footer.xtype) {
37328         
37329         var ctr = this.grid.getView().getFooterPanel(true);
37330         this.footer.dataSource = this.grid.dataSource;
37331         this.footer = Roo.factory(this.footer, Roo);
37332         this.footer.render(ctr);
37333         
37334     }
37335     
37336     
37337     
37338     
37339      
37340 };
37341
37342 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37343     getId : function(){
37344         return this.grid.id;
37345     },
37346     
37347     /**
37348      * Returns the grid for this panel
37349      * @return {Roo.bootstrap.Table} 
37350      */
37351     getGrid : function(){
37352         return this.grid;    
37353     },
37354     
37355     setSize : function(width, height){
37356         if(!this.ignoreResize(width, height)){
37357             var grid = this.grid;
37358             var size = this.adjustForComponents(width, height);
37359             var gridel = grid.getGridEl();
37360             gridel.setSize(size.width, size.height);
37361             /*
37362             var thd = grid.getGridEl().select('thead',true).first();
37363             var tbd = grid.getGridEl().select('tbody', true).first();
37364             if (tbd) {
37365                 tbd.setSize(width, height - thd.getHeight());
37366             }
37367             */
37368             grid.autoSize();
37369         }
37370     },
37371      
37372     
37373     
37374     beforeSlide : function(){
37375         this.grid.getView().scroller.clip();
37376     },
37377     
37378     afterSlide : function(){
37379         this.grid.getView().scroller.unclip();
37380     },
37381     
37382     destroy : function(){
37383         this.grid.destroy();
37384         delete this.grid;
37385         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37386     }
37387 });
37388
37389 /**
37390  * @class Roo.bootstrap.panel.Nest
37391  * @extends Roo.bootstrap.panel.Content
37392  * @constructor
37393  * Create a new Panel, that can contain a layout.Border.
37394  * 
37395  * 
37396  * @param {Roo.BorderLayout} layout The layout for this panel
37397  * @param {String/Object} config A string to set only the title or a config object
37398  */
37399 Roo.bootstrap.panel.Nest = function(config)
37400 {
37401     // construct with only one argument..
37402     /* FIXME - implement nicer consturctors
37403     if (layout.layout) {
37404         config = layout;
37405         layout = config.layout;
37406         delete config.layout;
37407     }
37408     if (layout.xtype && !layout.getEl) {
37409         // then layout needs constructing..
37410         layout = Roo.factory(layout, Roo);
37411     }
37412     */
37413     
37414     config.el =  config.layout.getEl();
37415     
37416     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37417     
37418     config.layout.monitorWindowResize = false; // turn off autosizing
37419     this.layout = config.layout;
37420     this.layout.getEl().addClass("roo-layout-nested-layout");
37421     
37422     
37423     
37424     
37425 };
37426
37427 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37428
37429     setSize : function(width, height){
37430         if(!this.ignoreResize(width, height)){
37431             var size = this.adjustForComponents(width, height);
37432             var el = this.layout.getEl();
37433             if (size.height < 1) {
37434                 el.setWidth(size.width);   
37435             } else {
37436                 el.setSize(size.width, size.height);
37437             }
37438             var touch = el.dom.offsetWidth;
37439             this.layout.layout();
37440             // ie requires a double layout on the first pass
37441             if(Roo.isIE && !this.initialized){
37442                 this.initialized = true;
37443                 this.layout.layout();
37444             }
37445         }
37446     },
37447     
37448     // activate all subpanels if not currently active..
37449     
37450     setActiveState : function(active){
37451         this.active = active;
37452         this.setActiveClass(active);
37453         
37454         if(!active){
37455             this.fireEvent("deactivate", this);
37456             return;
37457         }
37458         
37459         this.fireEvent("activate", this);
37460         // not sure if this should happen before or after..
37461         if (!this.layout) {
37462             return; // should not happen..
37463         }
37464         var reg = false;
37465         for (var r in this.layout.regions) {
37466             reg = this.layout.getRegion(r);
37467             if (reg.getActivePanel()) {
37468                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37469                 reg.setActivePanel(reg.getActivePanel());
37470                 continue;
37471             }
37472             if (!reg.panels.length) {
37473                 continue;
37474             }
37475             reg.showPanel(reg.getPanel(0));
37476         }
37477         
37478         
37479         
37480         
37481     },
37482     
37483     /**
37484      * Returns the nested BorderLayout for this panel
37485      * @return {Roo.BorderLayout} 
37486      */
37487     getLayout : function(){
37488         return this.layout;
37489     },
37490     
37491      /**
37492      * Adds a xtype elements to the layout of the nested panel
37493      * <pre><code>
37494
37495 panel.addxtype({
37496        xtype : 'ContentPanel',
37497        region: 'west',
37498        items: [ .... ]
37499    }
37500 );
37501
37502 panel.addxtype({
37503         xtype : 'NestedLayoutPanel',
37504         region: 'west',
37505         layout: {
37506            center: { },
37507            west: { }   
37508         },
37509         items : [ ... list of content panels or nested layout panels.. ]
37510    }
37511 );
37512 </code></pre>
37513      * @param {Object} cfg Xtype definition of item to add.
37514      */
37515     addxtype : function(cfg) {
37516         return this.layout.addxtype(cfg);
37517     
37518     }
37519 });        /*
37520  * Based on:
37521  * Ext JS Library 1.1.1
37522  * Copyright(c) 2006-2007, Ext JS, LLC.
37523  *
37524  * Originally Released Under LGPL - original licence link has changed is not relivant.
37525  *
37526  * Fork - LGPL
37527  * <script type="text/javascript">
37528  */
37529 /**
37530  * @class Roo.TabPanel
37531  * @extends Roo.util.Observable
37532  * A lightweight tab container.
37533  * <br><br>
37534  * Usage:
37535  * <pre><code>
37536 // basic tabs 1, built from existing content
37537 var tabs = new Roo.TabPanel("tabs1");
37538 tabs.addTab("script", "View Script");
37539 tabs.addTab("markup", "View Markup");
37540 tabs.activate("script");
37541
37542 // more advanced tabs, built from javascript
37543 var jtabs = new Roo.TabPanel("jtabs");
37544 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37545
37546 // set up the UpdateManager
37547 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37548 var updater = tab2.getUpdateManager();
37549 updater.setDefaultUrl("ajax1.htm");
37550 tab2.on('activate', updater.refresh, updater, true);
37551
37552 // Use setUrl for Ajax loading
37553 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37554 tab3.setUrl("ajax2.htm", null, true);
37555
37556 // Disabled tab
37557 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37558 tab4.disable();
37559
37560 jtabs.activate("jtabs-1");
37561  * </code></pre>
37562  * @constructor
37563  * Create a new TabPanel.
37564  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37565  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37566  */
37567 Roo.bootstrap.panel.Tabs = function(config){
37568     /**
37569     * The container element for this TabPanel.
37570     * @type Roo.Element
37571     */
37572     this.el = Roo.get(config.el);
37573     delete config.el;
37574     if(config){
37575         if(typeof config == "boolean"){
37576             this.tabPosition = config ? "bottom" : "top";
37577         }else{
37578             Roo.apply(this, config);
37579         }
37580     }
37581     
37582     if(this.tabPosition == "bottom"){
37583         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37584         this.el.addClass("roo-tabs-bottom");
37585     }
37586     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37587     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37588     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37589     if(Roo.isIE){
37590         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37591     }
37592     if(this.tabPosition != "bottom"){
37593         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37594          * @type Roo.Element
37595          */
37596         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37597         this.el.addClass("roo-tabs-top");
37598     }
37599     this.items = [];
37600
37601     this.bodyEl.setStyle("position", "relative");
37602
37603     this.active = null;
37604     this.activateDelegate = this.activate.createDelegate(this);
37605
37606     this.addEvents({
37607         /**
37608          * @event tabchange
37609          * Fires when the active tab changes
37610          * @param {Roo.TabPanel} this
37611          * @param {Roo.TabPanelItem} activePanel The new active tab
37612          */
37613         "tabchange": true,
37614         /**
37615          * @event beforetabchange
37616          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37617          * @param {Roo.TabPanel} this
37618          * @param {Object} e Set cancel to true on this object to cancel the tab change
37619          * @param {Roo.TabPanelItem} tab The tab being changed to
37620          */
37621         "beforetabchange" : true
37622     });
37623
37624     Roo.EventManager.onWindowResize(this.onResize, this);
37625     this.cpad = this.el.getPadding("lr");
37626     this.hiddenCount = 0;
37627
37628
37629     // toolbar on the tabbar support...
37630     if (this.toolbar) {
37631         alert("no toolbar support yet");
37632         this.toolbar  = false;
37633         /*
37634         var tcfg = this.toolbar;
37635         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37636         this.toolbar = new Roo.Toolbar(tcfg);
37637         if (Roo.isSafari) {
37638             var tbl = tcfg.container.child('table', true);
37639             tbl.setAttribute('width', '100%');
37640         }
37641         */
37642         
37643     }
37644    
37645
37646
37647     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37648 };
37649
37650 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37651     /*
37652      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37653      */
37654     tabPosition : "top",
37655     /*
37656      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37657      */
37658     currentTabWidth : 0,
37659     /*
37660      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37661      */
37662     minTabWidth : 40,
37663     /*
37664      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37665      */
37666     maxTabWidth : 250,
37667     /*
37668      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37669      */
37670     preferredTabWidth : 175,
37671     /*
37672      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37673      */
37674     resizeTabs : false,
37675     /*
37676      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37677      */
37678     monitorResize : true,
37679     /*
37680      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37681      */
37682     toolbar : false,
37683
37684     /**
37685      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37686      * @param {String} id The id of the div to use <b>or create</b>
37687      * @param {String} text The text for the tab
37688      * @param {String} content (optional) Content to put in the TabPanelItem body
37689      * @param {Boolean} closable (optional) True to create a close icon on the tab
37690      * @return {Roo.TabPanelItem} The created TabPanelItem
37691      */
37692     addTab : function(id, text, content, closable, tpl)
37693     {
37694         var item = new Roo.bootstrap.panel.TabItem({
37695             panel: this,
37696             id : id,
37697             text : text,
37698             closable : closable,
37699             tpl : tpl
37700         });
37701         this.addTabItem(item);
37702         if(content){
37703             item.setContent(content);
37704         }
37705         return item;
37706     },
37707
37708     /**
37709      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37710      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37711      * @return {Roo.TabPanelItem}
37712      */
37713     getTab : function(id){
37714         return this.items[id];
37715     },
37716
37717     /**
37718      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37719      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37720      */
37721     hideTab : function(id){
37722         var t = this.items[id];
37723         if(!t.isHidden()){
37724            t.setHidden(true);
37725            this.hiddenCount++;
37726            this.autoSizeTabs();
37727         }
37728     },
37729
37730     /**
37731      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37732      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37733      */
37734     unhideTab : function(id){
37735         var t = this.items[id];
37736         if(t.isHidden()){
37737            t.setHidden(false);
37738            this.hiddenCount--;
37739            this.autoSizeTabs();
37740         }
37741     },
37742
37743     /**
37744      * Adds an existing {@link Roo.TabPanelItem}.
37745      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37746      */
37747     addTabItem : function(item){
37748         this.items[item.id] = item;
37749         this.items.push(item);
37750       //  if(this.resizeTabs){
37751     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37752   //         this.autoSizeTabs();
37753 //        }else{
37754 //            item.autoSize();
37755        // }
37756     },
37757
37758     /**
37759      * Removes a {@link Roo.TabPanelItem}.
37760      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37761      */
37762     removeTab : function(id){
37763         var items = this.items;
37764         var tab = items[id];
37765         if(!tab) { return; }
37766         var index = items.indexOf(tab);
37767         if(this.active == tab && items.length > 1){
37768             var newTab = this.getNextAvailable(index);
37769             if(newTab) {
37770                 newTab.activate();
37771             }
37772         }
37773         this.stripEl.dom.removeChild(tab.pnode.dom);
37774         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37775             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37776         }
37777         items.splice(index, 1);
37778         delete this.items[tab.id];
37779         tab.fireEvent("close", tab);
37780         tab.purgeListeners();
37781         this.autoSizeTabs();
37782     },
37783
37784     getNextAvailable : function(start){
37785         var items = this.items;
37786         var index = start;
37787         // look for a next tab that will slide over to
37788         // replace the one being removed
37789         while(index < items.length){
37790             var item = items[++index];
37791             if(item && !item.isHidden()){
37792                 return item;
37793             }
37794         }
37795         // if one isn't found select the previous tab (on the left)
37796         index = start;
37797         while(index >= 0){
37798             var item = items[--index];
37799             if(item && !item.isHidden()){
37800                 return item;
37801             }
37802         }
37803         return null;
37804     },
37805
37806     /**
37807      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37808      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37809      */
37810     disableTab : function(id){
37811         var tab = this.items[id];
37812         if(tab && this.active != tab){
37813             tab.disable();
37814         }
37815     },
37816
37817     /**
37818      * Enables a {@link Roo.TabPanelItem} that is disabled.
37819      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37820      */
37821     enableTab : function(id){
37822         var tab = this.items[id];
37823         tab.enable();
37824     },
37825
37826     /**
37827      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37828      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37829      * @return {Roo.TabPanelItem} The TabPanelItem.
37830      */
37831     activate : function(id){
37832         var tab = this.items[id];
37833         if(!tab){
37834             return null;
37835         }
37836         if(tab == this.active || tab.disabled){
37837             return tab;
37838         }
37839         var e = {};
37840         this.fireEvent("beforetabchange", this, e, tab);
37841         if(e.cancel !== true && !tab.disabled){
37842             if(this.active){
37843                 this.active.hide();
37844             }
37845             this.active = this.items[id];
37846             this.active.show();
37847             this.fireEvent("tabchange", this, this.active);
37848         }
37849         return tab;
37850     },
37851
37852     /**
37853      * Gets the active {@link Roo.TabPanelItem}.
37854      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37855      */
37856     getActiveTab : function(){
37857         return this.active;
37858     },
37859
37860     /**
37861      * Updates the tab body element to fit the height of the container element
37862      * for overflow scrolling
37863      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37864      */
37865     syncHeight : function(targetHeight){
37866         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37867         var bm = this.bodyEl.getMargins();
37868         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37869         this.bodyEl.setHeight(newHeight);
37870         return newHeight;
37871     },
37872
37873     onResize : function(){
37874         if(this.monitorResize){
37875             this.autoSizeTabs();
37876         }
37877     },
37878
37879     /**
37880      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37881      */
37882     beginUpdate : function(){
37883         this.updating = true;
37884     },
37885
37886     /**
37887      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37888      */
37889     endUpdate : function(){
37890         this.updating = false;
37891         this.autoSizeTabs();
37892     },
37893
37894     /**
37895      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37896      */
37897     autoSizeTabs : function(){
37898         var count = this.items.length;
37899         var vcount = count - this.hiddenCount;
37900         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37901             return;
37902         }
37903         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37904         var availWidth = Math.floor(w / vcount);
37905         var b = this.stripBody;
37906         if(b.getWidth() > w){
37907             var tabs = this.items;
37908             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37909             if(availWidth < this.minTabWidth){
37910                 /*if(!this.sleft){    // incomplete scrolling code
37911                     this.createScrollButtons();
37912                 }
37913                 this.showScroll();
37914                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37915             }
37916         }else{
37917             if(this.currentTabWidth < this.preferredTabWidth){
37918                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37919             }
37920         }
37921     },
37922
37923     /**
37924      * Returns the number of tabs in this TabPanel.
37925      * @return {Number}
37926      */
37927      getCount : function(){
37928          return this.items.length;
37929      },
37930
37931     /**
37932      * Resizes all the tabs to the passed width
37933      * @param {Number} The new width
37934      */
37935     setTabWidth : function(width){
37936         this.currentTabWidth = width;
37937         for(var i = 0, len = this.items.length; i < len; i++) {
37938                 if(!this.items[i].isHidden()) {
37939                 this.items[i].setWidth(width);
37940             }
37941         }
37942     },
37943
37944     /**
37945      * Destroys this TabPanel
37946      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37947      */
37948     destroy : function(removeEl){
37949         Roo.EventManager.removeResizeListener(this.onResize, this);
37950         for(var i = 0, len = this.items.length; i < len; i++){
37951             this.items[i].purgeListeners();
37952         }
37953         if(removeEl === true){
37954             this.el.update("");
37955             this.el.remove();
37956         }
37957     },
37958     
37959     createStrip : function(container)
37960     {
37961         var strip = document.createElement("nav");
37962         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37963         container.appendChild(strip);
37964         return strip;
37965     },
37966     
37967     createStripList : function(strip)
37968     {
37969         // div wrapper for retard IE
37970         // returns the "tr" element.
37971         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37972         //'<div class="x-tabs-strip-wrap">'+
37973           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37974           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37975         return strip.firstChild; //.firstChild.firstChild.firstChild;
37976     },
37977     createBody : function(container)
37978     {
37979         var body = document.createElement("div");
37980         Roo.id(body, "tab-body");
37981         //Roo.fly(body).addClass("x-tabs-body");
37982         Roo.fly(body).addClass("tab-content");
37983         container.appendChild(body);
37984         return body;
37985     },
37986     createItemBody :function(bodyEl, id){
37987         var body = Roo.getDom(id);
37988         if(!body){
37989             body = document.createElement("div");
37990             body.id = id;
37991         }
37992         //Roo.fly(body).addClass("x-tabs-item-body");
37993         Roo.fly(body).addClass("tab-pane");
37994          bodyEl.insertBefore(body, bodyEl.firstChild);
37995         return body;
37996     },
37997     /** @private */
37998     createStripElements :  function(stripEl, text, closable, tpl)
37999     {
38000         var td = document.createElement("li"); // was td..
38001         
38002         
38003         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38004         
38005         
38006         stripEl.appendChild(td);
38007         /*if(closable){
38008             td.className = "x-tabs-closable";
38009             if(!this.closeTpl){
38010                 this.closeTpl = new Roo.Template(
38011                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38012                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38013                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38014                 );
38015             }
38016             var el = this.closeTpl.overwrite(td, {"text": text});
38017             var close = el.getElementsByTagName("div")[0];
38018             var inner = el.getElementsByTagName("em")[0];
38019             return {"el": el, "close": close, "inner": inner};
38020         } else {
38021         */
38022         // not sure what this is..
38023 //            if(!this.tabTpl){
38024                 //this.tabTpl = new Roo.Template(
38025                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38026                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38027                 //);
38028 //                this.tabTpl = new Roo.Template(
38029 //                   '<a href="#">' +
38030 //                   '<span unselectable="on"' +
38031 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38032 //                            ' >{text}</span></a>'
38033 //                );
38034 //                
38035 //            }
38036
38037
38038             var template = tpl || this.tabTpl || false;
38039             
38040             if(!template){
38041                 
38042                 template = new Roo.Template(
38043                    '<a href="#">' +
38044                    '<span unselectable="on"' +
38045                             (this.disableTooltips ? '' : ' title="{text}"') +
38046                             ' >{text}</span></a>'
38047                 );
38048             }
38049             
38050             switch (typeof(template)) {
38051                 case 'object' :
38052                     break;
38053                 case 'string' :
38054                     template = new Roo.Template(template);
38055                     break;
38056                 default :
38057                     break;
38058             }
38059             
38060             var el = template.overwrite(td, {"text": text});
38061             
38062             var inner = el.getElementsByTagName("span")[0];
38063             
38064             return {"el": el, "inner": inner};
38065             
38066     }
38067         
38068     
38069 });
38070
38071 /**
38072  * @class Roo.TabPanelItem
38073  * @extends Roo.util.Observable
38074  * Represents an individual item (tab plus body) in a TabPanel.
38075  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38076  * @param {String} id The id of this TabPanelItem
38077  * @param {String} text The text for the tab of this TabPanelItem
38078  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38079  */
38080 Roo.bootstrap.panel.TabItem = function(config){
38081     /**
38082      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38083      * @type Roo.TabPanel
38084      */
38085     this.tabPanel = config.panel;
38086     /**
38087      * The id for this TabPanelItem
38088      * @type String
38089      */
38090     this.id = config.id;
38091     /** @private */
38092     this.disabled = false;
38093     /** @private */
38094     this.text = config.text;
38095     /** @private */
38096     this.loaded = false;
38097     this.closable = config.closable;
38098
38099     /**
38100      * The body element for this TabPanelItem.
38101      * @type Roo.Element
38102      */
38103     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38104     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38105     this.bodyEl.setStyle("display", "block");
38106     this.bodyEl.setStyle("zoom", "1");
38107     //this.hideAction();
38108
38109     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38110     /** @private */
38111     this.el = Roo.get(els.el);
38112     this.inner = Roo.get(els.inner, true);
38113     this.textEl = Roo.get(this.el.dom.firstChild, true);
38114     this.pnode = Roo.get(els.el.parentNode, true);
38115 //    this.el.on("mousedown", this.onTabMouseDown, this);
38116     this.el.on("click", this.onTabClick, this);
38117     /** @private */
38118     if(config.closable){
38119         var c = Roo.get(els.close, true);
38120         c.dom.title = this.closeText;
38121         c.addClassOnOver("close-over");
38122         c.on("click", this.closeClick, this);
38123      }
38124
38125     this.addEvents({
38126          /**
38127          * @event activate
38128          * Fires when this tab becomes the active tab.
38129          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38130          * @param {Roo.TabPanelItem} this
38131          */
38132         "activate": true,
38133         /**
38134          * @event beforeclose
38135          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38136          * @param {Roo.TabPanelItem} this
38137          * @param {Object} e Set cancel to true on this object to cancel the close.
38138          */
38139         "beforeclose": true,
38140         /**
38141          * @event close
38142          * Fires when this tab is closed.
38143          * @param {Roo.TabPanelItem} this
38144          */
38145          "close": true,
38146         /**
38147          * @event deactivate
38148          * Fires when this tab is no longer the active tab.
38149          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38150          * @param {Roo.TabPanelItem} this
38151          */
38152          "deactivate" : true
38153     });
38154     this.hidden = false;
38155
38156     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38157 };
38158
38159 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38160            {
38161     purgeListeners : function(){
38162        Roo.util.Observable.prototype.purgeListeners.call(this);
38163        this.el.removeAllListeners();
38164     },
38165     /**
38166      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38167      */
38168     show : function(){
38169         this.pnode.addClass("active");
38170         this.showAction();
38171         if(Roo.isOpera){
38172             this.tabPanel.stripWrap.repaint();
38173         }
38174         this.fireEvent("activate", this.tabPanel, this);
38175     },
38176
38177     /**
38178      * Returns true if this tab is the active tab.
38179      * @return {Boolean}
38180      */
38181     isActive : function(){
38182         return this.tabPanel.getActiveTab() == this;
38183     },
38184
38185     /**
38186      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38187      */
38188     hide : function(){
38189         this.pnode.removeClass("active");
38190         this.hideAction();
38191         this.fireEvent("deactivate", this.tabPanel, this);
38192     },
38193
38194     hideAction : function(){
38195         this.bodyEl.hide();
38196         this.bodyEl.setStyle("position", "absolute");
38197         this.bodyEl.setLeft("-20000px");
38198         this.bodyEl.setTop("-20000px");
38199     },
38200
38201     showAction : function(){
38202         this.bodyEl.setStyle("position", "relative");
38203         this.bodyEl.setTop("");
38204         this.bodyEl.setLeft("");
38205         this.bodyEl.show();
38206     },
38207
38208     /**
38209      * Set the tooltip for the tab.
38210      * @param {String} tooltip The tab's tooltip
38211      */
38212     setTooltip : function(text){
38213         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38214             this.textEl.dom.qtip = text;
38215             this.textEl.dom.removeAttribute('title');
38216         }else{
38217             this.textEl.dom.title = text;
38218         }
38219     },
38220
38221     onTabClick : function(e){
38222         e.preventDefault();
38223         this.tabPanel.activate(this.id);
38224     },
38225
38226     onTabMouseDown : function(e){
38227         e.preventDefault();
38228         this.tabPanel.activate(this.id);
38229     },
38230 /*
38231     getWidth : function(){
38232         return this.inner.getWidth();
38233     },
38234
38235     setWidth : function(width){
38236         var iwidth = width - this.pnode.getPadding("lr");
38237         this.inner.setWidth(iwidth);
38238         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38239         this.pnode.setWidth(width);
38240     },
38241 */
38242     /**
38243      * Show or hide the tab
38244      * @param {Boolean} hidden True to hide or false to show.
38245      */
38246     setHidden : function(hidden){
38247         this.hidden = hidden;
38248         this.pnode.setStyle("display", hidden ? "none" : "");
38249     },
38250
38251     /**
38252      * Returns true if this tab is "hidden"
38253      * @return {Boolean}
38254      */
38255     isHidden : function(){
38256         return this.hidden;
38257     },
38258
38259     /**
38260      * Returns the text for this tab
38261      * @return {String}
38262      */
38263     getText : function(){
38264         return this.text;
38265     },
38266     /*
38267     autoSize : function(){
38268         //this.el.beginMeasure();
38269         this.textEl.setWidth(1);
38270         /*
38271          *  #2804 [new] Tabs in Roojs
38272          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38273          */
38274         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38275         //this.el.endMeasure();
38276     //},
38277
38278     /**
38279      * Sets the text for the tab (Note: this also sets the tooltip text)
38280      * @param {String} text The tab's text and tooltip
38281      */
38282     setText : function(text){
38283         this.text = text;
38284         this.textEl.update(text);
38285         this.setTooltip(text);
38286         //if(!this.tabPanel.resizeTabs){
38287         //    this.autoSize();
38288         //}
38289     },
38290     /**
38291      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38292      */
38293     activate : function(){
38294         this.tabPanel.activate(this.id);
38295     },
38296
38297     /**
38298      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38299      */
38300     disable : function(){
38301         if(this.tabPanel.active != this){
38302             this.disabled = true;
38303             this.pnode.addClass("disabled");
38304         }
38305     },
38306
38307     /**
38308      * Enables this TabPanelItem if it was previously disabled.
38309      */
38310     enable : function(){
38311         this.disabled = false;
38312         this.pnode.removeClass("disabled");
38313     },
38314
38315     /**
38316      * Sets the content for this TabPanelItem.
38317      * @param {String} content The content
38318      * @param {Boolean} loadScripts true to look for and load scripts
38319      */
38320     setContent : function(content, loadScripts){
38321         this.bodyEl.update(content, loadScripts);
38322     },
38323
38324     /**
38325      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38326      * @return {Roo.UpdateManager} The UpdateManager
38327      */
38328     getUpdateManager : function(){
38329         return this.bodyEl.getUpdateManager();
38330     },
38331
38332     /**
38333      * Set a URL to be used to load the content for this TabPanelItem.
38334      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38335      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
38336      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
38337      * @return {Roo.UpdateManager} The UpdateManager
38338      */
38339     setUrl : function(url, params, loadOnce){
38340         if(this.refreshDelegate){
38341             this.un('activate', this.refreshDelegate);
38342         }
38343         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38344         this.on("activate", this.refreshDelegate);
38345         return this.bodyEl.getUpdateManager();
38346     },
38347
38348     /** @private */
38349     _handleRefresh : function(url, params, loadOnce){
38350         if(!loadOnce || !this.loaded){
38351             var updater = this.bodyEl.getUpdateManager();
38352             updater.update(url, params, this._setLoaded.createDelegate(this));
38353         }
38354     },
38355
38356     /**
38357      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38358      *   Will fail silently if the setUrl method has not been called.
38359      *   This does not activate the panel, just updates its content.
38360      */
38361     refresh : function(){
38362         if(this.refreshDelegate){
38363            this.loaded = false;
38364            this.refreshDelegate();
38365         }
38366     },
38367
38368     /** @private */
38369     _setLoaded : function(){
38370         this.loaded = true;
38371     },
38372
38373     /** @private */
38374     closeClick : function(e){
38375         var o = {};
38376         e.stopEvent();
38377         this.fireEvent("beforeclose", this, o);
38378         if(o.cancel !== true){
38379             this.tabPanel.removeTab(this.id);
38380         }
38381     },
38382     /**
38383      * The text displayed in the tooltip for the close icon.
38384      * @type String
38385      */
38386     closeText : "Close this tab"
38387 });
38388 /**
38389 *    This script refer to:
38390 *    Title: International Telephone Input
38391 *    Author: Jack O'Connor
38392 *    Code version:  v12.1.12
38393 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38394 **/
38395
38396 Roo.bootstrap.PhoneInputData = function() {
38397     var d = [
38398       [
38399         "Afghanistan (‫افغانستان‬‎)",
38400         "af",
38401         "93"
38402       ],
38403       [
38404         "Albania (Shqipëri)",
38405         "al",
38406         "355"
38407       ],
38408       [
38409         "Algeria (‫الجزائر‬‎)",
38410         "dz",
38411         "213"
38412       ],
38413       [
38414         "American Samoa",
38415         "as",
38416         "1684"
38417       ],
38418       [
38419         "Andorra",
38420         "ad",
38421         "376"
38422       ],
38423       [
38424         "Angola",
38425         "ao",
38426         "244"
38427       ],
38428       [
38429         "Anguilla",
38430         "ai",
38431         "1264"
38432       ],
38433       [
38434         "Antigua and Barbuda",
38435         "ag",
38436         "1268"
38437       ],
38438       [
38439         "Argentina",
38440         "ar",
38441         "54"
38442       ],
38443       [
38444         "Armenia (Հայաստան)",
38445         "am",
38446         "374"
38447       ],
38448       [
38449         "Aruba",
38450         "aw",
38451         "297"
38452       ],
38453       [
38454         "Australia",
38455         "au",
38456         "61",
38457         0
38458       ],
38459       [
38460         "Austria (Österreich)",
38461         "at",
38462         "43"
38463       ],
38464       [
38465         "Azerbaijan (Azərbaycan)",
38466         "az",
38467         "994"
38468       ],
38469       [
38470         "Bahamas",
38471         "bs",
38472         "1242"
38473       ],
38474       [
38475         "Bahrain (‫البحرين‬‎)",
38476         "bh",
38477         "973"
38478       ],
38479       [
38480         "Bangladesh (বাংলাদেশ)",
38481         "bd",
38482         "880"
38483       ],
38484       [
38485         "Barbados",
38486         "bb",
38487         "1246"
38488       ],
38489       [
38490         "Belarus (Беларусь)",
38491         "by",
38492         "375"
38493       ],
38494       [
38495         "Belgium (België)",
38496         "be",
38497         "32"
38498       ],
38499       [
38500         "Belize",
38501         "bz",
38502         "501"
38503       ],
38504       [
38505         "Benin (Bénin)",
38506         "bj",
38507         "229"
38508       ],
38509       [
38510         "Bermuda",
38511         "bm",
38512         "1441"
38513       ],
38514       [
38515         "Bhutan (འབྲུག)",
38516         "bt",
38517         "975"
38518       ],
38519       [
38520         "Bolivia",
38521         "bo",
38522         "591"
38523       ],
38524       [
38525         "Bosnia and Herzegovina (Босна и Херцеговина)",
38526         "ba",
38527         "387"
38528       ],
38529       [
38530         "Botswana",
38531         "bw",
38532         "267"
38533       ],
38534       [
38535         "Brazil (Brasil)",
38536         "br",
38537         "55"
38538       ],
38539       [
38540         "British Indian Ocean Territory",
38541         "io",
38542         "246"
38543       ],
38544       [
38545         "British Virgin Islands",
38546         "vg",
38547         "1284"
38548       ],
38549       [
38550         "Brunei",
38551         "bn",
38552         "673"
38553       ],
38554       [
38555         "Bulgaria (България)",
38556         "bg",
38557         "359"
38558       ],
38559       [
38560         "Burkina Faso",
38561         "bf",
38562         "226"
38563       ],
38564       [
38565         "Burundi (Uburundi)",
38566         "bi",
38567         "257"
38568       ],
38569       [
38570         "Cambodia (កម្ពុជា)",
38571         "kh",
38572         "855"
38573       ],
38574       [
38575         "Cameroon (Cameroun)",
38576         "cm",
38577         "237"
38578       ],
38579       [
38580         "Canada",
38581         "ca",
38582         "1",
38583         1,
38584         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38585       ],
38586       [
38587         "Cape Verde (Kabu Verdi)",
38588         "cv",
38589         "238"
38590       ],
38591       [
38592         "Caribbean Netherlands",
38593         "bq",
38594         "599",
38595         1
38596       ],
38597       [
38598         "Cayman Islands",
38599         "ky",
38600         "1345"
38601       ],
38602       [
38603         "Central African Republic (République centrafricaine)",
38604         "cf",
38605         "236"
38606       ],
38607       [
38608         "Chad (Tchad)",
38609         "td",
38610         "235"
38611       ],
38612       [
38613         "Chile",
38614         "cl",
38615         "56"
38616       ],
38617       [
38618         "China (中国)",
38619         "cn",
38620         "86"
38621       ],
38622       [
38623         "Christmas Island",
38624         "cx",
38625         "61",
38626         2
38627       ],
38628       [
38629         "Cocos (Keeling) Islands",
38630         "cc",
38631         "61",
38632         1
38633       ],
38634       [
38635         "Colombia",
38636         "co",
38637         "57"
38638       ],
38639       [
38640         "Comoros (‫جزر القمر‬‎)",
38641         "km",
38642         "269"
38643       ],
38644       [
38645         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38646         "cd",
38647         "243"
38648       ],
38649       [
38650         "Congo (Republic) (Congo-Brazzaville)",
38651         "cg",
38652         "242"
38653       ],
38654       [
38655         "Cook Islands",
38656         "ck",
38657         "682"
38658       ],
38659       [
38660         "Costa Rica",
38661         "cr",
38662         "506"
38663       ],
38664       [
38665         "Côte d’Ivoire",
38666         "ci",
38667         "225"
38668       ],
38669       [
38670         "Croatia (Hrvatska)",
38671         "hr",
38672         "385"
38673       ],
38674       [
38675         "Cuba",
38676         "cu",
38677         "53"
38678       ],
38679       [
38680         "Curaçao",
38681         "cw",
38682         "599",
38683         0
38684       ],
38685       [
38686         "Cyprus (Κύπρος)",
38687         "cy",
38688         "357"
38689       ],
38690       [
38691         "Czech Republic (Česká republika)",
38692         "cz",
38693         "420"
38694       ],
38695       [
38696         "Denmark (Danmark)",
38697         "dk",
38698         "45"
38699       ],
38700       [
38701         "Djibouti",
38702         "dj",
38703         "253"
38704       ],
38705       [
38706         "Dominica",
38707         "dm",
38708         "1767"
38709       ],
38710       [
38711         "Dominican Republic (República Dominicana)",
38712         "do",
38713         "1",
38714         2,
38715         ["809", "829", "849"]
38716       ],
38717       [
38718         "Ecuador",
38719         "ec",
38720         "593"
38721       ],
38722       [
38723         "Egypt (‫مصر‬‎)",
38724         "eg",
38725         "20"
38726       ],
38727       [
38728         "El Salvador",
38729         "sv",
38730         "503"
38731       ],
38732       [
38733         "Equatorial Guinea (Guinea Ecuatorial)",
38734         "gq",
38735         "240"
38736       ],
38737       [
38738         "Eritrea",
38739         "er",
38740         "291"
38741       ],
38742       [
38743         "Estonia (Eesti)",
38744         "ee",
38745         "372"
38746       ],
38747       [
38748         "Ethiopia",
38749         "et",
38750         "251"
38751       ],
38752       [
38753         "Falkland Islands (Islas Malvinas)",
38754         "fk",
38755         "500"
38756       ],
38757       [
38758         "Faroe Islands (Føroyar)",
38759         "fo",
38760         "298"
38761       ],
38762       [
38763         "Fiji",
38764         "fj",
38765         "679"
38766       ],
38767       [
38768         "Finland (Suomi)",
38769         "fi",
38770         "358",
38771         0
38772       ],
38773       [
38774         "France",
38775         "fr",
38776         "33"
38777       ],
38778       [
38779         "French Guiana (Guyane française)",
38780         "gf",
38781         "594"
38782       ],
38783       [
38784         "French Polynesia (Polynésie française)",
38785         "pf",
38786         "689"
38787       ],
38788       [
38789         "Gabon",
38790         "ga",
38791         "241"
38792       ],
38793       [
38794         "Gambia",
38795         "gm",
38796         "220"
38797       ],
38798       [
38799         "Georgia (საქართველო)",
38800         "ge",
38801         "995"
38802       ],
38803       [
38804         "Germany (Deutschland)",
38805         "de",
38806         "49"
38807       ],
38808       [
38809         "Ghana (Gaana)",
38810         "gh",
38811         "233"
38812       ],
38813       [
38814         "Gibraltar",
38815         "gi",
38816         "350"
38817       ],
38818       [
38819         "Greece (Ελλάδα)",
38820         "gr",
38821         "30"
38822       ],
38823       [
38824         "Greenland (Kalaallit Nunaat)",
38825         "gl",
38826         "299"
38827       ],
38828       [
38829         "Grenada",
38830         "gd",
38831         "1473"
38832       ],
38833       [
38834         "Guadeloupe",
38835         "gp",
38836         "590",
38837         0
38838       ],
38839       [
38840         "Guam",
38841         "gu",
38842         "1671"
38843       ],
38844       [
38845         "Guatemala",
38846         "gt",
38847         "502"
38848       ],
38849       [
38850         "Guernsey",
38851         "gg",
38852         "44",
38853         1
38854       ],
38855       [
38856         "Guinea (Guinée)",
38857         "gn",
38858         "224"
38859       ],
38860       [
38861         "Guinea-Bissau (Guiné Bissau)",
38862         "gw",
38863         "245"
38864       ],
38865       [
38866         "Guyana",
38867         "gy",
38868         "592"
38869       ],
38870       [
38871         "Haiti",
38872         "ht",
38873         "509"
38874       ],
38875       [
38876         "Honduras",
38877         "hn",
38878         "504"
38879       ],
38880       [
38881         "Hong Kong (香港)",
38882         "hk",
38883         "852"
38884       ],
38885       [
38886         "Hungary (Magyarország)",
38887         "hu",
38888         "36"
38889       ],
38890       [
38891         "Iceland (Ísland)",
38892         "is",
38893         "354"
38894       ],
38895       [
38896         "India (भारत)",
38897         "in",
38898         "91"
38899       ],
38900       [
38901         "Indonesia",
38902         "id",
38903         "62"
38904       ],
38905       [
38906         "Iran (‫ایران‬‎)",
38907         "ir",
38908         "98"
38909       ],
38910       [
38911         "Iraq (‫العراق‬‎)",
38912         "iq",
38913         "964"
38914       ],
38915       [
38916         "Ireland",
38917         "ie",
38918         "353"
38919       ],
38920       [
38921         "Isle of Man",
38922         "im",
38923         "44",
38924         2
38925       ],
38926       [
38927         "Israel (‫ישראל‬‎)",
38928         "il",
38929         "972"
38930       ],
38931       [
38932         "Italy (Italia)",
38933         "it",
38934         "39",
38935         0
38936       ],
38937       [
38938         "Jamaica",
38939         "jm",
38940         "1876"
38941       ],
38942       [
38943         "Japan (日本)",
38944         "jp",
38945         "81"
38946       ],
38947       [
38948         "Jersey",
38949         "je",
38950         "44",
38951         3
38952       ],
38953       [
38954         "Jordan (‫الأردن‬‎)",
38955         "jo",
38956         "962"
38957       ],
38958       [
38959         "Kazakhstan (Казахстан)",
38960         "kz",
38961         "7",
38962         1
38963       ],
38964       [
38965         "Kenya",
38966         "ke",
38967         "254"
38968       ],
38969       [
38970         "Kiribati",
38971         "ki",
38972         "686"
38973       ],
38974       [
38975         "Kosovo",
38976         "xk",
38977         "383"
38978       ],
38979       [
38980         "Kuwait (‫الكويت‬‎)",
38981         "kw",
38982         "965"
38983       ],
38984       [
38985         "Kyrgyzstan (Кыргызстан)",
38986         "kg",
38987         "996"
38988       ],
38989       [
38990         "Laos (ລາວ)",
38991         "la",
38992         "856"
38993       ],
38994       [
38995         "Latvia (Latvija)",
38996         "lv",
38997         "371"
38998       ],
38999       [
39000         "Lebanon (‫لبنان‬‎)",
39001         "lb",
39002         "961"
39003       ],
39004       [
39005         "Lesotho",
39006         "ls",
39007         "266"
39008       ],
39009       [
39010         "Liberia",
39011         "lr",
39012         "231"
39013       ],
39014       [
39015         "Libya (‫ليبيا‬‎)",
39016         "ly",
39017         "218"
39018       ],
39019       [
39020         "Liechtenstein",
39021         "li",
39022         "423"
39023       ],
39024       [
39025         "Lithuania (Lietuva)",
39026         "lt",
39027         "370"
39028       ],
39029       [
39030         "Luxembourg",
39031         "lu",
39032         "352"
39033       ],
39034       [
39035         "Macau (澳門)",
39036         "mo",
39037         "853"
39038       ],
39039       [
39040         "Macedonia (FYROM) (Македонија)",
39041         "mk",
39042         "389"
39043       ],
39044       [
39045         "Madagascar (Madagasikara)",
39046         "mg",
39047         "261"
39048       ],
39049       [
39050         "Malawi",
39051         "mw",
39052         "265"
39053       ],
39054       [
39055         "Malaysia",
39056         "my",
39057         "60"
39058       ],
39059       [
39060         "Maldives",
39061         "mv",
39062         "960"
39063       ],
39064       [
39065         "Mali",
39066         "ml",
39067         "223"
39068       ],
39069       [
39070         "Malta",
39071         "mt",
39072         "356"
39073       ],
39074       [
39075         "Marshall Islands",
39076         "mh",
39077         "692"
39078       ],
39079       [
39080         "Martinique",
39081         "mq",
39082         "596"
39083       ],
39084       [
39085         "Mauritania (‫موريتانيا‬‎)",
39086         "mr",
39087         "222"
39088       ],
39089       [
39090         "Mauritius (Moris)",
39091         "mu",
39092         "230"
39093       ],
39094       [
39095         "Mayotte",
39096         "yt",
39097         "262",
39098         1
39099       ],
39100       [
39101         "Mexico (México)",
39102         "mx",
39103         "52"
39104       ],
39105       [
39106         "Micronesia",
39107         "fm",
39108         "691"
39109       ],
39110       [
39111         "Moldova (Republica Moldova)",
39112         "md",
39113         "373"
39114       ],
39115       [
39116         "Monaco",
39117         "mc",
39118         "377"
39119       ],
39120       [
39121         "Mongolia (Монгол)",
39122         "mn",
39123         "976"
39124       ],
39125       [
39126         "Montenegro (Crna Gora)",
39127         "me",
39128         "382"
39129       ],
39130       [
39131         "Montserrat",
39132         "ms",
39133         "1664"
39134       ],
39135       [
39136         "Morocco (‫المغرب‬‎)",
39137         "ma",
39138         "212",
39139         0
39140       ],
39141       [
39142         "Mozambique (Moçambique)",
39143         "mz",
39144         "258"
39145       ],
39146       [
39147         "Myanmar (Burma) (မြန်မာ)",
39148         "mm",
39149         "95"
39150       ],
39151       [
39152         "Namibia (Namibië)",
39153         "na",
39154         "264"
39155       ],
39156       [
39157         "Nauru",
39158         "nr",
39159         "674"
39160       ],
39161       [
39162         "Nepal (नेपाल)",
39163         "np",
39164         "977"
39165       ],
39166       [
39167         "Netherlands (Nederland)",
39168         "nl",
39169         "31"
39170       ],
39171       [
39172         "New Caledonia (Nouvelle-Calédonie)",
39173         "nc",
39174         "687"
39175       ],
39176       [
39177         "New Zealand",
39178         "nz",
39179         "64"
39180       ],
39181       [
39182         "Nicaragua",
39183         "ni",
39184         "505"
39185       ],
39186       [
39187         "Niger (Nijar)",
39188         "ne",
39189         "227"
39190       ],
39191       [
39192         "Nigeria",
39193         "ng",
39194         "234"
39195       ],
39196       [
39197         "Niue",
39198         "nu",
39199         "683"
39200       ],
39201       [
39202         "Norfolk Island",
39203         "nf",
39204         "672"
39205       ],
39206       [
39207         "North Korea (조선 민주주의 인민 공화국)",
39208         "kp",
39209         "850"
39210       ],
39211       [
39212         "Northern Mariana Islands",
39213         "mp",
39214         "1670"
39215       ],
39216       [
39217         "Norway (Norge)",
39218         "no",
39219         "47",
39220         0
39221       ],
39222       [
39223         "Oman (‫عُمان‬‎)",
39224         "om",
39225         "968"
39226       ],
39227       [
39228         "Pakistan (‫پاکستان‬‎)",
39229         "pk",
39230         "92"
39231       ],
39232       [
39233         "Palau",
39234         "pw",
39235         "680"
39236       ],
39237       [
39238         "Palestine (‫فلسطين‬‎)",
39239         "ps",
39240         "970"
39241       ],
39242       [
39243         "Panama (Panamá)",
39244         "pa",
39245         "507"
39246       ],
39247       [
39248         "Papua New Guinea",
39249         "pg",
39250         "675"
39251       ],
39252       [
39253         "Paraguay",
39254         "py",
39255         "595"
39256       ],
39257       [
39258         "Peru (Perú)",
39259         "pe",
39260         "51"
39261       ],
39262       [
39263         "Philippines",
39264         "ph",
39265         "63"
39266       ],
39267       [
39268         "Poland (Polska)",
39269         "pl",
39270         "48"
39271       ],
39272       [
39273         "Portugal",
39274         "pt",
39275         "351"
39276       ],
39277       [
39278         "Puerto Rico",
39279         "pr",
39280         "1",
39281         3,
39282         ["787", "939"]
39283       ],
39284       [
39285         "Qatar (‫قطر‬‎)",
39286         "qa",
39287         "974"
39288       ],
39289       [
39290         "Réunion (La Réunion)",
39291         "re",
39292         "262",
39293         0
39294       ],
39295       [
39296         "Romania (România)",
39297         "ro",
39298         "40"
39299       ],
39300       [
39301         "Russia (Россия)",
39302         "ru",
39303         "7",
39304         0
39305       ],
39306       [
39307         "Rwanda",
39308         "rw",
39309         "250"
39310       ],
39311       [
39312         "Saint Barthélemy",
39313         "bl",
39314         "590",
39315         1
39316       ],
39317       [
39318         "Saint Helena",
39319         "sh",
39320         "290"
39321       ],
39322       [
39323         "Saint Kitts and Nevis",
39324         "kn",
39325         "1869"
39326       ],
39327       [
39328         "Saint Lucia",
39329         "lc",
39330         "1758"
39331       ],
39332       [
39333         "Saint Martin (Saint-Martin (partie française))",
39334         "mf",
39335         "590",
39336         2
39337       ],
39338       [
39339         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39340         "pm",
39341         "508"
39342       ],
39343       [
39344         "Saint Vincent and the Grenadines",
39345         "vc",
39346         "1784"
39347       ],
39348       [
39349         "Samoa",
39350         "ws",
39351         "685"
39352       ],
39353       [
39354         "San Marino",
39355         "sm",
39356         "378"
39357       ],
39358       [
39359         "São Tomé and Príncipe (São Tomé e Príncipe)",
39360         "st",
39361         "239"
39362       ],
39363       [
39364         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39365         "sa",
39366         "966"
39367       ],
39368       [
39369         "Senegal (Sénégal)",
39370         "sn",
39371         "221"
39372       ],
39373       [
39374         "Serbia (Србија)",
39375         "rs",
39376         "381"
39377       ],
39378       [
39379         "Seychelles",
39380         "sc",
39381         "248"
39382       ],
39383       [
39384         "Sierra Leone",
39385         "sl",
39386         "232"
39387       ],
39388       [
39389         "Singapore",
39390         "sg",
39391         "65"
39392       ],
39393       [
39394         "Sint Maarten",
39395         "sx",
39396         "1721"
39397       ],
39398       [
39399         "Slovakia (Slovensko)",
39400         "sk",
39401         "421"
39402       ],
39403       [
39404         "Slovenia (Slovenija)",
39405         "si",
39406         "386"
39407       ],
39408       [
39409         "Solomon Islands",
39410         "sb",
39411         "677"
39412       ],
39413       [
39414         "Somalia (Soomaaliya)",
39415         "so",
39416         "252"
39417       ],
39418       [
39419         "South Africa",
39420         "za",
39421         "27"
39422       ],
39423       [
39424         "South Korea (대한민국)",
39425         "kr",
39426         "82"
39427       ],
39428       [
39429         "South Sudan (‫جنوب السودان‬‎)",
39430         "ss",
39431         "211"
39432       ],
39433       [
39434         "Spain (España)",
39435         "es",
39436         "34"
39437       ],
39438       [
39439         "Sri Lanka (ශ්‍රී ලංකාව)",
39440         "lk",
39441         "94"
39442       ],
39443       [
39444         "Sudan (‫السودان‬‎)",
39445         "sd",
39446         "249"
39447       ],
39448       [
39449         "Suriname",
39450         "sr",
39451         "597"
39452       ],
39453       [
39454         "Svalbard and Jan Mayen",
39455         "sj",
39456         "47",
39457         1
39458       ],
39459       [
39460         "Swaziland",
39461         "sz",
39462         "268"
39463       ],
39464       [
39465         "Sweden (Sverige)",
39466         "se",
39467         "46"
39468       ],
39469       [
39470         "Switzerland (Schweiz)",
39471         "ch",
39472         "41"
39473       ],
39474       [
39475         "Syria (‫سوريا‬‎)",
39476         "sy",
39477         "963"
39478       ],
39479       [
39480         "Taiwan (台灣)",
39481         "tw",
39482         "886"
39483       ],
39484       [
39485         "Tajikistan",
39486         "tj",
39487         "992"
39488       ],
39489       [
39490         "Tanzania",
39491         "tz",
39492         "255"
39493       ],
39494       [
39495         "Thailand (ไทย)",
39496         "th",
39497         "66"
39498       ],
39499       [
39500         "Timor-Leste",
39501         "tl",
39502         "670"
39503       ],
39504       [
39505         "Togo",
39506         "tg",
39507         "228"
39508       ],
39509       [
39510         "Tokelau",
39511         "tk",
39512         "690"
39513       ],
39514       [
39515         "Tonga",
39516         "to",
39517         "676"
39518       ],
39519       [
39520         "Trinidad and Tobago",
39521         "tt",
39522         "1868"
39523       ],
39524       [
39525         "Tunisia (‫تونس‬‎)",
39526         "tn",
39527         "216"
39528       ],
39529       [
39530         "Turkey (Türkiye)",
39531         "tr",
39532         "90"
39533       ],
39534       [
39535         "Turkmenistan",
39536         "tm",
39537         "993"
39538       ],
39539       [
39540         "Turks and Caicos Islands",
39541         "tc",
39542         "1649"
39543       ],
39544       [
39545         "Tuvalu",
39546         "tv",
39547         "688"
39548       ],
39549       [
39550         "U.S. Virgin Islands",
39551         "vi",
39552         "1340"
39553       ],
39554       [
39555         "Uganda",
39556         "ug",
39557         "256"
39558       ],
39559       [
39560         "Ukraine (Україна)",
39561         "ua",
39562         "380"
39563       ],
39564       [
39565         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39566         "ae",
39567         "971"
39568       ],
39569       [
39570         "United Kingdom",
39571         "gb",
39572         "44",
39573         0
39574       ],
39575       [
39576         "United States",
39577         "us",
39578         "1",
39579         0
39580       ],
39581       [
39582         "Uruguay",
39583         "uy",
39584         "598"
39585       ],
39586       [
39587         "Uzbekistan (Oʻzbekiston)",
39588         "uz",
39589         "998"
39590       ],
39591       [
39592         "Vanuatu",
39593         "vu",
39594         "678"
39595       ],
39596       [
39597         "Vatican City (Città del Vaticano)",
39598         "va",
39599         "39",
39600         1
39601       ],
39602       [
39603         "Venezuela",
39604         "ve",
39605         "58"
39606       ],
39607       [
39608         "Vietnam (Việt Nam)",
39609         "vn",
39610         "84"
39611       ],
39612       [
39613         "Wallis and Futuna (Wallis-et-Futuna)",
39614         "wf",
39615         "681"
39616       ],
39617       [
39618         "Western Sahara (‫الصحراء الغربية‬‎)",
39619         "eh",
39620         "212",
39621         1
39622       ],
39623       [
39624         "Yemen (‫اليمن‬‎)",
39625         "ye",
39626         "967"
39627       ],
39628       [
39629         "Zambia",
39630         "zm",
39631         "260"
39632       ],
39633       [
39634         "Zimbabwe",
39635         "zw",
39636         "263"
39637       ],
39638       [
39639         "Åland Islands",
39640         "ax",
39641         "358",
39642         1
39643       ]
39644   ];
39645   
39646   return d;
39647 }/**
39648 *    This script refer to:
39649 *    Title: International Telephone Input
39650 *    Author: Jack O'Connor
39651 *    Code version:  v12.1.12
39652 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39653 **/
39654
39655 /**
39656  * @class Roo.bootstrap.PhoneInput
39657  * @extends Roo.bootstrap.TriggerField
39658  * An input with International dial-code selection
39659  
39660  * @cfg {String} defaultDialCode default '+852'
39661  * @cfg {Array} preferedCountries default []
39662   
39663  * @constructor
39664  * Create a new PhoneInput.
39665  * @param {Object} config Configuration options
39666  */
39667
39668 Roo.bootstrap.PhoneInput = function(config) {
39669     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39670 };
39671
39672 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39673         
39674         listWidth: undefined,
39675         
39676         selectedClass: 'active',
39677         
39678         invalidClass : "has-warning",
39679         
39680         validClass: 'has-success',
39681         
39682         allowed: '0123456789',
39683         
39684         /**
39685          * @cfg {String} defaultDialCode The default dial code when initializing the input
39686          */
39687         defaultDialCode: '+852',
39688         
39689         /**
39690          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39691          */
39692         preferedCountries: false,
39693         
39694         getAutoCreate : function()
39695         {
39696             var data = Roo.bootstrap.PhoneInputData();
39697             var align = this.labelAlign || this.parentLabelAlign();
39698             var id = Roo.id();
39699             
39700             this.allCountries = [];
39701             this.dialCodeMapping = [];
39702             
39703             for (var i = 0; i < data.length; i++) {
39704               var c = data[i];
39705               this.allCountries[i] = {
39706                 name: c[0],
39707                 iso2: c[1],
39708                 dialCode: c[2],
39709                 priority: c[3] || 0,
39710                 areaCodes: c[4] || null
39711               };
39712               this.dialCodeMapping[c[2]] = {
39713                   name: c[0],
39714                   iso2: c[1],
39715                   priority: c[3] || 0,
39716                   areaCodes: c[4] || null
39717               };
39718             }
39719             
39720             var cfg = {
39721                 cls: 'form-group',
39722                 cn: []
39723             };
39724             
39725             var input =  {
39726                 tag: 'input',
39727                 id : id,
39728                 cls : 'form-control tel-input',
39729                 autocomplete: 'new-password'
39730             };
39731             
39732             var hiddenInput = {
39733                 tag: 'input',
39734                 type: 'hidden',
39735                 cls: 'hidden-tel-input'
39736             };
39737             
39738             if (this.name) {
39739                 hiddenInput.name = this.name;
39740             }
39741             
39742             if (this.disabled) {
39743                 input.disabled = true;
39744             }
39745             
39746             var flag_container = {
39747                 tag: 'div',
39748                 cls: 'flag-box',
39749                 cn: [
39750                     {
39751                         tag: 'div',
39752                         cls: 'flag'
39753                     },
39754                     {
39755                         tag: 'div',
39756                         cls: 'caret'
39757                     }
39758                 ]
39759             };
39760             
39761             var box = {
39762                 tag: 'div',
39763                 cls: this.hasFeedback ? 'has-feedback' : '',
39764                 cn: [
39765                     hiddenInput,
39766                     input,
39767                     {
39768                         tag: 'input',
39769                         cls: 'dial-code-holder',
39770                         disabled: true
39771                     }
39772                 ]
39773             };
39774             
39775             var container = {
39776                 cls: 'roo-select2-container input-group',
39777                 cn: [
39778                     flag_container,
39779                     box
39780                 ]
39781             };
39782             
39783             if (this.fieldLabel.length) {
39784                 var indicator = {
39785                     tag: 'i',
39786                     tooltip: 'This field is required'
39787                 };
39788                 
39789                 var label = {
39790                     tag: 'label',
39791                     'for':  id,
39792                     cls: 'control-label',
39793                     cn: []
39794                 };
39795                 
39796                 var label_text = {
39797                     tag: 'span',
39798                     html: this.fieldLabel
39799                 };
39800                 
39801                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39802                 label.cn = [
39803                     indicator,
39804                     label_text
39805                 ];
39806                 
39807                 if(this.indicatorpos == 'right') {
39808                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39809                     label.cn = [
39810                         label_text,
39811                         indicator
39812                     ];
39813                 }
39814                 
39815                 if(align == 'left') {
39816                     container = {
39817                         tag: 'div',
39818                         cn: [
39819                             container
39820                         ]
39821                     };
39822                     
39823                     if(this.labelWidth > 12){
39824                         label.style = "width: " + this.labelWidth + 'px';
39825                     }
39826                     if(this.labelWidth < 13 && this.labelmd == 0){
39827                         this.labelmd = this.labelWidth;
39828                     }
39829                     if(this.labellg > 0){
39830                         label.cls += ' col-lg-' + this.labellg;
39831                         input.cls += ' col-lg-' + (12 - this.labellg);
39832                     }
39833                     if(this.labelmd > 0){
39834                         label.cls += ' col-md-' + this.labelmd;
39835                         container.cls += ' col-md-' + (12 - this.labelmd);
39836                     }
39837                     if(this.labelsm > 0){
39838                         label.cls += ' col-sm-' + this.labelsm;
39839                         container.cls += ' col-sm-' + (12 - this.labelsm);
39840                     }
39841                     if(this.labelxs > 0){
39842                         label.cls += ' col-xs-' + this.labelxs;
39843                         container.cls += ' col-xs-' + (12 - this.labelxs);
39844                     }
39845                 }
39846             }
39847             
39848             cfg.cn = [
39849                 label,
39850                 container
39851             ];
39852             
39853             var settings = this;
39854             
39855             ['xs','sm','md','lg'].map(function(size){
39856                 if (settings[size]) {
39857                     cfg.cls += ' col-' + size + '-' + settings[size];
39858                 }
39859             });
39860             
39861             this.store = new Roo.data.Store({
39862                 proxy : new Roo.data.MemoryProxy({}),
39863                 reader : new Roo.data.JsonReader({
39864                     fields : [
39865                         {
39866                             'name' : 'name',
39867                             'type' : 'string'
39868                         },
39869                         {
39870                             'name' : 'iso2',
39871                             'type' : 'string'
39872                         },
39873                         {
39874                             'name' : 'dialCode',
39875                             'type' : 'string'
39876                         },
39877                         {
39878                             'name' : 'priority',
39879                             'type' : 'string'
39880                         },
39881                         {
39882                             'name' : 'areaCodes',
39883                             'type' : 'string'
39884                         }
39885                     ]
39886                 })
39887             });
39888             
39889             if(!this.preferedCountries) {
39890                 this.preferedCountries = [
39891                     'hk',
39892                     'gb',
39893                     'us'
39894                 ];
39895             }
39896             
39897             var p = this.preferedCountries.reverse();
39898             
39899             if(p) {
39900                 for (var i = 0; i < p.length; i++) {
39901                     for (var j = 0; j < this.allCountries.length; j++) {
39902                         if(this.allCountries[j].iso2 == p[i]) {
39903                             var t = this.allCountries[j];
39904                             this.allCountries.splice(j,1);
39905                             this.allCountries.unshift(t);
39906                         }
39907                     } 
39908                 }
39909             }
39910             
39911             this.store.proxy.data = {
39912                 success: true,
39913                 data: this.allCountries
39914             };
39915             
39916             return cfg;
39917         },
39918         
39919         initEvents : function()
39920         {
39921             this.createList();
39922             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39923             
39924             this.indicator = this.indicatorEl();
39925             this.flag = this.flagEl();
39926             this.dialCodeHolder = this.dialCodeHolderEl();
39927             
39928             this.trigger = this.el.select('div.flag-box',true).first();
39929             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39930             
39931             var _this = this;
39932             
39933             (function(){
39934                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39935                 _this.list.setWidth(lw);
39936             }).defer(100);
39937             
39938             this.list.on('mouseover', this.onViewOver, this);
39939             this.list.on('mousemove', this.onViewMove, this);
39940             this.inputEl().on("keyup", this.onKeyUp, this);
39941             
39942             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39943
39944             this.view = new Roo.View(this.list, this.tpl, {
39945                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39946             });
39947             
39948             this.view.on('click', this.onViewClick, this);
39949             this.setValue(this.defaultDialCode);
39950         },
39951         
39952         onTriggerClick : function(e)
39953         {
39954             Roo.log('trigger click');
39955             if(this.disabled){
39956                 return;
39957             }
39958             
39959             if(this.isExpanded()){
39960                 this.collapse();
39961                 this.hasFocus = false;
39962             }else {
39963                 this.store.load({});
39964                 this.hasFocus = true;
39965                 this.expand();
39966             }
39967         },
39968         
39969         isExpanded : function()
39970         {
39971             return this.list.isVisible();
39972         },
39973         
39974         collapse : function()
39975         {
39976             if(!this.isExpanded()){
39977                 return;
39978             }
39979             this.list.hide();
39980             Roo.get(document).un('mousedown', this.collapseIf, this);
39981             Roo.get(document).un('mousewheel', this.collapseIf, this);
39982             this.fireEvent('collapse', this);
39983             this.validate();
39984         },
39985         
39986         expand : function()
39987         {
39988             Roo.log('expand');
39989
39990             if(this.isExpanded() || !this.hasFocus){
39991                 return;
39992             }
39993             
39994             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39995             this.list.setWidth(lw);
39996             
39997             this.list.show();
39998             this.restrictHeight();
39999             
40000             Roo.get(document).on('mousedown', this.collapseIf, this);
40001             Roo.get(document).on('mousewheel', this.collapseIf, this);
40002             
40003             this.fireEvent('expand', this);
40004         },
40005         
40006         restrictHeight : function()
40007         {
40008             this.list.alignTo(this.inputEl(), this.listAlign);
40009             this.list.alignTo(this.inputEl(), this.listAlign);
40010         },
40011         
40012         onViewOver : function(e, t)
40013         {
40014             if(this.inKeyMode){
40015                 return;
40016             }
40017             var item = this.view.findItemFromChild(t);
40018             
40019             if(item){
40020                 var index = this.view.indexOf(item);
40021                 this.select(index, false);
40022             }
40023         },
40024
40025         // private
40026         onViewClick : function(view, doFocus, el, e)
40027         {
40028             var index = this.view.getSelectedIndexes()[0];
40029             
40030             var r = this.store.getAt(index);
40031             
40032             if(r){
40033                 this.onSelect(r, index);
40034             }
40035             if(doFocus !== false && !this.blockFocus){
40036                 this.inputEl().focus();
40037             }
40038         },
40039         
40040         onViewMove : function(e, t)
40041         {
40042             this.inKeyMode = false;
40043         },
40044         
40045         select : function(index, scrollIntoView)
40046         {
40047             this.selectedIndex = index;
40048             this.view.select(index);
40049             if(scrollIntoView !== false){
40050                 var el = this.view.getNode(index);
40051                 if(el){
40052                     this.list.scrollChildIntoView(el, false);
40053                 }
40054             }
40055         },
40056         
40057         createList : function()
40058         {
40059             this.list = Roo.get(document.body).createChild({
40060                 tag: 'ul',
40061                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40062                 style: 'display:none'
40063             });
40064             
40065             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40066         },
40067         
40068         collapseIf : function(e)
40069         {
40070             var in_combo  = e.within(this.el);
40071             var in_list =  e.within(this.list);
40072             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40073             
40074             if (in_combo || in_list || is_list) {
40075                 return;
40076             }
40077             this.collapse();
40078         },
40079         
40080         onSelect : function(record, index)
40081         {
40082             if(this.fireEvent('beforeselect', this, record, index) !== false){
40083                 
40084                 this.setFlagClass(record.data.iso2);
40085                 this.setDialCode(record.data.dialCode);
40086                 this.hasFocus = false;
40087                 this.collapse();
40088                 this.fireEvent('select', this, record, index);
40089             }
40090         },
40091         
40092         flagEl : function()
40093         {
40094             var flag = this.el.select('div.flag',true).first();
40095             if(!flag){
40096                 return false;
40097             }
40098             return flag;
40099         },
40100         
40101         dialCodeHolderEl : function()
40102         {
40103             var d = this.el.select('input.dial-code-holder',true).first();
40104             if(!d){
40105                 return false;
40106             }
40107             return d;
40108         },
40109         
40110         setDialCode : function(v)
40111         {
40112             this.dialCodeHolder.dom.value = '+'+v;
40113         },
40114         
40115         setFlagClass : function(n)
40116         {
40117             this.flag.dom.className = 'flag '+n;
40118         },
40119         
40120         getValue : function()
40121         {
40122             var v = this.inputEl().getValue();
40123             if(this.dialCodeHolder) {
40124                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40125             }
40126             return v;
40127         },
40128         
40129         setValue : function(v)
40130         {
40131             var d = this.getDialCode(v);
40132             
40133             //invalid dial code
40134             if(v.length == 0 || !d || d.length == 0) {
40135                 if(this.rendered){
40136                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40137                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40138                 }
40139                 return;
40140             }
40141             
40142             //valid dial code
40143             this.setFlagClass(this.dialCodeMapping[d].iso2);
40144             this.setDialCode(d);
40145             this.inputEl().dom.value = v.replace('+'+d,'');
40146             this.hiddenEl().dom.value = this.getValue();
40147             
40148             this.validate();
40149         },
40150         
40151         getDialCode : function(v)
40152         {
40153             v = v ||  '';
40154             
40155             if (v.length == 0) {
40156                 return this.dialCodeHolder.dom.value;
40157             }
40158             
40159             var dialCode = "";
40160             if (v.charAt(0) != "+") {
40161                 return false;
40162             }
40163             var numericChars = "";
40164             for (var i = 1; i < v.length; i++) {
40165               var c = v.charAt(i);
40166               if (!isNaN(c)) {
40167                 numericChars += c;
40168                 if (this.dialCodeMapping[numericChars]) {
40169                   dialCode = v.substr(1, i);
40170                 }
40171                 if (numericChars.length == 4) {
40172                   break;
40173                 }
40174               }
40175             }
40176             return dialCode;
40177         },
40178         
40179         reset : function()
40180         {
40181             this.setValue(this.defaultDialCode);
40182             this.validate();
40183         },
40184         
40185         hiddenEl : function()
40186         {
40187             return this.el.select('input.hidden-tel-input',true).first();
40188         },
40189         
40190         onKeyUp : function(e){
40191             
40192             var k = e.getKey();
40193             var c = e.getCharCode();
40194             
40195             if(
40196                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40197                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40198             ){
40199                 e.stopEvent();
40200             }
40201             
40202             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40203             //     return;
40204             // }
40205             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40206                 e.stopEvent();
40207             }
40208             
40209             this.setValue(this.getValue());
40210         }
40211         
40212 });
40213 /**
40214  * @class Roo.bootstrap.MoneyField
40215  * @extends Roo.bootstrap.ComboBox
40216  * Bootstrap MoneyField class
40217  * 
40218  * @constructor
40219  * Create a new MoneyField.
40220  * @param {Object} config Configuration options
40221  */
40222
40223 Roo.bootstrap.MoneyField = function(config) {
40224     
40225     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40226     
40227 };
40228
40229 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40230     
40231     /**
40232      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40233      */
40234     allowDecimals : true,
40235     /**
40236      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40237      */
40238     decimalSeparator : ".",
40239     /**
40240      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40241      */
40242     decimalPrecision : 0,
40243     /**
40244      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40245      */
40246     allowNegative : true,
40247     /**
40248      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40249      */
40250     allowZero: true,
40251     /**
40252      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40253      */
40254     minValue : Number.NEGATIVE_INFINITY,
40255     /**
40256      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40257      */
40258     maxValue : Number.MAX_VALUE,
40259     /**
40260      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40261      */
40262     minText : "The minimum value for this field is {0}",
40263     /**
40264      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40265      */
40266     maxText : "The maximum value for this field is {0}",
40267     /**
40268      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40269      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40270      */
40271     nanText : "{0} is not a valid number",
40272     /**
40273      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40274      */
40275     castInt : true,
40276     /**
40277      * @cfg {String} defaults currency of the MoneyField
40278      * value should be in lkey
40279      */
40280     defaultCurrency : false,
40281     /**
40282      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40283      */
40284     thousandsDelimiter : false,
40285     
40286     
40287     inputlg : 9,
40288     inputmd : 9,
40289     inputsm : 9,
40290     inputxs : 6,
40291     
40292     store : false,
40293     
40294     getAutoCreate : function()
40295     {
40296         var align = this.labelAlign || this.parentLabelAlign();
40297         
40298         var id = Roo.id();
40299
40300         var cfg = {
40301             cls: 'form-group',
40302             cn: []
40303         };
40304
40305         var input =  {
40306             tag: 'input',
40307             id : id,
40308             cls : 'form-control roo-money-amount-input',
40309             autocomplete: 'new-password'
40310         };
40311         
40312         var hiddenInput = {
40313             tag: 'input',
40314             type: 'hidden',
40315             id: Roo.id(),
40316             cls: 'hidden-number-input'
40317         };
40318         
40319         if (this.name) {
40320             hiddenInput.name = this.name;
40321         }
40322
40323         if (this.disabled) {
40324             input.disabled = true;
40325         }
40326
40327         var clg = 12 - this.inputlg;
40328         var cmd = 12 - this.inputmd;
40329         var csm = 12 - this.inputsm;
40330         var cxs = 12 - this.inputxs;
40331         
40332         var container = {
40333             tag : 'div',
40334             cls : 'row roo-money-field',
40335             cn : [
40336                 {
40337                     tag : 'div',
40338                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40339                     cn : [
40340                         {
40341                             tag : 'div',
40342                             cls: 'roo-select2-container input-group',
40343                             cn: [
40344                                 {
40345                                     tag : 'input',
40346                                     cls : 'form-control roo-money-currency-input',
40347                                     autocomplete: 'new-password',
40348                                     readOnly : 1,
40349                                     name : this.currencyName
40350                                 },
40351                                 {
40352                                     tag :'span',
40353                                     cls : 'input-group-addon',
40354                                     cn : [
40355                                         {
40356                                             tag: 'span',
40357                                             cls: 'caret'
40358                                         }
40359                                     ]
40360                                 }
40361                             ]
40362                         }
40363                     ]
40364                 },
40365                 {
40366                     tag : 'div',
40367                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40368                     cn : [
40369                         {
40370                             tag: 'div',
40371                             cls: this.hasFeedback ? 'has-feedback' : '',
40372                             cn: [
40373                                 input
40374                             ]
40375                         }
40376                     ]
40377                 }
40378             ]
40379             
40380         };
40381         
40382         if (this.fieldLabel.length) {
40383             var indicator = {
40384                 tag: 'i',
40385                 tooltip: 'This field is required'
40386             };
40387
40388             var label = {
40389                 tag: 'label',
40390                 'for':  id,
40391                 cls: 'control-label',
40392                 cn: []
40393             };
40394
40395             var label_text = {
40396                 tag: 'span',
40397                 html: this.fieldLabel
40398             };
40399
40400             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40401             label.cn = [
40402                 indicator,
40403                 label_text
40404             ];
40405
40406             if(this.indicatorpos == 'right') {
40407                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40408                 label.cn = [
40409                     label_text,
40410                     indicator
40411                 ];
40412             }
40413
40414             if(align == 'left') {
40415                 container = {
40416                     tag: 'div',
40417                     cn: [
40418                         container
40419                     ]
40420                 };
40421
40422                 if(this.labelWidth > 12){
40423                     label.style = "width: " + this.labelWidth + 'px';
40424                 }
40425                 if(this.labelWidth < 13 && this.labelmd == 0){
40426                     this.labelmd = this.labelWidth;
40427                 }
40428                 if(this.labellg > 0){
40429                     label.cls += ' col-lg-' + this.labellg;
40430                     input.cls += ' col-lg-' + (12 - this.labellg);
40431                 }
40432                 if(this.labelmd > 0){
40433                     label.cls += ' col-md-' + this.labelmd;
40434                     container.cls += ' col-md-' + (12 - this.labelmd);
40435                 }
40436                 if(this.labelsm > 0){
40437                     label.cls += ' col-sm-' + this.labelsm;
40438                     container.cls += ' col-sm-' + (12 - this.labelsm);
40439                 }
40440                 if(this.labelxs > 0){
40441                     label.cls += ' col-xs-' + this.labelxs;
40442                     container.cls += ' col-xs-' + (12 - this.labelxs);
40443                 }
40444             }
40445         }
40446
40447         cfg.cn = [
40448             label,
40449             container,
40450             hiddenInput
40451         ];
40452         
40453         var settings = this;
40454
40455         ['xs','sm','md','lg'].map(function(size){
40456             if (settings[size]) {
40457                 cfg.cls += ' col-' + size + '-' + settings[size];
40458             }
40459         });
40460         
40461         return cfg;
40462     },
40463     
40464     initEvents : function()
40465     {
40466         this.indicator = this.indicatorEl();
40467         
40468         this.initCurrencyEvent();
40469         
40470         this.initNumberEvent();
40471     },
40472     
40473     initCurrencyEvent : function()
40474     {
40475         if (!this.store) {
40476             throw "can not find store for combo";
40477         }
40478         
40479         this.store = Roo.factory(this.store, Roo.data);
40480         this.store.parent = this;
40481         
40482         this.createList();
40483         
40484         this.triggerEl = this.el.select('.input-group-addon', true).first();
40485         
40486         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40487         
40488         var _this = this;
40489         
40490         (function(){
40491             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40492             _this.list.setWidth(lw);
40493         }).defer(100);
40494         
40495         this.list.on('mouseover', this.onViewOver, this);
40496         this.list.on('mousemove', this.onViewMove, this);
40497         this.list.on('scroll', this.onViewScroll, this);
40498         
40499         if(!this.tpl){
40500             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40501         }
40502         
40503         this.view = new Roo.View(this.list, this.tpl, {
40504             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40505         });
40506         
40507         this.view.on('click', this.onViewClick, this);
40508         
40509         this.store.on('beforeload', this.onBeforeLoad, this);
40510         this.store.on('load', this.onLoad, this);
40511         this.store.on('loadexception', this.onLoadException, this);
40512         
40513         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40514             "up" : function(e){
40515                 this.inKeyMode = true;
40516                 this.selectPrev();
40517             },
40518
40519             "down" : function(e){
40520                 if(!this.isExpanded()){
40521                     this.onTriggerClick();
40522                 }else{
40523                     this.inKeyMode = true;
40524                     this.selectNext();
40525                 }
40526             },
40527
40528             "enter" : function(e){
40529                 this.collapse();
40530                 
40531                 if(this.fireEvent("specialkey", this, e)){
40532                     this.onViewClick(false);
40533                 }
40534                 
40535                 return true;
40536             },
40537
40538             "esc" : function(e){
40539                 this.collapse();
40540             },
40541
40542             "tab" : function(e){
40543                 this.collapse();
40544                 
40545                 if(this.fireEvent("specialkey", this, e)){
40546                     this.onViewClick(false);
40547                 }
40548                 
40549                 return true;
40550             },
40551
40552             scope : this,
40553
40554             doRelay : function(foo, bar, hname){
40555                 if(hname == 'down' || this.scope.isExpanded()){
40556                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40557                 }
40558                 return true;
40559             },
40560
40561             forceKeyDown: true
40562         });
40563         
40564         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40565         
40566     },
40567     
40568     initNumberEvent : function(e)
40569     {
40570         this.inputEl().on("keydown" , this.fireKey,  this);
40571         this.inputEl().on("focus", this.onFocus,  this);
40572         this.inputEl().on("blur", this.onBlur,  this);
40573         
40574         this.inputEl().relayEvent('keyup', this);
40575         
40576         if(this.indicator){
40577             this.indicator.addClass('invisible');
40578         }
40579  
40580         this.originalValue = this.getValue();
40581         
40582         if(this.validationEvent == 'keyup'){
40583             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40584             this.inputEl().on('keyup', this.filterValidation, this);
40585         }
40586         else if(this.validationEvent !== false){
40587             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40588         }
40589         
40590         if(this.selectOnFocus){
40591             this.on("focus", this.preFocus, this);
40592             
40593         }
40594         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40595             this.inputEl().on("keypress", this.filterKeys, this);
40596         } else {
40597             this.inputEl().relayEvent('keypress', this);
40598         }
40599         
40600         var allowed = "0123456789";
40601         
40602         if(this.allowDecimals){
40603             allowed += this.decimalSeparator;
40604         }
40605         
40606         if(this.allowNegative){
40607             allowed += "-";
40608         }
40609         
40610         if(this.thousandsDelimiter) {
40611             allowed += ",";
40612         }
40613         
40614         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40615         
40616         var keyPress = function(e){
40617             
40618             var k = e.getKey();
40619             
40620             var c = e.getCharCode();
40621             
40622             if(
40623                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40624                     allowed.indexOf(String.fromCharCode(c)) === -1
40625             ){
40626                 e.stopEvent();
40627                 return;
40628             }
40629             
40630             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40631                 return;
40632             }
40633             
40634             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40635                 e.stopEvent();
40636             }
40637         };
40638         
40639         this.inputEl().on("keypress", keyPress, this);
40640         
40641     },
40642     
40643     onTriggerClick : function(e)
40644     {   
40645         if(this.disabled){
40646             return;
40647         }
40648         
40649         this.page = 0;
40650         this.loadNext = false;
40651         
40652         if(this.isExpanded()){
40653             this.collapse();
40654             return;
40655         }
40656         
40657         this.hasFocus = true;
40658         
40659         if(this.triggerAction == 'all') {
40660             this.doQuery(this.allQuery, true);
40661             return;
40662         }
40663         
40664         this.doQuery(this.getRawValue());
40665     },
40666     
40667     getCurrency : function()
40668     {   
40669         var v = this.currencyEl().getValue();
40670         
40671         return v;
40672     },
40673     
40674     restrictHeight : function()
40675     {
40676         this.list.alignTo(this.currencyEl(), this.listAlign);
40677         this.list.alignTo(this.currencyEl(), this.listAlign);
40678     },
40679     
40680     onViewClick : function(view, doFocus, el, e)
40681     {
40682         var index = this.view.getSelectedIndexes()[0];
40683         
40684         var r = this.store.getAt(index);
40685         
40686         if(r){
40687             this.onSelect(r, index);
40688         }
40689     },
40690     
40691     onSelect : function(record, index){
40692         
40693         if(this.fireEvent('beforeselect', this, record, index) !== false){
40694         
40695             this.setFromCurrencyData(index > -1 ? record.data : false);
40696             
40697             this.collapse();
40698             
40699             this.fireEvent('select', this, record, index);
40700         }
40701     },
40702     
40703     setFromCurrencyData : function(o)
40704     {
40705         var currency = '';
40706         
40707         this.lastCurrency = o;
40708         
40709         if (this.currencyField) {
40710             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40711         } else {
40712             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40713         }
40714         
40715         this.lastSelectionText = currency;
40716         
40717         //setting default currency
40718         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40719             this.setCurrency(this.defaultCurrency);
40720             return;
40721         }
40722         
40723         this.setCurrency(currency);
40724     },
40725     
40726     setFromData : function(o)
40727     {
40728         var c = {};
40729         
40730         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40731         
40732         this.setFromCurrencyData(c);
40733         
40734         var value = '';
40735         
40736         if (this.name) {
40737             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40738         } else {
40739             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40740         }
40741         
40742         this.setValue(value);
40743         
40744     },
40745     
40746     setCurrency : function(v)
40747     {   
40748         this.currencyValue = v;
40749         
40750         if(this.rendered){
40751             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40752             this.validate();
40753         }
40754     },
40755     
40756     setValue : function(v)
40757     {
40758         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40759         
40760         this.value = v;
40761         
40762         if(this.rendered){
40763             
40764             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40765             
40766             this.inputEl().dom.value = (v == '') ? '' :
40767                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40768             
40769             if(!this.allowZero && v === '0') {
40770                 this.hiddenEl().dom.value = '';
40771                 this.inputEl().dom.value = '';
40772             }
40773             
40774             this.validate();
40775         }
40776     },
40777     
40778     getRawValue : function()
40779     {
40780         var v = this.inputEl().getValue();
40781         
40782         return v;
40783     },
40784     
40785     getValue : function()
40786     {
40787         return this.fixPrecision(this.parseValue(this.getRawValue()));
40788     },
40789     
40790     parseValue : function(value)
40791     {
40792         if(this.thousandsDelimiter) {
40793             value += "";
40794             r = new RegExp(",", "g");
40795             value = value.replace(r, "");
40796         }
40797         
40798         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40799         return isNaN(value) ? '' : value;
40800         
40801     },
40802     
40803     fixPrecision : function(value)
40804     {
40805         if(this.thousandsDelimiter) {
40806             value += "";
40807             r = new RegExp(",", "g");
40808             value = value.replace(r, "");
40809         }
40810         
40811         var nan = isNaN(value);
40812         
40813         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40814             return nan ? '' : value;
40815         }
40816         return parseFloat(value).toFixed(this.decimalPrecision);
40817     },
40818     
40819     decimalPrecisionFcn : function(v)
40820     {
40821         return Math.floor(v);
40822     },
40823     
40824     validateValue : function(value)
40825     {
40826         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40827             return false;
40828         }
40829         
40830         var num = this.parseValue(value);
40831         
40832         if(isNaN(num)){
40833             this.markInvalid(String.format(this.nanText, value));
40834             return false;
40835         }
40836         
40837         if(num < this.minValue){
40838             this.markInvalid(String.format(this.minText, this.minValue));
40839             return false;
40840         }
40841         
40842         if(num > this.maxValue){
40843             this.markInvalid(String.format(this.maxText, this.maxValue));
40844             return false;
40845         }
40846         
40847         return true;
40848     },
40849     
40850     validate : function()
40851     {
40852         if(this.disabled || this.allowBlank){
40853             this.markValid();
40854             return true;
40855         }
40856         
40857         var currency = this.getCurrency();
40858         
40859         if(this.validateValue(this.getRawValue()) && currency.length){
40860             this.markValid();
40861             return true;
40862         }
40863         
40864         this.markInvalid();
40865         return false;
40866     },
40867     
40868     getName: function()
40869     {
40870         return this.name;
40871     },
40872     
40873     beforeBlur : function()
40874     {
40875         if(!this.castInt){
40876             return;
40877         }
40878         
40879         var v = this.parseValue(this.getRawValue());
40880         
40881         if(v || v == 0){
40882             this.setValue(v);
40883         }
40884     },
40885     
40886     onBlur : function()
40887     {
40888         this.beforeBlur();
40889         
40890         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40891             //this.el.removeClass(this.focusClass);
40892         }
40893         
40894         this.hasFocus = false;
40895         
40896         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40897             this.validate();
40898         }
40899         
40900         var v = this.getValue();
40901         
40902         if(String(v) !== String(this.startValue)){
40903             this.fireEvent('change', this, v, this.startValue);
40904         }
40905         
40906         this.fireEvent("blur", this);
40907     },
40908     
40909     inputEl : function()
40910     {
40911         return this.el.select('.roo-money-amount-input', true).first();
40912     },
40913     
40914     currencyEl : function()
40915     {
40916         return this.el.select('.roo-money-currency-input', true).first();
40917     },
40918     
40919     hiddenEl : function()
40920     {
40921         return this.el.select('input.hidden-number-input',true).first();
40922     }
40923     
40924 });