roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         
405     },
406     /**
407      * Hide a component - adds 'hidden' class
408      */
409     hide: function()
410     {
411         if(!this.getVisibilityEl()){
412             return;
413         }
414         
415         this.getVisibilityEl().addClass('hidden');
416         
417     }
418 });
419
420  /*
421  * - LGPL
422  *
423  * Body
424  *
425  */
426
427 /**
428  * @class Roo.bootstrap.Body
429  * @extends Roo.bootstrap.Component
430  * Bootstrap Body class
431  *
432  * @constructor
433  * Create a new body
434  * @param {Object} config The config object
435  */
436
437 Roo.bootstrap.Body = function(config){
438
439     config = config || {};
440
441     Roo.bootstrap.Body.superclass.constructor.call(this, config);
442     this.el = Roo.get(config.el ? config.el : document.body );
443     if (this.cls && this.cls.length) {
444         Roo.get(document.body).addClass(this.cls);
445     }
446 };
447
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
449
450     is_body : true,// just to make sure it's constructed?
451
452         autoCreate : {
453         cls: 'container'
454     },
455     onRender : function(ct, position)
456     {
457        /* Roo.log("Roo.bootstrap.Body - onRender");
458         if (this.cls && this.cls.length) {
459             Roo.get(document.body).addClass(this.cls);
460         }
461         // style??? xttr???
462         */
463     }
464
465
466
467
468 });
469 /*
470  * - LGPL
471  *
472  * button group
473  * 
474  */
475
476
477 /**
478  * @class Roo.bootstrap.ButtonGroup
479  * @extends Roo.bootstrap.Component
480  * Bootstrap ButtonGroup class
481  * @cfg {String} size lg | sm | xs (default empty normal)
482  * @cfg {String} align vertical | justified  (default none)
483  * @cfg {String} direction up | down (default down)
484  * @cfg {Boolean} toolbar false | true
485  * @cfg {Boolean} btn true | false
486  * 
487  * 
488  * @constructor
489  * Create a new Input
490  * @param {Object} config The config object
491  */
492
493 Roo.bootstrap.ButtonGroup = function(config){
494     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
495 };
496
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
498     
499     size: '',
500     align: '',
501     direction: '',
502     toolbar: false,
503     btn: true,
504
505     getAutoCreate : function(){
506         var cfg = {
507             cls: 'btn-group',
508             html : null
509         };
510         
511         cfg.html = this.html || cfg.html;
512         
513         if (this.toolbar) {
514             cfg = {
515                 cls: 'btn-toolbar',
516                 html: null
517             };
518             
519             return cfg;
520         }
521         
522         if (['vertical','justified'].indexOf(this.align)!==-1) {
523             cfg.cls = 'btn-group-' + this.align;
524             
525             if (this.align == 'justified') {
526                 console.log(this.items);
527             }
528         }
529         
530         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531             cfg.cls += ' btn-group-' + this.size;
532         }
533         
534         if (this.direction == 'up') {
535             cfg.cls += ' dropup' ;
536         }
537         
538         return cfg;
539     }
540    
541 });
542
543  /*
544  * - LGPL
545  *
546  * button
547  * 
548  */
549
550 /**
551  * @class Roo.bootstrap.Button
552  * @extends Roo.bootstrap.Component
553  * Bootstrap Button class
554  * @cfg {String} html The button content
555  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
556  * @cfg {String} size ( lg | sm | xs)
557  * @cfg {String} tag ( a | input | submit)
558  * @cfg {String} href empty or href
559  * @cfg {Boolean} disabled default false;
560  * @cfg {Boolean} isClose default false;
561  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
562  * @cfg {String} badge text for badge
563  * @cfg {String} theme default 
564  * @cfg {Boolean} inverse 
565  * @cfg {Boolean} toggle 
566  * @cfg {String} ontext text for on toggle state
567  * @cfg {String} offtext text for off toggle state
568  * @cfg {Boolean} defaulton 
569  * @cfg {Boolean} preventDefault  default true
570  * @cfg {Boolean} removeClass remove the standard class..
571  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
572  * 
573  * @constructor
574  * Create a new button
575  * @param {Object} config The config object
576  */
577
578
579 Roo.bootstrap.Button = function(config){
580     Roo.bootstrap.Button.superclass.constructor.call(this, config);
581     this.weightClass = ["btn-default", 
582                        "btn-primary", 
583                        "btn-success", 
584                        "btn-info", 
585                        "btn-warning",
586                        "btn-danger",
587                        "btn-link"
588                       ],  
589     this.addEvents({
590         // raw events
591         /**
592          * @event click
593          * When a butotn is pressed
594          * @param {Roo.bootstrap.Button} this
595          * @param {Roo.EventObject} e
596          */
597         "click" : true,
598          /**
599          * @event toggle
600          * After the button has been toggles
601          * @param {Roo.EventObject} e
602          * @param {boolean} pressed (also available as button.pressed)
603          */
604         "toggle" : true
605     });
606 };
607
608 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
609     html: false,
610     active: false,
611     weight: '',
612     size: '',
613     tag: 'button',
614     href: '',
615     disabled: false,
616     isClose: false,
617     glyphicon: '',
618     badge: '',
619     theme: 'default',
620     inverse: false,
621     
622     toggle: false,
623     ontext: 'ON',
624     offtext: 'OFF',
625     defaulton: true,
626     preventDefault: true,
627     removeClass: false,
628     name: false,
629     target: false,
630     
631     
632     pressed : null,
633      
634     
635     getAutoCreate : function(){
636         
637         var cfg = {
638             tag : 'button',
639             cls : 'roo-button',
640             html: ''
641         };
642         
643         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
645             this.tag = 'button';
646         } else {
647             cfg.tag = this.tag;
648         }
649         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
650         
651         if (this.toggle == true) {
652             cfg={
653                 tag: 'div',
654                 cls: 'slider-frame roo-button',
655                 cn: [
656                     {
657                         tag: 'span',
658                         'data-on-text':'ON',
659                         'data-off-text':'OFF',
660                         cls: 'slider-button',
661                         html: this.offtext
662                     }
663                 ]
664             };
665             
666             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667                 cfg.cls += ' '+this.weight;
668             }
669             
670             return cfg;
671         }
672         
673         if (this.isClose) {
674             cfg.cls += ' close';
675             
676             cfg["aria-hidden"] = true;
677             
678             cfg.html = "&times;";
679             
680             return cfg;
681         }
682         
683          
684         if (this.theme==='default') {
685             cfg.cls = 'btn roo-button';
686             
687             //if (this.parentType != 'Navbar') {
688             this.weight = this.weight.length ?  this.weight : 'default';
689             //}
690             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
691                 
692                 cfg.cls += ' btn-' + this.weight;
693             }
694         } else if (this.theme==='glow') {
695             
696             cfg.tag = 'a';
697             cfg.cls = 'btn-glow roo-button';
698             
699             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
700                 
701                 cfg.cls += ' ' + this.weight;
702             }
703         }
704    
705         
706         if (this.inverse) {
707             this.cls += ' inverse';
708         }
709         
710         
711         if (this.active) {
712             cfg.cls += ' active';
713         }
714         
715         if (this.disabled) {
716             cfg.disabled = 'disabled';
717         }
718         
719         if (this.items) {
720             Roo.log('changing to ul' );
721             cfg.tag = 'ul';
722             this.glyphicon = 'caret';
723         }
724         
725         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
726          
727         //gsRoo.log(this.parentType);
728         if (this.parentType === 'Navbar' && !this.parent().bar) {
729             Roo.log('changing to li?');
730             
731             cfg.tag = 'li';
732             
733             cfg.cls = '';
734             cfg.cn =  [{
735                 tag : 'a',
736                 cls : 'roo-button',
737                 html : this.html,
738                 href : this.href || '#'
739             }];
740             if (this.menu) {
741                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
742                 cfg.cls += ' dropdown';
743             }   
744             
745             delete cfg.html;
746             
747         }
748         
749        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
750         
751         if (this.glyphicon) {
752             cfg.html = ' ' + cfg.html;
753             
754             cfg.cn = [
755                 {
756                     tag: 'span',
757                     cls: 'glyphicon glyphicon-' + this.glyphicon
758                 }
759             ];
760         }
761         
762         if (this.badge) {
763             cfg.html += ' ';
764             
765             cfg.tag = 'a';
766             
767 //            cfg.cls='btn roo-button';
768             
769             cfg.href=this.href;
770             
771             var value = cfg.html;
772             
773             if(this.glyphicon){
774                 value = {
775                             tag: 'span',
776                             cls: 'glyphicon glyphicon-' + this.glyphicon,
777                             html: this.html
778                         };
779                 
780             }
781             
782             cfg.cn = [
783                 value,
784                 {
785                     tag: 'span',
786                     cls: 'badge',
787                     html: this.badge
788                 }
789             ];
790             
791             cfg.html='';
792         }
793         
794         if (this.menu) {
795             cfg.cls += ' dropdown';
796             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
797         }
798         
799         if (cfg.tag !== 'a' && this.href !== '') {
800             throw "Tag must be a to set href.";
801         } else if (this.href.length > 0) {
802             cfg.href = this.href;
803         }
804         
805         if(this.removeClass){
806             cfg.cls = '';
807         }
808         
809         if(this.target){
810             cfg.target = this.target;
811         }
812         
813         return cfg;
814     },
815     initEvents: function() {
816        // Roo.log('init events?');
817 //        Roo.log(this.el.dom);
818         // add the menu...
819         
820         if (typeof (this.menu) != 'undefined') {
821             this.menu.parentType = this.xtype;
822             this.menu.triggerEl = this.el;
823             this.addxtype(Roo.apply({}, this.menu));
824         }
825
826
827        if (this.el.hasClass('roo-button')) {
828             this.el.on('click', this.onClick, this);
829        } else {
830             this.el.select('.roo-button').on('click', this.onClick, this);
831        }
832        
833        if(this.removeClass){
834            this.el.on('click', this.onClick, this);
835        }
836        
837        this.el.enableDisplayMode();
838         
839     },
840     onClick : function(e)
841     {
842         if (this.disabled) {
843             return;
844         }
845         
846         
847         Roo.log('button on click ');
848         if(this.preventDefault){
849             e.preventDefault();
850         }
851         if (this.pressed === true || this.pressed === false) {
852             this.pressed = !this.pressed;
853             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
854             this.fireEvent('toggle', this, e, this.pressed);
855         }
856         
857         
858         this.fireEvent('click', this, e);
859     },
860     
861     /**
862      * Enables this button
863      */
864     enable : function()
865     {
866         this.disabled = false;
867         this.el.removeClass('disabled');
868     },
869     
870     /**
871      * Disable this button
872      */
873     disable : function()
874     {
875         this.disabled = true;
876         this.el.addClass('disabled');
877     },
878      /**
879      * sets the active state on/off, 
880      * @param {Boolean} state (optional) Force a particular state
881      */
882     setActive : function(v) {
883         
884         this.el[v ? 'addClass' : 'removeClass']('active');
885     },
886      /**
887      * toggles the current active state 
888      */
889     toggleActive : function()
890     {
891        var active = this.el.hasClass('active');
892        this.setActive(!active);
893        
894         
895     },
896     setText : function(str)
897     {
898         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
899     },
900     getText : function()
901     {
902         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
903     },
904     hide: function() {
905        
906      
907         this.el.hide();   
908     },
909     show: function() {
910        
911         this.el.show();   
912     },
913     setWeight : function(str)
914     {
915           this.el.removeClass(this.weightClass);
916         this.el.addClass('btn-' + str);        
917     }
918     
919     
920 });
921
922  /*
923  * - LGPL
924  *
925  * column
926  * 
927  */
928
929 /**
930  * @class Roo.bootstrap.Column
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Column class
933  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
934  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
935  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
936  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
937  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
938  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
939  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
940  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
941  *
942  * 
943  * @cfg {Boolean} hidden (true|false) hide the element
944  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
945  * @cfg {String} fa (ban|check|...) font awesome icon
946  * @cfg {Number} fasize (1|2|....) font awsome size
947
948  * @cfg {String} icon (info-sign|check|...) glyphicon name
949
950  * @cfg {String} html content of column.
951  * 
952  * @constructor
953  * Create a new Column
954  * @param {Object} config The config object
955  */
956
957 Roo.bootstrap.Column = function(config){
958     Roo.bootstrap.Column.superclass.constructor.call(this, config);
959 };
960
961 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
962     
963     xs: false,
964     sm: false,
965     md: false,
966     lg: false,
967     xsoff: false,
968     smoff: false,
969     mdoff: false,
970     lgoff: false,
971     html: '',
972     offset: 0,
973     alert: false,
974     fa: false,
975     icon : false,
976     hidden : false,
977     fasize : 1,
978     
979     getAutoCreate : function(){
980         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
981         
982         cfg = {
983             tag: 'div',
984             cls: 'column'
985         };
986         
987         var settings=this;
988         ['xs','sm','md','lg'].map(function(size){
989             //Roo.log( size + ':' + settings[size]);
990             
991             if (settings[size+'off'] !== false) {
992                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
993             }
994             
995             if (settings[size] === false) {
996                 return;
997             }
998             
999             if (!settings[size]) { // 0 = hidden
1000                 cfg.cls += ' hidden-' + size;
1001                 return;
1002             }
1003             cfg.cls += ' col-' + size + '-' + settings[size];
1004             
1005         });
1006         
1007         if (this.hidden) {
1008             cfg.cls += ' hidden';
1009         }
1010         
1011         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1012             cfg.cls +=' alert alert-' + this.alert;
1013         }
1014         
1015         
1016         if (this.html.length) {
1017             cfg.html = this.html;
1018         }
1019         if (this.fa) {
1020             var fasize = '';
1021             if (this.fasize > 1) {
1022                 fasize = ' fa-' + this.fasize + 'x';
1023             }
1024             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1025             
1026             
1027         }
1028         if (this.icon) {
1029             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1030         }
1031         
1032         return cfg;
1033     }
1034    
1035 });
1036
1037  
1038
1039  /*
1040  * - LGPL
1041  *
1042  * page container.
1043  * 
1044  */
1045
1046
1047 /**
1048  * @class Roo.bootstrap.Container
1049  * @extends Roo.bootstrap.Component
1050  * Bootstrap Container class
1051  * @cfg {Boolean} jumbotron is it a jumbotron element
1052  * @cfg {String} html content of element
1053  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1054  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1055  * @cfg {String} header content of header (for panel)
1056  * @cfg {String} footer content of footer (for panel)
1057  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1058  * @cfg {String} tag (header|aside|section) type of HTML tag.
1059  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1060  * @cfg {String} fa font awesome icon
1061  * @cfg {String} icon (info-sign|check|...) glyphicon name
1062  * @cfg {Boolean} hidden (true|false) hide the element
1063  * @cfg {Boolean} expandable (true|false) default false
1064  * @cfg {Boolean} expanded (true|false) default true
1065  * @cfg {String} rheader contet on the right of header
1066  * @cfg {Boolean} clickable (true|false) default false
1067
1068  *     
1069  * @constructor
1070  * Create a new Container
1071  * @param {Object} config The config object
1072  */
1073
1074 Roo.bootstrap.Container = function(config){
1075     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1076     
1077     this.addEvents({
1078         // raw events
1079          /**
1080          * @event expand
1081          * After the panel has been expand
1082          * 
1083          * @param {Roo.bootstrap.Container} this
1084          */
1085         "expand" : true,
1086         /**
1087          * @event collapse
1088          * After the panel has been collapsed
1089          * 
1090          * @param {Roo.bootstrap.Container} this
1091          */
1092         "collapse" : true,
1093         /**
1094          * @event click
1095          * When a element is chick
1096          * @param {Roo.bootstrap.Container} this
1097          * @param {Roo.EventObject} e
1098          */
1099         "click" : true
1100     });
1101 };
1102
1103 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1104     
1105     jumbotron : false,
1106     well: '',
1107     panel : '',
1108     header: '',
1109     footer : '',
1110     sticky: '',
1111     tag : false,
1112     alert : false,
1113     fa: false,
1114     icon : false,
1115     expandable : false,
1116     rheader : '',
1117     expanded : true,
1118     clickable: false,
1119   
1120      
1121     getChildContainer : function() {
1122         
1123         if(!this.el){
1124             return false;
1125         }
1126         
1127         if (this.panel.length) {
1128             return this.el.select('.panel-body',true).first();
1129         }
1130         
1131         return this.el;
1132     },
1133     
1134     
1135     getAutoCreate : function(){
1136         
1137         var cfg = {
1138             tag : this.tag || 'div',
1139             html : '',
1140             cls : ''
1141         };
1142         if (this.jumbotron) {
1143             cfg.cls = 'jumbotron';
1144         }
1145         
1146         
1147         
1148         // - this is applied by the parent..
1149         //if (this.cls) {
1150         //    cfg.cls = this.cls + '';
1151         //}
1152         
1153         if (this.sticky.length) {
1154             
1155             var bd = Roo.get(document.body);
1156             if (!bd.hasClass('bootstrap-sticky')) {
1157                 bd.addClass('bootstrap-sticky');
1158                 Roo.select('html',true).setStyle('height', '100%');
1159             }
1160              
1161             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1162         }
1163         
1164         
1165         if (this.well.length) {
1166             switch (this.well) {
1167                 case 'lg':
1168                 case 'sm':
1169                     cfg.cls +=' well well-' +this.well;
1170                     break;
1171                 default:
1172                     cfg.cls +=' well';
1173                     break;
1174             }
1175         }
1176         
1177         if (this.hidden) {
1178             cfg.cls += ' hidden';
1179         }
1180         
1181         
1182         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1183             cfg.cls +=' alert alert-' + this.alert;
1184         }
1185         
1186         var body = cfg;
1187         
1188         if (this.panel.length) {
1189             cfg.cls += ' panel panel-' + this.panel;
1190             cfg.cn = [];
1191             if (this.header.length) {
1192                 
1193                 var h = [];
1194                 
1195                 if(this.expandable){
1196                     
1197                     cfg.cls = cfg.cls + ' expandable';
1198                     
1199                     h.push({
1200                         tag: 'i',
1201                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1202                     });
1203                     
1204                 }
1205                 
1206                 h.push(
1207                     {
1208                         tag: 'span',
1209                         cls : 'panel-title',
1210                         html : (this.expandable ? '&nbsp;' : '') + this.header
1211                     },
1212                     {
1213                         tag: 'span',
1214                         cls: 'panel-header-right',
1215                         html: this.rheader
1216                     }
1217                 );
1218                 
1219                 cfg.cn.push({
1220                     cls : 'panel-heading',
1221                     style : this.expandable ? 'cursor: pointer' : '',
1222                     cn : h
1223                 });
1224                 
1225             }
1226             
1227             body = false;
1228             cfg.cn.push({
1229                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1230                 html : this.html
1231             });
1232             
1233             
1234             if (this.footer.length) {
1235                 cfg.cn.push({
1236                     cls : 'panel-footer',
1237                     html : this.footer
1238                     
1239                 });
1240             }
1241             
1242         }
1243         
1244         if (body) {
1245             body.html = this.html || cfg.html;
1246             // prefix with the icons..
1247             if (this.fa) {
1248                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1249             }
1250             if (this.icon) {
1251                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1252             }
1253             
1254             
1255         }
1256         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1257             cfg.cls =  'container';
1258         }
1259         
1260         return cfg;
1261     },
1262     
1263     initEvents: function() 
1264     {
1265         if(this.expandable){
1266             var headerEl = this.headerEl();
1267         
1268             if(headerEl){
1269                 headerEl.on('click', this.onToggleClick, this);
1270             }
1271         }
1272         
1273         if(this.clickable){
1274             this.el.on('click', this.onClick, this);
1275         }
1276         
1277     },
1278     
1279     onToggleClick : function()
1280     {
1281         var headerEl = this.headerEl();
1282         
1283         if(!headerEl){
1284             return;
1285         }
1286         
1287         if(this.expanded){
1288             this.collapse();
1289             return;
1290         }
1291         
1292         this.expand();
1293     },
1294     
1295     expand : function()
1296     {
1297         if(this.fireEvent('expand', this)) {
1298             
1299             this.expanded = true;
1300             
1301             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1302             
1303             this.el.select('.panel-body',true).first().removeClass('hide');
1304             
1305             var toggleEl = this.toggleEl();
1306
1307             if(!toggleEl){
1308                 return;
1309             }
1310
1311             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1312         }
1313         
1314     },
1315     
1316     collapse : function()
1317     {
1318         if(this.fireEvent('collapse', this)) {
1319             
1320             this.expanded = false;
1321             
1322             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1323             this.el.select('.panel-body',true).first().addClass('hide');
1324         
1325             var toggleEl = this.toggleEl();
1326
1327             if(!toggleEl){
1328                 return;
1329             }
1330
1331             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1332         }
1333     },
1334     
1335     toggleEl : function()
1336     {
1337         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1338             return;
1339         }
1340         
1341         return this.el.select('.panel-heading .fa',true).first();
1342     },
1343     
1344     headerEl : function()
1345     {
1346         if(!this.el || !this.panel.length || !this.header.length){
1347             return;
1348         }
1349         
1350         return this.el.select('.panel-heading',true).first()
1351     },
1352     
1353     bodyEl : function()
1354     {
1355         if(!this.el || !this.panel.length){
1356             return;
1357         }
1358         
1359         return this.el.select('.panel-body',true).first()
1360     },
1361     
1362     titleEl : function()
1363     {
1364         if(!this.el || !this.panel.length || !this.header.length){
1365             return;
1366         }
1367         
1368         return this.el.select('.panel-title',true).first();
1369     },
1370     
1371     setTitle : function(v)
1372     {
1373         var titleEl = this.titleEl();
1374         
1375         if(!titleEl){
1376             return;
1377         }
1378         
1379         titleEl.dom.innerHTML = v;
1380     },
1381     
1382     getTitle : function()
1383     {
1384         
1385         var titleEl = this.titleEl();
1386         
1387         if(!titleEl){
1388             return '';
1389         }
1390         
1391         return titleEl.dom.innerHTML;
1392     },
1393     
1394     setRightTitle : function(v)
1395     {
1396         var t = this.el.select('.panel-header-right',true).first();
1397         
1398         if(!t){
1399             return;
1400         }
1401         
1402         t.dom.innerHTML = v;
1403     },
1404     
1405     onClick : function(e)
1406     {
1407         e.preventDefault();
1408         
1409         this.fireEvent('click', this, e);
1410     }
1411 });
1412
1413  /*
1414  * - LGPL
1415  *
1416  * image
1417  * 
1418  */
1419
1420
1421 /**
1422  * @class Roo.bootstrap.Img
1423  * @extends Roo.bootstrap.Component
1424  * Bootstrap Img class
1425  * @cfg {Boolean} imgResponsive false | true
1426  * @cfg {String} border rounded | circle | thumbnail
1427  * @cfg {String} src image source
1428  * @cfg {String} alt image alternative text
1429  * @cfg {String} href a tag href
1430  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1431  * @cfg {String} xsUrl xs image source
1432  * @cfg {String} smUrl sm image source
1433  * @cfg {String} mdUrl md image source
1434  * @cfg {String} lgUrl lg image source
1435  * 
1436  * @constructor
1437  * Create a new Input
1438  * @param {Object} config The config object
1439  */
1440
1441 Roo.bootstrap.Img = function(config){
1442     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1443     
1444     this.addEvents({
1445         // img events
1446         /**
1447          * @event click
1448          * The img click event for the img.
1449          * @param {Roo.EventObject} e
1450          */
1451         "click" : true
1452     });
1453 };
1454
1455 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1456     
1457     imgResponsive: true,
1458     border: '',
1459     src: 'about:blank',
1460     href: false,
1461     target: false,
1462     xsUrl: '',
1463     smUrl: '',
1464     mdUrl: '',
1465     lgUrl: '',
1466
1467     getAutoCreate : function()
1468     {   
1469         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1470             return this.createSingleImg();
1471         }
1472         
1473         var cfg = {
1474             tag: 'div',
1475             cls: 'roo-image-responsive-group',
1476             cn: []
1477         };
1478         var _this = this;
1479         
1480         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1481             
1482             if(!_this[size + 'Url']){
1483                 return;
1484             }
1485             
1486             var img = {
1487                 tag: 'img',
1488                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1489                 html: _this.html || cfg.html,
1490                 src: _this[size + 'Url']
1491             };
1492             
1493             img.cls += ' roo-image-responsive-' + size;
1494             
1495             var s = ['xs', 'sm', 'md', 'lg'];
1496             
1497             s.splice(s.indexOf(size), 1);
1498             
1499             Roo.each(s, function(ss){
1500                 img.cls += ' hidden-' + ss;
1501             });
1502             
1503             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1504                 cfg.cls += ' img-' + _this.border;
1505             }
1506             
1507             if(_this.alt){
1508                 cfg.alt = _this.alt;
1509             }
1510             
1511             if(_this.href){
1512                 var a = {
1513                     tag: 'a',
1514                     href: _this.href,
1515                     cn: [
1516                         img
1517                     ]
1518                 };
1519
1520                 if(this.target){
1521                     a.target = _this.target;
1522                 }
1523             }
1524             
1525             cfg.cn.push((_this.href) ? a : img);
1526             
1527         });
1528         
1529         return cfg;
1530     },
1531     
1532     createSingleImg : function()
1533     {
1534         var cfg = {
1535             tag: 'img',
1536             cls: (this.imgResponsive) ? 'img-responsive' : '',
1537             html : null,
1538             src : 'about:blank'  // just incase src get's set to undefined?!?
1539         };
1540         
1541         cfg.html = this.html || cfg.html;
1542         
1543         cfg.src = this.src || cfg.src;
1544         
1545         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1546             cfg.cls += ' img-' + this.border;
1547         }
1548         
1549         if(this.alt){
1550             cfg.alt = this.alt;
1551         }
1552         
1553         if(this.href){
1554             var a = {
1555                 tag: 'a',
1556                 href: this.href,
1557                 cn: [
1558                     cfg
1559                 ]
1560             };
1561             
1562             if(this.target){
1563                 a.target = this.target;
1564             }
1565             
1566         }
1567         
1568         return (this.href) ? a : cfg;
1569     },
1570     
1571     initEvents: function() 
1572     {
1573         if(!this.href){
1574             this.el.on('click', this.onClick, this);
1575         }
1576         
1577     },
1578     
1579     onClick : function(e)
1580     {
1581         Roo.log('img onclick');
1582         this.fireEvent('click', this, e);
1583     },
1584     /**
1585      * Sets the url of the image - used to update it
1586      * @param {String} url the url of the image
1587      */
1588     
1589     setSrc : function(url)
1590     {
1591         this.src =  url;
1592         
1593         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1594             this.el.dom.src =  url;
1595             return;
1596         }
1597         
1598         this.el.select('img', true).first().dom.src =  url;
1599     }
1600     
1601     
1602    
1603 });
1604
1605  /*
1606  * - LGPL
1607  *
1608  * image
1609  * 
1610  */
1611
1612
1613 /**
1614  * @class Roo.bootstrap.Link
1615  * @extends Roo.bootstrap.Component
1616  * Bootstrap Link Class
1617  * @cfg {String} alt image alternative text
1618  * @cfg {String} href a tag href
1619  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1620  * @cfg {String} html the content of the link.
1621  * @cfg {String} anchor name for the anchor link
1622  * @cfg {String} fa - favicon
1623
1624  * @cfg {Boolean} preventDefault (true | false) default false
1625
1626  * 
1627  * @constructor
1628  * Create a new Input
1629  * @param {Object} config The config object
1630  */
1631
1632 Roo.bootstrap.Link = function(config){
1633     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1634     
1635     this.addEvents({
1636         // img events
1637         /**
1638          * @event click
1639          * The img click event for the img.
1640          * @param {Roo.EventObject} e
1641          */
1642         "click" : true
1643     });
1644 };
1645
1646 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1647     
1648     href: false,
1649     target: false,
1650     preventDefault: false,
1651     anchor : false,
1652     alt : false,
1653     fa: false,
1654
1655
1656     getAutoCreate : function()
1657     {
1658         var html = this.html || '';
1659         
1660         if (this.fa !== false) {
1661             html = '<i class="fa fa-' + this.fa + '"></i>';
1662         }
1663         var cfg = {
1664             tag: 'a'
1665         };
1666         // anchor's do not require html/href...
1667         if (this.anchor === false) {
1668             cfg.html = html;
1669             cfg.href = this.href || '#';
1670         } else {
1671             cfg.name = this.anchor;
1672             if (this.html !== false || this.fa !== false) {
1673                 cfg.html = html;
1674             }
1675             if (this.href !== false) {
1676                 cfg.href = this.href;
1677             }
1678         }
1679         
1680         if(this.alt !== false){
1681             cfg.alt = this.alt;
1682         }
1683         
1684         
1685         if(this.target !== false) {
1686             cfg.target = this.target;
1687         }
1688         
1689         return cfg;
1690     },
1691     
1692     initEvents: function() {
1693         
1694         if(!this.href || this.preventDefault){
1695             this.el.on('click', this.onClick, this);
1696         }
1697     },
1698     
1699     onClick : function(e)
1700     {
1701         if(this.preventDefault){
1702             e.preventDefault();
1703         }
1704         //Roo.log('img onclick');
1705         this.fireEvent('click', this, e);
1706     }
1707    
1708 });
1709
1710  /*
1711  * - LGPL
1712  *
1713  * header
1714  * 
1715  */
1716
1717 /**
1718  * @class Roo.bootstrap.Header
1719  * @extends Roo.bootstrap.Component
1720  * Bootstrap Header class
1721  * @cfg {String} html content of header
1722  * @cfg {Number} level (1|2|3|4|5|6) default 1
1723  * 
1724  * @constructor
1725  * Create a new Header
1726  * @param {Object} config The config object
1727  */
1728
1729
1730 Roo.bootstrap.Header  = function(config){
1731     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1732 };
1733
1734 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1735     
1736     //href : false,
1737     html : false,
1738     level : 1,
1739     
1740     
1741     
1742     getAutoCreate : function(){
1743         
1744         
1745         
1746         var cfg = {
1747             tag: 'h' + (1 *this.level),
1748             html: this.html || ''
1749         } ;
1750         
1751         return cfg;
1752     }
1753    
1754 });
1755
1756  
1757
1758  /*
1759  * Based on:
1760  * Ext JS Library 1.1.1
1761  * Copyright(c) 2006-2007, Ext JS, LLC.
1762  *
1763  * Originally Released Under LGPL - original licence link has changed is not relivant.
1764  *
1765  * Fork - LGPL
1766  * <script type="text/javascript">
1767  */
1768  
1769 /**
1770  * @class Roo.bootstrap.MenuMgr
1771  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1772  * @singleton
1773  */
1774 Roo.bootstrap.MenuMgr = function(){
1775    var menus, active, groups = {}, attached = false, lastShow = new Date();
1776
1777    // private - called when first menu is created
1778    function init(){
1779        menus = {};
1780        active = new Roo.util.MixedCollection();
1781        Roo.get(document).addKeyListener(27, function(){
1782            if(active.length > 0){
1783                hideAll();
1784            }
1785        });
1786    }
1787
1788    // private
1789    function hideAll(){
1790        if(active && active.length > 0){
1791            var c = active.clone();
1792            c.each(function(m){
1793                m.hide();
1794            });
1795        }
1796    }
1797
1798    // private
1799    function onHide(m){
1800        active.remove(m);
1801        if(active.length < 1){
1802            Roo.get(document).un("mouseup", onMouseDown);
1803             
1804            attached = false;
1805        }
1806    }
1807
1808    // private
1809    function onShow(m){
1810        var last = active.last();
1811        lastShow = new Date();
1812        active.add(m);
1813        if(!attached){
1814           Roo.get(document).on("mouseup", onMouseDown);
1815            
1816            attached = true;
1817        }
1818        if(m.parentMenu){
1819           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1820           m.parentMenu.activeChild = m;
1821        }else if(last && last.isVisible()){
1822           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1823        }
1824    }
1825
1826    // private
1827    function onBeforeHide(m){
1828        if(m.activeChild){
1829            m.activeChild.hide();
1830        }
1831        if(m.autoHideTimer){
1832            clearTimeout(m.autoHideTimer);
1833            delete m.autoHideTimer;
1834        }
1835    }
1836
1837    // private
1838    function onBeforeShow(m){
1839        var pm = m.parentMenu;
1840        if(!pm && !m.allowOtherMenus){
1841            hideAll();
1842        }else if(pm && pm.activeChild && active != m){
1843            pm.activeChild.hide();
1844        }
1845    }
1846
1847    // private this should really trigger on mouseup..
1848    function onMouseDown(e){
1849         Roo.log("on Mouse Up");
1850         
1851         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1852             Roo.log("MenuManager hideAll");
1853             hideAll();
1854             e.stopEvent();
1855         }
1856         
1857         
1858    }
1859
1860    // private
1861    function onBeforeCheck(mi, state){
1862        if(state){
1863            var g = groups[mi.group];
1864            for(var i = 0, l = g.length; i < l; i++){
1865                if(g[i] != mi){
1866                    g[i].setChecked(false);
1867                }
1868            }
1869        }
1870    }
1871
1872    return {
1873
1874        /**
1875         * Hides all menus that are currently visible
1876         */
1877        hideAll : function(){
1878             hideAll();  
1879        },
1880
1881        // private
1882        register : function(menu){
1883            if(!menus){
1884                init();
1885            }
1886            menus[menu.id] = menu;
1887            menu.on("beforehide", onBeforeHide);
1888            menu.on("hide", onHide);
1889            menu.on("beforeshow", onBeforeShow);
1890            menu.on("show", onShow);
1891            var g = menu.group;
1892            if(g && menu.events["checkchange"]){
1893                if(!groups[g]){
1894                    groups[g] = [];
1895                }
1896                groups[g].push(menu);
1897                menu.on("checkchange", onCheck);
1898            }
1899        },
1900
1901         /**
1902          * Returns a {@link Roo.menu.Menu} object
1903          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1904          * be used to generate and return a new Menu instance.
1905          */
1906        get : function(menu){
1907            if(typeof menu == "string"){ // menu id
1908                return menus[menu];
1909            }else if(menu.events){  // menu instance
1910                return menu;
1911            }
1912            /*else if(typeof menu.length == 'number'){ // array of menu items?
1913                return new Roo.bootstrap.Menu({items:menu});
1914            }else{ // otherwise, must be a config
1915                return new Roo.bootstrap.Menu(menu);
1916            }
1917            */
1918            return false;
1919        },
1920
1921        // private
1922        unregister : function(menu){
1923            delete menus[menu.id];
1924            menu.un("beforehide", onBeforeHide);
1925            menu.un("hide", onHide);
1926            menu.un("beforeshow", onBeforeShow);
1927            menu.un("show", onShow);
1928            var g = menu.group;
1929            if(g && menu.events["checkchange"]){
1930                groups[g].remove(menu);
1931                menu.un("checkchange", onCheck);
1932            }
1933        },
1934
1935        // private
1936        registerCheckable : function(menuItem){
1937            var g = menuItem.group;
1938            if(g){
1939                if(!groups[g]){
1940                    groups[g] = [];
1941                }
1942                groups[g].push(menuItem);
1943                menuItem.on("beforecheckchange", onBeforeCheck);
1944            }
1945        },
1946
1947        // private
1948        unregisterCheckable : function(menuItem){
1949            var g = menuItem.group;
1950            if(g){
1951                groups[g].remove(menuItem);
1952                menuItem.un("beforecheckchange", onBeforeCheck);
1953            }
1954        }
1955    };
1956 }();/*
1957  * - LGPL
1958  *
1959  * menu
1960  * 
1961  */
1962
1963 /**
1964  * @class Roo.bootstrap.Menu
1965  * @extends Roo.bootstrap.Component
1966  * Bootstrap Menu class - container for MenuItems
1967  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1968  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1969  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1970  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1971  * 
1972  * @constructor
1973  * Create a new Menu
1974  * @param {Object} config The config object
1975  */
1976
1977
1978 Roo.bootstrap.Menu = function(config){
1979     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1980     if (this.registerMenu && this.type != 'treeview')  {
1981         Roo.bootstrap.MenuMgr.register(this);
1982     }
1983     this.addEvents({
1984         /**
1985          * @event beforeshow
1986          * Fires before this menu is displayed
1987          * @param {Roo.menu.Menu} this
1988          */
1989         beforeshow : true,
1990         /**
1991          * @event beforehide
1992          * Fires before this menu is hidden
1993          * @param {Roo.menu.Menu} this
1994          */
1995         beforehide : true,
1996         /**
1997          * @event show
1998          * Fires after this menu is displayed
1999          * @param {Roo.menu.Menu} this
2000          */
2001         show : true,
2002         /**
2003          * @event hide
2004          * Fires after this menu is hidden
2005          * @param {Roo.menu.Menu} this
2006          */
2007         hide : true,
2008         /**
2009          * @event click
2010          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2011          * @param {Roo.menu.Menu} this
2012          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2013          * @param {Roo.EventObject} e
2014          */
2015         click : true,
2016         /**
2017          * @event mouseover
2018          * Fires when the mouse is hovering over this menu
2019          * @param {Roo.menu.Menu} this
2020          * @param {Roo.EventObject} e
2021          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2022          */
2023         mouseover : true,
2024         /**
2025          * @event mouseout
2026          * Fires when the mouse exits this menu
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.EventObject} e
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          */
2031         mouseout : true,
2032         /**
2033          * @event itemclick
2034          * Fires when a menu item contained in this menu is clicked
2035          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2036          * @param {Roo.EventObject} e
2037          */
2038         itemclick: true
2039     });
2040     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2041 };
2042
2043 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2044     
2045    /// html : false,
2046     //align : '',
2047     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2048     type: false,
2049     /**
2050      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2051      */
2052     registerMenu : true,
2053     
2054     menuItems :false, // stores the menu items..
2055     
2056     hidden:true,
2057         
2058     parentMenu : false,
2059     
2060     stopEvent : true,
2061     
2062     isLink : false,
2063     
2064     getChildContainer : function() {
2065         return this.el;  
2066     },
2067     
2068     getAutoCreate : function(){
2069          
2070         //if (['right'].indexOf(this.align)!==-1) {
2071         //    cfg.cn[1].cls += ' pull-right'
2072         //}
2073         
2074         
2075         var cfg = {
2076             tag : 'ul',
2077             cls : 'dropdown-menu' ,
2078             style : 'z-index:1000'
2079             
2080         };
2081         
2082         if (this.type === 'submenu') {
2083             cfg.cls = 'submenu active';
2084         }
2085         if (this.type === 'treeview') {
2086             cfg.cls = 'treeview-menu';
2087         }
2088         
2089         return cfg;
2090     },
2091     initEvents : function() {
2092         
2093        // Roo.log("ADD event");
2094        // Roo.log(this.triggerEl.dom);
2095         
2096         this.triggerEl.on('click', this.onTriggerClick, this);
2097         
2098         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2099         
2100         this.triggerEl.addClass('dropdown-toggle');
2101         
2102         if (Roo.isTouch) {
2103             this.el.on('touchstart'  , this.onTouch, this);
2104         }
2105         this.el.on('click' , this.onClick, this);
2106
2107         this.el.on("mouseover", this.onMouseOver, this);
2108         this.el.on("mouseout", this.onMouseOut, this);
2109         
2110     },
2111     
2112     findTargetItem : function(e)
2113     {
2114         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2115         if(!t){
2116             return false;
2117         }
2118         //Roo.log(t);         Roo.log(t.id);
2119         if(t && t.id){
2120             //Roo.log(this.menuitems);
2121             return this.menuitems.get(t.id);
2122             
2123             //return this.items.get(t.menuItemId);
2124         }
2125         
2126         return false;
2127     },
2128     
2129     onTouch : function(e) 
2130     {
2131         Roo.log("menu.onTouch");
2132         //e.stopEvent(); this make the user popdown broken
2133         this.onClick(e);
2134     },
2135     
2136     onClick : function(e)
2137     {
2138         Roo.log("menu.onClick");
2139         
2140         var t = this.findTargetItem(e);
2141         if(!t || t.isContainer){
2142             return;
2143         }
2144         Roo.log(e);
2145         /*
2146         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2147             if(t == this.activeItem && t.shouldDeactivate(e)){
2148                 this.activeItem.deactivate();
2149                 delete this.activeItem;
2150                 return;
2151             }
2152             if(t.canActivate){
2153                 this.setActiveItem(t, true);
2154             }
2155             return;
2156             
2157             
2158         }
2159         */
2160        
2161         Roo.log('pass click event');
2162         
2163         t.onClick(e);
2164         
2165         this.fireEvent("click", this, t, e);
2166         
2167         var _this = this;
2168         
2169         if(!t.href.length || t.href == '#'){
2170             (function() { _this.hide(); }).defer(100);
2171         }
2172         
2173     },
2174     
2175     onMouseOver : function(e){
2176         var t  = this.findTargetItem(e);
2177         //Roo.log(t);
2178         //if(t){
2179         //    if(t.canActivate && !t.disabled){
2180         //        this.setActiveItem(t, true);
2181         //    }
2182         //}
2183         
2184         this.fireEvent("mouseover", this, e, t);
2185     },
2186     isVisible : function(){
2187         return !this.hidden;
2188     },
2189      onMouseOut : function(e){
2190         var t  = this.findTargetItem(e);
2191         
2192         //if(t ){
2193         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2194         //        this.activeItem.deactivate();
2195         //        delete this.activeItem;
2196         //    }
2197         //}
2198         this.fireEvent("mouseout", this, e, t);
2199     },
2200     
2201     
2202     /**
2203      * Displays this menu relative to another element
2204      * @param {String/HTMLElement/Roo.Element} element The element to align to
2205      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2206      * the element (defaults to this.defaultAlign)
2207      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2208      */
2209     show : function(el, pos, parentMenu){
2210         this.parentMenu = parentMenu;
2211         if(!this.el){
2212             this.render();
2213         }
2214         this.fireEvent("beforeshow", this);
2215         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2216     },
2217      /**
2218      * Displays this menu at a specific xy position
2219      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2220      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2221      */
2222     showAt : function(xy, parentMenu, /* private: */_e){
2223         this.parentMenu = parentMenu;
2224         if(!this.el){
2225             this.render();
2226         }
2227         if(_e !== false){
2228             this.fireEvent("beforeshow", this);
2229             //xy = this.el.adjustForConstraints(xy);
2230         }
2231         
2232         //this.el.show();
2233         this.hideMenuItems();
2234         this.hidden = false;
2235         this.triggerEl.addClass('open');
2236         
2237         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2238             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2239         }
2240         
2241         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2242             this.el.setXY(xy);
2243         }
2244         
2245         this.focus();
2246         this.fireEvent("show", this);
2247     },
2248     
2249     focus : function(){
2250         return;
2251         if(!this.hidden){
2252             this.doFocus.defer(50, this);
2253         }
2254     },
2255
2256     doFocus : function(){
2257         if(!this.hidden){
2258             this.focusEl.focus();
2259         }
2260     },
2261
2262     /**
2263      * Hides this menu and optionally all parent menus
2264      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2265      */
2266     hide : function(deep)
2267     {
2268         
2269         this.hideMenuItems();
2270         if(this.el && this.isVisible()){
2271             this.fireEvent("beforehide", this);
2272             if(this.activeItem){
2273                 this.activeItem.deactivate();
2274                 this.activeItem = null;
2275             }
2276             this.triggerEl.removeClass('open');;
2277             this.hidden = true;
2278             this.fireEvent("hide", this);
2279         }
2280         if(deep === true && this.parentMenu){
2281             this.parentMenu.hide(true);
2282         }
2283     },
2284     
2285     onTriggerClick : function(e)
2286     {
2287         Roo.log('trigger click');
2288         
2289         var target = e.getTarget();
2290         
2291         Roo.log(target.nodeName.toLowerCase());
2292         
2293         if(target.nodeName.toLowerCase() === 'i'){
2294             e.preventDefault();
2295         }
2296         
2297     },
2298     
2299     onTriggerPress  : function(e)
2300     {
2301         Roo.log('trigger press');
2302         //Roo.log(e.getTarget());
2303        // Roo.log(this.triggerEl.dom);
2304        
2305         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2306         var pel = Roo.get(e.getTarget());
2307         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2308             Roo.log('is treeview or dropdown?');
2309             return;
2310         }
2311         
2312         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2313             return;
2314         }
2315         
2316         if (this.isVisible()) {
2317             Roo.log('hide');
2318             this.hide();
2319         } else {
2320             Roo.log('show');
2321             this.show(this.triggerEl, false, false);
2322         }
2323         
2324         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2325             e.stopEvent();
2326         }
2327         
2328     },
2329        
2330     
2331     hideMenuItems : function()
2332     {
2333         Roo.log("hide Menu Items");
2334         if (!this.el) { 
2335             return;
2336         }
2337         //$(backdrop).remove()
2338         this.el.select('.open',true).each(function(aa) {
2339             
2340             aa.removeClass('open');
2341           //var parent = getParent($(this))
2342           //var relatedTarget = { relatedTarget: this }
2343           
2344            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2345           //if (e.isDefaultPrevented()) return
2346            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2347         });
2348     },
2349     addxtypeChild : function (tree, cntr) {
2350         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2351           
2352         this.menuitems.add(comp);
2353         return comp;
2354
2355     },
2356     getEl : function()
2357     {
2358         Roo.log(this.el);
2359         return this.el;
2360     },
2361     
2362     clear : function()
2363     {
2364         this.getEl().dom.innerHTML = '';
2365         this.menuitems.clear();
2366     }
2367 });
2368
2369  
2370  /*
2371  * - LGPL
2372  *
2373  * menu item
2374  * 
2375  */
2376
2377
2378 /**
2379  * @class Roo.bootstrap.MenuItem
2380  * @extends Roo.bootstrap.Component
2381  * Bootstrap MenuItem class
2382  * @cfg {String} html the menu label
2383  * @cfg {String} href the link
2384  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2385  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2386  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2387  * @cfg {String} fa favicon to show on left of menu item.
2388  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2389  * 
2390  * 
2391  * @constructor
2392  * Create a new MenuItem
2393  * @param {Object} config The config object
2394  */
2395
2396
2397 Roo.bootstrap.MenuItem = function(config){
2398     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2399     this.addEvents({
2400         // raw events
2401         /**
2402          * @event click
2403          * The raw click event for the entire grid.
2404          * @param {Roo.bootstrap.MenuItem} this
2405          * @param {Roo.EventObject} e
2406          */
2407         "click" : true
2408     });
2409 };
2410
2411 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2412     
2413     href : false,
2414     html : false,
2415     preventDefault: false,
2416     isContainer : false,
2417     active : false,
2418     fa: false,
2419     
2420     getAutoCreate : function(){
2421         
2422         if(this.isContainer){
2423             return {
2424                 tag: 'li',
2425                 cls: 'dropdown-menu-item'
2426             };
2427         }
2428         var ctag = {
2429             tag: 'span',
2430             html: 'Link'
2431         };
2432         
2433         var anc = {
2434             tag : 'a',
2435             href : '#',
2436             cn : [  ]
2437         };
2438         
2439         if (this.fa !== false) {
2440             anc.cn.push({
2441                 tag : 'i',
2442                 cls : 'fa fa-' + this.fa
2443             });
2444         }
2445         
2446         anc.cn.push(ctag);
2447         
2448         
2449         var cfg= {
2450             tag: 'li',
2451             cls: 'dropdown-menu-item',
2452             cn: [ anc ]
2453         };
2454         if (this.parent().type == 'treeview') {
2455             cfg.cls = 'treeview-menu';
2456         }
2457         if (this.active) {
2458             cfg.cls += ' active';
2459         }
2460         
2461         
2462         
2463         anc.href = this.href || cfg.cn[0].href ;
2464         ctag.html = this.html || cfg.cn[0].html ;
2465         return cfg;
2466     },
2467     
2468     initEvents: function()
2469     {
2470         if (this.parent().type == 'treeview') {
2471             this.el.select('a').on('click', this.onClick, this);
2472         }
2473         
2474         if (this.menu) {
2475             this.menu.parentType = this.xtype;
2476             this.menu.triggerEl = this.el;
2477             this.menu = this.addxtype(Roo.apply({}, this.menu));
2478         }
2479         
2480     },
2481     onClick : function(e)
2482     {
2483         Roo.log('item on click ');
2484         
2485         if(this.preventDefault){
2486             e.preventDefault();
2487         }
2488         //this.parent().hideMenuItems();
2489         
2490         this.fireEvent('click', this, e);
2491     },
2492     getEl : function()
2493     {
2494         return this.el;
2495     } 
2496 });
2497
2498  
2499
2500  /*
2501  * - LGPL
2502  *
2503  * menu separator
2504  * 
2505  */
2506
2507
2508 /**
2509  * @class Roo.bootstrap.MenuSeparator
2510  * @extends Roo.bootstrap.Component
2511  * Bootstrap MenuSeparator class
2512  * 
2513  * @constructor
2514  * Create a new MenuItem
2515  * @param {Object} config The config object
2516  */
2517
2518
2519 Roo.bootstrap.MenuSeparator = function(config){
2520     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2521 };
2522
2523 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2524     
2525     getAutoCreate : function(){
2526         var cfg = {
2527             cls: 'divider',
2528             tag : 'li'
2529         };
2530         
2531         return cfg;
2532     }
2533    
2534 });
2535
2536  
2537
2538  
2539 /*
2540 * Licence: LGPL
2541 */
2542
2543 /**
2544  * @class Roo.bootstrap.Modal
2545  * @extends Roo.bootstrap.Component
2546  * Bootstrap Modal class
2547  * @cfg {String} title Title of dialog
2548  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2549  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2550  * @cfg {Boolean} specificTitle default false
2551  * @cfg {Array} buttons Array of buttons or standard button set..
2552  * @cfg {String} buttonPosition (left|right|center) default right
2553  * @cfg {Boolean} animate default true
2554  * @cfg {Boolean} allow_close default true
2555  * @cfg {Boolean} fitwindow default false
2556  * @cfg {String} size (sm|lg) default empty
2557  *
2558  *
2559  * @constructor
2560  * Create a new Modal Dialog
2561  * @param {Object} config The config object
2562  */
2563
2564 Roo.bootstrap.Modal = function(config){
2565     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2566     this.addEvents({
2567         // raw events
2568         /**
2569          * @event btnclick
2570          * The raw btnclick event for the button
2571          * @param {Roo.EventObject} e
2572          */
2573         "btnclick" : true,
2574         /**
2575          * @event resize
2576          * Fire when dialog resize
2577          * @param {Roo.bootstrap.Modal} this
2578          * @param {Roo.EventObject} e
2579          */
2580         "resize" : true
2581     });
2582     this.buttons = this.buttons || [];
2583
2584     if (this.tmpl) {
2585         this.tmpl = Roo.factory(this.tmpl);
2586     }
2587
2588 };
2589
2590 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2591
2592     title : 'test dialog',
2593
2594     buttons : false,
2595
2596     // set on load...
2597
2598     html: false,
2599
2600     tmp: false,
2601
2602     specificTitle: false,
2603
2604     buttonPosition: 'right',
2605
2606     allow_close : true,
2607
2608     animate : true,
2609
2610     fitwindow: false,
2611
2612
2613      // private
2614     dialogEl: false,
2615     bodyEl:  false,
2616     footerEl:  false,
2617     titleEl:  false,
2618     closeEl:  false,
2619
2620     size: '',
2621
2622
2623     onRender : function(ct, position)
2624     {
2625         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2626
2627         if(!this.el){
2628             var cfg = Roo.apply({},  this.getAutoCreate());
2629             cfg.id = Roo.id();
2630             //if(!cfg.name){
2631             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2632             //}
2633             //if (!cfg.name.length) {
2634             //    delete cfg.name;
2635            // }
2636             if (this.cls) {
2637                 cfg.cls += ' ' + this.cls;
2638             }
2639             if (this.style) {
2640                 cfg.style = this.style;
2641             }
2642             this.el = Roo.get(document.body).createChild(cfg, position);
2643         }
2644         //var type = this.el.dom.type;
2645
2646
2647         if(this.tabIndex !== undefined){
2648             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2649         }
2650
2651         this.dialogEl = this.el.select('.modal-dialog',true).first();
2652         this.bodyEl = this.el.select('.modal-body',true).first();
2653         this.closeEl = this.el.select('.modal-header .close', true).first();
2654         this.headerEl = this.el.select('.modal-header',true).first();
2655         this.titleEl = this.el.select('.modal-title',true).first();
2656         this.footerEl = this.el.select('.modal-footer',true).first();
2657
2658         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2659         this.maskEl.enableDisplayMode("block");
2660         this.maskEl.hide();
2661         //this.el.addClass("x-dlg-modal");
2662
2663         if (this.buttons.length) {
2664             Roo.each(this.buttons, function(bb) {
2665                 var b = Roo.apply({}, bb);
2666                 b.xns = b.xns || Roo.bootstrap;
2667                 b.xtype = b.xtype || 'Button';
2668                 if (typeof(b.listeners) == 'undefined') {
2669                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2670                 }
2671
2672                 var btn = Roo.factory(b);
2673
2674                 btn.render(this.el.select('.modal-footer div').first());
2675
2676             },this);
2677         }
2678         // render the children.
2679         var nitems = [];
2680
2681         if(typeof(this.items) != 'undefined'){
2682             var items = this.items;
2683             delete this.items;
2684
2685             for(var i =0;i < items.length;i++) {
2686                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2687             }
2688         }
2689
2690         this.items = nitems;
2691
2692         // where are these used - they used to be body/close/footer
2693
2694
2695         this.initEvents();
2696         //this.el.addClass([this.fieldClass, this.cls]);
2697
2698     },
2699
2700     getAutoCreate : function(){
2701
2702
2703         var bdy = {
2704                 cls : 'modal-body',
2705                 html : this.html || ''
2706         };
2707
2708         var title = {
2709             tag: 'h4',
2710             cls : 'modal-title',
2711             html : this.title
2712         };
2713
2714         if(this.specificTitle){
2715             title = this.title;
2716
2717         };
2718
2719         var header = [];
2720         if (this.allow_close) {
2721             header.push({
2722                 tag: 'button',
2723                 cls : 'close',
2724                 html : '&times'
2725             });
2726         }
2727
2728         header.push(title);
2729
2730         var size = '';
2731
2732         if(this.size.length){
2733             size = 'modal-' + this.size;
2734         }
2735
2736         var modal = {
2737             cls: "modal",
2738             style : 'display: none',
2739             cn : [
2740                 {
2741                     cls: "modal-dialog " + size,
2742                     cn : [
2743                         {
2744                             cls : "modal-content",
2745                             cn : [
2746                                 {
2747                                     cls : 'modal-header',
2748                                     cn : header
2749                                 },
2750                                 bdy,
2751                                 {
2752                                     cls : 'modal-footer',
2753                                     cn : [
2754                                         {
2755                                             tag: 'div',
2756                                             cls: 'btn-' + this.buttonPosition
2757                                         }
2758                                     ]
2759
2760                                 }
2761
2762
2763                             ]
2764
2765                         }
2766                     ]
2767
2768                 }
2769             ]
2770         };
2771
2772         if(this.animate){
2773             modal.cls += ' fade';
2774         }
2775
2776         return modal;
2777
2778     },
2779     getChildContainer : function() {
2780
2781          return this.bodyEl;
2782
2783     },
2784     getButtonContainer : function() {
2785          return this.el.select('.modal-footer div',true).first();
2786
2787     },
2788     initEvents : function()
2789     {
2790         if (this.allow_close) {
2791             this.closeEl.on('click', this.hide, this);
2792         }
2793         Roo.EventManager.onWindowResize(this.resize, this, true);
2794
2795
2796     },
2797
2798     resize : function()
2799     {
2800         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2801         if (this.fitwindow) {
2802             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2803             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2804             this.setSize(w,h);
2805         }
2806     },
2807
2808     setSize : function(w,h)
2809     {
2810         if (!w && !h) {
2811             return;
2812         }
2813         this.resizeTo(w,h);
2814     },
2815
2816     show : function() {
2817
2818         if (!this.rendered) {
2819             this.render();
2820         }
2821
2822         this.el.setStyle('display', 'block');
2823
2824         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2825             var _this = this;
2826             (function(){
2827                 this.el.addClass('in');
2828             }).defer(50, this);
2829         }else{
2830             this.el.addClass('in');
2831
2832         }
2833
2834         // not sure how we can show data in here..
2835         //if (this.tmpl) {
2836         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2837         //}
2838
2839         Roo.get(document.body).addClass("x-body-masked");
2840         
2841         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2842         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2843         this.maskEl.show();
2844         
2845         this.resize();
2846         
2847         this.fireEvent('show', this);
2848
2849         // set zindex here - otherwise it appears to be ignored...
2850         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2851
2852         (function () {
2853             this.items.forEach( function(e) {
2854                 e.layout ? e.layout() : false;
2855
2856             });
2857         }).defer(100,this);
2858
2859     },
2860     hide : function()
2861     {
2862         if(this.fireEvent("beforehide", this) !== false){
2863             this.maskEl.hide();
2864             Roo.get(document.body).removeClass("x-body-masked");
2865             this.el.removeClass('in');
2866             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2867
2868             if(this.animate){ // why
2869                 var _this = this;
2870                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2871             }else{
2872                 this.el.setStyle('display', 'none');
2873             }
2874             this.fireEvent('hide', this);
2875         }
2876     },
2877
2878     addButton : function(str, cb)
2879     {
2880
2881
2882         var b = Roo.apply({}, { html : str } );
2883         b.xns = b.xns || Roo.bootstrap;
2884         b.xtype = b.xtype || 'Button';
2885         if (typeof(b.listeners) == 'undefined') {
2886             b.listeners = { click : cb.createDelegate(this)  };
2887         }
2888
2889         var btn = Roo.factory(b);
2890
2891         btn.render(this.el.select('.modal-footer div').first());
2892
2893         return btn;
2894
2895     },
2896
2897     setDefaultButton : function(btn)
2898     {
2899         //this.el.select('.modal-footer').()
2900     },
2901     diff : false,
2902
2903     resizeTo: function(w,h)
2904     {
2905         // skip.. ?? why??
2906
2907         this.dialogEl.setWidth(w);
2908         if (this.diff === false) {
2909             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2910         }
2911
2912         this.bodyEl.setHeight(h-this.diff);
2913
2914         this.fireEvent('resize', this);
2915
2916     },
2917     setContentSize  : function(w, h)
2918     {
2919
2920     },
2921     onButtonClick: function(btn,e)
2922     {
2923         //Roo.log([a,b,c]);
2924         this.fireEvent('btnclick', btn.name, e);
2925     },
2926      /**
2927      * Set the title of the Dialog
2928      * @param {String} str new Title
2929      */
2930     setTitle: function(str) {
2931         this.titleEl.dom.innerHTML = str;
2932     },
2933     /**
2934      * Set the body of the Dialog
2935      * @param {String} str new Title
2936      */
2937     setBody: function(str) {
2938         this.bodyEl.dom.innerHTML = str;
2939     },
2940     /**
2941      * Set the body of the Dialog using the template
2942      * @param {Obj} data - apply this data to the template and replace the body contents.
2943      */
2944     applyBody: function(obj)
2945     {
2946         if (!this.tmpl) {
2947             Roo.log("Error - using apply Body without a template");
2948             //code
2949         }
2950         this.tmpl.overwrite(this.bodyEl, obj);
2951     }
2952
2953 });
2954
2955
2956 Roo.apply(Roo.bootstrap.Modal,  {
2957     /**
2958          * Button config that displays a single OK button
2959          * @type Object
2960          */
2961         OK :  [{
2962             name : 'ok',
2963             weight : 'primary',
2964             html : 'OK'
2965         }],
2966         /**
2967          * Button config that displays Yes and No buttons
2968          * @type Object
2969          */
2970         YESNO : [
2971             {
2972                 name  : 'no',
2973                 html : 'No'
2974             },
2975             {
2976                 name  :'yes',
2977                 weight : 'primary',
2978                 html : 'Yes'
2979             }
2980         ],
2981
2982         /**
2983          * Button config that displays OK and Cancel buttons
2984          * @type Object
2985          */
2986         OKCANCEL : [
2987             {
2988                name : 'cancel',
2989                 html : 'Cancel'
2990             },
2991             {
2992                 name : 'ok',
2993                 weight : 'primary',
2994                 html : 'OK'
2995             }
2996         ],
2997         /**
2998          * Button config that displays Yes, No and Cancel buttons
2999          * @type Object
3000          */
3001         YESNOCANCEL : [
3002             {
3003                 name : 'yes',
3004                 weight : 'primary',
3005                 html : 'Yes'
3006             },
3007             {
3008                 name : 'no',
3009                 html : 'No'
3010             },
3011             {
3012                 name : 'cancel',
3013                 html : 'Cancel'
3014             }
3015         ],
3016         
3017         zIndex : 10001
3018 });
3019 /*
3020  * - LGPL
3021  *
3022  * messagebox - can be used as a replace
3023  * 
3024  */
3025 /**
3026  * @class Roo.MessageBox
3027  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3028  * Example usage:
3029  *<pre><code>
3030 // Basic alert:
3031 Roo.Msg.alert('Status', 'Changes saved successfully.');
3032
3033 // Prompt for user data:
3034 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3035     if (btn == 'ok'){
3036         // process text value...
3037     }
3038 });
3039
3040 // Show a dialog using config options:
3041 Roo.Msg.show({
3042    title:'Save Changes?',
3043    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3044    buttons: Roo.Msg.YESNOCANCEL,
3045    fn: processResult,
3046    animEl: 'elId'
3047 });
3048 </code></pre>
3049  * @singleton
3050  */
3051 Roo.bootstrap.MessageBox = function(){
3052     var dlg, opt, mask, waitTimer;
3053     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3054     var buttons, activeTextEl, bwidth;
3055
3056     
3057     // private
3058     var handleButton = function(button){
3059         dlg.hide();
3060         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3061     };
3062
3063     // private
3064     var handleHide = function(){
3065         if(opt && opt.cls){
3066             dlg.el.removeClass(opt.cls);
3067         }
3068         //if(waitTimer){
3069         //    Roo.TaskMgr.stop(waitTimer);
3070         //    waitTimer = null;
3071         //}
3072     };
3073
3074     // private
3075     var updateButtons = function(b){
3076         var width = 0;
3077         if(!b){
3078             buttons["ok"].hide();
3079             buttons["cancel"].hide();
3080             buttons["yes"].hide();
3081             buttons["no"].hide();
3082             //dlg.footer.dom.style.display = 'none';
3083             return width;
3084         }
3085         dlg.footerEl.dom.style.display = '';
3086         for(var k in buttons){
3087             if(typeof buttons[k] != "function"){
3088                 if(b[k]){
3089                     buttons[k].show();
3090                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3091                     width += buttons[k].el.getWidth()+15;
3092                 }else{
3093                     buttons[k].hide();
3094                 }
3095             }
3096         }
3097         return width;
3098     };
3099
3100     // private
3101     var handleEsc = function(d, k, e){
3102         if(opt && opt.closable !== false){
3103             dlg.hide();
3104         }
3105         if(e){
3106             e.stopEvent();
3107         }
3108     };
3109
3110     return {
3111         /**
3112          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3113          * @return {Roo.BasicDialog} The BasicDialog element
3114          */
3115         getDialog : function(){
3116            if(!dlg){
3117                 dlg = new Roo.bootstrap.Modal( {
3118                     //draggable: true,
3119                     //resizable:false,
3120                     //constraintoviewport:false,
3121                     //fixedcenter:true,
3122                     //collapsible : false,
3123                     //shim:true,
3124                     //modal: true,
3125                 //    width: 'auto',
3126                   //  height:100,
3127                     //buttonAlign:"center",
3128                     closeClick : function(){
3129                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3130                             handleButton("no");
3131                         }else{
3132                             handleButton("cancel");
3133                         }
3134                     }
3135                 });
3136                 dlg.render();
3137                 dlg.on("hide", handleHide);
3138                 mask = dlg.mask;
3139                 //dlg.addKeyListener(27, handleEsc);
3140                 buttons = {};
3141                 this.buttons = buttons;
3142                 var bt = this.buttonText;
3143                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3144                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3145                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3146                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3147                 //Roo.log(buttons);
3148                 bodyEl = dlg.bodyEl.createChild({
3149
3150                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3151                         '<textarea class="roo-mb-textarea"></textarea>' +
3152                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3153                 });
3154                 msgEl = bodyEl.dom.firstChild;
3155                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3156                 textboxEl.enableDisplayMode();
3157                 textboxEl.addKeyListener([10,13], function(){
3158                     if(dlg.isVisible() && opt && opt.buttons){
3159                         if(opt.buttons.ok){
3160                             handleButton("ok");
3161                         }else if(opt.buttons.yes){
3162                             handleButton("yes");
3163                         }
3164                     }
3165                 });
3166                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3167                 textareaEl.enableDisplayMode();
3168                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3169                 progressEl.enableDisplayMode();
3170                 
3171                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3172                 var pf = progressEl.dom.firstChild;
3173                 if (pf) {
3174                     pp = Roo.get(pf.firstChild);
3175                     pp.setHeight(pf.offsetHeight);
3176                 }
3177                 
3178             }
3179             return dlg;
3180         },
3181
3182         /**
3183          * Updates the message box body text
3184          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3185          * the XHTML-compliant non-breaking space character '&amp;#160;')
3186          * @return {Roo.MessageBox} This message box
3187          */
3188         updateText : function(text)
3189         {
3190             if(!dlg.isVisible() && !opt.width){
3191                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3192                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3193             }
3194             msgEl.innerHTML = text || '&#160;';
3195       
3196             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3197             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3198             var w = Math.max(
3199                     Math.min(opt.width || cw , this.maxWidth), 
3200                     Math.max(opt.minWidth || this.minWidth, bwidth)
3201             );
3202             if(opt.prompt){
3203                 activeTextEl.setWidth(w);
3204             }
3205             if(dlg.isVisible()){
3206                 dlg.fixedcenter = false;
3207             }
3208             // to big, make it scroll. = But as usual stupid IE does not support
3209             // !important..
3210             
3211             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3212                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3213                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3214             } else {
3215                 bodyEl.dom.style.height = '';
3216                 bodyEl.dom.style.overflowY = '';
3217             }
3218             if (cw > w) {
3219                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3220             } else {
3221                 bodyEl.dom.style.overflowX = '';
3222             }
3223             
3224             dlg.setContentSize(w, bodyEl.getHeight());
3225             if(dlg.isVisible()){
3226                 dlg.fixedcenter = true;
3227             }
3228             return this;
3229         },
3230
3231         /**
3232          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3233          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3234          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3235          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3236          * @return {Roo.MessageBox} This message box
3237          */
3238         updateProgress : function(value, text){
3239             if(text){
3240                 this.updateText(text);
3241             }
3242             
3243             if (pp) { // weird bug on my firefox - for some reason this is not defined
3244                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3245                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3246             }
3247             return this;
3248         },        
3249
3250         /**
3251          * Returns true if the message box is currently displayed
3252          * @return {Boolean} True if the message box is visible, else false
3253          */
3254         isVisible : function(){
3255             return dlg && dlg.isVisible();  
3256         },
3257
3258         /**
3259          * Hides the message box if it is displayed
3260          */
3261         hide : function(){
3262             if(this.isVisible()){
3263                 dlg.hide();
3264             }  
3265         },
3266
3267         /**
3268          * Displays a new message box, or reinitializes an existing message box, based on the config options
3269          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3270          * The following config object properties are supported:
3271          * <pre>
3272 Property    Type             Description
3273 ----------  ---------------  ------------------------------------------------------------------------------------
3274 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3275                                    closes (defaults to undefined)
3276 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3277                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3278 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3279                                    progress and wait dialogs will ignore this property and always hide the
3280                                    close button as they can only be closed programmatically.
3281 cls               String           A custom CSS class to apply to the message box element
3282 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3283                                    displayed (defaults to 75)
3284 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3285                                    function will be btn (the name of the button that was clicked, if applicable,
3286                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3287                                    Progress and wait dialogs will ignore this option since they do not respond to
3288                                    user actions and can only be closed programmatically, so any required function
3289                                    should be called by the same code after it closes the dialog.
3290 icon              String           A CSS class that provides a background image to be used as an icon for
3291                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3292 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3293 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3294 modal             Boolean          False to allow user interaction with the page while the message box is
3295                                    displayed (defaults to true)
3296 msg               String           A string that will replace the existing message box body text (defaults
3297                                    to the XHTML-compliant non-breaking space character '&#160;')
3298 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3299 progress          Boolean          True to display a progress bar (defaults to false)
3300 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3301 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3302 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3303 title             String           The title text
3304 value             String           The string value to set into the active textbox element if displayed
3305 wait              Boolean          True to display a progress bar (defaults to false)
3306 width             Number           The width of the dialog in pixels
3307 </pre>
3308          *
3309          * Example usage:
3310          * <pre><code>
3311 Roo.Msg.show({
3312    title: 'Address',
3313    msg: 'Please enter your address:',
3314    width: 300,
3315    buttons: Roo.MessageBox.OKCANCEL,
3316    multiline: true,
3317    fn: saveAddress,
3318    animEl: 'addAddressBtn'
3319 });
3320 </code></pre>
3321          * @param {Object} config Configuration options
3322          * @return {Roo.MessageBox} This message box
3323          */
3324         show : function(options)
3325         {
3326             
3327             // this causes nightmares if you show one dialog after another
3328             // especially on callbacks..
3329              
3330             if(this.isVisible()){
3331                 
3332                 this.hide();
3333                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3334                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3335                 Roo.log("New Dialog Message:" +  options.msg )
3336                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3337                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3338                 
3339             }
3340             var d = this.getDialog();
3341             opt = options;
3342             d.setTitle(opt.title || "&#160;");
3343             d.closeEl.setDisplayed(opt.closable !== false);
3344             activeTextEl = textboxEl;
3345             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3346             if(opt.prompt){
3347                 if(opt.multiline){
3348                     textboxEl.hide();
3349                     textareaEl.show();
3350                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3351                         opt.multiline : this.defaultTextHeight);
3352                     activeTextEl = textareaEl;
3353                 }else{
3354                     textboxEl.show();
3355                     textareaEl.hide();
3356                 }
3357             }else{
3358                 textboxEl.hide();
3359                 textareaEl.hide();
3360             }
3361             progressEl.setDisplayed(opt.progress === true);
3362             this.updateProgress(0);
3363             activeTextEl.dom.value = opt.value || "";
3364             if(opt.prompt){
3365                 dlg.setDefaultButton(activeTextEl);
3366             }else{
3367                 var bs = opt.buttons;
3368                 var db = null;
3369                 if(bs && bs.ok){
3370                     db = buttons["ok"];
3371                 }else if(bs && bs.yes){
3372                     db = buttons["yes"];
3373                 }
3374                 dlg.setDefaultButton(db);
3375             }
3376             bwidth = updateButtons(opt.buttons);
3377             this.updateText(opt.msg);
3378             if(opt.cls){
3379                 d.el.addClass(opt.cls);
3380             }
3381             d.proxyDrag = opt.proxyDrag === true;
3382             d.modal = opt.modal !== false;
3383             d.mask = opt.modal !== false ? mask : false;
3384             if(!d.isVisible()){
3385                 // force it to the end of the z-index stack so it gets a cursor in FF
3386                 document.body.appendChild(dlg.el.dom);
3387                 d.animateTarget = null;
3388                 d.show(options.animEl);
3389             }
3390             return this;
3391         },
3392
3393         /**
3394          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3395          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3396          * and closing the message box when the process is complete.
3397          * @param {String} title The title bar text
3398          * @param {String} msg The message box body text
3399          * @return {Roo.MessageBox} This message box
3400          */
3401         progress : function(title, msg){
3402             this.show({
3403                 title : title,
3404                 msg : msg,
3405                 buttons: false,
3406                 progress:true,
3407                 closable:false,
3408                 minWidth: this.minProgressWidth,
3409                 modal : true
3410             });
3411             return this;
3412         },
3413
3414         /**
3415          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3416          * If a callback function is passed it will be called after the user clicks the button, and the
3417          * id of the button that was clicked will be passed as the only parameter to the callback
3418          * (could also be the top-right close button).
3419          * @param {String} title The title bar text
3420          * @param {String} msg The message box body text
3421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422          * @param {Object} scope (optional) The scope of the callback function
3423          * @return {Roo.MessageBox} This message box
3424          */
3425         alert : function(title, msg, fn, scope)
3426         {
3427             this.show({
3428                 title : title,
3429                 msg : msg,
3430                 buttons: this.OK,
3431                 fn: fn,
3432                 closable : false,
3433                 scope : scope,
3434                 modal : true
3435             });
3436             return this;
3437         },
3438
3439         /**
3440          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3441          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3442          * You are responsible for closing the message box when the process is complete.
3443          * @param {String} msg The message box body text
3444          * @param {String} title (optional) The title bar text
3445          * @return {Roo.MessageBox} This message box
3446          */
3447         wait : function(msg, title){
3448             this.show({
3449                 title : title,
3450                 msg : msg,
3451                 buttons: false,
3452                 closable:false,
3453                 progress:true,
3454                 modal:true,
3455                 width:300,
3456                 wait:true
3457             });
3458             waitTimer = Roo.TaskMgr.start({
3459                 run: function(i){
3460                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3461                 },
3462                 interval: 1000
3463             });
3464             return this;
3465         },
3466
3467         /**
3468          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3469          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3470          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3471          * @param {String} title The title bar text
3472          * @param {String} msg The message box body text
3473          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3474          * @param {Object} scope (optional) The scope of the callback function
3475          * @return {Roo.MessageBox} This message box
3476          */
3477         confirm : function(title, msg, fn, scope){
3478             this.show({
3479                 title : title,
3480                 msg : msg,
3481                 buttons: this.YESNO,
3482                 fn: fn,
3483                 scope : scope,
3484                 modal : true
3485             });
3486             return this;
3487         },
3488
3489         /**
3490          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3491          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3492          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3493          * (could also be the top-right close button) and the text that was entered will be passed as the two
3494          * parameters to the callback.
3495          * @param {String} title The title bar text
3496          * @param {String} msg The message box body text
3497          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3498          * @param {Object} scope (optional) The scope of the callback function
3499          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3500          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3501          * @return {Roo.MessageBox} This message box
3502          */
3503         prompt : function(title, msg, fn, scope, multiline){
3504             this.show({
3505                 title : title,
3506                 msg : msg,
3507                 buttons: this.OKCANCEL,
3508                 fn: fn,
3509                 minWidth:250,
3510                 scope : scope,
3511                 prompt:true,
3512                 multiline: multiline,
3513                 modal : true
3514             });
3515             return this;
3516         },
3517
3518         /**
3519          * Button config that displays a single OK button
3520          * @type Object
3521          */
3522         OK : {ok:true},
3523         /**
3524          * Button config that displays Yes and No buttons
3525          * @type Object
3526          */
3527         YESNO : {yes:true, no:true},
3528         /**
3529          * Button config that displays OK and Cancel buttons
3530          * @type Object
3531          */
3532         OKCANCEL : {ok:true, cancel:true},
3533         /**
3534          * Button config that displays Yes, No and Cancel buttons
3535          * @type Object
3536          */
3537         YESNOCANCEL : {yes:true, no:true, cancel:true},
3538
3539         /**
3540          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3541          * @type Number
3542          */
3543         defaultTextHeight : 75,
3544         /**
3545          * The maximum width in pixels of the message box (defaults to 600)
3546          * @type Number
3547          */
3548         maxWidth : 600,
3549         /**
3550          * The minimum width in pixels of the message box (defaults to 100)
3551          * @type Number
3552          */
3553         minWidth : 100,
3554         /**
3555          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3556          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3557          * @type Number
3558          */
3559         minProgressWidth : 250,
3560         /**
3561          * An object containing the default button text strings that can be overriden for localized language support.
3562          * Supported properties are: ok, cancel, yes and no.
3563          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3564          * @type Object
3565          */
3566         buttonText : {
3567             ok : "OK",
3568             cancel : "Cancel",
3569             yes : "Yes",
3570             no : "No"
3571         }
3572     };
3573 }();
3574
3575 /**
3576  * Shorthand for {@link Roo.MessageBox}
3577  */
3578 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3579 Roo.Msg = Roo.Msg || Roo.MessageBox;
3580 /*
3581  * - LGPL
3582  *
3583  * navbar
3584  * 
3585  */
3586
3587 /**
3588  * @class Roo.bootstrap.Navbar
3589  * @extends Roo.bootstrap.Component
3590  * Bootstrap Navbar class
3591
3592  * @constructor
3593  * Create a new Navbar
3594  * @param {Object} config The config object
3595  */
3596
3597
3598 Roo.bootstrap.Navbar = function(config){
3599     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3600     this.addEvents({
3601         // raw events
3602         /**
3603          * @event beforetoggle
3604          * Fire before toggle the menu
3605          * @param {Roo.EventObject} e
3606          */
3607         "beforetoggle" : true
3608     });
3609 };
3610
3611 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3612     
3613     
3614    
3615     // private
3616     navItems : false,
3617     loadMask : false,
3618     
3619     
3620     getAutoCreate : function(){
3621         
3622         
3623         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3624         
3625     },
3626     
3627     initEvents :function ()
3628     {
3629         //Roo.log(this.el.select('.navbar-toggle',true));
3630         this.el.select('.navbar-toggle',true).on('click', function() {
3631             if(this.fireEvent('beforetoggle', this) !== false){
3632                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3633             }
3634             
3635         }, this);
3636         
3637         var mark = {
3638             tag: "div",
3639             cls:"x-dlg-mask"
3640         };
3641         
3642         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3643         
3644         var size = this.el.getSize();
3645         this.maskEl.setSize(size.width, size.height);
3646         this.maskEl.enableDisplayMode("block");
3647         this.maskEl.hide();
3648         
3649         if(this.loadMask){
3650             this.maskEl.show();
3651         }
3652     },
3653     
3654     
3655     getChildContainer : function()
3656     {
3657         if (this.el.select('.collapse').getCount()) {
3658             return this.el.select('.collapse',true).first();
3659         }
3660         
3661         return this.el;
3662     },
3663     
3664     mask : function()
3665     {
3666         this.maskEl.show();
3667     },
3668     
3669     unmask : function()
3670     {
3671         this.maskEl.hide();
3672     } 
3673     
3674     
3675     
3676     
3677 });
3678
3679
3680
3681  
3682
3683  /*
3684  * - LGPL
3685  *
3686  * navbar
3687  * 
3688  */
3689
3690 /**
3691  * @class Roo.bootstrap.NavSimplebar
3692  * @extends Roo.bootstrap.Navbar
3693  * Bootstrap Sidebar class
3694  *
3695  * @cfg {Boolean} inverse is inverted color
3696  * 
3697  * @cfg {String} type (nav | pills | tabs)
3698  * @cfg {Boolean} arrangement stacked | justified
3699  * @cfg {String} align (left | right) alignment
3700  * 
3701  * @cfg {Boolean} main (true|false) main nav bar? default false
3702  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3703  * 
3704  * @cfg {String} tag (header|footer|nav|div) default is nav 
3705
3706  * 
3707  * 
3708  * 
3709  * @constructor
3710  * Create a new Sidebar
3711  * @param {Object} config The config object
3712  */
3713
3714
3715 Roo.bootstrap.NavSimplebar = function(config){
3716     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3717 };
3718
3719 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3720     
3721     inverse: false,
3722     
3723     type: false,
3724     arrangement: '',
3725     align : false,
3726     
3727     
3728     
3729     main : false,
3730     
3731     
3732     tag : false,
3733     
3734     
3735     getAutoCreate : function(){
3736         
3737         
3738         var cfg = {
3739             tag : this.tag || 'div',
3740             cls : 'navbar'
3741         };
3742           
3743         
3744         cfg.cn = [
3745             {
3746                 cls: 'nav',
3747                 tag : 'ul'
3748             }
3749         ];
3750         
3751          
3752         this.type = this.type || 'nav';
3753         if (['tabs','pills'].indexOf(this.type)!==-1) {
3754             cfg.cn[0].cls += ' nav-' + this.type
3755         
3756         
3757         } else {
3758             if (this.type!=='nav') {
3759                 Roo.log('nav type must be nav/tabs/pills')
3760             }
3761             cfg.cn[0].cls += ' navbar-nav'
3762         }
3763         
3764         
3765         
3766         
3767         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3768             cfg.cn[0].cls += ' nav-' + this.arrangement;
3769         }
3770         
3771         
3772         if (this.align === 'right') {
3773             cfg.cn[0].cls += ' navbar-right';
3774         }
3775         
3776         if (this.inverse) {
3777             cfg.cls += ' navbar-inverse';
3778             
3779         }
3780         
3781         
3782         return cfg;
3783     
3784         
3785     }
3786     
3787     
3788     
3789 });
3790
3791
3792
3793  
3794
3795  
3796        /*
3797  * - LGPL
3798  *
3799  * navbar
3800  * 
3801  */
3802
3803 /**
3804  * @class Roo.bootstrap.NavHeaderbar
3805  * @extends Roo.bootstrap.NavSimplebar
3806  * Bootstrap Sidebar class
3807  *
3808  * @cfg {String} brand what is brand
3809  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3810  * @cfg {String} brand_href href of the brand
3811  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3812  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3813  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3814  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3815  * 
3816  * @constructor
3817  * Create a new Sidebar
3818  * @param {Object} config The config object
3819  */
3820
3821
3822 Roo.bootstrap.NavHeaderbar = function(config){
3823     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3824       
3825 };
3826
3827 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3828     
3829     position: '',
3830     brand: '',
3831     brand_href: false,
3832     srButton : true,
3833     autohide : false,
3834     desktopCenter : false,
3835    
3836     
3837     getAutoCreate : function(){
3838         
3839         var   cfg = {
3840             tag: this.nav || 'nav',
3841             cls: 'navbar',
3842             role: 'navigation',
3843             cn: []
3844         };
3845         
3846         var cn = cfg.cn;
3847         if (this.desktopCenter) {
3848             cn.push({cls : 'container', cn : []});
3849             cn = cn[0].cn;
3850         }
3851         
3852         if(this.srButton){
3853             cn.push({
3854                 tag: 'div',
3855                 cls: 'navbar-header',
3856                 cn: [
3857                     {
3858                         tag: 'button',
3859                         type: 'button',
3860                         cls: 'navbar-toggle',
3861                         'data-toggle': 'collapse',
3862                         cn: [
3863                             {
3864                                 tag: 'span',
3865                                 cls: 'sr-only',
3866                                 html: 'Toggle navigation'
3867                             },
3868                             {
3869                                 tag: 'span',
3870                                 cls: 'icon-bar'
3871                             },
3872                             {
3873                                 tag: 'span',
3874                                 cls: 'icon-bar'
3875                             },
3876                             {
3877                                 tag: 'span',
3878                                 cls: 'icon-bar'
3879                             }
3880                         ]
3881                     }
3882                 ]
3883             });
3884         }
3885         
3886         cn.push({
3887             tag: 'div',
3888             cls: 'collapse navbar-collapse',
3889             cn : []
3890         });
3891         
3892         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3893         
3894         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3895             cfg.cls += ' navbar-' + this.position;
3896             
3897             // tag can override this..
3898             
3899             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3900         }
3901         
3902         if (this.brand !== '') {
3903             cn[0].cn.push({
3904                 tag: 'a',
3905                 href: this.brand_href ? this.brand_href : '#',
3906                 cls: 'navbar-brand',
3907                 cn: [
3908                 this.brand
3909                 ]
3910             });
3911         }
3912         
3913         if(this.main){
3914             cfg.cls += ' main-nav';
3915         }
3916         
3917         
3918         return cfg;
3919
3920         
3921     },
3922     getHeaderChildContainer : function()
3923     {
3924         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3925             return this.el.select('.navbar-header',true).first();
3926         }
3927         
3928         return this.getChildContainer();
3929     },
3930     
3931     
3932     initEvents : function()
3933     {
3934         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3935         
3936         if (this.autohide) {
3937             
3938             var prevScroll = 0;
3939             var ft = this.el;
3940             
3941             Roo.get(document).on('scroll',function(e) {
3942                 var ns = Roo.get(document).getScroll().top;
3943                 var os = prevScroll;
3944                 prevScroll = ns;
3945                 
3946                 if(ns > os){
3947                     ft.removeClass('slideDown');
3948                     ft.addClass('slideUp');
3949                     return;
3950                 }
3951                 ft.removeClass('slideUp');
3952                 ft.addClass('slideDown');
3953                  
3954               
3955           },this);
3956         }
3957     }    
3958     
3959 });
3960
3961
3962
3963  
3964
3965  /*
3966  * - LGPL
3967  *
3968  * navbar
3969  * 
3970  */
3971
3972 /**
3973  * @class Roo.bootstrap.NavSidebar
3974  * @extends Roo.bootstrap.Navbar
3975  * Bootstrap Sidebar class
3976  * 
3977  * @constructor
3978  * Create a new Sidebar
3979  * @param {Object} config The config object
3980  */
3981
3982
3983 Roo.bootstrap.NavSidebar = function(config){
3984     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3985 };
3986
3987 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3988     
3989     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3990     
3991     getAutoCreate : function(){
3992         
3993         
3994         return  {
3995             tag: 'div',
3996             cls: 'sidebar sidebar-nav'
3997         };
3998     
3999         
4000     }
4001     
4002     
4003     
4004 });
4005
4006
4007
4008  
4009
4010  /*
4011  * - LGPL
4012  *
4013  * nav group
4014  * 
4015  */
4016
4017 /**
4018  * @class Roo.bootstrap.NavGroup
4019  * @extends Roo.bootstrap.Component
4020  * Bootstrap NavGroup class
4021  * @cfg {String} align (left|right)
4022  * @cfg {Boolean} inverse
4023  * @cfg {String} type (nav|pills|tab) default nav
4024  * @cfg {String} navId - reference Id for navbar.
4025
4026  * 
4027  * @constructor
4028  * Create a new nav group
4029  * @param {Object} config The config object
4030  */
4031
4032 Roo.bootstrap.NavGroup = function(config){
4033     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4034     this.navItems = [];
4035    
4036     Roo.bootstrap.NavGroup.register(this);
4037      this.addEvents({
4038         /**
4039              * @event changed
4040              * Fires when the active item changes
4041              * @param {Roo.bootstrap.NavGroup} this
4042              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4043              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4044          */
4045         'changed': true
4046      });
4047     
4048 };
4049
4050 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4051     
4052     align: '',
4053     inverse: false,
4054     form: false,
4055     type: 'nav',
4056     navId : '',
4057     // private
4058     
4059     navItems : false, 
4060     
4061     getAutoCreate : function()
4062     {
4063         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4064         
4065         cfg = {
4066             tag : 'ul',
4067             cls: 'nav' 
4068         };
4069         
4070         if (['tabs','pills'].indexOf(this.type)!==-1) {
4071             cfg.cls += ' nav-' + this.type
4072         } else {
4073             if (this.type!=='nav') {
4074                 Roo.log('nav type must be nav/tabs/pills')
4075             }
4076             cfg.cls += ' navbar-nav'
4077         }
4078         
4079         if (this.parent() && this.parent().sidebar) {
4080             cfg = {
4081                 tag: 'ul',
4082                 cls: 'dashboard-menu sidebar-menu'
4083             };
4084             
4085             return cfg;
4086         }
4087         
4088         if (this.form === true) {
4089             cfg = {
4090                 tag: 'form',
4091                 cls: 'navbar-form'
4092             };
4093             
4094             if (this.align === 'right') {
4095                 cfg.cls += ' navbar-right';
4096             } else {
4097                 cfg.cls += ' navbar-left';
4098             }
4099         }
4100         
4101         if (this.align === 'right') {
4102             cfg.cls += ' navbar-right';
4103         }
4104         
4105         if (this.inverse) {
4106             cfg.cls += ' navbar-inverse';
4107             
4108         }
4109         
4110         
4111         return cfg;
4112     },
4113     /**
4114     * sets the active Navigation item
4115     * @param {Roo.bootstrap.NavItem} the new current navitem
4116     */
4117     setActiveItem : function(item)
4118     {
4119         var prev = false;
4120         Roo.each(this.navItems, function(v){
4121             if (v == item) {
4122                 return ;
4123             }
4124             if (v.isActive()) {
4125                 v.setActive(false, true);
4126                 prev = v;
4127                 
4128             }
4129             
4130         });
4131
4132         item.setActive(true, true);
4133         this.fireEvent('changed', this, item, prev);
4134         
4135         
4136     },
4137     /**
4138     * gets the active Navigation item
4139     * @return {Roo.bootstrap.NavItem} the current navitem
4140     */
4141     getActive : function()
4142     {
4143         
4144         var prev = false;
4145         Roo.each(this.navItems, function(v){
4146             
4147             if (v.isActive()) {
4148                 prev = v;
4149                 
4150             }
4151             
4152         });
4153         return prev;
4154     },
4155     
4156     indexOfNav : function()
4157     {
4158         
4159         var prev = false;
4160         Roo.each(this.navItems, function(v,i){
4161             
4162             if (v.isActive()) {
4163                 prev = i;
4164                 
4165             }
4166             
4167         });
4168         return prev;
4169     },
4170     /**
4171     * adds a Navigation item
4172     * @param {Roo.bootstrap.NavItem} the navitem to add
4173     */
4174     addItem : function(cfg)
4175     {
4176         var cn = new Roo.bootstrap.NavItem(cfg);
4177         this.register(cn);
4178         cn.parentId = this.id;
4179         cn.onRender(this.el, null);
4180         return cn;
4181     },
4182     /**
4183     * register a Navigation item
4184     * @param {Roo.bootstrap.NavItem} the navitem to add
4185     */
4186     register : function(item)
4187     {
4188         this.navItems.push( item);
4189         item.navId = this.navId;
4190     
4191     },
4192     
4193     /**
4194     * clear all the Navigation item
4195     */
4196    
4197     clearAll : function()
4198     {
4199         this.navItems = [];
4200         this.el.dom.innerHTML = '';
4201     },
4202     
4203     getNavItem: function(tabId)
4204     {
4205         var ret = false;
4206         Roo.each(this.navItems, function(e) {
4207             if (e.tabId == tabId) {
4208                ret =  e;
4209                return false;
4210             }
4211             return true;
4212             
4213         });
4214         return ret;
4215     },
4216     
4217     setActiveNext : function()
4218     {
4219         var i = this.indexOfNav(this.getActive());
4220         if (i > this.navItems.length) {
4221             return;
4222         }
4223         this.setActiveItem(this.navItems[i+1]);
4224     },
4225     setActivePrev : function()
4226     {
4227         var i = this.indexOfNav(this.getActive());
4228         if (i  < 1) {
4229             return;
4230         }
4231         this.setActiveItem(this.navItems[i-1]);
4232     },
4233     clearWasActive : function(except) {
4234         Roo.each(this.navItems, function(e) {
4235             if (e.tabId != except.tabId && e.was_active) {
4236                e.was_active = false;
4237                return false;
4238             }
4239             return true;
4240             
4241         });
4242     },
4243     getWasActive : function ()
4244     {
4245         var r = false;
4246         Roo.each(this.navItems, function(e) {
4247             if (e.was_active) {
4248                r = e;
4249                return false;
4250             }
4251             return true;
4252             
4253         });
4254         return r;
4255     }
4256     
4257     
4258 });
4259
4260  
4261 Roo.apply(Roo.bootstrap.NavGroup, {
4262     
4263     groups: {},
4264      /**
4265     * register a Navigation Group
4266     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4267     */
4268     register : function(navgrp)
4269     {
4270         this.groups[navgrp.navId] = navgrp;
4271         
4272     },
4273     /**
4274     * fetch a Navigation Group based on the navigation ID
4275     * @param {string} the navgroup to add
4276     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4277     */
4278     get: function(navId) {
4279         if (typeof(this.groups[navId]) == 'undefined') {
4280             return false;
4281             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4282         }
4283         return this.groups[navId] ;
4284     }
4285     
4286     
4287     
4288 });
4289
4290  /*
4291  * - LGPL
4292  *
4293  * row
4294  * 
4295  */
4296
4297 /**
4298  * @class Roo.bootstrap.NavItem
4299  * @extends Roo.bootstrap.Component
4300  * Bootstrap Navbar.NavItem class
4301  * @cfg {String} href  link to
4302  * @cfg {String} html content of button
4303  * @cfg {String} badge text inside badge
4304  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4305  * @cfg {String} glyphicon name of glyphicon
4306  * @cfg {String} icon name of font awesome icon
4307  * @cfg {Boolean} active Is item active
4308  * @cfg {Boolean} disabled Is item disabled
4309  
4310  * @cfg {Boolean} preventDefault (true | false) default false
4311  * @cfg {String} tabId the tab that this item activates.
4312  * @cfg {String} tagtype (a|span) render as a href or span?
4313  * @cfg {Boolean} animateRef (true|false) link to element default false  
4314   
4315  * @constructor
4316  * Create a new Navbar Item
4317  * @param {Object} config The config object
4318  */
4319 Roo.bootstrap.NavItem = function(config){
4320     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4321     this.addEvents({
4322         // raw events
4323         /**
4324          * @event click
4325          * The raw click event for the entire grid.
4326          * @param {Roo.EventObject} e
4327          */
4328         "click" : true,
4329          /**
4330             * @event changed
4331             * Fires when the active item active state changes
4332             * @param {Roo.bootstrap.NavItem} this
4333             * @param {boolean} state the new state
4334              
4335          */
4336         'changed': true,
4337         /**
4338             * @event scrollto
4339             * Fires when scroll to element
4340             * @param {Roo.bootstrap.NavItem} this
4341             * @param {Object} options
4342             * @param {Roo.EventObject} e
4343              
4344          */
4345         'scrollto': true
4346     });
4347    
4348 };
4349
4350 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4351     
4352     href: false,
4353     html: '',
4354     badge: '',
4355     icon: false,
4356     glyphicon: false,
4357     active: false,
4358     preventDefault : false,
4359     tabId : false,
4360     tagtype : 'a',
4361     disabled : false,
4362     animateRef : false,
4363     was_active : false,
4364     
4365     getAutoCreate : function(){
4366          
4367         var cfg = {
4368             tag: 'li',
4369             cls: 'nav-item'
4370             
4371         };
4372         
4373         if (this.active) {
4374             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4375         }
4376         if (this.disabled) {
4377             cfg.cls += ' disabled';
4378         }
4379         
4380         if (this.href || this.html || this.glyphicon || this.icon) {
4381             cfg.cn = [
4382                 {
4383                     tag: this.tagtype,
4384                     href : this.href || "#",
4385                     html: this.html || ''
4386                 }
4387             ];
4388             
4389             if (this.icon) {
4390                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4391             }
4392
4393             if(this.glyphicon) {
4394                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4395             }
4396             
4397             if (this.menu) {
4398                 
4399                 cfg.cn[0].html += " <span class='caret'></span>";
4400              
4401             }
4402             
4403             if (this.badge !== '') {
4404                  
4405                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4406             }
4407         }
4408         
4409         
4410         
4411         return cfg;
4412     },
4413     initEvents: function() 
4414     {
4415         if (typeof (this.menu) != 'undefined') {
4416             this.menu.parentType = this.xtype;
4417             this.menu.triggerEl = this.el;
4418             this.menu = this.addxtype(Roo.apply({}, this.menu));
4419         }
4420         
4421         this.el.select('a',true).on('click', this.onClick, this);
4422         
4423         if(this.tagtype == 'span'){
4424             this.el.select('span',true).on('click', this.onClick, this);
4425         }
4426        
4427         // at this point parent should be available..
4428         this.parent().register(this);
4429     },
4430     
4431     onClick : function(e)
4432     {
4433         if (e.getTarget('.dropdown-menu-item')) {
4434             // did you click on a menu itemm.... - then don't trigger onclick..
4435             return;
4436         }
4437         
4438         if(
4439                 this.preventDefault || 
4440                 this.href == '#' 
4441         ){
4442             Roo.log("NavItem - prevent Default?");
4443             e.preventDefault();
4444         }
4445         
4446         if (this.disabled) {
4447             return;
4448         }
4449         
4450         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4451         if (tg && tg.transition) {
4452             Roo.log("waiting for the transitionend");
4453             return;
4454         }
4455         
4456         
4457         
4458         //Roo.log("fire event clicked");
4459         if(this.fireEvent('click', this, e) === false){
4460             return;
4461         };
4462         
4463         if(this.tagtype == 'span'){
4464             return;
4465         }
4466         
4467         //Roo.log(this.href);
4468         var ael = this.el.select('a',true).first();
4469         //Roo.log(ael);
4470         
4471         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4472             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4473             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4474                 return; // ignore... - it's a 'hash' to another page.
4475             }
4476             Roo.log("NavItem - prevent Default?");
4477             e.preventDefault();
4478             this.scrollToElement(e);
4479         }
4480         
4481         
4482         var p =  this.parent();
4483    
4484         if (['tabs','pills'].indexOf(p.type)!==-1) {
4485             if (typeof(p.setActiveItem) !== 'undefined') {
4486                 p.setActiveItem(this);
4487             }
4488         }
4489         
4490         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4491         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4492             // remove the collapsed menu expand...
4493             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4494         }
4495     },
4496     
4497     isActive: function () {
4498         return this.active
4499     },
4500     setActive : function(state, fire, is_was_active)
4501     {
4502         if (this.active && !state && this.navId) {
4503             this.was_active = true;
4504             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4505             if (nv) {
4506                 nv.clearWasActive(this);
4507             }
4508             
4509         }
4510         this.active = state;
4511         
4512         if (!state ) {
4513             this.el.removeClass('active');
4514         } else if (!this.el.hasClass('active')) {
4515             this.el.addClass('active');
4516         }
4517         if (fire) {
4518             this.fireEvent('changed', this, state);
4519         }
4520         
4521         // show a panel if it's registered and related..
4522         
4523         if (!this.navId || !this.tabId || !state || is_was_active) {
4524             return;
4525         }
4526         
4527         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4528         if (!tg) {
4529             return;
4530         }
4531         var pan = tg.getPanelByName(this.tabId);
4532         if (!pan) {
4533             return;
4534         }
4535         // if we can not flip to new panel - go back to old nav highlight..
4536         if (false == tg.showPanel(pan)) {
4537             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4538             if (nv) {
4539                 var onav = nv.getWasActive();
4540                 if (onav) {
4541                     onav.setActive(true, false, true);
4542                 }
4543             }
4544             
4545         }
4546         
4547         
4548         
4549     },
4550      // this should not be here...
4551     setDisabled : function(state)
4552     {
4553         this.disabled = state;
4554         if (!state ) {
4555             this.el.removeClass('disabled');
4556         } else if (!this.el.hasClass('disabled')) {
4557             this.el.addClass('disabled');
4558         }
4559         
4560     },
4561     
4562     /**
4563      * Fetch the element to display the tooltip on.
4564      * @return {Roo.Element} defaults to this.el
4565      */
4566     tooltipEl : function()
4567     {
4568         return this.el.select('' + this.tagtype + '', true).first();
4569     },
4570     
4571     scrollToElement : function(e)
4572     {
4573         var c = document.body;
4574         
4575         /*
4576          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4577          */
4578         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4579             c = document.documentElement;
4580         }
4581         
4582         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4583         
4584         if(!target){
4585             return;
4586         }
4587
4588         var o = target.calcOffsetsTo(c);
4589         
4590         var options = {
4591             target : target,
4592             value : o[1]
4593         };
4594         
4595         this.fireEvent('scrollto', this, options, e);
4596         
4597         Roo.get(c).scrollTo('top', options.value, true);
4598         
4599         return;
4600     }
4601 });
4602  
4603
4604  /*
4605  * - LGPL
4606  *
4607  * sidebar item
4608  *
4609  *  li
4610  *    <span> icon </span>
4611  *    <span> text </span>
4612  *    <span>badge </span>
4613  */
4614
4615 /**
4616  * @class Roo.bootstrap.NavSidebarItem
4617  * @extends Roo.bootstrap.NavItem
4618  * Bootstrap Navbar.NavSidebarItem class
4619  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4620  * {Boolean} open is the menu open
4621  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4622  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4623  * {String} buttonSize (sm|md|lg)the extra classes for the button
4624  * {Boolean} showArrow show arrow next to the text (default true)
4625  * @constructor
4626  * Create a new Navbar Button
4627  * @param {Object} config The config object
4628  */
4629 Roo.bootstrap.NavSidebarItem = function(config){
4630     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4631     this.addEvents({
4632         // raw events
4633         /**
4634          * @event click
4635          * The raw click event for the entire grid.
4636          * @param {Roo.EventObject} e
4637          */
4638         "click" : true,
4639          /**
4640             * @event changed
4641             * Fires when the active item active state changes
4642             * @param {Roo.bootstrap.NavSidebarItem} this
4643             * @param {boolean} state the new state
4644              
4645          */
4646         'changed': true
4647     });
4648    
4649 };
4650
4651 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4652     
4653     badgeWeight : 'default',
4654     
4655     open: false,
4656     
4657     buttonView : false,
4658     
4659     buttonWeight : 'default',
4660     
4661     buttonSize : 'md',
4662     
4663     showArrow : true,
4664     
4665     getAutoCreate : function(){
4666         
4667         
4668         var a = {
4669                 tag: 'a',
4670                 href : this.href || '#',
4671                 cls: '',
4672                 html : '',
4673                 cn : []
4674         };
4675         
4676         if(this.buttonView){
4677             a = {
4678                 tag: 'button',
4679                 href : this.href || '#',
4680                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4681                 html : this.html,
4682                 cn : []
4683             };
4684         }
4685         
4686         var cfg = {
4687             tag: 'li',
4688             cls: '',
4689             cn: [ a ]
4690         };
4691         
4692         if (this.active) {
4693             cfg.cls += ' active';
4694         }
4695         
4696         if (this.disabled) {
4697             cfg.cls += ' disabled';
4698         }
4699         if (this.open) {
4700             cfg.cls += ' open x-open';
4701         }
4702         // left icon..
4703         if (this.glyphicon || this.icon) {
4704             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4705             a.cn.push({ tag : 'i', cls : c }) ;
4706         }
4707         
4708         if(!this.buttonView){
4709             var span = {
4710                 tag: 'span',
4711                 html : this.html || ''
4712             };
4713
4714             a.cn.push(span);
4715             
4716         }
4717         
4718         if (this.badge !== '') {
4719             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4720         }
4721         
4722         if (this.menu) {
4723             
4724             if(this.showArrow){
4725                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4726             }
4727             
4728             a.cls += ' dropdown-toggle treeview' ;
4729         }
4730         
4731         return cfg;
4732     },
4733     
4734     initEvents : function()
4735     { 
4736         if (typeof (this.menu) != 'undefined') {
4737             this.menu.parentType = this.xtype;
4738             this.menu.triggerEl = this.el;
4739             this.menu = this.addxtype(Roo.apply({}, this.menu));
4740         }
4741         
4742         this.el.on('click', this.onClick, this);
4743         
4744         if(this.badge !== ''){
4745             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4746         }
4747         
4748     },
4749     
4750     onClick : function(e)
4751     {
4752         if(this.disabled){
4753             e.preventDefault();
4754             return;
4755         }
4756         
4757         if(this.preventDefault){
4758             e.preventDefault();
4759         }
4760         
4761         this.fireEvent('click', this);
4762     },
4763     
4764     disable : function()
4765     {
4766         this.setDisabled(true);
4767     },
4768     
4769     enable : function()
4770     {
4771         this.setDisabled(false);
4772     },
4773     
4774     setDisabled : function(state)
4775     {
4776         if(this.disabled == state){
4777             return;
4778         }
4779         
4780         this.disabled = state;
4781         
4782         if (state) {
4783             this.el.addClass('disabled');
4784             return;
4785         }
4786         
4787         this.el.removeClass('disabled');
4788         
4789         return;
4790     },
4791     
4792     setActive : function(state)
4793     {
4794         if(this.active == state){
4795             return;
4796         }
4797         
4798         this.active = state;
4799         
4800         if (state) {
4801             this.el.addClass('active');
4802             return;
4803         }
4804         
4805         this.el.removeClass('active');
4806         
4807         return;
4808     },
4809     
4810     isActive: function () 
4811     {
4812         return this.active;
4813     },
4814     
4815     setBadge : function(str)
4816     {
4817         if(!this.badgeEl){
4818             return;
4819         }
4820         
4821         this.badgeEl.dom.innerHTML = str;
4822     }
4823     
4824    
4825      
4826  
4827 });
4828  
4829
4830  /*
4831  * - LGPL
4832  *
4833  * row
4834  * 
4835  */
4836
4837 /**
4838  * @class Roo.bootstrap.Row
4839  * @extends Roo.bootstrap.Component
4840  * Bootstrap Row class (contains columns...)
4841  * 
4842  * @constructor
4843  * Create a new Row
4844  * @param {Object} config The config object
4845  */
4846
4847 Roo.bootstrap.Row = function(config){
4848     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4849 };
4850
4851 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4852     
4853     getAutoCreate : function(){
4854        return {
4855             cls: 'row clearfix'
4856        };
4857     }
4858     
4859     
4860 });
4861
4862  
4863
4864  /*
4865  * - LGPL
4866  *
4867  * element
4868  * 
4869  */
4870
4871 /**
4872  * @class Roo.bootstrap.Element
4873  * @extends Roo.bootstrap.Component
4874  * Bootstrap Element class
4875  * @cfg {String} html contents of the element
4876  * @cfg {String} tag tag of the element
4877  * @cfg {String} cls class of the element
4878  * @cfg {Boolean} preventDefault (true|false) default false
4879  * @cfg {Boolean} clickable (true|false) default false
4880  * 
4881  * @constructor
4882  * Create a new Element
4883  * @param {Object} config The config object
4884  */
4885
4886 Roo.bootstrap.Element = function(config){
4887     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4888     
4889     this.addEvents({
4890         // raw events
4891         /**
4892          * @event click
4893          * When a element is chick
4894          * @param {Roo.bootstrap.Element} this
4895          * @param {Roo.EventObject} e
4896          */
4897         "click" : true
4898     });
4899 };
4900
4901 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4902     
4903     tag: 'div',
4904     cls: '',
4905     html: '',
4906     preventDefault: false, 
4907     clickable: false,
4908     
4909     getAutoCreate : function(){
4910         
4911         var cfg = {
4912             tag: this.tag,
4913             // cls: this.cls, double assign in parent class Component.js :: onRender
4914             html: this.html
4915         };
4916         
4917         return cfg;
4918     },
4919     
4920     initEvents: function() 
4921     {
4922         Roo.bootstrap.Element.superclass.initEvents.call(this);
4923         
4924         if(this.clickable){
4925             this.el.on('click', this.onClick, this);
4926         }
4927         
4928     },
4929     
4930     onClick : function(e)
4931     {
4932         if(this.preventDefault){
4933             e.preventDefault();
4934         }
4935         
4936         this.fireEvent('click', this, e);
4937     },
4938     
4939     getValue : function()
4940     {
4941         return this.el.dom.innerHTML;
4942     },
4943     
4944     setValue : function(value)
4945     {
4946         this.el.dom.innerHTML = value;
4947     }
4948    
4949 });
4950
4951  
4952
4953  /*
4954  * - LGPL
4955  *
4956  * pagination
4957  * 
4958  */
4959
4960 /**
4961  * @class Roo.bootstrap.Pagination
4962  * @extends Roo.bootstrap.Component
4963  * Bootstrap Pagination class
4964  * @cfg {String} size xs | sm | md | lg
4965  * @cfg {Boolean} inverse false | true
4966  * 
4967  * @constructor
4968  * Create a new Pagination
4969  * @param {Object} config The config object
4970  */
4971
4972 Roo.bootstrap.Pagination = function(config){
4973     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4974 };
4975
4976 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4977     
4978     cls: false,
4979     size: false,
4980     inverse: false,
4981     
4982     getAutoCreate : function(){
4983         var cfg = {
4984             tag: 'ul',
4985                 cls: 'pagination'
4986         };
4987         if (this.inverse) {
4988             cfg.cls += ' inverse';
4989         }
4990         if (this.html) {
4991             cfg.html=this.html;
4992         }
4993         if (this.cls) {
4994             cfg.cls += " " + this.cls;
4995         }
4996         return cfg;
4997     }
4998    
4999 });
5000
5001  
5002
5003  /*
5004  * - LGPL
5005  *
5006  * Pagination item
5007  * 
5008  */
5009
5010
5011 /**
5012  * @class Roo.bootstrap.PaginationItem
5013  * @extends Roo.bootstrap.Component
5014  * Bootstrap PaginationItem class
5015  * @cfg {String} html text
5016  * @cfg {String} href the link
5017  * @cfg {Boolean} preventDefault (true | false) default true
5018  * @cfg {Boolean} active (true | false) default false
5019  * @cfg {Boolean} disabled default false
5020  * 
5021  * 
5022  * @constructor
5023  * Create a new PaginationItem
5024  * @param {Object} config The config object
5025  */
5026
5027
5028 Roo.bootstrap.PaginationItem = function(config){
5029     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5030     this.addEvents({
5031         // raw events
5032         /**
5033          * @event click
5034          * The raw click event for the entire grid.
5035          * @param {Roo.EventObject} e
5036          */
5037         "click" : true
5038     });
5039 };
5040
5041 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5042     
5043     href : false,
5044     html : false,
5045     preventDefault: true,
5046     active : false,
5047     cls : false,
5048     disabled: false,
5049     
5050     getAutoCreate : function(){
5051         var cfg= {
5052             tag: 'li',
5053             cn: [
5054                 {
5055                     tag : 'a',
5056                     href : this.href ? this.href : '#',
5057                     html : this.html ? this.html : ''
5058                 }
5059             ]
5060         };
5061         
5062         if(this.cls){
5063             cfg.cls = this.cls;
5064         }
5065         
5066         if(this.disabled){
5067             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5068         }
5069         
5070         if(this.active){
5071             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5072         }
5073         
5074         return cfg;
5075     },
5076     
5077     initEvents: function() {
5078         
5079         this.el.on('click', this.onClick, this);
5080         
5081     },
5082     onClick : function(e)
5083     {
5084         Roo.log('PaginationItem on click ');
5085         if(this.preventDefault){
5086             e.preventDefault();
5087         }
5088         
5089         if(this.disabled){
5090             return;
5091         }
5092         
5093         this.fireEvent('click', this, e);
5094     }
5095    
5096 });
5097
5098  
5099
5100  /*
5101  * - LGPL
5102  *
5103  * slider
5104  * 
5105  */
5106
5107
5108 /**
5109  * @class Roo.bootstrap.Slider
5110  * @extends Roo.bootstrap.Component
5111  * Bootstrap Slider class
5112  *    
5113  * @constructor
5114  * Create a new Slider
5115  * @param {Object} config The config object
5116  */
5117
5118 Roo.bootstrap.Slider = function(config){
5119     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5120 };
5121
5122 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5123     
5124     getAutoCreate : function(){
5125         
5126         var cfg = {
5127             tag: 'div',
5128             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5129             cn: [
5130                 {
5131                     tag: 'a',
5132                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5133                 }
5134             ]
5135         };
5136         
5137         return cfg;
5138     }
5139    
5140 });
5141
5142  /*
5143  * Based on:
5144  * Ext JS Library 1.1.1
5145  * Copyright(c) 2006-2007, Ext JS, LLC.
5146  *
5147  * Originally Released Under LGPL - original licence link has changed is not relivant.
5148  *
5149  * Fork - LGPL
5150  * <script type="text/javascript">
5151  */
5152  
5153
5154 /**
5155  * @class Roo.grid.ColumnModel
5156  * @extends Roo.util.Observable
5157  * This is the default implementation of a ColumnModel used by the Grid. It defines
5158  * the columns in the grid.
5159  * <br>Usage:<br>
5160  <pre><code>
5161  var colModel = new Roo.grid.ColumnModel([
5162         {header: "Ticker", width: 60, sortable: true, locked: true},
5163         {header: "Company Name", width: 150, sortable: true},
5164         {header: "Market Cap.", width: 100, sortable: true},
5165         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5166         {header: "Employees", width: 100, sortable: true, resizable: false}
5167  ]);
5168  </code></pre>
5169  * <p>
5170  
5171  * The config options listed for this class are options which may appear in each
5172  * individual column definition.
5173  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5174  * @constructor
5175  * @param {Object} config An Array of column config objects. See this class's
5176  * config objects for details.
5177 */
5178 Roo.grid.ColumnModel = function(config){
5179         /**
5180      * The config passed into the constructor
5181      */
5182     this.config = config;
5183     this.lookup = {};
5184
5185     // if no id, create one
5186     // if the column does not have a dataIndex mapping,
5187     // map it to the order it is in the config
5188     for(var i = 0, len = config.length; i < len; i++){
5189         var c = config[i];
5190         if(typeof c.dataIndex == "undefined"){
5191             c.dataIndex = i;
5192         }
5193         if(typeof c.renderer == "string"){
5194             c.renderer = Roo.util.Format[c.renderer];
5195         }
5196         if(typeof c.id == "undefined"){
5197             c.id = Roo.id();
5198         }
5199         if(c.editor && c.editor.xtype){
5200             c.editor  = Roo.factory(c.editor, Roo.grid);
5201         }
5202         if(c.editor && c.editor.isFormField){
5203             c.editor = new Roo.grid.GridEditor(c.editor);
5204         }
5205         this.lookup[c.id] = c;
5206     }
5207
5208     /**
5209      * The width of columns which have no width specified (defaults to 100)
5210      * @type Number
5211      */
5212     this.defaultWidth = 100;
5213
5214     /**
5215      * Default sortable of columns which have no sortable specified (defaults to false)
5216      * @type Boolean
5217      */
5218     this.defaultSortable = false;
5219
5220     this.addEvents({
5221         /**
5222              * @event widthchange
5223              * Fires when the width of a column changes.
5224              * @param {ColumnModel} this
5225              * @param {Number} columnIndex The column index
5226              * @param {Number} newWidth The new width
5227              */
5228             "widthchange": true,
5229         /**
5230              * @event headerchange
5231              * Fires when the text of a header changes.
5232              * @param {ColumnModel} this
5233              * @param {Number} columnIndex The column index
5234              * @param {Number} newText The new header text
5235              */
5236             "headerchange": true,
5237         /**
5238              * @event hiddenchange
5239              * Fires when a column is hidden or "unhidden".
5240              * @param {ColumnModel} this
5241              * @param {Number} columnIndex The column index
5242              * @param {Boolean} hidden true if hidden, false otherwise
5243              */
5244             "hiddenchange": true,
5245             /**
5246          * @event columnmoved
5247          * Fires when a column is moved.
5248          * @param {ColumnModel} this
5249          * @param {Number} oldIndex
5250          * @param {Number} newIndex
5251          */
5252         "columnmoved" : true,
5253         /**
5254          * @event columlockchange
5255          * Fires when a column's locked state is changed
5256          * @param {ColumnModel} this
5257          * @param {Number} colIndex
5258          * @param {Boolean} locked true if locked
5259          */
5260         "columnlockchange" : true
5261     });
5262     Roo.grid.ColumnModel.superclass.constructor.call(this);
5263 };
5264 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5265     /**
5266      * @cfg {String} header The header text to display in the Grid view.
5267      */
5268     /**
5269      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5270      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5271      * specified, the column's index is used as an index into the Record's data Array.
5272      */
5273     /**
5274      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5275      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5276      */
5277     /**
5278      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5279      * Defaults to the value of the {@link #defaultSortable} property.
5280      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5281      */
5282     /**
5283      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5284      */
5285     /**
5286      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5287      */
5288     /**
5289      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5290      */
5291     /**
5292      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5293      */
5294     /**
5295      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5296      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5297      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5298      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5299      */
5300        /**
5301      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5302      */
5303     /**
5304      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5305      */
5306     /**
5307      * @cfg {String} cursor (Optional)
5308      */
5309     /**
5310      * @cfg {String} tooltip (Optional)
5311      */
5312     /**
5313      * @cfg {Number} xs (Optional)
5314      */
5315     /**
5316      * @cfg {Number} sm (Optional)
5317      */
5318     /**
5319      * @cfg {Number} md (Optional)
5320      */
5321     /**
5322      * @cfg {Number} lg (Optional)
5323      */
5324     /**
5325      * Returns the id of the column at the specified index.
5326      * @param {Number} index The column index
5327      * @return {String} the id
5328      */
5329     getColumnId : function(index){
5330         return this.config[index].id;
5331     },
5332
5333     /**
5334      * Returns the column for a specified id.
5335      * @param {String} id The column id
5336      * @return {Object} the column
5337      */
5338     getColumnById : function(id){
5339         return this.lookup[id];
5340     },
5341
5342     
5343     /**
5344      * Returns the column for a specified dataIndex.
5345      * @param {String} dataIndex The column dataIndex
5346      * @return {Object|Boolean} the column or false if not found
5347      */
5348     getColumnByDataIndex: function(dataIndex){
5349         var index = this.findColumnIndex(dataIndex);
5350         return index > -1 ? this.config[index] : false;
5351     },
5352     
5353     /**
5354      * Returns the index for a specified column id.
5355      * @param {String} id The column id
5356      * @return {Number} the index, or -1 if not found
5357      */
5358     getIndexById : function(id){
5359         for(var i = 0, len = this.config.length; i < len; i++){
5360             if(this.config[i].id == id){
5361                 return i;
5362             }
5363         }
5364         return -1;
5365     },
5366     
5367     /**
5368      * Returns the index for a specified column dataIndex.
5369      * @param {String} dataIndex The column dataIndex
5370      * @return {Number} the index, or -1 if not found
5371      */
5372     
5373     findColumnIndex : function(dataIndex){
5374         for(var i = 0, len = this.config.length; i < len; i++){
5375             if(this.config[i].dataIndex == dataIndex){
5376                 return i;
5377             }
5378         }
5379         return -1;
5380     },
5381     
5382     
5383     moveColumn : function(oldIndex, newIndex){
5384         var c = this.config[oldIndex];
5385         this.config.splice(oldIndex, 1);
5386         this.config.splice(newIndex, 0, c);
5387         this.dataMap = null;
5388         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5389     },
5390
5391     isLocked : function(colIndex){
5392         return this.config[colIndex].locked === true;
5393     },
5394
5395     setLocked : function(colIndex, value, suppressEvent){
5396         if(this.isLocked(colIndex) == value){
5397             return;
5398         }
5399         this.config[colIndex].locked = value;
5400         if(!suppressEvent){
5401             this.fireEvent("columnlockchange", this, colIndex, value);
5402         }
5403     },
5404
5405     getTotalLockedWidth : function(){
5406         var totalWidth = 0;
5407         for(var i = 0; i < this.config.length; i++){
5408             if(this.isLocked(i) && !this.isHidden(i)){
5409                 this.totalWidth += this.getColumnWidth(i);
5410             }
5411         }
5412         return totalWidth;
5413     },
5414
5415     getLockedCount : function(){
5416         for(var i = 0, len = this.config.length; i < len; i++){
5417             if(!this.isLocked(i)){
5418                 return i;
5419             }
5420         }
5421         
5422         return this.config.length;
5423     },
5424
5425     /**
5426      * Returns the number of columns.
5427      * @return {Number}
5428      */
5429     getColumnCount : function(visibleOnly){
5430         if(visibleOnly === true){
5431             var c = 0;
5432             for(var i = 0, len = this.config.length; i < len; i++){
5433                 if(!this.isHidden(i)){
5434                     c++;
5435                 }
5436             }
5437             return c;
5438         }
5439         return this.config.length;
5440     },
5441
5442     /**
5443      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5444      * @param {Function} fn
5445      * @param {Object} scope (optional)
5446      * @return {Array} result
5447      */
5448     getColumnsBy : function(fn, scope){
5449         var r = [];
5450         for(var i = 0, len = this.config.length; i < len; i++){
5451             var c = this.config[i];
5452             if(fn.call(scope||this, c, i) === true){
5453                 r[r.length] = c;
5454             }
5455         }
5456         return r;
5457     },
5458
5459     /**
5460      * Returns true if the specified column is sortable.
5461      * @param {Number} col The column index
5462      * @return {Boolean}
5463      */
5464     isSortable : function(col){
5465         if(typeof this.config[col].sortable == "undefined"){
5466             return this.defaultSortable;
5467         }
5468         return this.config[col].sortable;
5469     },
5470
5471     /**
5472      * Returns the rendering (formatting) function defined for the column.
5473      * @param {Number} col The column index.
5474      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5475      */
5476     getRenderer : function(col){
5477         if(!this.config[col].renderer){
5478             return Roo.grid.ColumnModel.defaultRenderer;
5479         }
5480         return this.config[col].renderer;
5481     },
5482
5483     /**
5484      * Sets the rendering (formatting) function for a column.
5485      * @param {Number} col The column index
5486      * @param {Function} fn The function to use to process the cell's raw data
5487      * to return HTML markup for the grid view. The render function is called with
5488      * the following parameters:<ul>
5489      * <li>Data value.</li>
5490      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5491      * <li>css A CSS style string to apply to the table cell.</li>
5492      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5493      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5494      * <li>Row index</li>
5495      * <li>Column index</li>
5496      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5497      */
5498     setRenderer : function(col, fn){
5499         this.config[col].renderer = fn;
5500     },
5501
5502     /**
5503      * Returns the width for the specified column.
5504      * @param {Number} col The column index
5505      * @return {Number}
5506      */
5507     getColumnWidth : function(col){
5508         return this.config[col].width * 1 || this.defaultWidth;
5509     },
5510
5511     /**
5512      * Sets the width for a column.
5513      * @param {Number} col The column index
5514      * @param {Number} width The new width
5515      */
5516     setColumnWidth : function(col, width, suppressEvent){
5517         this.config[col].width = width;
5518         this.totalWidth = null;
5519         if(!suppressEvent){
5520              this.fireEvent("widthchange", this, col, width);
5521         }
5522     },
5523
5524     /**
5525      * Returns the total width of all columns.
5526      * @param {Boolean} includeHidden True to include hidden column widths
5527      * @return {Number}
5528      */
5529     getTotalWidth : function(includeHidden){
5530         if(!this.totalWidth){
5531             this.totalWidth = 0;
5532             for(var i = 0, len = this.config.length; i < len; i++){
5533                 if(includeHidden || !this.isHidden(i)){
5534                     this.totalWidth += this.getColumnWidth(i);
5535                 }
5536             }
5537         }
5538         return this.totalWidth;
5539     },
5540
5541     /**
5542      * Returns the header for the specified column.
5543      * @param {Number} col The column index
5544      * @return {String}
5545      */
5546     getColumnHeader : function(col){
5547         return this.config[col].header;
5548     },
5549
5550     /**
5551      * Sets the header for a column.
5552      * @param {Number} col The column index
5553      * @param {String} header The new header
5554      */
5555     setColumnHeader : function(col, header){
5556         this.config[col].header = header;
5557         this.fireEvent("headerchange", this, col, header);
5558     },
5559
5560     /**
5561      * Returns the tooltip for the specified column.
5562      * @param {Number} col The column index
5563      * @return {String}
5564      */
5565     getColumnTooltip : function(col){
5566             return this.config[col].tooltip;
5567     },
5568     /**
5569      * Sets the tooltip for a column.
5570      * @param {Number} col The column index
5571      * @param {String} tooltip The new tooltip
5572      */
5573     setColumnTooltip : function(col, tooltip){
5574             this.config[col].tooltip = tooltip;
5575     },
5576
5577     /**
5578      * Returns the dataIndex for the specified column.
5579      * @param {Number} col The column index
5580      * @return {Number}
5581      */
5582     getDataIndex : function(col){
5583         return this.config[col].dataIndex;
5584     },
5585
5586     /**
5587      * Sets the dataIndex for a column.
5588      * @param {Number} col The column index
5589      * @param {Number} dataIndex The new dataIndex
5590      */
5591     setDataIndex : function(col, dataIndex){
5592         this.config[col].dataIndex = dataIndex;
5593     },
5594
5595     
5596     
5597     /**
5598      * Returns true if the cell is editable.
5599      * @param {Number} colIndex The column index
5600      * @param {Number} rowIndex The row index - this is nto actually used..?
5601      * @return {Boolean}
5602      */
5603     isCellEditable : function(colIndex, rowIndex){
5604         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5605     },
5606
5607     /**
5608      * Returns the editor defined for the cell/column.
5609      * return false or null to disable editing.
5610      * @param {Number} colIndex The column index
5611      * @param {Number} rowIndex The row index
5612      * @return {Object}
5613      */
5614     getCellEditor : function(colIndex, rowIndex){
5615         return this.config[colIndex].editor;
5616     },
5617
5618     /**
5619      * Sets if a column is editable.
5620      * @param {Number} col The column index
5621      * @param {Boolean} editable True if the column is editable
5622      */
5623     setEditable : function(col, editable){
5624         this.config[col].editable = editable;
5625     },
5626
5627
5628     /**
5629      * Returns true if the column is hidden.
5630      * @param {Number} colIndex The column index
5631      * @return {Boolean}
5632      */
5633     isHidden : function(colIndex){
5634         return this.config[colIndex].hidden;
5635     },
5636
5637
5638     /**
5639      * Returns true if the column width cannot be changed
5640      */
5641     isFixed : function(colIndex){
5642         return this.config[colIndex].fixed;
5643     },
5644
5645     /**
5646      * Returns true if the column can be resized
5647      * @return {Boolean}
5648      */
5649     isResizable : function(colIndex){
5650         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5651     },
5652     /**
5653      * Sets if a column is hidden.
5654      * @param {Number} colIndex The column index
5655      * @param {Boolean} hidden True if the column is hidden
5656      */
5657     setHidden : function(colIndex, hidden){
5658         this.config[colIndex].hidden = hidden;
5659         this.totalWidth = null;
5660         this.fireEvent("hiddenchange", this, colIndex, hidden);
5661     },
5662
5663     /**
5664      * Sets the editor for a column.
5665      * @param {Number} col The column index
5666      * @param {Object} editor The editor object
5667      */
5668     setEditor : function(col, editor){
5669         this.config[col].editor = editor;
5670     }
5671 });
5672
5673 Roo.grid.ColumnModel.defaultRenderer = function(value)
5674 {
5675     if(typeof value == "object") {
5676         return value;
5677     }
5678         if(typeof value == "string" && value.length < 1){
5679             return "&#160;";
5680         }
5681     
5682         return String.format("{0}", value);
5683 };
5684
5685 // Alias for backwards compatibility
5686 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5687 /*
5688  * Based on:
5689  * Ext JS Library 1.1.1
5690  * Copyright(c) 2006-2007, Ext JS, LLC.
5691  *
5692  * Originally Released Under LGPL - original licence link has changed is not relivant.
5693  *
5694  * Fork - LGPL
5695  * <script type="text/javascript">
5696  */
5697  
5698 /**
5699  * @class Roo.LoadMask
5700  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5701  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5702  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5703  * element's UpdateManager load indicator and will be destroyed after the initial load.
5704  * @constructor
5705  * Create a new LoadMask
5706  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5707  * @param {Object} config The config object
5708  */
5709 Roo.LoadMask = function(el, config){
5710     this.el = Roo.get(el);
5711     Roo.apply(this, config);
5712     if(this.store){
5713         this.store.on('beforeload', this.onBeforeLoad, this);
5714         this.store.on('load', this.onLoad, this);
5715         this.store.on('loadexception', this.onLoadException, this);
5716         this.removeMask = false;
5717     }else{
5718         var um = this.el.getUpdateManager();
5719         um.showLoadIndicator = false; // disable the default indicator
5720         um.on('beforeupdate', this.onBeforeLoad, this);
5721         um.on('update', this.onLoad, this);
5722         um.on('failure', this.onLoad, this);
5723         this.removeMask = true;
5724     }
5725 };
5726
5727 Roo.LoadMask.prototype = {
5728     /**
5729      * @cfg {Boolean} removeMask
5730      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5731      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5732      */
5733     /**
5734      * @cfg {String} msg
5735      * The text to display in a centered loading message box (defaults to 'Loading...')
5736      */
5737     msg : 'Loading...',
5738     /**
5739      * @cfg {String} msgCls
5740      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5741      */
5742     msgCls : 'x-mask-loading',
5743
5744     /**
5745      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5746      * @type Boolean
5747      */
5748     disabled: false,
5749
5750     /**
5751      * Disables the mask to prevent it from being displayed
5752      */
5753     disable : function(){
5754        this.disabled = true;
5755     },
5756
5757     /**
5758      * Enables the mask so that it can be displayed
5759      */
5760     enable : function(){
5761         this.disabled = false;
5762     },
5763     
5764     onLoadException : function()
5765     {
5766         Roo.log(arguments);
5767         
5768         if (typeof(arguments[3]) != 'undefined') {
5769             Roo.MessageBox.alert("Error loading",arguments[3]);
5770         } 
5771         /*
5772         try {
5773             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5774                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5775             }   
5776         } catch(e) {
5777             
5778         }
5779         */
5780     
5781         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5782     },
5783     // private
5784     onLoad : function()
5785     {
5786         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5787     },
5788
5789     // private
5790     onBeforeLoad : function(){
5791         if(!this.disabled){
5792             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5793         }
5794     },
5795
5796     // private
5797     destroy : function(){
5798         if(this.store){
5799             this.store.un('beforeload', this.onBeforeLoad, this);
5800             this.store.un('load', this.onLoad, this);
5801             this.store.un('loadexception', this.onLoadException, this);
5802         }else{
5803             var um = this.el.getUpdateManager();
5804             um.un('beforeupdate', this.onBeforeLoad, this);
5805             um.un('update', this.onLoad, this);
5806             um.un('failure', this.onLoad, this);
5807         }
5808     }
5809 };/*
5810  * - LGPL
5811  *
5812  * table
5813  * 
5814  */
5815
5816 /**
5817  * @class Roo.bootstrap.Table
5818  * @extends Roo.bootstrap.Component
5819  * Bootstrap Table class
5820  * @cfg {String} cls table class
5821  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5822  * @cfg {String} bgcolor Specifies the background color for a table
5823  * @cfg {Number} border Specifies whether the table cells should have borders or not
5824  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5825  * @cfg {Number} cellspacing Specifies the space between cells
5826  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5827  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5828  * @cfg {String} sortable Specifies that the table should be sortable
5829  * @cfg {String} summary Specifies a summary of the content of a table
5830  * @cfg {Number} width Specifies the width of a table
5831  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5832  * 
5833  * @cfg {boolean} striped Should the rows be alternative striped
5834  * @cfg {boolean} bordered Add borders to the table
5835  * @cfg {boolean} hover Add hover highlighting
5836  * @cfg {boolean} condensed Format condensed
5837  * @cfg {boolean} responsive Format condensed
5838  * @cfg {Boolean} loadMask (true|false) default false
5839  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5840  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5841  * @cfg {Boolean} rowSelection (true|false) default false
5842  * @cfg {Boolean} cellSelection (true|false) default false
5843  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5844  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5845  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5846  
5847  * 
5848  * @constructor
5849  * Create a new Table
5850  * @param {Object} config The config object
5851  */
5852
5853 Roo.bootstrap.Table = function(config){
5854     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5855     
5856   
5857     
5858     // BC...
5859     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5860     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5861     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5862     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5863     
5864     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5865     if (this.sm) {
5866         this.sm.grid = this;
5867         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5868         this.sm = this.selModel;
5869         this.sm.xmodule = this.xmodule || false;
5870     }
5871     
5872     if (this.cm && typeof(this.cm.config) == 'undefined') {
5873         this.colModel = new Roo.grid.ColumnModel(this.cm);
5874         this.cm = this.colModel;
5875         this.cm.xmodule = this.xmodule || false;
5876     }
5877     if (this.store) {
5878         this.store= Roo.factory(this.store, Roo.data);
5879         this.ds = this.store;
5880         this.ds.xmodule = this.xmodule || false;
5881          
5882     }
5883     if (this.footer && this.store) {
5884         this.footer.dataSource = this.ds;
5885         this.footer = Roo.factory(this.footer);
5886     }
5887     
5888     /** @private */
5889     this.addEvents({
5890         /**
5891          * @event cellclick
5892          * Fires when a cell is clicked
5893          * @param {Roo.bootstrap.Table} this
5894          * @param {Roo.Element} el
5895          * @param {Number} rowIndex
5896          * @param {Number} columnIndex
5897          * @param {Roo.EventObject} e
5898          */
5899         "cellclick" : true,
5900         /**
5901          * @event celldblclick
5902          * Fires when a cell is double clicked
5903          * @param {Roo.bootstrap.Table} this
5904          * @param {Roo.Element} el
5905          * @param {Number} rowIndex
5906          * @param {Number} columnIndex
5907          * @param {Roo.EventObject} e
5908          */
5909         "celldblclick" : true,
5910         /**
5911          * @event rowclick
5912          * Fires when a row is clicked
5913          * @param {Roo.bootstrap.Table} this
5914          * @param {Roo.Element} el
5915          * @param {Number} rowIndex
5916          * @param {Roo.EventObject} e
5917          */
5918         "rowclick" : true,
5919         /**
5920          * @event rowdblclick
5921          * Fires when a row is double clicked
5922          * @param {Roo.bootstrap.Table} this
5923          * @param {Roo.Element} el
5924          * @param {Number} rowIndex
5925          * @param {Roo.EventObject} e
5926          */
5927         "rowdblclick" : true,
5928         /**
5929          * @event mouseover
5930          * Fires when a mouseover occur
5931          * @param {Roo.bootstrap.Table} this
5932          * @param {Roo.Element} el
5933          * @param {Number} rowIndex
5934          * @param {Number} columnIndex
5935          * @param {Roo.EventObject} e
5936          */
5937         "mouseover" : true,
5938         /**
5939          * @event mouseout
5940          * Fires when a mouseout occur
5941          * @param {Roo.bootstrap.Table} this
5942          * @param {Roo.Element} el
5943          * @param {Number} rowIndex
5944          * @param {Number} columnIndex
5945          * @param {Roo.EventObject} e
5946          */
5947         "mouseout" : true,
5948         /**
5949          * @event rowclass
5950          * Fires when a row is rendered, so you can change add a style to it.
5951          * @param {Roo.bootstrap.Table} this
5952          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5953          */
5954         'rowclass' : true,
5955           /**
5956          * @event rowsrendered
5957          * Fires when all the  rows have been rendered
5958          * @param {Roo.bootstrap.Table} this
5959          */
5960         'rowsrendered' : true,
5961         /**
5962          * @event contextmenu
5963          * The raw contextmenu event for the entire grid.
5964          * @param {Roo.EventObject} e
5965          */
5966         "contextmenu" : true,
5967         /**
5968          * @event rowcontextmenu
5969          * Fires when a row is right clicked
5970          * @param {Roo.bootstrap.Table} this
5971          * @param {Number} rowIndex
5972          * @param {Roo.EventObject} e
5973          */
5974         "rowcontextmenu" : true,
5975         /**
5976          * @event cellcontextmenu
5977          * Fires when a cell is right clicked
5978          * @param {Roo.bootstrap.Table} this
5979          * @param {Number} rowIndex
5980          * @param {Number} cellIndex
5981          * @param {Roo.EventObject} e
5982          */
5983          "cellcontextmenu" : true,
5984          /**
5985          * @event headercontextmenu
5986          * Fires when a header is right clicked
5987          * @param {Roo.bootstrap.Table} this
5988          * @param {Number} columnIndex
5989          * @param {Roo.EventObject} e
5990          */
5991         "headercontextmenu" : true
5992     });
5993 };
5994
5995 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5996     
5997     cls: false,
5998     align: false,
5999     bgcolor: false,
6000     border: false,
6001     cellpadding: false,
6002     cellspacing: false,
6003     frame: false,
6004     rules: false,
6005     sortable: false,
6006     summary: false,
6007     width: false,
6008     striped : false,
6009     scrollBody : false,
6010     bordered: false,
6011     hover:  false,
6012     condensed : false,
6013     responsive : false,
6014     sm : false,
6015     cm : false,
6016     store : false,
6017     loadMask : false,
6018     footerShow : true,
6019     headerShow : true,
6020   
6021     rowSelection : false,
6022     cellSelection : false,
6023     layout : false,
6024     
6025     // Roo.Element - the tbody
6026     mainBody: false,
6027     // Roo.Element - thead element
6028     mainHead: false,
6029     
6030     container: false, // used by gridpanel...
6031     
6032     lazyLoad : false,
6033     
6034     getAutoCreate : function()
6035     {
6036         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6037         
6038         cfg = {
6039             tag: 'table',
6040             cls : 'table',
6041             cn : []
6042         };
6043         if (this.scrollBody) {
6044             cfg.cls += ' table-body-fixed';
6045         }    
6046         if (this.striped) {
6047             cfg.cls += ' table-striped';
6048         }
6049         
6050         if (this.hover) {
6051             cfg.cls += ' table-hover';
6052         }
6053         if (this.bordered) {
6054             cfg.cls += ' table-bordered';
6055         }
6056         if (this.condensed) {
6057             cfg.cls += ' table-condensed';
6058         }
6059         if (this.responsive) {
6060             cfg.cls += ' table-responsive';
6061         }
6062         
6063         if (this.cls) {
6064             cfg.cls+=  ' ' +this.cls;
6065         }
6066         
6067         // this lot should be simplifed...
6068         
6069         if (this.align) {
6070             cfg.align=this.align;
6071         }
6072         if (this.bgcolor) {
6073             cfg.bgcolor=this.bgcolor;
6074         }
6075         if (this.border) {
6076             cfg.border=this.border;
6077         }
6078         if (this.cellpadding) {
6079             cfg.cellpadding=this.cellpadding;
6080         }
6081         if (this.cellspacing) {
6082             cfg.cellspacing=this.cellspacing;
6083         }
6084         if (this.frame) {
6085             cfg.frame=this.frame;
6086         }
6087         if (this.rules) {
6088             cfg.rules=this.rules;
6089         }
6090         if (this.sortable) {
6091             cfg.sortable=this.sortable;
6092         }
6093         if (this.summary) {
6094             cfg.summary=this.summary;
6095         }
6096         if (this.width) {
6097             cfg.width=this.width;
6098         }
6099         if (this.layout) {
6100             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6101         }
6102         
6103         if(this.store || this.cm){
6104             if(this.headerShow){
6105                 cfg.cn.push(this.renderHeader());
6106             }
6107             
6108             cfg.cn.push(this.renderBody());
6109             
6110             if(this.footerShow){
6111                 cfg.cn.push(this.renderFooter());
6112             }
6113             // where does this come from?
6114             //cfg.cls+=  ' TableGrid';
6115         }
6116         
6117         return { cn : [ cfg ] };
6118     },
6119     
6120     initEvents : function()
6121     {   
6122         if(!this.store || !this.cm){
6123             return;
6124         }
6125         if (this.selModel) {
6126             this.selModel.initEvents();
6127         }
6128         
6129         
6130         //Roo.log('initEvents with ds!!!!');
6131         
6132         this.mainBody = this.el.select('tbody', true).first();
6133         this.mainHead = this.el.select('thead', true).first();
6134         
6135         
6136         
6137         
6138         var _this = this;
6139         
6140         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6141             e.on('click', _this.sort, _this);
6142         });
6143         
6144         this.mainBody.on("click", this.onClick, this);
6145         this.mainBody.on("dblclick", this.onDblClick, this);
6146         
6147         // why is this done????? = it breaks dialogs??
6148         //this.parent().el.setStyle('position', 'relative');
6149         
6150         
6151         if (this.footer) {
6152             this.footer.parentId = this.id;
6153             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6154             
6155             if(this.lazyLoad){
6156                 this.el.select('tfoot tr td').first().addClass('hide');
6157             }
6158         } 
6159         
6160         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6161         
6162         this.store.on('load', this.onLoad, this);
6163         this.store.on('beforeload', this.onBeforeLoad, this);
6164         this.store.on('update', this.onUpdate, this);
6165         this.store.on('add', this.onAdd, this);
6166         this.store.on("clear", this.clear, this);
6167         
6168         this.el.on("contextmenu", this.onContextMenu, this);
6169         
6170         this.mainBody.on('scroll', this.onBodyScroll, this);
6171         
6172         this.cm.on("headerchange", this.onHeaderChange, this);
6173         
6174         this.cm.on("hiddenchange", this.onHiddenChange, this);
6175         
6176     },
6177     
6178     onContextMenu : function(e, t)
6179     {
6180         this.processEvent("contextmenu", e);
6181     },
6182     
6183     processEvent : function(name, e)
6184     {
6185         if (name != 'touchstart' ) {
6186             this.fireEvent(name, e);    
6187         }
6188         
6189         var t = e.getTarget();
6190         
6191         var cell = Roo.get(t);
6192         
6193         if(!cell){
6194             return;
6195         }
6196         
6197         if(cell.findParent('tfoot', false, true)){
6198             return;
6199         }
6200         
6201         if(cell.findParent('thead', false, true)){
6202             
6203             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6204                 cell = Roo.get(t).findParent('th', false, true);
6205                 if (!cell) {
6206                     Roo.log("failed to find th in thead?");
6207                     Roo.log(e.getTarget());
6208                     return;
6209                 }
6210             }
6211             
6212             var cellIndex = cell.dom.cellIndex;
6213             
6214             var ename = name == 'touchstart' ? 'click' : name;
6215             this.fireEvent("header" + ename, this, cellIndex, e);
6216             
6217             return;
6218         }
6219         
6220         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6221             cell = Roo.get(t).findParent('td', false, true);
6222             if (!cell) {
6223                 Roo.log("failed to find th in tbody?");
6224                 Roo.log(e.getTarget());
6225                 return;
6226             }
6227         }
6228         
6229         var row = cell.findParent('tr', false, true);
6230         var cellIndex = cell.dom.cellIndex;
6231         var rowIndex = row.dom.rowIndex - 1;
6232         
6233         if(row !== false){
6234             
6235             this.fireEvent("row" + name, this, rowIndex, e);
6236             
6237             if(cell !== false){
6238             
6239                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6240             }
6241         }
6242         
6243     },
6244     
6245     onMouseover : function(e, el)
6246     {
6247         var cell = Roo.get(el);
6248         
6249         if(!cell){
6250             return;
6251         }
6252         
6253         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6254             cell = cell.findParent('td', false, true);
6255         }
6256         
6257         var row = cell.findParent('tr', false, true);
6258         var cellIndex = cell.dom.cellIndex;
6259         var rowIndex = row.dom.rowIndex - 1; // start from 0
6260         
6261         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6262         
6263     },
6264     
6265     onMouseout : function(e, el)
6266     {
6267         var cell = Roo.get(el);
6268         
6269         if(!cell){
6270             return;
6271         }
6272         
6273         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6274             cell = cell.findParent('td', false, true);
6275         }
6276         
6277         var row = cell.findParent('tr', false, true);
6278         var cellIndex = cell.dom.cellIndex;
6279         var rowIndex = row.dom.rowIndex - 1; // start from 0
6280         
6281         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6282         
6283     },
6284     
6285     onClick : function(e, el)
6286     {
6287         var cell = Roo.get(el);
6288         
6289         if(!cell || (!this.cellSelection && !this.rowSelection)){
6290             return;
6291         }
6292         
6293         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6294             cell = cell.findParent('td', false, true);
6295         }
6296         
6297         if(!cell || typeof(cell) == 'undefined'){
6298             return;
6299         }
6300         
6301         var row = cell.findParent('tr', false, true);
6302         
6303         if(!row || typeof(row) == 'undefined'){
6304             return;
6305         }
6306         
6307         var cellIndex = cell.dom.cellIndex;
6308         var rowIndex = this.getRowIndex(row);
6309         
6310         // why??? - should these not be based on SelectionModel?
6311         if(this.cellSelection){
6312             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6313         }
6314         
6315         if(this.rowSelection){
6316             this.fireEvent('rowclick', this, row, rowIndex, e);
6317         }
6318         
6319         
6320     },
6321         
6322     onDblClick : 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         if(this.cellSelection){
6348             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6349         }
6350         
6351         if(this.rowSelection){
6352             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6353         }
6354     },
6355     
6356     sort : function(e,el)
6357     {
6358         var col = Roo.get(el);
6359         
6360         if(!col.hasClass('sortable')){
6361             return;
6362         }
6363         
6364         var sort = col.attr('sort');
6365         var dir = 'ASC';
6366         
6367         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6368             dir = 'DESC';
6369         }
6370         
6371         this.store.sortInfo = {field : sort, direction : dir};
6372         
6373         if (this.footer) {
6374             Roo.log("calling footer first");
6375             this.footer.onClick('first');
6376         } else {
6377         
6378             this.store.load({ params : { start : 0 } });
6379         }
6380     },
6381     
6382     renderHeader : function()
6383     {
6384         var header = {
6385             tag: 'thead',
6386             cn : []
6387         };
6388         
6389         var cm = this.cm;
6390         this.totalWidth = 0;
6391         
6392         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6393             
6394             var config = cm.config[i];
6395             
6396             var c = {
6397                 tag: 'th',
6398                 style : '',
6399                 html: cm.getColumnHeader(i)
6400             };
6401             
6402             var hh = '';
6403             
6404             if(typeof(config.sortable) != 'undefined' && config.sortable){
6405                 c.cls = 'sortable';
6406                 c.html = '<i class="glyphicon"></i>' + c.html;
6407             }
6408             
6409             if(typeof(config.lgHeader) != 'undefined'){
6410                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6411             }
6412             
6413             if(typeof(config.mdHeader) != 'undefined'){
6414                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6415             }
6416             
6417             if(typeof(config.smHeader) != 'undefined'){
6418                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6419             }
6420             
6421             if(typeof(config.xsHeader) != 'undefined'){
6422                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6423             }
6424             
6425             if(hh.length){
6426                 c.html = hh;
6427             }
6428             
6429             if(typeof(config.tooltip) != 'undefined'){
6430                 c.tooltip = config.tooltip;
6431             }
6432             
6433             if(typeof(config.colspan) != 'undefined'){
6434                 c.colspan = config.colspan;
6435             }
6436             
6437             if(typeof(config.hidden) != 'undefined' && config.hidden){
6438                 c.style += ' display:none;';
6439             }
6440             
6441             if(typeof(config.dataIndex) != 'undefined'){
6442                 c.sort = config.dataIndex;
6443             }
6444             
6445            
6446             
6447             if(typeof(config.align) != 'undefined' && config.align.length){
6448                 c.style += ' text-align:' + config.align + ';';
6449             }
6450             
6451             if(typeof(config.width) != 'undefined'){
6452                 c.style += ' width:' + config.width + 'px;';
6453                 this.totalWidth += config.width;
6454             } else {
6455                 this.totalWidth += 100; // assume minimum of 100 per column?
6456             }
6457             
6458             if(typeof(config.cls) != 'undefined'){
6459                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6460             }
6461             
6462             ['xs','sm','md','lg'].map(function(size){
6463                 
6464                 if(typeof(config[size]) == 'undefined'){
6465                     return;
6466                 }
6467                 
6468                 if (!config[size]) { // 0 = hidden
6469                     c.cls += ' hidden-' + size;
6470                     return;
6471                 }
6472                 
6473                 c.cls += ' col-' + size + '-' + config[size];
6474
6475             });
6476             
6477             if(config.hidden){
6478                 c.cls += ' hidden';
6479             }
6480             
6481             header.cn.push(c)
6482         }
6483         
6484         return header;
6485     },
6486     
6487     renderBody : function()
6488     {
6489         var body = {
6490             tag: 'tbody',
6491             cn : [
6492                 {
6493                     tag: 'tr',
6494                     cn : [
6495                         {
6496                             tag : 'td',
6497                             colspan :  this.cm.getColumnCount()
6498                         }
6499                     ]
6500                 }
6501             ]
6502         };
6503         
6504         return body;
6505     },
6506     
6507     renderFooter : function()
6508     {
6509         var footer = {
6510             tag: 'tfoot',
6511             cn : [
6512                 {
6513                     tag: 'tr',
6514                     cn : [
6515                         {
6516                             tag : 'td',
6517                             colspan :  this.cm.getColumnCount()
6518                         }
6519                     ]
6520                 }
6521             ]
6522         };
6523         
6524         return footer;
6525     },
6526     
6527     
6528     
6529     onLoad : function()
6530     {
6531 //        Roo.log('ds onload');
6532         this.clear();
6533         
6534         var _this = this;
6535         var cm = this.cm;
6536         var ds = this.store;
6537         
6538         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6539             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6540             if (_this.store.sortInfo) {
6541                     
6542                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6543                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6544                 }
6545                 
6546                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6547                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6548                 }
6549             }
6550         });
6551         
6552         var tbody =  this.mainBody;
6553               
6554         if(ds.getCount() > 0){
6555             ds.data.each(function(d,rowIndex){
6556                 var row =  this.renderRow(cm, ds, rowIndex);
6557                 
6558                 tbody.createChild(row);
6559                 
6560                 var _this = this;
6561                 
6562                 if(row.cellObjects.length){
6563                     Roo.each(row.cellObjects, function(r){
6564                         _this.renderCellObject(r);
6565                     })
6566                 }
6567                 
6568             }, this);
6569         }
6570         
6571         Roo.each(this.el.select('tbody td', true).elements, function(e){
6572             e.on('mouseover', _this.onMouseover, _this);
6573         });
6574         
6575         Roo.each(this.el.select('tbody td', true).elements, function(e){
6576             e.on('mouseout', _this.onMouseout, _this);
6577         });
6578         this.fireEvent('rowsrendered', this);
6579         //if(this.loadMask){
6580         //    this.maskEl.hide();
6581         //}
6582         
6583         this.autoSize();
6584     },
6585     
6586     
6587     onUpdate : function(ds,record)
6588     {
6589         this.refreshRow(record);
6590         this.autoSize();
6591     },
6592     
6593     onRemove : function(ds, record, index, isUpdate){
6594         if(isUpdate !== true){
6595             this.fireEvent("beforerowremoved", this, index, record);
6596         }
6597         var bt = this.mainBody.dom;
6598         
6599         var rows = this.el.select('tbody > tr', true).elements;
6600         
6601         if(typeof(rows[index]) != 'undefined'){
6602             bt.removeChild(rows[index].dom);
6603         }
6604         
6605 //        if(bt.rows[index]){
6606 //            bt.removeChild(bt.rows[index]);
6607 //        }
6608         
6609         if(isUpdate !== true){
6610             //this.stripeRows(index);
6611             //this.syncRowHeights(index, index);
6612             //this.layout();
6613             this.fireEvent("rowremoved", this, index, record);
6614         }
6615     },
6616     
6617     onAdd : function(ds, records, rowIndex)
6618     {
6619         //Roo.log('on Add called');
6620         // - note this does not handle multiple adding very well..
6621         var bt = this.mainBody.dom;
6622         for (var i =0 ; i < records.length;i++) {
6623             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6624             //Roo.log(records[i]);
6625             //Roo.log(this.store.getAt(rowIndex+i));
6626             this.insertRow(this.store, rowIndex + i, false);
6627             return;
6628         }
6629         
6630     },
6631     
6632     
6633     refreshRow : function(record){
6634         var ds = this.store, index;
6635         if(typeof record == 'number'){
6636             index = record;
6637             record = ds.getAt(index);
6638         }else{
6639             index = ds.indexOf(record);
6640         }
6641         this.insertRow(ds, index, true);
6642         this.autoSize();
6643         this.onRemove(ds, record, index+1, true);
6644         this.autoSize();
6645         //this.syncRowHeights(index, index);
6646         //this.layout();
6647         this.fireEvent("rowupdated", this, index, record);
6648     },
6649     
6650     insertRow : function(dm, rowIndex, isUpdate){
6651         
6652         if(!isUpdate){
6653             this.fireEvent("beforerowsinserted", this, rowIndex);
6654         }
6655             //var s = this.getScrollState();
6656         var row = this.renderRow(this.cm, this.store, rowIndex);
6657         // insert before rowIndex..
6658         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6659         
6660         var _this = this;
6661                 
6662         if(row.cellObjects.length){
6663             Roo.each(row.cellObjects, function(r){
6664                 _this.renderCellObject(r);
6665             })
6666         }
6667             
6668         if(!isUpdate){
6669             this.fireEvent("rowsinserted", this, rowIndex);
6670             //this.syncRowHeights(firstRow, lastRow);
6671             //this.stripeRows(firstRow);
6672             //this.layout();
6673         }
6674         
6675     },
6676     
6677     
6678     getRowDom : function(rowIndex)
6679     {
6680         var rows = this.el.select('tbody > tr', true).elements;
6681         
6682         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6683         
6684     },
6685     // returns the object tree for a tr..
6686   
6687     
6688     renderRow : function(cm, ds, rowIndex) 
6689     {
6690         
6691         var d = ds.getAt(rowIndex);
6692         
6693         var row = {
6694             tag : 'tr',
6695             cn : []
6696         };
6697             
6698         var cellObjects = [];
6699         
6700         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6701             var config = cm.config[i];
6702             
6703             var renderer = cm.getRenderer(i);
6704             var value = '';
6705             var id = false;
6706             
6707             if(typeof(renderer) !== 'undefined'){
6708                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6709             }
6710             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6711             // and are rendered into the cells after the row is rendered - using the id for the element.
6712             
6713             if(typeof(value) === 'object'){
6714                 id = Roo.id();
6715                 cellObjects.push({
6716                     container : id,
6717                     cfg : value 
6718                 })
6719             }
6720             
6721             var rowcfg = {
6722                 record: d,
6723                 rowIndex : rowIndex,
6724                 colIndex : i,
6725                 rowClass : ''
6726             };
6727
6728             this.fireEvent('rowclass', this, rowcfg);
6729             
6730             var td = {
6731                 tag: 'td',
6732                 cls : rowcfg.rowClass,
6733                 style: '',
6734                 html: (typeof(value) === 'object') ? '' : value
6735             };
6736             
6737             if (id) {
6738                 td.id = id;
6739             }
6740             
6741             if(typeof(config.colspan) != 'undefined'){
6742                 td.colspan = config.colspan;
6743             }
6744             
6745             if(typeof(config.hidden) != 'undefined' && config.hidden){
6746                 td.style += ' display:none;';
6747             }
6748             
6749             if(typeof(config.align) != 'undefined' && config.align.length){
6750                 td.style += ' text-align:' + config.align + ';';
6751             }
6752             
6753             if(typeof(config.width) != 'undefined'){
6754                 td.style += ' width:' +  config.width + 'px;';
6755             }
6756             
6757             if(typeof(config.cursor) != 'undefined'){
6758                 td.style += ' cursor:' +  config.cursor + ';';
6759             }
6760             
6761             if(typeof(config.cls) != 'undefined'){
6762                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6763             }
6764             
6765             ['xs','sm','md','lg'].map(function(size){
6766                 
6767                 if(typeof(config[size]) == 'undefined'){
6768                     return;
6769                 }
6770                 
6771                 if (!config[size]) { // 0 = hidden
6772                     td.cls += ' hidden-' + size;
6773                     return;
6774                 }
6775                 
6776                 td.cls += ' col-' + size + '-' + config[size];
6777
6778             });
6779             
6780             if(config.hidden){
6781                 td.cls += ' hidden';
6782             }
6783              
6784             row.cn.push(td);
6785            
6786         }
6787         
6788         row.cellObjects = cellObjects;
6789         
6790         return row;
6791           
6792     },
6793     
6794     
6795     
6796     onBeforeLoad : function()
6797     {
6798         //Roo.log('ds onBeforeLoad');
6799         
6800         //this.clear();
6801         
6802         //if(this.loadMask){
6803         //    this.maskEl.show();
6804         //}
6805     },
6806      /**
6807      * Remove all rows
6808      */
6809     clear : function()
6810     {
6811         this.el.select('tbody', true).first().dom.innerHTML = '';
6812     },
6813     /**
6814      * Show or hide a row.
6815      * @param {Number} rowIndex to show or hide
6816      * @param {Boolean} state hide
6817      */
6818     setRowVisibility : function(rowIndex, state)
6819     {
6820         var bt = this.mainBody.dom;
6821         
6822         var rows = this.el.select('tbody > tr', true).elements;
6823         
6824         if(typeof(rows[rowIndex]) == 'undefined'){
6825             return;
6826         }
6827         rows[rowIndex].dom.style.display = state ? '' : 'none';
6828     },
6829     
6830     
6831     getSelectionModel : function(){
6832         if(!this.selModel){
6833             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6834         }
6835         return this.selModel;
6836     },
6837     /*
6838      * Render the Roo.bootstrap object from renderder
6839      */
6840     renderCellObject : function(r)
6841     {
6842         var _this = this;
6843         
6844         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6845         
6846         var t = r.cfg.render(r.container);
6847         
6848         if(r.cfg.cn){
6849             Roo.each(r.cfg.cn, function(c){
6850                 var child = {
6851                     container: t.getChildContainer(),
6852                     cfg: c
6853                 };
6854                 _this.renderCellObject(child);
6855             })
6856         }
6857     },
6858     
6859     getRowIndex : function(row)
6860     {
6861         var rowIndex = -1;
6862         
6863         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6864             if(el != row){
6865                 return;
6866             }
6867             
6868             rowIndex = index;
6869         });
6870         
6871         return rowIndex;
6872     },
6873      /**
6874      * Returns the grid's underlying element = used by panel.Grid
6875      * @return {Element} The element
6876      */
6877     getGridEl : function(){
6878         return this.el;
6879     },
6880      /**
6881      * Forces a resize - used by panel.Grid
6882      * @return {Element} The element
6883      */
6884     autoSize : function()
6885     {
6886         //var ctr = Roo.get(this.container.dom.parentElement);
6887         var ctr = Roo.get(this.el.dom);
6888         
6889         var thd = this.getGridEl().select('thead',true).first();
6890         var tbd = this.getGridEl().select('tbody', true).first();
6891         var tfd = this.getGridEl().select('tfoot', true).first();
6892         
6893         var cw = ctr.getWidth();
6894         
6895         if (tbd) {
6896             
6897             tbd.setSize(ctr.getWidth(),
6898                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6899             );
6900             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6901             cw -= barsize;
6902         }
6903         cw = Math.max(cw, this.totalWidth);
6904         this.getGridEl().select('tr',true).setWidth(cw);
6905         // resize 'expandable coloumn?
6906         
6907         return; // we doe not have a view in this design..
6908         
6909     },
6910     onBodyScroll: function()
6911     {
6912         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6913         if(this.mainHead){
6914             this.mainHead.setStyle({
6915                 'position' : 'relative',
6916                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6917             });
6918         }
6919         
6920         if(this.lazyLoad){
6921             
6922             var scrollHeight = this.mainBody.dom.scrollHeight;
6923             
6924             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6925             
6926             var height = this.mainBody.getHeight();
6927             
6928             if(scrollHeight - height == scrollTop) {
6929                 
6930                 var total = this.ds.getTotalCount();
6931                 
6932                 if(this.footer.cursor + this.footer.pageSize < total){
6933                     
6934                     this.footer.ds.load({
6935                         params : {
6936                             start : this.footer.cursor + this.footer.pageSize,
6937                             limit : this.footer.pageSize
6938                         },
6939                         add : true
6940                     });
6941                 }
6942             }
6943             
6944         }
6945     },
6946     
6947     onHeaderChange : function()
6948     {
6949         
6950         var header = this.renderHeader();
6951         var table = this.el.select('table', true).first();
6952         
6953         this.mainHead.remove();
6954         this.mainHead = table.createChild(header, this.mainBody, false);
6955     },
6956     
6957     onHiddenChange : function()
6958     {
6959         this.onHeaderChange();
6960         this.onLoad();
6961     }
6962     
6963 });
6964
6965  
6966
6967  /*
6968  * - LGPL
6969  *
6970  * table cell
6971  * 
6972  */
6973
6974 /**
6975  * @class Roo.bootstrap.TableCell
6976  * @extends Roo.bootstrap.Component
6977  * Bootstrap TableCell class
6978  * @cfg {String} html cell contain text
6979  * @cfg {String} cls cell class
6980  * @cfg {String} tag cell tag (td|th) default td
6981  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6982  * @cfg {String} align Aligns the content in a cell
6983  * @cfg {String} axis Categorizes cells
6984  * @cfg {String} bgcolor Specifies the background color of a cell
6985  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6986  * @cfg {Number} colspan Specifies the number of columns a cell should span
6987  * @cfg {String} headers Specifies one or more header cells a cell is related to
6988  * @cfg {Number} height Sets the height of a cell
6989  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6990  * @cfg {Number} rowspan Sets the number of rows a cell should span
6991  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6992  * @cfg {String} valign Vertical aligns the content in a cell
6993  * @cfg {Number} width Specifies the width of a cell
6994  * 
6995  * @constructor
6996  * Create a new TableCell
6997  * @param {Object} config The config object
6998  */
6999
7000 Roo.bootstrap.TableCell = function(config){
7001     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7002 };
7003
7004 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7005     
7006     html: false,
7007     cls: false,
7008     tag: false,
7009     abbr: false,
7010     align: false,
7011     axis: false,
7012     bgcolor: false,
7013     charoff: false,
7014     colspan: false,
7015     headers: false,
7016     height: false,
7017     nowrap: false,
7018     rowspan: false,
7019     scope: false,
7020     valign: false,
7021     width: false,
7022     
7023     
7024     getAutoCreate : function(){
7025         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7026         
7027         cfg = {
7028             tag: 'td'
7029         };
7030         
7031         if(this.tag){
7032             cfg.tag = this.tag;
7033         }
7034         
7035         if (this.html) {
7036             cfg.html=this.html
7037         }
7038         if (this.cls) {
7039             cfg.cls=this.cls
7040         }
7041         if (this.abbr) {
7042             cfg.abbr=this.abbr
7043         }
7044         if (this.align) {
7045             cfg.align=this.align
7046         }
7047         if (this.axis) {
7048             cfg.axis=this.axis
7049         }
7050         if (this.bgcolor) {
7051             cfg.bgcolor=this.bgcolor
7052         }
7053         if (this.charoff) {
7054             cfg.charoff=this.charoff
7055         }
7056         if (this.colspan) {
7057             cfg.colspan=this.colspan
7058         }
7059         if (this.headers) {
7060             cfg.headers=this.headers
7061         }
7062         if (this.height) {
7063             cfg.height=this.height
7064         }
7065         if (this.nowrap) {
7066             cfg.nowrap=this.nowrap
7067         }
7068         if (this.rowspan) {
7069             cfg.rowspan=this.rowspan
7070         }
7071         if (this.scope) {
7072             cfg.scope=this.scope
7073         }
7074         if (this.valign) {
7075             cfg.valign=this.valign
7076         }
7077         if (this.width) {
7078             cfg.width=this.width
7079         }
7080         
7081         
7082         return cfg;
7083     }
7084    
7085 });
7086
7087  
7088
7089  /*
7090  * - LGPL
7091  *
7092  * table row
7093  * 
7094  */
7095
7096 /**
7097  * @class Roo.bootstrap.TableRow
7098  * @extends Roo.bootstrap.Component
7099  * Bootstrap TableRow class
7100  * @cfg {String} cls row class
7101  * @cfg {String} align Aligns the content in a table row
7102  * @cfg {String} bgcolor Specifies a background color for a table row
7103  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7104  * @cfg {String} valign Vertical aligns the content in a table row
7105  * 
7106  * @constructor
7107  * Create a new TableRow
7108  * @param {Object} config The config object
7109  */
7110
7111 Roo.bootstrap.TableRow = function(config){
7112     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7113 };
7114
7115 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7116     
7117     cls: false,
7118     align: false,
7119     bgcolor: false,
7120     charoff: false,
7121     valign: false,
7122     
7123     getAutoCreate : function(){
7124         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7125         
7126         cfg = {
7127             tag: 'tr'
7128         };
7129             
7130         if(this.cls){
7131             cfg.cls = this.cls;
7132         }
7133         if(this.align){
7134             cfg.align = this.align;
7135         }
7136         if(this.bgcolor){
7137             cfg.bgcolor = this.bgcolor;
7138         }
7139         if(this.charoff){
7140             cfg.charoff = this.charoff;
7141         }
7142         if(this.valign){
7143             cfg.valign = this.valign;
7144         }
7145         
7146         return cfg;
7147     }
7148    
7149 });
7150
7151  
7152
7153  /*
7154  * - LGPL
7155  *
7156  * table body
7157  * 
7158  */
7159
7160 /**
7161  * @class Roo.bootstrap.TableBody
7162  * @extends Roo.bootstrap.Component
7163  * Bootstrap TableBody class
7164  * @cfg {String} cls element class
7165  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7166  * @cfg {String} align Aligns the content inside the element
7167  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7168  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7169  * 
7170  * @constructor
7171  * Create a new TableBody
7172  * @param {Object} config The config object
7173  */
7174
7175 Roo.bootstrap.TableBody = function(config){
7176     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7177 };
7178
7179 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7180     
7181     cls: false,
7182     tag: false,
7183     align: false,
7184     charoff: false,
7185     valign: false,
7186     
7187     getAutoCreate : function(){
7188         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7189         
7190         cfg = {
7191             tag: 'tbody'
7192         };
7193             
7194         if (this.cls) {
7195             cfg.cls=this.cls
7196         }
7197         if(this.tag){
7198             cfg.tag = this.tag;
7199         }
7200         
7201         if(this.align){
7202             cfg.align = this.align;
7203         }
7204         if(this.charoff){
7205             cfg.charoff = this.charoff;
7206         }
7207         if(this.valign){
7208             cfg.valign = this.valign;
7209         }
7210         
7211         return cfg;
7212     }
7213     
7214     
7215 //    initEvents : function()
7216 //    {
7217 //        
7218 //        if(!this.store){
7219 //            return;
7220 //        }
7221 //        
7222 //        this.store = Roo.factory(this.store, Roo.data);
7223 //        this.store.on('load', this.onLoad, this);
7224 //        
7225 //        this.store.load();
7226 //        
7227 //    },
7228 //    
7229 //    onLoad: function () 
7230 //    {   
7231 //        this.fireEvent('load', this);
7232 //    }
7233 //    
7234 //   
7235 });
7236
7237  
7238
7239  /*
7240  * Based on:
7241  * Ext JS Library 1.1.1
7242  * Copyright(c) 2006-2007, Ext JS, LLC.
7243  *
7244  * Originally Released Under LGPL - original licence link has changed is not relivant.
7245  *
7246  * Fork - LGPL
7247  * <script type="text/javascript">
7248  */
7249
7250 // as we use this in bootstrap.
7251 Roo.namespace('Roo.form');
7252  /**
7253  * @class Roo.form.Action
7254  * Internal Class used to handle form actions
7255  * @constructor
7256  * @param {Roo.form.BasicForm} el The form element or its id
7257  * @param {Object} config Configuration options
7258  */
7259
7260  
7261  
7262 // define the action interface
7263 Roo.form.Action = function(form, options){
7264     this.form = form;
7265     this.options = options || {};
7266 };
7267 /**
7268  * Client Validation Failed
7269  * @const 
7270  */
7271 Roo.form.Action.CLIENT_INVALID = 'client';
7272 /**
7273  * Server Validation Failed
7274  * @const 
7275  */
7276 Roo.form.Action.SERVER_INVALID = 'server';
7277  /**
7278  * Connect to Server Failed
7279  * @const 
7280  */
7281 Roo.form.Action.CONNECT_FAILURE = 'connect';
7282 /**
7283  * Reading Data from Server Failed
7284  * @const 
7285  */
7286 Roo.form.Action.LOAD_FAILURE = 'load';
7287
7288 Roo.form.Action.prototype = {
7289     type : 'default',
7290     failureType : undefined,
7291     response : undefined,
7292     result : undefined,
7293
7294     // interface method
7295     run : function(options){
7296
7297     },
7298
7299     // interface method
7300     success : function(response){
7301
7302     },
7303
7304     // interface method
7305     handleResponse : function(response){
7306
7307     },
7308
7309     // default connection failure
7310     failure : function(response){
7311         
7312         this.response = response;
7313         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7314         this.form.afterAction(this, false);
7315     },
7316
7317     processResponse : function(response){
7318         this.response = response;
7319         if(!response.responseText){
7320             return true;
7321         }
7322         this.result = this.handleResponse(response);
7323         return this.result;
7324     },
7325
7326     // utility functions used internally
7327     getUrl : function(appendParams){
7328         var url = this.options.url || this.form.url || this.form.el.dom.action;
7329         if(appendParams){
7330             var p = this.getParams();
7331             if(p){
7332                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7333             }
7334         }
7335         return url;
7336     },
7337
7338     getMethod : function(){
7339         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7340     },
7341
7342     getParams : function(){
7343         var bp = this.form.baseParams;
7344         var p = this.options.params;
7345         if(p){
7346             if(typeof p == "object"){
7347                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7348             }else if(typeof p == 'string' && bp){
7349                 p += '&' + Roo.urlEncode(bp);
7350             }
7351         }else if(bp){
7352             p = Roo.urlEncode(bp);
7353         }
7354         return p;
7355     },
7356
7357     createCallback : function(){
7358         return {
7359             success: this.success,
7360             failure: this.failure,
7361             scope: this,
7362             timeout: (this.form.timeout*1000),
7363             upload: this.form.fileUpload ? this.success : undefined
7364         };
7365     }
7366 };
7367
7368 Roo.form.Action.Submit = function(form, options){
7369     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7370 };
7371
7372 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7373     type : 'submit',
7374
7375     haveProgress : false,
7376     uploadComplete : false,
7377     
7378     // uploadProgress indicator.
7379     uploadProgress : function()
7380     {
7381         if (!this.form.progressUrl) {
7382             return;
7383         }
7384         
7385         if (!this.haveProgress) {
7386             Roo.MessageBox.progress("Uploading", "Uploading");
7387         }
7388         if (this.uploadComplete) {
7389            Roo.MessageBox.hide();
7390            return;
7391         }
7392         
7393         this.haveProgress = true;
7394    
7395         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7396         
7397         var c = new Roo.data.Connection();
7398         c.request({
7399             url : this.form.progressUrl,
7400             params: {
7401                 id : uid
7402             },
7403             method: 'GET',
7404             success : function(req){
7405                //console.log(data);
7406                 var rdata = false;
7407                 var edata;
7408                 try  {
7409                    rdata = Roo.decode(req.responseText)
7410                 } catch (e) {
7411                     Roo.log("Invalid data from server..");
7412                     Roo.log(edata);
7413                     return;
7414                 }
7415                 if (!rdata || !rdata.success) {
7416                     Roo.log(rdata);
7417                     Roo.MessageBox.alert(Roo.encode(rdata));
7418                     return;
7419                 }
7420                 var data = rdata.data;
7421                 
7422                 if (this.uploadComplete) {
7423                    Roo.MessageBox.hide();
7424                    return;
7425                 }
7426                    
7427                 if (data){
7428                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7429                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7430                     );
7431                 }
7432                 this.uploadProgress.defer(2000,this);
7433             },
7434        
7435             failure: function(data) {
7436                 Roo.log('progress url failed ');
7437                 Roo.log(data);
7438             },
7439             scope : this
7440         });
7441            
7442     },
7443     
7444     
7445     run : function()
7446     {
7447         // run get Values on the form, so it syncs any secondary forms.
7448         this.form.getValues();
7449         
7450         var o = this.options;
7451         var method = this.getMethod();
7452         var isPost = method == 'POST';
7453         if(o.clientValidation === false || this.form.isValid()){
7454             
7455             if (this.form.progressUrl) {
7456                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7457                     (new Date() * 1) + '' + Math.random());
7458                     
7459             } 
7460             
7461             
7462             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7463                 form:this.form.el.dom,
7464                 url:this.getUrl(!isPost),
7465                 method: method,
7466                 params:isPost ? this.getParams() : null,
7467                 isUpload: this.form.fileUpload
7468             }));
7469             
7470             this.uploadProgress();
7471
7472         }else if (o.clientValidation !== false){ // client validation failed
7473             this.failureType = Roo.form.Action.CLIENT_INVALID;
7474             this.form.afterAction(this, false);
7475         }
7476     },
7477
7478     success : function(response)
7479     {
7480         this.uploadComplete= true;
7481         if (this.haveProgress) {
7482             Roo.MessageBox.hide();
7483         }
7484         
7485         
7486         var result = this.processResponse(response);
7487         if(result === true || result.success){
7488             this.form.afterAction(this, true);
7489             return;
7490         }
7491         if(result.errors){
7492             this.form.markInvalid(result.errors);
7493             this.failureType = Roo.form.Action.SERVER_INVALID;
7494         }
7495         this.form.afterAction(this, false);
7496     },
7497     failure : function(response)
7498     {
7499         this.uploadComplete= true;
7500         if (this.haveProgress) {
7501             Roo.MessageBox.hide();
7502         }
7503         
7504         this.response = response;
7505         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7506         this.form.afterAction(this, false);
7507     },
7508     
7509     handleResponse : function(response){
7510         if(this.form.errorReader){
7511             var rs = this.form.errorReader.read(response);
7512             var errors = [];
7513             if(rs.records){
7514                 for(var i = 0, len = rs.records.length; i < len; i++) {
7515                     var r = rs.records[i];
7516                     errors[i] = r.data;
7517                 }
7518             }
7519             if(errors.length < 1){
7520                 errors = null;
7521             }
7522             return {
7523                 success : rs.success,
7524                 errors : errors
7525             };
7526         }
7527         var ret = false;
7528         try {
7529             ret = Roo.decode(response.responseText);
7530         } catch (e) {
7531             ret = {
7532                 success: false,
7533                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7534                 errors : []
7535             };
7536         }
7537         return ret;
7538         
7539     }
7540 });
7541
7542
7543 Roo.form.Action.Load = function(form, options){
7544     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7545     this.reader = this.form.reader;
7546 };
7547
7548 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7549     type : 'load',
7550
7551     run : function(){
7552         
7553         Roo.Ajax.request(Roo.apply(
7554                 this.createCallback(), {
7555                     method:this.getMethod(),
7556                     url:this.getUrl(false),
7557                     params:this.getParams()
7558         }));
7559     },
7560
7561     success : function(response){
7562         
7563         var result = this.processResponse(response);
7564         if(result === true || !result.success || !result.data){
7565             this.failureType = Roo.form.Action.LOAD_FAILURE;
7566             this.form.afterAction(this, false);
7567             return;
7568         }
7569         this.form.clearInvalid();
7570         this.form.setValues(result.data);
7571         this.form.afterAction(this, true);
7572     },
7573
7574     handleResponse : function(response){
7575         if(this.form.reader){
7576             var rs = this.form.reader.read(response);
7577             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7578             return {
7579                 success : rs.success,
7580                 data : data
7581             };
7582         }
7583         return Roo.decode(response.responseText);
7584     }
7585 });
7586
7587 Roo.form.Action.ACTION_TYPES = {
7588     'load' : Roo.form.Action.Load,
7589     'submit' : Roo.form.Action.Submit
7590 };/*
7591  * - LGPL
7592  *
7593  * form
7594  *
7595  */
7596
7597 /**
7598  * @class Roo.bootstrap.Form
7599  * @extends Roo.bootstrap.Component
7600  * Bootstrap Form class
7601  * @cfg {String} method  GET | POST (default POST)
7602  * @cfg {String} labelAlign top | left (default top)
7603  * @cfg {String} align left  | right - for navbars
7604  * @cfg {Boolean} loadMask load mask when submit (default true)
7605
7606  *
7607  * @constructor
7608  * Create a new Form
7609  * @param {Object} config The config object
7610  */
7611
7612
7613 Roo.bootstrap.Form = function(config){
7614     
7615     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7616     
7617     Roo.bootstrap.Form.popover.apply();
7618     
7619     this.addEvents({
7620         /**
7621          * @event clientvalidation
7622          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7623          * @param {Form} this
7624          * @param {Boolean} valid true if the form has passed client-side validation
7625          */
7626         clientvalidation: true,
7627         /**
7628          * @event beforeaction
7629          * Fires before any action is performed. Return false to cancel the action.
7630          * @param {Form} this
7631          * @param {Action} action The action to be performed
7632          */
7633         beforeaction: true,
7634         /**
7635          * @event actionfailed
7636          * Fires when an action fails.
7637          * @param {Form} this
7638          * @param {Action} action The action that failed
7639          */
7640         actionfailed : true,
7641         /**
7642          * @event actioncomplete
7643          * Fires when an action is completed.
7644          * @param {Form} this
7645          * @param {Action} action The action that completed
7646          */
7647         actioncomplete : true
7648     });
7649 };
7650
7651 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7652
7653      /**
7654      * @cfg {String} method
7655      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7656      */
7657     method : 'POST',
7658     /**
7659      * @cfg {String} url
7660      * The URL to use for form actions if one isn't supplied in the action options.
7661      */
7662     /**
7663      * @cfg {Boolean} fileUpload
7664      * Set to true if this form is a file upload.
7665      */
7666
7667     /**
7668      * @cfg {Object} baseParams
7669      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7670      */
7671
7672     /**
7673      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7674      */
7675     timeout: 30,
7676     /**
7677      * @cfg {Sting} align (left|right) for navbar forms
7678      */
7679     align : 'left',
7680
7681     // private
7682     activeAction : null,
7683
7684     /**
7685      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7686      * element by passing it or its id or mask the form itself by passing in true.
7687      * @type Mixed
7688      */
7689     waitMsgTarget : false,
7690
7691     loadMask : true,
7692     
7693     /**
7694      * @cfg {Boolean} errorMask (true|false) default false
7695      */
7696     errorMask : false,
7697     
7698     /**
7699      * @cfg {Number} maskOffset Default 100
7700      */
7701     maskOffset : 100,
7702     
7703     /**
7704      * @cfg {Boolean} maskBody
7705      */
7706     maskBody : false,
7707
7708     getAutoCreate : function(){
7709
7710         var cfg = {
7711             tag: 'form',
7712             method : this.method || 'POST',
7713             id : this.id || Roo.id(),
7714             cls : ''
7715         };
7716         if (this.parent().xtype.match(/^Nav/)) {
7717             cfg.cls = 'navbar-form navbar-' + this.align;
7718
7719         }
7720
7721         if (this.labelAlign == 'left' ) {
7722             cfg.cls += ' form-horizontal';
7723         }
7724
7725
7726         return cfg;
7727     },
7728     initEvents : function()
7729     {
7730         this.el.on('submit', this.onSubmit, this);
7731         // this was added as random key presses on the form where triggering form submit.
7732         this.el.on('keypress', function(e) {
7733             if (e.getCharCode() != 13) {
7734                 return true;
7735             }
7736             // we might need to allow it for textareas.. and some other items.
7737             // check e.getTarget().
7738
7739             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7740                 return true;
7741             }
7742
7743             Roo.log("keypress blocked");
7744
7745             e.preventDefault();
7746             return false;
7747         });
7748         
7749     },
7750     // private
7751     onSubmit : function(e){
7752         e.stopEvent();
7753     },
7754
7755      /**
7756      * Returns true if client-side validation on the form is successful.
7757      * @return Boolean
7758      */
7759     isValid : function(){
7760         var items = this.getItems();
7761         var valid = true;
7762         var target = false;
7763         
7764         items.each(function(f){
7765             if(f.validate()){
7766                 return;
7767             }
7768             valid = false;
7769
7770             if(!target && f.el.isVisible(true)){
7771                 target = f;
7772             }
7773            
7774         });
7775         
7776         if(this.errorMask && !valid){
7777             Roo.bootstrap.Form.popover.mask(this, target);
7778         }
7779         
7780         return valid;
7781     },
7782     
7783     /**
7784      * Returns true if any fields in this form have changed since their original load.
7785      * @return Boolean
7786      */
7787     isDirty : function(){
7788         var dirty = false;
7789         var items = this.getItems();
7790         items.each(function(f){
7791            if(f.isDirty()){
7792                dirty = true;
7793                return false;
7794            }
7795            return true;
7796         });
7797         return dirty;
7798     },
7799      /**
7800      * Performs a predefined action (submit or load) or custom actions you define on this form.
7801      * @param {String} actionName The name of the action type
7802      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7803      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7804      * accept other config options):
7805      * <pre>
7806 Property          Type             Description
7807 ----------------  ---------------  ----------------------------------------------------------------------------------
7808 url               String           The url for the action (defaults to the form's url)
7809 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7810 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7811 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7812                                    validate the form on the client (defaults to false)
7813      * </pre>
7814      * @return {BasicForm} this
7815      */
7816     doAction : function(action, options){
7817         if(typeof action == 'string'){
7818             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7819         }
7820         if(this.fireEvent('beforeaction', this, action) !== false){
7821             this.beforeAction(action);
7822             action.run.defer(100, action);
7823         }
7824         return this;
7825     },
7826
7827     // private
7828     beforeAction : function(action){
7829         var o = action.options;
7830         
7831         if(this.loadMask){
7832             
7833             if(this.maskBody){
7834                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7835             } else {
7836                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7837             }
7838         }
7839         // not really supported yet.. ??
7840
7841         //if(this.waitMsgTarget === true){
7842         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7843         //}else if(this.waitMsgTarget){
7844         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7845         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7846         //}else {
7847         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7848        // }
7849
7850     },
7851
7852     // private
7853     afterAction : function(action, success){
7854         this.activeAction = null;
7855         var o = action.options;
7856
7857         if(this.loadMask){
7858             
7859             if(this.maskBody){
7860                 Roo.get(document.body).unmask();
7861             } else {
7862                 this.el.unmask();
7863             }
7864         }
7865         
7866         //if(this.waitMsgTarget === true){
7867 //            this.el.unmask();
7868         //}else if(this.waitMsgTarget){
7869         //    this.waitMsgTarget.unmask();
7870         //}else{
7871         //    Roo.MessageBox.updateProgress(1);
7872         //    Roo.MessageBox.hide();
7873        // }
7874         //
7875         if(success){
7876             if(o.reset){
7877                 this.reset();
7878             }
7879             Roo.callback(o.success, o.scope, [this, action]);
7880             this.fireEvent('actioncomplete', this, action);
7881
7882         }else{
7883
7884             // failure condition..
7885             // we have a scenario where updates need confirming.
7886             // eg. if a locking scenario exists..
7887             // we look for { errors : { needs_confirm : true }} in the response.
7888             if (
7889                 (typeof(action.result) != 'undefined')  &&
7890                 (typeof(action.result.errors) != 'undefined')  &&
7891                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7892            ){
7893                 var _t = this;
7894                 Roo.log("not supported yet");
7895                  /*
7896
7897                 Roo.MessageBox.confirm(
7898                     "Change requires confirmation",
7899                     action.result.errorMsg,
7900                     function(r) {
7901                         if (r != 'yes') {
7902                             return;
7903                         }
7904                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7905                     }
7906
7907                 );
7908                 */
7909
7910
7911                 return;
7912             }
7913
7914             Roo.callback(o.failure, o.scope, [this, action]);
7915             // show an error message if no failed handler is set..
7916             if (!this.hasListener('actionfailed')) {
7917                 Roo.log("need to add dialog support");
7918                 /*
7919                 Roo.MessageBox.alert("Error",
7920                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7921                         action.result.errorMsg :
7922                         "Saving Failed, please check your entries or try again"
7923                 );
7924                 */
7925             }
7926
7927             this.fireEvent('actionfailed', this, action);
7928         }
7929
7930     },
7931     /**
7932      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7933      * @param {String} id The value to search for
7934      * @return Field
7935      */
7936     findField : function(id){
7937         var items = this.getItems();
7938         var field = items.get(id);
7939         if(!field){
7940              items.each(function(f){
7941                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7942                     field = f;
7943                     return false;
7944                 }
7945                 return true;
7946             });
7947         }
7948         return field || null;
7949     },
7950      /**
7951      * Mark fields in this form invalid in bulk.
7952      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7953      * @return {BasicForm} this
7954      */
7955     markInvalid : function(errors){
7956         if(errors instanceof Array){
7957             for(var i = 0, len = errors.length; i < len; i++){
7958                 var fieldError = errors[i];
7959                 var f = this.findField(fieldError.id);
7960                 if(f){
7961                     f.markInvalid(fieldError.msg);
7962                 }
7963             }
7964         }else{
7965             var field, id;
7966             for(id in errors){
7967                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7968                     field.markInvalid(errors[id]);
7969                 }
7970             }
7971         }
7972         //Roo.each(this.childForms || [], function (f) {
7973         //    f.markInvalid(errors);
7974         //});
7975
7976         return this;
7977     },
7978
7979     /**
7980      * Set values for fields in this form in bulk.
7981      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7982      * @return {BasicForm} this
7983      */
7984     setValues : function(values){
7985         if(values instanceof Array){ // array of objects
7986             for(var i = 0, len = values.length; i < len; i++){
7987                 var v = values[i];
7988                 var f = this.findField(v.id);
7989                 if(f){
7990                     f.setValue(v.value);
7991                     if(this.trackResetOnLoad){
7992                         f.originalValue = f.getValue();
7993                     }
7994                 }
7995             }
7996         }else{ // object hash
7997             var field, id;
7998             for(id in values){
7999                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8000
8001                     if (field.setFromData &&
8002                         field.valueField &&
8003                         field.displayField &&
8004                         // combos' with local stores can
8005                         // be queried via setValue()
8006                         // to set their value..
8007                         (field.store && !field.store.isLocal)
8008                         ) {
8009                         // it's a combo
8010                         var sd = { };
8011                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8012                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8013                         field.setFromData(sd);
8014
8015                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8016                         
8017                         field.setFromData(values);
8018                         
8019                     } else {
8020                         field.setValue(values[id]);
8021                     }
8022
8023
8024                     if(this.trackResetOnLoad){
8025                         field.originalValue = field.getValue();
8026                     }
8027                 }
8028             }
8029         }
8030
8031         //Roo.each(this.childForms || [], function (f) {
8032         //    f.setValues(values);
8033         //});
8034
8035         return this;
8036     },
8037
8038     /**
8039      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8040      * they are returned as an array.
8041      * @param {Boolean} asString
8042      * @return {Object}
8043      */
8044     getValues : function(asString){
8045         //if (this.childForms) {
8046             // copy values from the child forms
8047         //    Roo.each(this.childForms, function (f) {
8048         //        this.setValues(f.getValues());
8049         //    }, this);
8050         //}
8051
8052
8053
8054         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8055         if(asString === true){
8056             return fs;
8057         }
8058         return Roo.urlDecode(fs);
8059     },
8060
8061     /**
8062      * Returns the fields in this form as an object with key/value pairs.
8063      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8064      * @return {Object}
8065      */
8066     getFieldValues : function(with_hidden)
8067     {
8068         var items = this.getItems();
8069         var ret = {};
8070         items.each(function(f){
8071             
8072             if (!f.getName()) {
8073                 return;
8074             }
8075             
8076             var v = f.getValue();
8077             
8078             if (f.inputType =='radio') {
8079                 if (typeof(ret[f.getName()]) == 'undefined') {
8080                     ret[f.getName()] = ''; // empty..
8081                 }
8082
8083                 if (!f.el.dom.checked) {
8084                     return;
8085
8086                 }
8087                 v = f.el.dom.value;
8088
8089             }
8090             
8091             if(f.xtype == 'MoneyField'){
8092                 ret[f.currencyName] = f.getCurrency();
8093             }
8094
8095             // not sure if this supported any more..
8096             if ((typeof(v) == 'object') && f.getRawValue) {
8097                 v = f.getRawValue() ; // dates..
8098             }
8099             // combo boxes where name != hiddenName...
8100             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8101                 ret[f.name] = f.getRawValue();
8102             }
8103             ret[f.getName()] = v;
8104         });
8105
8106         return ret;
8107     },
8108
8109     /**
8110      * Clears all invalid messages in this form.
8111      * @return {BasicForm} this
8112      */
8113     clearInvalid : function(){
8114         var items = this.getItems();
8115
8116         items.each(function(f){
8117            f.clearInvalid();
8118         });
8119
8120         return this;
8121     },
8122
8123     /**
8124      * Resets this form.
8125      * @return {BasicForm} this
8126      */
8127     reset : function(){
8128         var items = this.getItems();
8129         items.each(function(f){
8130             f.reset();
8131         });
8132
8133         Roo.each(this.childForms || [], function (f) {
8134             f.reset();
8135         });
8136
8137
8138         return this;
8139     },
8140     
8141     getItems : function()
8142     {
8143         var r=new Roo.util.MixedCollection(false, function(o){
8144             return o.id || (o.id = Roo.id());
8145         });
8146         var iter = function(el) {
8147             if (el.inputEl) {
8148                 r.add(el);
8149             }
8150             if (!el.items) {
8151                 return;
8152             }
8153             Roo.each(el.items,function(e) {
8154                 iter(e);
8155             });
8156         };
8157
8158         iter(this);
8159         return r;
8160     },
8161     
8162     hideFields : function(items)
8163     {
8164         Roo.each(items, function(i){
8165             
8166             var f = this.findField(i);
8167             
8168             if(!f){
8169                 return;
8170             }
8171             
8172             if(f.xtype == 'DateField'){
8173                 f.setVisible(false);
8174                 return;
8175             }
8176             
8177             f.hide();
8178             
8179         }, this);
8180     },
8181     
8182     showFields : function(items)
8183     {
8184         Roo.each(items, function(i){
8185             
8186             var f = this.findField(i);
8187             
8188             if(!f){
8189                 return;
8190             }
8191             
8192             if(f.xtype == 'DateField'){
8193                 f.setVisible(true);
8194                 return;
8195             }
8196             
8197             f.show();
8198             
8199         }, this);
8200     }
8201
8202 });
8203
8204 Roo.apply(Roo.bootstrap.Form, {
8205     
8206     popover : {
8207         
8208         padding : 5,
8209         
8210         isApplied : false,
8211         
8212         isMasked : false,
8213         
8214         form : false,
8215         
8216         target : false,
8217         
8218         toolTip : false,
8219         
8220         intervalID : false,
8221         
8222         maskEl : false,
8223         
8224         apply : function()
8225         {
8226             if(this.isApplied){
8227                 return;
8228             }
8229             
8230             this.maskEl = {
8231                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8232                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8233                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8234                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8235             };
8236             
8237             this.maskEl.top.enableDisplayMode("block");
8238             this.maskEl.left.enableDisplayMode("block");
8239             this.maskEl.bottom.enableDisplayMode("block");
8240             this.maskEl.right.enableDisplayMode("block");
8241             
8242             this.toolTip = new Roo.bootstrap.Tooltip({
8243                 cls : 'roo-form-error-popover',
8244                 alignment : {
8245                     'left' : ['r-l', [-2,0], 'right'],
8246                     'right' : ['l-r', [2,0], 'left'],
8247                     'bottom' : ['tl-bl', [0,2], 'top'],
8248                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8249                 }
8250             });
8251             
8252             this.toolTip.render(Roo.get(document.body));
8253
8254             this.toolTip.el.enableDisplayMode("block");
8255             
8256             Roo.get(document.body).on('click', function(){
8257                 this.unmask();
8258             }, this);
8259             
8260             Roo.get(document.body).on('touchstart', function(){
8261                 this.unmask();
8262             }, this);
8263             
8264             this.isApplied = true
8265         },
8266         
8267         mask : function(form, target)
8268         {
8269             this.form = form;
8270             
8271             this.target = target;
8272             
8273             if(!this.form.errorMask || !target.el){
8274                 return;
8275             }
8276             
8277             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8278             
8279             Roo.log(scrollable);
8280             
8281             var ot = this.target.el.calcOffsetsTo(scrollable);
8282             
8283             var scrollTo = ot[1] - this.form.maskOffset;
8284             
8285             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8286             
8287             scrollable.scrollTo('top', scrollTo);
8288             
8289             var box = this.target.el.getBox();
8290             Roo.log(box);
8291             var zIndex = Roo.bootstrap.Modal.zIndex++;
8292
8293             
8294             this.maskEl.top.setStyle('position', 'absolute');
8295             this.maskEl.top.setStyle('z-index', zIndex);
8296             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8297             this.maskEl.top.setLeft(0);
8298             this.maskEl.top.setTop(0);
8299             this.maskEl.top.show();
8300             
8301             this.maskEl.left.setStyle('position', 'absolute');
8302             this.maskEl.left.setStyle('z-index', zIndex);
8303             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8304             this.maskEl.left.setLeft(0);
8305             this.maskEl.left.setTop(box.y - this.padding);
8306             this.maskEl.left.show();
8307
8308             this.maskEl.bottom.setStyle('position', 'absolute');
8309             this.maskEl.bottom.setStyle('z-index', zIndex);
8310             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8311             this.maskEl.bottom.setLeft(0);
8312             this.maskEl.bottom.setTop(box.bottom + this.padding);
8313             this.maskEl.bottom.show();
8314
8315             this.maskEl.right.setStyle('position', 'absolute');
8316             this.maskEl.right.setStyle('z-index', zIndex);
8317             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8318             this.maskEl.right.setLeft(box.right + this.padding);
8319             this.maskEl.right.setTop(box.y - this.padding);
8320             this.maskEl.right.show();
8321
8322             this.toolTip.bindEl = this.target.el;
8323
8324             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8325
8326             var tip = this.target.blankText;
8327
8328             if(this.target.getValue() !== '' ) {
8329                 
8330                 if (this.target.invalidText.length) {
8331                     tip = this.target.invalidText;
8332                 } else if (this.target.regexText.length){
8333                     tip = this.target.regexText;
8334                 }
8335             }
8336
8337             this.toolTip.show(tip);
8338
8339             this.intervalID = window.setInterval(function() {
8340                 Roo.bootstrap.Form.popover.unmask();
8341             }, 10000);
8342
8343             window.onwheel = function(){ return false;};
8344             
8345             (function(){ this.isMasked = true; }).defer(500, this);
8346             
8347         },
8348         
8349         unmask : function()
8350         {
8351             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8352                 return;
8353             }
8354             
8355             this.maskEl.top.setStyle('position', 'absolute');
8356             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8357             this.maskEl.top.hide();
8358
8359             this.maskEl.left.setStyle('position', 'absolute');
8360             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8361             this.maskEl.left.hide();
8362
8363             this.maskEl.bottom.setStyle('position', 'absolute');
8364             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8365             this.maskEl.bottom.hide();
8366
8367             this.maskEl.right.setStyle('position', 'absolute');
8368             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8369             this.maskEl.right.hide();
8370             
8371             this.toolTip.hide();
8372             
8373             this.toolTip.el.hide();
8374             
8375             window.onwheel = function(){ return true;};
8376             
8377             if(this.intervalID){
8378                 window.clearInterval(this.intervalID);
8379                 this.intervalID = false;
8380             }
8381             
8382             this.isMasked = false;
8383             
8384         }
8385         
8386     }
8387     
8388 });
8389
8390 /*
8391  * Based on:
8392  * Ext JS Library 1.1.1
8393  * Copyright(c) 2006-2007, Ext JS, LLC.
8394  *
8395  * Originally Released Under LGPL - original licence link has changed is not relivant.
8396  *
8397  * Fork - LGPL
8398  * <script type="text/javascript">
8399  */
8400 /**
8401  * @class Roo.form.VTypes
8402  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8403  * @singleton
8404  */
8405 Roo.form.VTypes = function(){
8406     // closure these in so they are only created once.
8407     var alpha = /^[a-zA-Z_]+$/;
8408     var alphanum = /^[a-zA-Z0-9_]+$/;
8409     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8410     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8411
8412     // All these messages and functions are configurable
8413     return {
8414         /**
8415          * The function used to validate email addresses
8416          * @param {String} value The email address
8417          */
8418         'email' : function(v){
8419             return email.test(v);
8420         },
8421         /**
8422          * The error text to display when the email validation function returns false
8423          * @type String
8424          */
8425         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8426         /**
8427          * The keystroke filter mask to be applied on email input
8428          * @type RegExp
8429          */
8430         'emailMask' : /[a-z0-9_\.\-@]/i,
8431
8432         /**
8433          * The function used to validate URLs
8434          * @param {String} value The URL
8435          */
8436         'url' : function(v){
8437             return url.test(v);
8438         },
8439         /**
8440          * The error text to display when the url validation function returns false
8441          * @type String
8442          */
8443         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8444         
8445         /**
8446          * The function used to validate alpha values
8447          * @param {String} value The value
8448          */
8449         'alpha' : function(v){
8450             return alpha.test(v);
8451         },
8452         /**
8453          * The error text to display when the alpha validation function returns false
8454          * @type String
8455          */
8456         'alphaText' : 'This field should only contain letters and _',
8457         /**
8458          * The keystroke filter mask to be applied on alpha input
8459          * @type RegExp
8460          */
8461         'alphaMask' : /[a-z_]/i,
8462
8463         /**
8464          * The function used to validate alphanumeric values
8465          * @param {String} value The value
8466          */
8467         'alphanum' : function(v){
8468             return alphanum.test(v);
8469         },
8470         /**
8471          * The error text to display when the alphanumeric validation function returns false
8472          * @type String
8473          */
8474         'alphanumText' : 'This field should only contain letters, numbers and _',
8475         /**
8476          * The keystroke filter mask to be applied on alphanumeric input
8477          * @type RegExp
8478          */
8479         'alphanumMask' : /[a-z0-9_]/i
8480     };
8481 }();/*
8482  * - LGPL
8483  *
8484  * Input
8485  * 
8486  */
8487
8488 /**
8489  * @class Roo.bootstrap.Input
8490  * @extends Roo.bootstrap.Component
8491  * Bootstrap Input class
8492  * @cfg {Boolean} disabled is it disabled
8493  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8494  * @cfg {String} name name of the input
8495  * @cfg {string} fieldLabel - the label associated
8496  * @cfg {string} placeholder - placeholder to put in text.
8497  * @cfg {string}  before - input group add on before
8498  * @cfg {string} after - input group add on after
8499  * @cfg {string} size - (lg|sm) or leave empty..
8500  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8501  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8502  * @cfg {Number} md colspan out of 12 for computer-sized screens
8503  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8504  * @cfg {string} value default value of the input
8505  * @cfg {Number} labelWidth set the width of label 
8506  * @cfg {Number} labellg set the width of label (1-12)
8507  * @cfg {Number} labelmd set the width of label (1-12)
8508  * @cfg {Number} labelsm set the width of label (1-12)
8509  * @cfg {Number} labelxs set the width of label (1-12)
8510  * @cfg {String} labelAlign (top|left)
8511  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8512  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8513  * @cfg {String} indicatorpos (left|right) default left
8514
8515  * @cfg {String} align (left|center|right) Default left
8516  * @cfg {Boolean} forceFeedback (true|false) Default false
8517  * 
8518  * @constructor
8519  * Create a new Input
8520  * @param {Object} config The config object
8521  */
8522
8523 Roo.bootstrap.Input = function(config){
8524     
8525     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8526     
8527     this.addEvents({
8528         /**
8529          * @event focus
8530          * Fires when this field receives input focus.
8531          * @param {Roo.form.Field} this
8532          */
8533         focus : true,
8534         /**
8535          * @event blur
8536          * Fires when this field loses input focus.
8537          * @param {Roo.form.Field} this
8538          */
8539         blur : true,
8540         /**
8541          * @event specialkey
8542          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8543          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8544          * @param {Roo.form.Field} this
8545          * @param {Roo.EventObject} e The event object
8546          */
8547         specialkey : true,
8548         /**
8549          * @event change
8550          * Fires just before the field blurs if the field value has changed.
8551          * @param {Roo.form.Field} this
8552          * @param {Mixed} newValue The new value
8553          * @param {Mixed} oldValue The original value
8554          */
8555         change : true,
8556         /**
8557          * @event invalid
8558          * Fires after the field has been marked as invalid.
8559          * @param {Roo.form.Field} this
8560          * @param {String} msg The validation message
8561          */
8562         invalid : true,
8563         /**
8564          * @event valid
8565          * Fires after the field has been validated with no errors.
8566          * @param {Roo.form.Field} this
8567          */
8568         valid : true,
8569          /**
8570          * @event keyup
8571          * Fires after the key up
8572          * @param {Roo.form.Field} this
8573          * @param {Roo.EventObject}  e The event Object
8574          */
8575         keyup : true
8576     });
8577 };
8578
8579 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8580      /**
8581      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8582       automatic validation (defaults to "keyup").
8583      */
8584     validationEvent : "keyup",
8585      /**
8586      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8587      */
8588     validateOnBlur : true,
8589     /**
8590      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8591      */
8592     validationDelay : 250,
8593      /**
8594      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8595      */
8596     focusClass : "x-form-focus",  // not needed???
8597     
8598        
8599     /**
8600      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8601      */
8602     invalidClass : "has-warning",
8603     
8604     /**
8605      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8606      */
8607     validClass : "has-success",
8608     
8609     /**
8610      * @cfg {Boolean} hasFeedback (true|false) default true
8611      */
8612     hasFeedback : true,
8613     
8614     /**
8615      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8616      */
8617     invalidFeedbackClass : "glyphicon-warning-sign",
8618     
8619     /**
8620      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8621      */
8622     validFeedbackClass : "glyphicon-ok",
8623     
8624     /**
8625      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8626      */
8627     selectOnFocus : false,
8628     
8629      /**
8630      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8631      */
8632     maskRe : null,
8633        /**
8634      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8635      */
8636     vtype : null,
8637     
8638       /**
8639      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8640      */
8641     disableKeyFilter : false,
8642     
8643        /**
8644      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8645      */
8646     disabled : false,
8647      /**
8648      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8649      */
8650     allowBlank : true,
8651     /**
8652      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8653      */
8654     blankText : "Please complete this mandatory field",
8655     
8656      /**
8657      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8658      */
8659     minLength : 0,
8660     /**
8661      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8662      */
8663     maxLength : Number.MAX_VALUE,
8664     /**
8665      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8666      */
8667     minLengthText : "The minimum length for this field is {0}",
8668     /**
8669      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8670      */
8671     maxLengthText : "The maximum length for this field is {0}",
8672   
8673     
8674     /**
8675      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8676      * If available, this function will be called only after the basic validators all return true, and will be passed the
8677      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8678      */
8679     validator : null,
8680     /**
8681      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8682      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8683      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8684      */
8685     regex : null,
8686     /**
8687      * @cfg {String} regexText -- Depricated - use Invalid Text
8688      */
8689     regexText : "",
8690     
8691     /**
8692      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8693      */
8694     invalidText : "",
8695     
8696     
8697     
8698     autocomplete: false,
8699     
8700     
8701     fieldLabel : '',
8702     inputType : 'text',
8703     
8704     name : false,
8705     placeholder: false,
8706     before : false,
8707     after : false,
8708     size : false,
8709     hasFocus : false,
8710     preventMark: false,
8711     isFormField : true,
8712     value : '',
8713     labelWidth : 2,
8714     labelAlign : false,
8715     readOnly : false,
8716     align : false,
8717     formatedValue : false,
8718     forceFeedback : false,
8719     
8720     indicatorpos : 'left',
8721     
8722     labellg : 0,
8723     labelmd : 0,
8724     labelsm : 0,
8725     labelxs : 0,
8726     
8727     parentLabelAlign : function()
8728     {
8729         var parent = this;
8730         while (parent.parent()) {
8731             parent = parent.parent();
8732             if (typeof(parent.labelAlign) !='undefined') {
8733                 return parent.labelAlign;
8734             }
8735         }
8736         return 'left';
8737         
8738     },
8739     
8740     getAutoCreate : function()
8741     {
8742         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8743         
8744         var id = Roo.id();
8745         
8746         var cfg = {};
8747         
8748         if(this.inputType != 'hidden'){
8749             cfg.cls = 'form-group' //input-group
8750         }
8751         
8752         var input =  {
8753             tag: 'input',
8754             id : id,
8755             type : this.inputType,
8756             value : this.value,
8757             cls : 'form-control',
8758             placeholder : this.placeholder || '',
8759             autocomplete : this.autocomplete || 'new-password'
8760         };
8761         
8762         if(this.align){
8763             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8764         }
8765         
8766         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8767             input.maxLength = this.maxLength;
8768         }
8769         
8770         if (this.disabled) {
8771             input.disabled=true;
8772         }
8773         
8774         if (this.readOnly) {
8775             input.readonly=true;
8776         }
8777         
8778         if (this.name) {
8779             input.name = this.name;
8780         }
8781         
8782         if (this.size) {
8783             input.cls += ' input-' + this.size;
8784         }
8785         
8786         var settings=this;
8787         ['xs','sm','md','lg'].map(function(size){
8788             if (settings[size]) {
8789                 cfg.cls += ' col-' + size + '-' + settings[size];
8790             }
8791         });
8792         
8793         var inputblock = input;
8794         
8795         var feedback = {
8796             tag: 'span',
8797             cls: 'glyphicon form-control-feedback'
8798         };
8799             
8800         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8801             
8802             inputblock = {
8803                 cls : 'has-feedback',
8804                 cn :  [
8805                     input,
8806                     feedback
8807                 ] 
8808             };  
8809         }
8810         
8811         if (this.before || this.after) {
8812             
8813             inputblock = {
8814                 cls : 'input-group',
8815                 cn :  [] 
8816             };
8817             
8818             if (this.before && typeof(this.before) == 'string') {
8819                 
8820                 inputblock.cn.push({
8821                     tag :'span',
8822                     cls : 'roo-input-before input-group-addon',
8823                     html : this.before
8824                 });
8825             }
8826             if (this.before && typeof(this.before) == 'object') {
8827                 this.before = Roo.factory(this.before);
8828                 
8829                 inputblock.cn.push({
8830                     tag :'span',
8831                     cls : 'roo-input-before input-group-' +
8832                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8833                 });
8834             }
8835             
8836             inputblock.cn.push(input);
8837             
8838             if (this.after && typeof(this.after) == 'string') {
8839                 inputblock.cn.push({
8840                     tag :'span',
8841                     cls : 'roo-input-after input-group-addon',
8842                     html : this.after
8843                 });
8844             }
8845             if (this.after && typeof(this.after) == 'object') {
8846                 this.after = Roo.factory(this.after);
8847                 
8848                 inputblock.cn.push({
8849                     tag :'span',
8850                     cls : 'roo-input-after input-group-' +
8851                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8852                 });
8853             }
8854             
8855             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8856                 inputblock.cls += ' has-feedback';
8857                 inputblock.cn.push(feedback);
8858             }
8859         };
8860         
8861         if (align ==='left' && this.fieldLabel.length) {
8862             
8863             cfg.cls += ' roo-form-group-label-left';
8864             
8865             cfg.cn = [
8866                 {
8867                     tag : 'i',
8868                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8869                     tooltip : 'This field is required'
8870                 },
8871                 {
8872                     tag: 'label',
8873                     'for' :  id,
8874                     cls : 'control-label',
8875                     html : this.fieldLabel
8876
8877                 },
8878                 {
8879                     cls : "", 
8880                     cn: [
8881                         inputblock
8882                     ]
8883                 }
8884             ];
8885             
8886             var labelCfg = cfg.cn[1];
8887             var contentCfg = cfg.cn[2];
8888             
8889             if(this.indicatorpos == 'right'){
8890                 cfg.cn = [
8891                     {
8892                         tag: 'label',
8893                         'for' :  id,
8894                         cls : 'control-label',
8895                         cn : [
8896                             {
8897                                 tag : 'span',
8898                                 html : this.fieldLabel
8899                             },
8900                             {
8901                                 tag : 'i',
8902                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8903                                 tooltip : 'This field is required'
8904                             }
8905                         ]
8906                     },
8907                     {
8908                         cls : "",
8909                         cn: [
8910                             inputblock
8911                         ]
8912                     }
8913
8914                 ];
8915                 
8916                 labelCfg = cfg.cn[0];
8917                 contentCfg = cfg.cn[1];
8918             
8919             }
8920             
8921             if(this.labelWidth > 12){
8922                 labelCfg.style = "width: " + this.labelWidth + 'px';
8923             }
8924             
8925             if(this.labelWidth < 13 && this.labelmd == 0){
8926                 this.labelmd = this.labelWidth;
8927             }
8928             
8929             if(this.labellg > 0){
8930                 labelCfg.cls += ' col-lg-' + this.labellg;
8931                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8932             }
8933             
8934             if(this.labelmd > 0){
8935                 labelCfg.cls += ' col-md-' + this.labelmd;
8936                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8937             }
8938             
8939             if(this.labelsm > 0){
8940                 labelCfg.cls += ' col-sm-' + this.labelsm;
8941                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8942             }
8943             
8944             if(this.labelxs > 0){
8945                 labelCfg.cls += ' col-xs-' + this.labelxs;
8946                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8947             }
8948             
8949             
8950         } else if ( this.fieldLabel.length) {
8951                 
8952             cfg.cn = [
8953                 {
8954                     tag : 'i',
8955                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8956                     tooltip : 'This field is required'
8957                 },
8958                 {
8959                     tag: 'label',
8960                    //cls : 'input-group-addon',
8961                     html : this.fieldLabel
8962
8963                 },
8964
8965                inputblock
8966
8967            ];
8968            
8969            if(this.indicatorpos == 'right'){
8970                 
8971                 cfg.cn = [
8972                     {
8973                         tag: 'label',
8974                        //cls : 'input-group-addon',
8975                         html : this.fieldLabel
8976
8977                     },
8978                     {
8979                         tag : 'i',
8980                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8981                         tooltip : 'This field is required'
8982                     },
8983
8984                    inputblock
8985
8986                ];
8987
8988             }
8989
8990         } else {
8991             
8992             cfg.cn = [
8993
8994                     inputblock
8995
8996             ];
8997                 
8998                 
8999         };
9000         
9001         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9002            cfg.cls += ' navbar-form';
9003         }
9004         
9005         if (this.parentType === 'NavGroup') {
9006            cfg.cls += ' navbar-form';
9007            cfg.tag = 'li';
9008         }
9009         
9010         return cfg;
9011         
9012     },
9013     /**
9014      * return the real input element.
9015      */
9016     inputEl: function ()
9017     {
9018         return this.el.select('input.form-control',true).first();
9019     },
9020     
9021     tooltipEl : function()
9022     {
9023         return this.inputEl();
9024     },
9025     
9026     indicatorEl : function()
9027     {
9028         var indicator = this.el.select('i.roo-required-indicator',true).first();
9029         
9030         if(!indicator){
9031             return false;
9032         }
9033         
9034         return indicator;
9035         
9036     },
9037     
9038     setDisabled : function(v)
9039     {
9040         var i  = this.inputEl().dom;
9041         if (!v) {
9042             i.removeAttribute('disabled');
9043             return;
9044             
9045         }
9046         i.setAttribute('disabled','true');
9047     },
9048     initEvents : function()
9049     {
9050           
9051         this.inputEl().on("keydown" , this.fireKey,  this);
9052         this.inputEl().on("focus", this.onFocus,  this);
9053         this.inputEl().on("blur", this.onBlur,  this);
9054         
9055         this.inputEl().relayEvent('keyup', this);
9056         
9057         this.indicator = this.indicatorEl();
9058         
9059         if(this.indicator){
9060             this.indicator.addClass('invisible');
9061         }
9062  
9063         // reference to original value for reset
9064         this.originalValue = this.getValue();
9065         //Roo.form.TextField.superclass.initEvents.call(this);
9066         if(this.validationEvent == 'keyup'){
9067             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9068             this.inputEl().on('keyup', this.filterValidation, this);
9069         }
9070         else if(this.validationEvent !== false){
9071             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9072         }
9073         
9074         if(this.selectOnFocus){
9075             this.on("focus", this.preFocus, this);
9076             
9077         }
9078         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9079             this.inputEl().on("keypress", this.filterKeys, this);
9080         } else {
9081             this.inputEl().relayEvent('keypress', this);
9082         }
9083        /* if(this.grow){
9084             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9085             this.el.on("click", this.autoSize,  this);
9086         }
9087         */
9088         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9089             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9090         }
9091         
9092         if (typeof(this.before) == 'object') {
9093             this.before.render(this.el.select('.roo-input-before',true).first());
9094         }
9095         if (typeof(this.after) == 'object') {
9096             this.after.render(this.el.select('.roo-input-after',true).first());
9097         }
9098         
9099         
9100     },
9101     filterValidation : function(e){
9102         if(!e.isNavKeyPress()){
9103             this.validationTask.delay(this.validationDelay);
9104         }
9105     },
9106      /**
9107      * Validates the field value
9108      * @return {Boolean} True if the value is valid, else false
9109      */
9110     validate : function(){
9111         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9112         if(this.disabled || this.validateValue(this.getRawValue())){
9113             this.markValid();
9114             return true;
9115         }
9116         
9117         this.markInvalid();
9118         return false;
9119     },
9120     
9121     
9122     /**
9123      * Validates a value according to the field's validation rules and marks the field as invalid
9124      * if the validation fails
9125      * @param {Mixed} value The value to validate
9126      * @return {Boolean} True if the value is valid, else false
9127      */
9128     validateValue : function(value)
9129     {
9130         if(this.getVisibilityEl().hasClass('hidden')){
9131             return true;
9132         }
9133         
9134         if(value.length < 1)  { // if it's blank
9135             if(this.allowBlank){
9136                 return true;
9137             }
9138             return false;
9139         }
9140         
9141         if(value.length < this.minLength){
9142             return false;
9143         }
9144         if(value.length > this.maxLength){
9145             return false;
9146         }
9147         if(this.vtype){
9148             var vt = Roo.form.VTypes;
9149             if(!vt[this.vtype](value, this)){
9150                 return false;
9151             }
9152         }
9153         if(typeof this.validator == "function"){
9154             var msg = this.validator(value);
9155             if(msg !== true){
9156                 return false;
9157             }
9158             if (typeof(msg) == 'string') {
9159                 this.invalidText = msg;
9160             }
9161         }
9162         
9163         if(this.regex && !this.regex.test(value)){
9164             return false;
9165         }
9166         
9167         return true;
9168     },
9169     
9170      // private
9171     fireKey : function(e){
9172         //Roo.log('field ' + e.getKey());
9173         if(e.isNavKeyPress()){
9174             this.fireEvent("specialkey", this, e);
9175         }
9176     },
9177     focus : function (selectText){
9178         if(this.rendered){
9179             this.inputEl().focus();
9180             if(selectText === true){
9181                 this.inputEl().dom.select();
9182             }
9183         }
9184         return this;
9185     } ,
9186     
9187     onFocus : function(){
9188         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9189            // this.el.addClass(this.focusClass);
9190         }
9191         if(!this.hasFocus){
9192             this.hasFocus = true;
9193             this.startValue = this.getValue();
9194             this.fireEvent("focus", this);
9195         }
9196     },
9197     
9198     beforeBlur : Roo.emptyFn,
9199
9200     
9201     // private
9202     onBlur : function(){
9203         this.beforeBlur();
9204         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9205             //this.el.removeClass(this.focusClass);
9206         }
9207         this.hasFocus = false;
9208         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9209             this.validate();
9210         }
9211         var v = this.getValue();
9212         if(String(v) !== String(this.startValue)){
9213             this.fireEvent('change', this, v, this.startValue);
9214         }
9215         this.fireEvent("blur", this);
9216     },
9217     
9218     /**
9219      * Resets the current field value to the originally loaded value and clears any validation messages
9220      */
9221     reset : function(){
9222         this.setValue(this.originalValue);
9223         this.validate();
9224     },
9225      /**
9226      * Returns the name of the field
9227      * @return {Mixed} name The name field
9228      */
9229     getName: function(){
9230         return this.name;
9231     },
9232      /**
9233      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9234      * @return {Mixed} value The field value
9235      */
9236     getValue : function(){
9237         
9238         var v = this.inputEl().getValue();
9239         
9240         return v;
9241     },
9242     /**
9243      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9244      * @return {Mixed} value The field value
9245      */
9246     getRawValue : function(){
9247         var v = this.inputEl().getValue();
9248         
9249         return v;
9250     },
9251     
9252     /**
9253      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9254      * @param {Mixed} value The value to set
9255      */
9256     setRawValue : function(v){
9257         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9258     },
9259     
9260     selectText : function(start, end){
9261         var v = this.getRawValue();
9262         if(v.length > 0){
9263             start = start === undefined ? 0 : start;
9264             end = end === undefined ? v.length : end;
9265             var d = this.inputEl().dom;
9266             if(d.setSelectionRange){
9267                 d.setSelectionRange(start, end);
9268             }else if(d.createTextRange){
9269                 var range = d.createTextRange();
9270                 range.moveStart("character", start);
9271                 range.moveEnd("character", v.length-end);
9272                 range.select();
9273             }
9274         }
9275     },
9276     
9277     /**
9278      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9279      * @param {Mixed} value The value to set
9280      */
9281     setValue : function(v){
9282         this.value = v;
9283         if(this.rendered){
9284             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9285             this.validate();
9286         }
9287     },
9288     
9289     /*
9290     processValue : function(value){
9291         if(this.stripCharsRe){
9292             var newValue = value.replace(this.stripCharsRe, '');
9293             if(newValue !== value){
9294                 this.setRawValue(newValue);
9295                 return newValue;
9296             }
9297         }
9298         return value;
9299     },
9300   */
9301     preFocus : function(){
9302         
9303         if(this.selectOnFocus){
9304             this.inputEl().dom.select();
9305         }
9306     },
9307     filterKeys : function(e){
9308         var k = e.getKey();
9309         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9310             return;
9311         }
9312         var c = e.getCharCode(), cc = String.fromCharCode(c);
9313         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9314             return;
9315         }
9316         if(!this.maskRe.test(cc)){
9317             e.stopEvent();
9318         }
9319     },
9320      /**
9321      * Clear any invalid styles/messages for this field
9322      */
9323     clearInvalid : function(){
9324         
9325         if(!this.el || this.preventMark){ // not rendered
9326             return;
9327         }
9328         
9329      
9330         this.el.removeClass(this.invalidClass);
9331         
9332         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9333             
9334             var feedback = this.el.select('.form-control-feedback', true).first();
9335             
9336             if(feedback){
9337                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9338             }
9339             
9340         }
9341         
9342         this.fireEvent('valid', this);
9343     },
9344     
9345      /**
9346      * Mark this field as valid
9347      */
9348     markValid : function()
9349     {
9350         if(!this.el  || this.preventMark){ // not rendered...
9351             return;
9352         }
9353         
9354         this.el.removeClass([this.invalidClass, this.validClass]);
9355         
9356         var feedback = this.el.select('.form-control-feedback', true).first();
9357             
9358         if(feedback){
9359             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9360         }
9361         
9362         if(this.indicator){
9363             this.indicator.removeClass('visible');
9364             this.indicator.addClass('invisible');
9365         }
9366         
9367         if(this.disabled){
9368             return;
9369         }
9370         
9371         if(this.allowBlank && !this.getRawValue().length){
9372             return;
9373         }
9374         
9375         this.el.addClass(this.validClass);
9376         
9377         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9378             
9379             var feedback = this.el.select('.form-control-feedback', true).first();
9380             
9381             if(feedback){
9382                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9383                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9384             }
9385             
9386         }
9387         
9388         this.fireEvent('valid', this);
9389     },
9390     
9391      /**
9392      * Mark this field as invalid
9393      * @param {String} msg The validation message
9394      */
9395     markInvalid : function(msg)
9396     {
9397         if(!this.el  || this.preventMark){ // not rendered
9398             return;
9399         }
9400         
9401         this.el.removeClass([this.invalidClass, this.validClass]);
9402         
9403         var feedback = this.el.select('.form-control-feedback', true).first();
9404             
9405         if(feedback){
9406             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9407         }
9408
9409         if(this.disabled){
9410             return;
9411         }
9412         
9413         if(this.allowBlank && !this.getRawValue().length){
9414             return;
9415         }
9416         
9417         if(this.indicator){
9418             this.indicator.removeClass('invisible');
9419             this.indicator.addClass('visible');
9420         }
9421         
9422         this.el.addClass(this.invalidClass);
9423         
9424         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9425             
9426             var feedback = this.el.select('.form-control-feedback', true).first();
9427             
9428             if(feedback){
9429                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9430                 
9431                 if(this.getValue().length || this.forceFeedback){
9432                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9433                 }
9434                 
9435             }
9436             
9437         }
9438         
9439         this.fireEvent('invalid', this, msg);
9440     },
9441     // private
9442     SafariOnKeyDown : function(event)
9443     {
9444         // this is a workaround for a password hang bug on chrome/ webkit.
9445         if (this.inputEl().dom.type != 'password') {
9446             return;
9447         }
9448         
9449         var isSelectAll = false;
9450         
9451         if(this.inputEl().dom.selectionEnd > 0){
9452             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9453         }
9454         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9455             event.preventDefault();
9456             this.setValue('');
9457             return;
9458         }
9459         
9460         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9461             
9462             event.preventDefault();
9463             // this is very hacky as keydown always get's upper case.
9464             //
9465             var cc = String.fromCharCode(event.getCharCode());
9466             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9467             
9468         }
9469     },
9470     adjustWidth : function(tag, w){
9471         tag = tag.toLowerCase();
9472         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9473             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9474                 if(tag == 'input'){
9475                     return w + 2;
9476                 }
9477                 if(tag == 'textarea'){
9478                     return w-2;
9479                 }
9480             }else if(Roo.isOpera){
9481                 if(tag == 'input'){
9482                     return w + 2;
9483                 }
9484                 if(tag == 'textarea'){
9485                     return w-2;
9486                 }
9487             }
9488         }
9489         return w;
9490     },
9491     
9492     setFieldLabel : function(v)
9493     {
9494         if(!this.rendered){
9495             return;
9496         }
9497         
9498         if(this.indicator){
9499             var ar = this.el.select('label > span',true);
9500             
9501             if (ar.elements.length) {
9502                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9503                 this.fieldLabel = v;
9504                 return;
9505             }
9506             
9507             var br = this.el.select('label',true);
9508             
9509             if(br.elements.length) {
9510                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9511                 this.fieldLabel = v;
9512                 return;
9513             }
9514             
9515             Roo.log('Cannot Found any of label > span || label in input');
9516             return;
9517         }
9518         
9519         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9520         this.fieldLabel = v;
9521         
9522         
9523     }
9524 });
9525
9526  
9527 /*
9528  * - LGPL
9529  *
9530  * Input
9531  * 
9532  */
9533
9534 /**
9535  * @class Roo.bootstrap.TextArea
9536  * @extends Roo.bootstrap.Input
9537  * Bootstrap TextArea class
9538  * @cfg {Number} cols Specifies the visible width of a text area
9539  * @cfg {Number} rows Specifies the visible number of lines in a text area
9540  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9541  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9542  * @cfg {string} html text
9543  * 
9544  * @constructor
9545  * Create a new TextArea
9546  * @param {Object} config The config object
9547  */
9548
9549 Roo.bootstrap.TextArea = function(config){
9550     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9551    
9552 };
9553
9554 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9555      
9556     cols : false,
9557     rows : 5,
9558     readOnly : false,
9559     warp : 'soft',
9560     resize : false,
9561     value: false,
9562     html: false,
9563     
9564     getAutoCreate : function(){
9565         
9566         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9567         
9568         var id = Roo.id();
9569         
9570         var cfg = {};
9571         
9572         if(this.inputType != 'hidden'){
9573             cfg.cls = 'form-group' //input-group
9574         }
9575         
9576         var input =  {
9577             tag: 'textarea',
9578             id : id,
9579             warp : this.warp,
9580             rows : this.rows,
9581             value : this.value || '',
9582             html: this.html || '',
9583             cls : 'form-control',
9584             placeholder : this.placeholder || '' 
9585             
9586         };
9587         
9588         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9589             input.maxLength = this.maxLength;
9590         }
9591         
9592         if(this.resize){
9593             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9594         }
9595         
9596         if(this.cols){
9597             input.cols = this.cols;
9598         }
9599         
9600         if (this.readOnly) {
9601             input.readonly = true;
9602         }
9603         
9604         if (this.name) {
9605             input.name = this.name;
9606         }
9607         
9608         if (this.size) {
9609             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9610         }
9611         
9612         var settings=this;
9613         ['xs','sm','md','lg'].map(function(size){
9614             if (settings[size]) {
9615                 cfg.cls += ' col-' + size + '-' + settings[size];
9616             }
9617         });
9618         
9619         var inputblock = input;
9620         
9621         if(this.hasFeedback && !this.allowBlank){
9622             
9623             var feedback = {
9624                 tag: 'span',
9625                 cls: 'glyphicon form-control-feedback'
9626             };
9627
9628             inputblock = {
9629                 cls : 'has-feedback',
9630                 cn :  [
9631                     input,
9632                     feedback
9633                 ] 
9634             };  
9635         }
9636         
9637         
9638         if (this.before || this.after) {
9639             
9640             inputblock = {
9641                 cls : 'input-group',
9642                 cn :  [] 
9643             };
9644             if (this.before) {
9645                 inputblock.cn.push({
9646                     tag :'span',
9647                     cls : 'input-group-addon',
9648                     html : this.before
9649                 });
9650             }
9651             
9652             inputblock.cn.push(input);
9653             
9654             if(this.hasFeedback && !this.allowBlank){
9655                 inputblock.cls += ' has-feedback';
9656                 inputblock.cn.push(feedback);
9657             }
9658             
9659             if (this.after) {
9660                 inputblock.cn.push({
9661                     tag :'span',
9662                     cls : 'input-group-addon',
9663                     html : this.after
9664                 });
9665             }
9666             
9667         }
9668         
9669         if (align ==='left' && this.fieldLabel.length) {
9670             cfg.cn = [
9671                 {
9672                     tag: 'label',
9673                     'for' :  id,
9674                     cls : 'control-label',
9675                     html : this.fieldLabel
9676                 },
9677                 {
9678                     cls : "",
9679                     cn: [
9680                         inputblock
9681                     ]
9682                 }
9683
9684             ];
9685             
9686             if(this.labelWidth > 12){
9687                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9688             }
9689
9690             if(this.labelWidth < 13 && this.labelmd == 0){
9691                 this.labelmd = this.labelWidth;
9692             }
9693
9694             if(this.labellg > 0){
9695                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9696                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9697             }
9698
9699             if(this.labelmd > 0){
9700                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9701                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9702             }
9703
9704             if(this.labelsm > 0){
9705                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9706                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9707             }
9708
9709             if(this.labelxs > 0){
9710                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9711                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9712             }
9713             
9714         } else if ( this.fieldLabel.length) {
9715             cfg.cn = [
9716
9717                {
9718                    tag: 'label',
9719                    //cls : 'input-group-addon',
9720                    html : this.fieldLabel
9721
9722                },
9723
9724                inputblock
9725
9726            ];
9727
9728         } else {
9729
9730             cfg.cn = [
9731
9732                 inputblock
9733
9734             ];
9735                 
9736         }
9737         
9738         if (this.disabled) {
9739             input.disabled=true;
9740         }
9741         
9742         return cfg;
9743         
9744     },
9745     /**
9746      * return the real textarea element.
9747      */
9748     inputEl: function ()
9749     {
9750         return this.el.select('textarea.form-control',true).first();
9751     },
9752     
9753     /**
9754      * Clear any invalid styles/messages for this field
9755      */
9756     clearInvalid : function()
9757     {
9758         
9759         if(!this.el || this.preventMark){ // not rendered
9760             return;
9761         }
9762         
9763         var label = this.el.select('label', true).first();
9764         var icon = this.el.select('i.fa-star', true).first();
9765         
9766         if(label && icon){
9767             icon.remove();
9768         }
9769         
9770         this.el.removeClass(this.invalidClass);
9771         
9772         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9773             
9774             var feedback = this.el.select('.form-control-feedback', true).first();
9775             
9776             if(feedback){
9777                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9778             }
9779             
9780         }
9781         
9782         this.fireEvent('valid', this);
9783     },
9784     
9785      /**
9786      * Mark this field as valid
9787      */
9788     markValid : function()
9789     {
9790         if(!this.el  || this.preventMark){ // not rendered
9791             return;
9792         }
9793         
9794         this.el.removeClass([this.invalidClass, this.validClass]);
9795         
9796         var feedback = this.el.select('.form-control-feedback', true).first();
9797             
9798         if(feedback){
9799             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9800         }
9801
9802         if(this.disabled || this.allowBlank){
9803             return;
9804         }
9805         
9806         var label = this.el.select('label', true).first();
9807         var icon = this.el.select('i.fa-star', true).first();
9808         
9809         if(label && icon){
9810             icon.remove();
9811         }
9812         
9813         this.el.addClass(this.validClass);
9814         
9815         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9816             
9817             var feedback = this.el.select('.form-control-feedback', true).first();
9818             
9819             if(feedback){
9820                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9821                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9822             }
9823             
9824         }
9825         
9826         this.fireEvent('valid', this);
9827     },
9828     
9829      /**
9830      * Mark this field as invalid
9831      * @param {String} msg The validation message
9832      */
9833     markInvalid : function(msg)
9834     {
9835         if(!this.el  || this.preventMark){ // not rendered
9836             return;
9837         }
9838         
9839         this.el.removeClass([this.invalidClass, this.validClass]);
9840         
9841         var feedback = this.el.select('.form-control-feedback', true).first();
9842             
9843         if(feedback){
9844             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9845         }
9846
9847         if(this.disabled || this.allowBlank){
9848             return;
9849         }
9850         
9851         var label = this.el.select('label', true).first();
9852         var icon = this.el.select('i.fa-star', true).first();
9853         
9854         if(!this.getValue().length && label && !icon){
9855             this.el.createChild({
9856                 tag : 'i',
9857                 cls : 'text-danger fa fa-lg fa-star',
9858                 tooltip : 'This field is required',
9859                 style : 'margin-right:5px;'
9860             }, label, true);
9861         }
9862
9863         this.el.addClass(this.invalidClass);
9864         
9865         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9866             
9867             var feedback = this.el.select('.form-control-feedback', true).first();
9868             
9869             if(feedback){
9870                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9871                 
9872                 if(this.getValue().length || this.forceFeedback){
9873                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9874                 }
9875                 
9876             }
9877             
9878         }
9879         
9880         this.fireEvent('invalid', this, msg);
9881     }
9882 });
9883
9884  
9885 /*
9886  * - LGPL
9887  *
9888  * trigger field - base class for combo..
9889  * 
9890  */
9891  
9892 /**
9893  * @class Roo.bootstrap.TriggerField
9894  * @extends Roo.bootstrap.Input
9895  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9896  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9897  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9898  * for which you can provide a custom implementation.  For example:
9899  * <pre><code>
9900 var trigger = new Roo.bootstrap.TriggerField();
9901 trigger.onTriggerClick = myTriggerFn;
9902 trigger.applyTo('my-field');
9903 </code></pre>
9904  *
9905  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9906  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9907  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9908  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9909  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9910
9911  * @constructor
9912  * Create a new TriggerField.
9913  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9914  * to the base TextField)
9915  */
9916 Roo.bootstrap.TriggerField = function(config){
9917     this.mimicing = false;
9918     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9919 };
9920
9921 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9922     /**
9923      * @cfg {String} triggerClass A CSS class to apply to the trigger
9924      */
9925      /**
9926      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9927      */
9928     hideTrigger:false,
9929
9930     /**
9931      * @cfg {Boolean} removable (true|false) special filter default false
9932      */
9933     removable : false,
9934     
9935     /** @cfg {Boolean} grow @hide */
9936     /** @cfg {Number} growMin @hide */
9937     /** @cfg {Number} growMax @hide */
9938
9939     /**
9940      * @hide 
9941      * @method
9942      */
9943     autoSize: Roo.emptyFn,
9944     // private
9945     monitorTab : true,
9946     // private
9947     deferHeight : true,
9948
9949     
9950     actionMode : 'wrap',
9951     
9952     caret : false,
9953     
9954     
9955     getAutoCreate : function(){
9956        
9957         var align = this.labelAlign || this.parentLabelAlign();
9958         
9959         var id = Roo.id();
9960         
9961         var cfg = {
9962             cls: 'form-group' //input-group
9963         };
9964         
9965         
9966         var input =  {
9967             tag: 'input',
9968             id : id,
9969             type : this.inputType,
9970             cls : 'form-control',
9971             autocomplete: 'new-password',
9972             placeholder : this.placeholder || '' 
9973             
9974         };
9975         if (this.name) {
9976             input.name = this.name;
9977         }
9978         if (this.size) {
9979             input.cls += ' input-' + this.size;
9980         }
9981         
9982         if (this.disabled) {
9983             input.disabled=true;
9984         }
9985         
9986         var inputblock = input;
9987         
9988         if(this.hasFeedback && !this.allowBlank){
9989             
9990             var feedback = {
9991                 tag: 'span',
9992                 cls: 'glyphicon form-control-feedback'
9993             };
9994             
9995             if(this.removable && !this.editable && !this.tickable){
9996                 inputblock = {
9997                     cls : 'has-feedback',
9998                     cn :  [
9999                         inputblock,
10000                         {
10001                             tag: 'button',
10002                             html : 'x',
10003                             cls : 'roo-combo-removable-btn close'
10004                         },
10005                         feedback
10006                     ] 
10007                 };
10008             } else {
10009                 inputblock = {
10010                     cls : 'has-feedback',
10011                     cn :  [
10012                         inputblock,
10013                         feedback
10014                     ] 
10015                 };
10016             }
10017
10018         } else {
10019             if(this.removable && !this.editable && !this.tickable){
10020                 inputblock = {
10021                     cls : 'roo-removable',
10022                     cn :  [
10023                         inputblock,
10024                         {
10025                             tag: 'button',
10026                             html : 'x',
10027                             cls : 'roo-combo-removable-btn close'
10028                         }
10029                     ] 
10030                 };
10031             }
10032         }
10033         
10034         if (this.before || this.after) {
10035             
10036             inputblock = {
10037                 cls : 'input-group',
10038                 cn :  [] 
10039             };
10040             if (this.before) {
10041                 inputblock.cn.push({
10042                     tag :'span',
10043                     cls : 'input-group-addon',
10044                     html : this.before
10045                 });
10046             }
10047             
10048             inputblock.cn.push(input);
10049             
10050             if(this.hasFeedback && !this.allowBlank){
10051                 inputblock.cls += ' has-feedback';
10052                 inputblock.cn.push(feedback);
10053             }
10054             
10055             if (this.after) {
10056                 inputblock.cn.push({
10057                     tag :'span',
10058                     cls : 'input-group-addon',
10059                     html : this.after
10060                 });
10061             }
10062             
10063         };
10064         
10065         var box = {
10066             tag: 'div',
10067             cn: [
10068                 {
10069                     tag: 'input',
10070                     type : 'hidden',
10071                     cls: 'form-hidden-field'
10072                 },
10073                 inputblock
10074             ]
10075             
10076         };
10077         
10078         if(this.multiple){
10079             box = {
10080                 tag: 'div',
10081                 cn: [
10082                     {
10083                         tag: 'input',
10084                         type : 'hidden',
10085                         cls: 'form-hidden-field'
10086                     },
10087                     {
10088                         tag: 'ul',
10089                         cls: 'roo-select2-choices',
10090                         cn:[
10091                             {
10092                                 tag: 'li',
10093                                 cls: 'roo-select2-search-field',
10094                                 cn: [
10095
10096                                     inputblock
10097                                 ]
10098                             }
10099                         ]
10100                     }
10101                 ]
10102             }
10103         };
10104         
10105         var combobox = {
10106             cls: 'roo-select2-container input-group',
10107             cn: [
10108                 box
10109 //                {
10110 //                    tag: 'ul',
10111 //                    cls: 'typeahead typeahead-long dropdown-menu',
10112 //                    style: 'display:none'
10113 //                }
10114             ]
10115         };
10116         
10117         if(!this.multiple && this.showToggleBtn){
10118             
10119             var caret = {
10120                         tag: 'span',
10121                         cls: 'caret'
10122              };
10123             if (this.caret != false) {
10124                 caret = {
10125                      tag: 'i',
10126                      cls: 'fa fa-' + this.caret
10127                 };
10128                 
10129             }
10130             
10131             combobox.cn.push({
10132                 tag :'span',
10133                 cls : 'input-group-addon btn dropdown-toggle',
10134                 cn : [
10135                     caret,
10136                     {
10137                         tag: 'span',
10138                         cls: 'combobox-clear',
10139                         cn  : [
10140                             {
10141                                 tag : 'i',
10142                                 cls: 'icon-remove'
10143                             }
10144                         ]
10145                     }
10146                 ]
10147
10148             })
10149         }
10150         
10151         if(this.multiple){
10152             combobox.cls += ' roo-select2-container-multi';
10153         }
10154         
10155         if (align ==='left' && this.fieldLabel.length) {
10156             
10157             cfg.cls += ' roo-form-group-label-left';
10158
10159             cfg.cn = [
10160                 {
10161                     tag : 'i',
10162                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10163                     tooltip : 'This field is required'
10164                 },
10165                 {
10166                     tag: 'label',
10167                     'for' :  id,
10168                     cls : 'control-label',
10169                     html : this.fieldLabel
10170
10171                 },
10172                 {
10173                     cls : "", 
10174                     cn: [
10175                         combobox
10176                     ]
10177                 }
10178
10179             ];
10180             
10181             var labelCfg = cfg.cn[1];
10182             var contentCfg = cfg.cn[2];
10183             
10184             if(this.indicatorpos == 'right'){
10185                 cfg.cn = [
10186                     {
10187                         tag: 'label',
10188                         'for' :  id,
10189                         cls : 'control-label',
10190                         cn : [
10191                             {
10192                                 tag : 'span',
10193                                 html : this.fieldLabel
10194                             },
10195                             {
10196                                 tag : 'i',
10197                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10198                                 tooltip : 'This field is required'
10199                             }
10200                         ]
10201                     },
10202                     {
10203                         cls : "", 
10204                         cn: [
10205                             combobox
10206                         ]
10207                     }
10208
10209                 ];
10210                 
10211                 labelCfg = cfg.cn[0];
10212                 contentCfg = cfg.cn[1];
10213             }
10214             
10215             if(this.labelWidth > 12){
10216                 labelCfg.style = "width: " + this.labelWidth + 'px';
10217             }
10218             
10219             if(this.labelWidth < 13 && this.labelmd == 0){
10220                 this.labelmd = this.labelWidth;
10221             }
10222             
10223             if(this.labellg > 0){
10224                 labelCfg.cls += ' col-lg-' + this.labellg;
10225                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10226             }
10227             
10228             if(this.labelmd > 0){
10229                 labelCfg.cls += ' col-md-' + this.labelmd;
10230                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10231             }
10232             
10233             if(this.labelsm > 0){
10234                 labelCfg.cls += ' col-sm-' + this.labelsm;
10235                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10236             }
10237             
10238             if(this.labelxs > 0){
10239                 labelCfg.cls += ' col-xs-' + this.labelxs;
10240                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10241             }
10242             
10243         } else if ( this.fieldLabel.length) {
10244 //                Roo.log(" label");
10245             cfg.cn = [
10246                 {
10247                    tag : 'i',
10248                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10249                    tooltip : 'This field is required'
10250                },
10251                {
10252                    tag: 'label',
10253                    //cls : 'input-group-addon',
10254                    html : this.fieldLabel
10255
10256                },
10257
10258                combobox
10259
10260             ];
10261             
10262             if(this.indicatorpos == 'right'){
10263                 
10264                 cfg.cn = [
10265                     {
10266                        tag: 'label',
10267                        cn : [
10268                            {
10269                                tag : 'span',
10270                                html : this.fieldLabel
10271                            },
10272                            {
10273                               tag : 'i',
10274                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10275                               tooltip : 'This field is required'
10276                            }
10277                        ]
10278
10279                     },
10280                     combobox
10281
10282                 ];
10283
10284             }
10285
10286         } else {
10287             
10288 //                Roo.log(" no label && no align");
10289                 cfg = combobox
10290                      
10291                 
10292         }
10293         
10294         var settings=this;
10295         ['xs','sm','md','lg'].map(function(size){
10296             if (settings[size]) {
10297                 cfg.cls += ' col-' + size + '-' + settings[size];
10298             }
10299         });
10300         
10301         return cfg;
10302         
10303     },
10304     
10305     
10306     
10307     // private
10308     onResize : function(w, h){
10309 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10310 //        if(typeof w == 'number'){
10311 //            var x = w - this.trigger.getWidth();
10312 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10313 //            this.trigger.setStyle('left', x+'px');
10314 //        }
10315     },
10316
10317     // private
10318     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10319
10320     // private
10321     getResizeEl : function(){
10322         return this.inputEl();
10323     },
10324
10325     // private
10326     getPositionEl : function(){
10327         return this.inputEl();
10328     },
10329
10330     // private
10331     alignErrorIcon : function(){
10332         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10333     },
10334
10335     // private
10336     initEvents : function(){
10337         
10338         this.createList();
10339         
10340         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10341         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10342         if(!this.multiple && this.showToggleBtn){
10343             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10344             if(this.hideTrigger){
10345                 this.trigger.setDisplayed(false);
10346             }
10347             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10348         }
10349         
10350         if(this.multiple){
10351             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10352         }
10353         
10354         if(this.removable && !this.editable && !this.tickable){
10355             var close = this.closeTriggerEl();
10356             
10357             if(close){
10358                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10359                 close.on('click', this.removeBtnClick, this, close);
10360             }
10361         }
10362         
10363         //this.trigger.addClassOnOver('x-form-trigger-over');
10364         //this.trigger.addClassOnClick('x-form-trigger-click');
10365         
10366         //if(!this.width){
10367         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10368         //}
10369     },
10370     
10371     closeTriggerEl : function()
10372     {
10373         var close = this.el.select('.roo-combo-removable-btn', true).first();
10374         return close ? close : false;
10375     },
10376     
10377     removeBtnClick : function(e, h, el)
10378     {
10379         e.preventDefault();
10380         
10381         if(this.fireEvent("remove", this) !== false){
10382             this.reset();
10383             this.fireEvent("afterremove", this)
10384         }
10385     },
10386     
10387     createList : function()
10388     {
10389         this.list = Roo.get(document.body).createChild({
10390             tag: 'ul',
10391             cls: 'typeahead typeahead-long dropdown-menu',
10392             style: 'display:none'
10393         });
10394         
10395         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10396         
10397     },
10398
10399     // private
10400     initTrigger : function(){
10401        
10402     },
10403
10404     // private
10405     onDestroy : function(){
10406         if(this.trigger){
10407             this.trigger.removeAllListeners();
10408           //  this.trigger.remove();
10409         }
10410         //if(this.wrap){
10411         //    this.wrap.remove();
10412         //}
10413         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10414     },
10415
10416     // private
10417     onFocus : function(){
10418         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10419         /*
10420         if(!this.mimicing){
10421             this.wrap.addClass('x-trigger-wrap-focus');
10422             this.mimicing = true;
10423             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10424             if(this.monitorTab){
10425                 this.el.on("keydown", this.checkTab, this);
10426             }
10427         }
10428         */
10429     },
10430
10431     // private
10432     checkTab : function(e){
10433         if(e.getKey() == e.TAB){
10434             this.triggerBlur();
10435         }
10436     },
10437
10438     // private
10439     onBlur : function(){
10440         // do nothing
10441     },
10442
10443     // private
10444     mimicBlur : function(e, t){
10445         /*
10446         if(!this.wrap.contains(t) && this.validateBlur()){
10447             this.triggerBlur();
10448         }
10449         */
10450     },
10451
10452     // private
10453     triggerBlur : function(){
10454         this.mimicing = false;
10455         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10456         if(this.monitorTab){
10457             this.el.un("keydown", this.checkTab, this);
10458         }
10459         //this.wrap.removeClass('x-trigger-wrap-focus');
10460         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10461     },
10462
10463     // private
10464     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10465     validateBlur : function(e, t){
10466         return true;
10467     },
10468
10469     // private
10470     onDisable : function(){
10471         this.inputEl().dom.disabled = true;
10472         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10473         //if(this.wrap){
10474         //    this.wrap.addClass('x-item-disabled');
10475         //}
10476     },
10477
10478     // private
10479     onEnable : function(){
10480         this.inputEl().dom.disabled = false;
10481         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10482         //if(this.wrap){
10483         //    this.el.removeClass('x-item-disabled');
10484         //}
10485     },
10486
10487     // private
10488     onShow : function(){
10489         var ae = this.getActionEl();
10490         
10491         if(ae){
10492             ae.dom.style.display = '';
10493             ae.dom.style.visibility = 'visible';
10494         }
10495     },
10496
10497     // private
10498     
10499     onHide : function(){
10500         var ae = this.getActionEl();
10501         ae.dom.style.display = 'none';
10502     },
10503
10504     /**
10505      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10506      * by an implementing function.
10507      * @method
10508      * @param {EventObject} e
10509      */
10510     onTriggerClick : Roo.emptyFn
10511 });
10512  /*
10513  * Based on:
10514  * Ext JS Library 1.1.1
10515  * Copyright(c) 2006-2007, Ext JS, LLC.
10516  *
10517  * Originally Released Under LGPL - original licence link has changed is not relivant.
10518  *
10519  * Fork - LGPL
10520  * <script type="text/javascript">
10521  */
10522
10523
10524 /**
10525  * @class Roo.data.SortTypes
10526  * @singleton
10527  * Defines the default sorting (casting?) comparison functions used when sorting data.
10528  */
10529 Roo.data.SortTypes = {
10530     /**
10531      * Default sort that does nothing
10532      * @param {Mixed} s The value being converted
10533      * @return {Mixed} The comparison value
10534      */
10535     none : function(s){
10536         return s;
10537     },
10538     
10539     /**
10540      * The regular expression used to strip tags
10541      * @type {RegExp}
10542      * @property
10543      */
10544     stripTagsRE : /<\/?[^>]+>/gi,
10545     
10546     /**
10547      * Strips all HTML tags to sort on text only
10548      * @param {Mixed} s The value being converted
10549      * @return {String} The comparison value
10550      */
10551     asText : function(s){
10552         return String(s).replace(this.stripTagsRE, "");
10553     },
10554     
10555     /**
10556      * Strips all HTML tags to sort on text only - Case insensitive
10557      * @param {Mixed} s The value being converted
10558      * @return {String} The comparison value
10559      */
10560     asUCText : function(s){
10561         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10562     },
10563     
10564     /**
10565      * Case insensitive string
10566      * @param {Mixed} s The value being converted
10567      * @return {String} The comparison value
10568      */
10569     asUCString : function(s) {
10570         return String(s).toUpperCase();
10571     },
10572     
10573     /**
10574      * Date sorting
10575      * @param {Mixed} s The value being converted
10576      * @return {Number} The comparison value
10577      */
10578     asDate : function(s) {
10579         if(!s){
10580             return 0;
10581         }
10582         if(s instanceof Date){
10583             return s.getTime();
10584         }
10585         return Date.parse(String(s));
10586     },
10587     
10588     /**
10589      * Float sorting
10590      * @param {Mixed} s The value being converted
10591      * @return {Float} The comparison value
10592      */
10593     asFloat : function(s) {
10594         var val = parseFloat(String(s).replace(/,/g, ""));
10595         if(isNaN(val)) {
10596             val = 0;
10597         }
10598         return val;
10599     },
10600     
10601     /**
10602      * Integer sorting
10603      * @param {Mixed} s The value being converted
10604      * @return {Number} The comparison value
10605      */
10606     asInt : function(s) {
10607         var val = parseInt(String(s).replace(/,/g, ""));
10608         if(isNaN(val)) {
10609             val = 0;
10610         }
10611         return val;
10612     }
10613 };/*
10614  * Based on:
10615  * Ext JS Library 1.1.1
10616  * Copyright(c) 2006-2007, Ext JS, LLC.
10617  *
10618  * Originally Released Under LGPL - original licence link has changed is not relivant.
10619  *
10620  * Fork - LGPL
10621  * <script type="text/javascript">
10622  */
10623
10624 /**
10625 * @class Roo.data.Record
10626  * Instances of this class encapsulate both record <em>definition</em> information, and record
10627  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10628  * to access Records cached in an {@link Roo.data.Store} object.<br>
10629  * <p>
10630  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10631  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10632  * objects.<br>
10633  * <p>
10634  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10635  * @constructor
10636  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10637  * {@link #create}. The parameters are the same.
10638  * @param {Array} data An associative Array of data values keyed by the field name.
10639  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10640  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10641  * not specified an integer id is generated.
10642  */
10643 Roo.data.Record = function(data, id){
10644     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10645     this.data = data;
10646 };
10647
10648 /**
10649  * Generate a constructor for a specific record layout.
10650  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10651  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10652  * Each field definition object may contain the following properties: <ul>
10653  * <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,
10654  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10655  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10656  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10657  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10658  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10659  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10660  * this may be omitted.</p></li>
10661  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10662  * <ul><li>auto (Default, implies no conversion)</li>
10663  * <li>string</li>
10664  * <li>int</li>
10665  * <li>float</li>
10666  * <li>boolean</li>
10667  * <li>date</li></ul></p></li>
10668  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10669  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10670  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10671  * by the Reader into an object that will be stored in the Record. It is passed the
10672  * following parameters:<ul>
10673  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10674  * </ul></p></li>
10675  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10676  * </ul>
10677  * <br>usage:<br><pre><code>
10678 var TopicRecord = Roo.data.Record.create(
10679     {name: 'title', mapping: 'topic_title'},
10680     {name: 'author', mapping: 'username'},
10681     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10682     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10683     {name: 'lastPoster', mapping: 'user2'},
10684     {name: 'excerpt', mapping: 'post_text'}
10685 );
10686
10687 var myNewRecord = new TopicRecord({
10688     title: 'Do my job please',
10689     author: 'noobie',
10690     totalPosts: 1,
10691     lastPost: new Date(),
10692     lastPoster: 'Animal',
10693     excerpt: 'No way dude!'
10694 });
10695 myStore.add(myNewRecord);
10696 </code></pre>
10697  * @method create
10698  * @static
10699  */
10700 Roo.data.Record.create = function(o){
10701     var f = function(){
10702         f.superclass.constructor.apply(this, arguments);
10703     };
10704     Roo.extend(f, Roo.data.Record);
10705     var p = f.prototype;
10706     p.fields = new Roo.util.MixedCollection(false, function(field){
10707         return field.name;
10708     });
10709     for(var i = 0, len = o.length; i < len; i++){
10710         p.fields.add(new Roo.data.Field(o[i]));
10711     }
10712     f.getField = function(name){
10713         return p.fields.get(name);  
10714     };
10715     return f;
10716 };
10717
10718 Roo.data.Record.AUTO_ID = 1000;
10719 Roo.data.Record.EDIT = 'edit';
10720 Roo.data.Record.REJECT = 'reject';
10721 Roo.data.Record.COMMIT = 'commit';
10722
10723 Roo.data.Record.prototype = {
10724     /**
10725      * Readonly flag - true if this record has been modified.
10726      * @type Boolean
10727      */
10728     dirty : false,
10729     editing : false,
10730     error: null,
10731     modified: null,
10732
10733     // private
10734     join : function(store){
10735         this.store = store;
10736     },
10737
10738     /**
10739      * Set the named field to the specified value.
10740      * @param {String} name The name of the field to set.
10741      * @param {Object} value The value to set the field to.
10742      */
10743     set : function(name, value){
10744         if(this.data[name] == value){
10745             return;
10746         }
10747         this.dirty = true;
10748         if(!this.modified){
10749             this.modified = {};
10750         }
10751         if(typeof this.modified[name] == 'undefined'){
10752             this.modified[name] = this.data[name];
10753         }
10754         this.data[name] = value;
10755         if(!this.editing && this.store){
10756             this.store.afterEdit(this);
10757         }       
10758     },
10759
10760     /**
10761      * Get the value of the named field.
10762      * @param {String} name The name of the field to get the value of.
10763      * @return {Object} The value of the field.
10764      */
10765     get : function(name){
10766         return this.data[name]; 
10767     },
10768
10769     // private
10770     beginEdit : function(){
10771         this.editing = true;
10772         this.modified = {}; 
10773     },
10774
10775     // private
10776     cancelEdit : function(){
10777         this.editing = false;
10778         delete this.modified;
10779     },
10780
10781     // private
10782     endEdit : function(){
10783         this.editing = false;
10784         if(this.dirty && this.store){
10785             this.store.afterEdit(this);
10786         }
10787     },
10788
10789     /**
10790      * Usually called by the {@link Roo.data.Store} which owns the Record.
10791      * Rejects all changes made to the Record since either creation, or the last commit operation.
10792      * Modified fields are reverted to their original values.
10793      * <p>
10794      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10795      * of reject operations.
10796      */
10797     reject : function(){
10798         var m = this.modified;
10799         for(var n in m){
10800             if(typeof m[n] != "function"){
10801                 this.data[n] = m[n];
10802             }
10803         }
10804         this.dirty = false;
10805         delete this.modified;
10806         this.editing = false;
10807         if(this.store){
10808             this.store.afterReject(this);
10809         }
10810     },
10811
10812     /**
10813      * Usually called by the {@link Roo.data.Store} which owns the Record.
10814      * Commits all changes made to the Record since either creation, or the last commit operation.
10815      * <p>
10816      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10817      * of commit operations.
10818      */
10819     commit : function(){
10820         this.dirty = false;
10821         delete this.modified;
10822         this.editing = false;
10823         if(this.store){
10824             this.store.afterCommit(this);
10825         }
10826     },
10827
10828     // private
10829     hasError : function(){
10830         return this.error != null;
10831     },
10832
10833     // private
10834     clearError : function(){
10835         this.error = null;
10836     },
10837
10838     /**
10839      * Creates a copy of this record.
10840      * @param {String} id (optional) A new record id if you don't want to use this record's id
10841      * @return {Record}
10842      */
10843     copy : function(newId) {
10844         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10845     }
10846 };/*
10847  * Based on:
10848  * Ext JS Library 1.1.1
10849  * Copyright(c) 2006-2007, Ext JS, LLC.
10850  *
10851  * Originally Released Under LGPL - original licence link has changed is not relivant.
10852  *
10853  * Fork - LGPL
10854  * <script type="text/javascript">
10855  */
10856
10857
10858
10859 /**
10860  * @class Roo.data.Store
10861  * @extends Roo.util.Observable
10862  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10863  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10864  * <p>
10865  * 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
10866  * has no knowledge of the format of the data returned by the Proxy.<br>
10867  * <p>
10868  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10869  * instances from the data object. These records are cached and made available through accessor functions.
10870  * @constructor
10871  * Creates a new Store.
10872  * @param {Object} config A config object containing the objects needed for the Store to access data,
10873  * and read the data into Records.
10874  */
10875 Roo.data.Store = function(config){
10876     this.data = new Roo.util.MixedCollection(false);
10877     this.data.getKey = function(o){
10878         return o.id;
10879     };
10880     this.baseParams = {};
10881     // private
10882     this.paramNames = {
10883         "start" : "start",
10884         "limit" : "limit",
10885         "sort" : "sort",
10886         "dir" : "dir",
10887         "multisort" : "_multisort"
10888     };
10889
10890     if(config && config.data){
10891         this.inlineData = config.data;
10892         delete config.data;
10893     }
10894
10895     Roo.apply(this, config);
10896     
10897     if(this.reader){ // reader passed
10898         this.reader = Roo.factory(this.reader, Roo.data);
10899         this.reader.xmodule = this.xmodule || false;
10900         if(!this.recordType){
10901             this.recordType = this.reader.recordType;
10902         }
10903         if(this.reader.onMetaChange){
10904             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10905         }
10906     }
10907
10908     if(this.recordType){
10909         this.fields = this.recordType.prototype.fields;
10910     }
10911     this.modified = [];
10912
10913     this.addEvents({
10914         /**
10915          * @event datachanged
10916          * Fires when the data cache has changed, and a widget which is using this Store
10917          * as a Record cache should refresh its view.
10918          * @param {Store} this
10919          */
10920         datachanged : true,
10921         /**
10922          * @event metachange
10923          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10924          * @param {Store} this
10925          * @param {Object} meta The JSON metadata
10926          */
10927         metachange : true,
10928         /**
10929          * @event add
10930          * Fires when Records have been added to the Store
10931          * @param {Store} this
10932          * @param {Roo.data.Record[]} records The array of Records added
10933          * @param {Number} index The index at which the record(s) were added
10934          */
10935         add : true,
10936         /**
10937          * @event remove
10938          * Fires when a Record has been removed from the Store
10939          * @param {Store} this
10940          * @param {Roo.data.Record} record The Record that was removed
10941          * @param {Number} index The index at which the record was removed
10942          */
10943         remove : true,
10944         /**
10945          * @event update
10946          * Fires when a Record has been updated
10947          * @param {Store} this
10948          * @param {Roo.data.Record} record The Record that was updated
10949          * @param {String} operation The update operation being performed.  Value may be one of:
10950          * <pre><code>
10951  Roo.data.Record.EDIT
10952  Roo.data.Record.REJECT
10953  Roo.data.Record.COMMIT
10954          * </code></pre>
10955          */
10956         update : true,
10957         /**
10958          * @event clear
10959          * Fires when the data cache has been cleared.
10960          * @param {Store} this
10961          */
10962         clear : true,
10963         /**
10964          * @event beforeload
10965          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10966          * the load action will be canceled.
10967          * @param {Store} this
10968          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10969          */
10970         beforeload : true,
10971         /**
10972          * @event beforeloadadd
10973          * Fires after a new set of Records has been loaded.
10974          * @param {Store} this
10975          * @param {Roo.data.Record[]} records The Records that were loaded
10976          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10977          */
10978         beforeloadadd : true,
10979         /**
10980          * @event load
10981          * Fires after a new set of Records has been loaded, before they are added to the store.
10982          * @param {Store} this
10983          * @param {Roo.data.Record[]} records The Records that were loaded
10984          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10985          * @params {Object} return from reader
10986          */
10987         load : true,
10988         /**
10989          * @event loadexception
10990          * Fires if an exception occurs in the Proxy during loading.
10991          * Called with the signature of the Proxy's "loadexception" event.
10992          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10993          * 
10994          * @param {Proxy} 
10995          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10996          * @param {Object} load options 
10997          * @param {Object} jsonData from your request (normally this contains the Exception)
10998          */
10999         loadexception : true
11000     });
11001     
11002     if(this.proxy){
11003         this.proxy = Roo.factory(this.proxy, Roo.data);
11004         this.proxy.xmodule = this.xmodule || false;
11005         this.relayEvents(this.proxy,  ["loadexception"]);
11006     }
11007     this.sortToggle = {};
11008     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11009
11010     Roo.data.Store.superclass.constructor.call(this);
11011
11012     if(this.inlineData){
11013         this.loadData(this.inlineData);
11014         delete this.inlineData;
11015     }
11016 };
11017
11018 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11019      /**
11020     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11021     * without a remote query - used by combo/forms at present.
11022     */
11023     
11024     /**
11025     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11026     */
11027     /**
11028     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11029     */
11030     /**
11031     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11032     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11033     */
11034     /**
11035     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11036     * on any HTTP request
11037     */
11038     /**
11039     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11040     */
11041     /**
11042     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11043     */
11044     multiSort: false,
11045     /**
11046     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11047     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11048     */
11049     remoteSort : false,
11050
11051     /**
11052     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11053      * loaded or when a record is removed. (defaults to false).
11054     */
11055     pruneModifiedRecords : false,
11056
11057     // private
11058     lastOptions : null,
11059
11060     /**
11061      * Add Records to the Store and fires the add event.
11062      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11063      */
11064     add : function(records){
11065         records = [].concat(records);
11066         for(var i = 0, len = records.length; i < len; i++){
11067             records[i].join(this);
11068         }
11069         var index = this.data.length;
11070         this.data.addAll(records);
11071         this.fireEvent("add", this, records, index);
11072     },
11073
11074     /**
11075      * Remove a Record from the Store and fires the remove event.
11076      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11077      */
11078     remove : function(record){
11079         var index = this.data.indexOf(record);
11080         this.data.removeAt(index);
11081         if(this.pruneModifiedRecords){
11082             this.modified.remove(record);
11083         }
11084         this.fireEvent("remove", this, record, index);
11085     },
11086
11087     /**
11088      * Remove all Records from the Store and fires the clear event.
11089      */
11090     removeAll : function(){
11091         this.data.clear();
11092         if(this.pruneModifiedRecords){
11093             this.modified = [];
11094         }
11095         this.fireEvent("clear", this);
11096     },
11097
11098     /**
11099      * Inserts Records to the Store at the given index and fires the add event.
11100      * @param {Number} index The start index at which to insert the passed Records.
11101      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11102      */
11103     insert : function(index, records){
11104         records = [].concat(records);
11105         for(var i = 0, len = records.length; i < len; i++){
11106             this.data.insert(index, records[i]);
11107             records[i].join(this);
11108         }
11109         this.fireEvent("add", this, records, index);
11110     },
11111
11112     /**
11113      * Get the index within the cache of the passed Record.
11114      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11115      * @return {Number} The index of the passed Record. Returns -1 if not found.
11116      */
11117     indexOf : function(record){
11118         return this.data.indexOf(record);
11119     },
11120
11121     /**
11122      * Get the index within the cache of the Record with the passed id.
11123      * @param {String} id The id of the Record to find.
11124      * @return {Number} The index of the Record. Returns -1 if not found.
11125      */
11126     indexOfId : function(id){
11127         return this.data.indexOfKey(id);
11128     },
11129
11130     /**
11131      * Get the Record with the specified id.
11132      * @param {String} id The id of the Record to find.
11133      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11134      */
11135     getById : function(id){
11136         return this.data.key(id);
11137     },
11138
11139     /**
11140      * Get the Record at the specified index.
11141      * @param {Number} index The index of the Record to find.
11142      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11143      */
11144     getAt : function(index){
11145         return this.data.itemAt(index);
11146     },
11147
11148     /**
11149      * Returns a range of Records between specified indices.
11150      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11151      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11152      * @return {Roo.data.Record[]} An array of Records
11153      */
11154     getRange : function(start, end){
11155         return this.data.getRange(start, end);
11156     },
11157
11158     // private
11159     storeOptions : function(o){
11160         o = Roo.apply({}, o);
11161         delete o.callback;
11162         delete o.scope;
11163         this.lastOptions = o;
11164     },
11165
11166     /**
11167      * Loads the Record cache from the configured Proxy using the configured Reader.
11168      * <p>
11169      * If using remote paging, then the first load call must specify the <em>start</em>
11170      * and <em>limit</em> properties in the options.params property to establish the initial
11171      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11172      * <p>
11173      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11174      * and this call will return before the new data has been loaded. Perform any post-processing
11175      * in a callback function, or in a "load" event handler.</strong>
11176      * <p>
11177      * @param {Object} options An object containing properties which control loading options:<ul>
11178      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11179      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11180      * passed the following arguments:<ul>
11181      * <li>r : Roo.data.Record[]</li>
11182      * <li>options: Options object from the load call</li>
11183      * <li>success: Boolean success indicator</li></ul></li>
11184      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11185      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11186      * </ul>
11187      */
11188     load : function(options){
11189         options = options || {};
11190         if(this.fireEvent("beforeload", this, options) !== false){
11191             this.storeOptions(options);
11192             var p = Roo.apply(options.params || {}, this.baseParams);
11193             // if meta was not loaded from remote source.. try requesting it.
11194             if (!this.reader.metaFromRemote) {
11195                 p._requestMeta = 1;
11196             }
11197             if(this.sortInfo && this.remoteSort){
11198                 var pn = this.paramNames;
11199                 p[pn["sort"]] = this.sortInfo.field;
11200                 p[pn["dir"]] = this.sortInfo.direction;
11201             }
11202             if (this.multiSort) {
11203                 var pn = this.paramNames;
11204                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11205             }
11206             
11207             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11208         }
11209     },
11210
11211     /**
11212      * Reloads the Record cache from the configured Proxy using the configured Reader and
11213      * the options from the last load operation performed.
11214      * @param {Object} options (optional) An object containing properties which may override the options
11215      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11216      * the most recently used options are reused).
11217      */
11218     reload : function(options){
11219         this.load(Roo.applyIf(options||{}, this.lastOptions));
11220     },
11221
11222     // private
11223     // Called as a callback by the Reader during a load operation.
11224     loadRecords : function(o, options, success){
11225         if(!o || success === false){
11226             if(success !== false){
11227                 this.fireEvent("load", this, [], options, o);
11228             }
11229             if(options.callback){
11230                 options.callback.call(options.scope || this, [], options, false);
11231             }
11232             return;
11233         }
11234         // if data returned failure - throw an exception.
11235         if (o.success === false) {
11236             // show a message if no listener is registered.
11237             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11238                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11239             }
11240             // loadmask wil be hooked into this..
11241             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11242             return;
11243         }
11244         var r = o.records, t = o.totalRecords || r.length;
11245         
11246         this.fireEvent("beforeloadadd", this, r, options, o);
11247         
11248         if(!options || options.add !== true){
11249             if(this.pruneModifiedRecords){
11250                 this.modified = [];
11251             }
11252             for(var i = 0, len = r.length; i < len; i++){
11253                 r[i].join(this);
11254             }
11255             if(this.snapshot){
11256                 this.data = this.snapshot;
11257                 delete this.snapshot;
11258             }
11259             this.data.clear();
11260             this.data.addAll(r);
11261             this.totalLength = t;
11262             this.applySort();
11263             this.fireEvent("datachanged", this);
11264         }else{
11265             this.totalLength = Math.max(t, this.data.length+r.length);
11266             this.add(r);
11267         }
11268         
11269         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11270                 
11271             var e = new Roo.data.Record({});
11272
11273             e.set(this.parent.displayField, this.parent.emptyTitle);
11274             e.set(this.parent.valueField, '');
11275
11276             this.insert(0, e);
11277         }
11278             
11279         this.fireEvent("load", this, r, options, o);
11280         if(options.callback){
11281             options.callback.call(options.scope || this, r, options, true);
11282         }
11283     },
11284
11285
11286     /**
11287      * Loads data from a passed data block. A Reader which understands the format of the data
11288      * must have been configured in the constructor.
11289      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11290      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11291      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11292      */
11293     loadData : function(o, append){
11294         var r = this.reader.readRecords(o);
11295         this.loadRecords(r, {add: append}, true);
11296     },
11297
11298     /**
11299      * Gets the number of cached records.
11300      * <p>
11301      * <em>If using paging, this may not be the total size of the dataset. If the data object
11302      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11303      * the data set size</em>
11304      */
11305     getCount : function(){
11306         return this.data.length || 0;
11307     },
11308
11309     /**
11310      * Gets the total number of records in the dataset as returned by the server.
11311      * <p>
11312      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11313      * the dataset size</em>
11314      */
11315     getTotalCount : function(){
11316         return this.totalLength || 0;
11317     },
11318
11319     /**
11320      * Returns the sort state of the Store as an object with two properties:
11321      * <pre><code>
11322  field {String} The name of the field by which the Records are sorted
11323  direction {String} The sort order, "ASC" or "DESC"
11324      * </code></pre>
11325      */
11326     getSortState : function(){
11327         return this.sortInfo;
11328     },
11329
11330     // private
11331     applySort : function(){
11332         if(this.sortInfo && !this.remoteSort){
11333             var s = this.sortInfo, f = s.field;
11334             var st = this.fields.get(f).sortType;
11335             var fn = function(r1, r2){
11336                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11337                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11338             };
11339             this.data.sort(s.direction, fn);
11340             if(this.snapshot && this.snapshot != this.data){
11341                 this.snapshot.sort(s.direction, fn);
11342             }
11343         }
11344     },
11345
11346     /**
11347      * Sets the default sort column and order to be used by the next load operation.
11348      * @param {String} fieldName The name of the field to sort by.
11349      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11350      */
11351     setDefaultSort : function(field, dir){
11352         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11353     },
11354
11355     /**
11356      * Sort the Records.
11357      * If remote sorting is used, the sort is performed on the server, and the cache is
11358      * reloaded. If local sorting is used, the cache is sorted internally.
11359      * @param {String} fieldName The name of the field to sort by.
11360      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11361      */
11362     sort : function(fieldName, dir){
11363         var f = this.fields.get(fieldName);
11364         if(!dir){
11365             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11366             
11367             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11368                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11369             }else{
11370                 dir = f.sortDir;
11371             }
11372         }
11373         this.sortToggle[f.name] = dir;
11374         this.sortInfo = {field: f.name, direction: dir};
11375         if(!this.remoteSort){
11376             this.applySort();
11377             this.fireEvent("datachanged", this);
11378         }else{
11379             this.load(this.lastOptions);
11380         }
11381     },
11382
11383     /**
11384      * Calls the specified function for each of the Records in the cache.
11385      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11386      * Returning <em>false</em> aborts and exits the iteration.
11387      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11388      */
11389     each : function(fn, scope){
11390         this.data.each(fn, scope);
11391     },
11392
11393     /**
11394      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11395      * (e.g., during paging).
11396      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11397      */
11398     getModifiedRecords : function(){
11399         return this.modified;
11400     },
11401
11402     // private
11403     createFilterFn : function(property, value, anyMatch){
11404         if(!value.exec){ // not a regex
11405             value = String(value);
11406             if(value.length == 0){
11407                 return false;
11408             }
11409             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11410         }
11411         return function(r){
11412             return value.test(r.data[property]);
11413         };
11414     },
11415
11416     /**
11417      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11418      * @param {String} property A field on your records
11419      * @param {Number} start The record index to start at (defaults to 0)
11420      * @param {Number} end The last record index to include (defaults to length - 1)
11421      * @return {Number} The sum
11422      */
11423     sum : function(property, start, end){
11424         var rs = this.data.items, v = 0;
11425         start = start || 0;
11426         end = (end || end === 0) ? end : rs.length-1;
11427
11428         for(var i = start; i <= end; i++){
11429             v += (rs[i].data[property] || 0);
11430         }
11431         return v;
11432     },
11433
11434     /**
11435      * Filter the records by a specified property.
11436      * @param {String} field A field on your records
11437      * @param {String/RegExp} value Either a string that the field
11438      * should start with or a RegExp to test against the field
11439      * @param {Boolean} anyMatch True to match any part not just the beginning
11440      */
11441     filter : function(property, value, anyMatch){
11442         var fn = this.createFilterFn(property, value, anyMatch);
11443         return fn ? this.filterBy(fn) : this.clearFilter();
11444     },
11445
11446     /**
11447      * Filter by a function. The specified function will be called with each
11448      * record in this data source. If the function returns true the record is included,
11449      * otherwise it is filtered.
11450      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11451      * @param {Object} scope (optional) The scope of the function (defaults to this)
11452      */
11453     filterBy : function(fn, scope){
11454         this.snapshot = this.snapshot || this.data;
11455         this.data = this.queryBy(fn, scope||this);
11456         this.fireEvent("datachanged", this);
11457     },
11458
11459     /**
11460      * Query the records by a specified property.
11461      * @param {String} field A field on your records
11462      * @param {String/RegExp} value Either a string that the field
11463      * should start with or a RegExp to test against the field
11464      * @param {Boolean} anyMatch True to match any part not just the beginning
11465      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11466      */
11467     query : function(property, value, anyMatch){
11468         var fn = this.createFilterFn(property, value, anyMatch);
11469         return fn ? this.queryBy(fn) : this.data.clone();
11470     },
11471
11472     /**
11473      * Query by a function. The specified function will be called with each
11474      * record in this data source. If the function returns true the record is included
11475      * in the results.
11476      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11477      * @param {Object} scope (optional) The scope of the function (defaults to this)
11478       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11479      **/
11480     queryBy : function(fn, scope){
11481         var data = this.snapshot || this.data;
11482         return data.filterBy(fn, scope||this);
11483     },
11484
11485     /**
11486      * Collects unique values for a particular dataIndex from this store.
11487      * @param {String} dataIndex The property to collect
11488      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11489      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11490      * @return {Array} An array of the unique values
11491      **/
11492     collect : function(dataIndex, allowNull, bypassFilter){
11493         var d = (bypassFilter === true && this.snapshot) ?
11494                 this.snapshot.items : this.data.items;
11495         var v, sv, r = [], l = {};
11496         for(var i = 0, len = d.length; i < len; i++){
11497             v = d[i].data[dataIndex];
11498             sv = String(v);
11499             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11500                 l[sv] = true;
11501                 r[r.length] = v;
11502             }
11503         }
11504         return r;
11505     },
11506
11507     /**
11508      * Revert to a view of the Record cache with no filtering applied.
11509      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11510      */
11511     clearFilter : function(suppressEvent){
11512         if(this.snapshot && this.snapshot != this.data){
11513             this.data = this.snapshot;
11514             delete this.snapshot;
11515             if(suppressEvent !== true){
11516                 this.fireEvent("datachanged", this);
11517             }
11518         }
11519     },
11520
11521     // private
11522     afterEdit : function(record){
11523         if(this.modified.indexOf(record) == -1){
11524             this.modified.push(record);
11525         }
11526         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11527     },
11528     
11529     // private
11530     afterReject : function(record){
11531         this.modified.remove(record);
11532         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11533     },
11534
11535     // private
11536     afterCommit : function(record){
11537         this.modified.remove(record);
11538         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11539     },
11540
11541     /**
11542      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11543      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11544      */
11545     commitChanges : function(){
11546         var m = this.modified.slice(0);
11547         this.modified = [];
11548         for(var i = 0, len = m.length; i < len; i++){
11549             m[i].commit();
11550         }
11551     },
11552
11553     /**
11554      * Cancel outstanding changes on all changed records.
11555      */
11556     rejectChanges : function(){
11557         var m = this.modified.slice(0);
11558         this.modified = [];
11559         for(var i = 0, len = m.length; i < len; i++){
11560             m[i].reject();
11561         }
11562     },
11563
11564     onMetaChange : function(meta, rtype, o){
11565         this.recordType = rtype;
11566         this.fields = rtype.prototype.fields;
11567         delete this.snapshot;
11568         this.sortInfo = meta.sortInfo || this.sortInfo;
11569         this.modified = [];
11570         this.fireEvent('metachange', this, this.reader.meta);
11571     },
11572     
11573     moveIndex : function(data, type)
11574     {
11575         var index = this.indexOf(data);
11576         
11577         var newIndex = index + type;
11578         
11579         this.remove(data);
11580         
11581         this.insert(newIndex, data);
11582         
11583     }
11584 });/*
11585  * Based on:
11586  * Ext JS Library 1.1.1
11587  * Copyright(c) 2006-2007, Ext JS, LLC.
11588  *
11589  * Originally Released Under LGPL - original licence link has changed is not relivant.
11590  *
11591  * Fork - LGPL
11592  * <script type="text/javascript">
11593  */
11594
11595 /**
11596  * @class Roo.data.SimpleStore
11597  * @extends Roo.data.Store
11598  * Small helper class to make creating Stores from Array data easier.
11599  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11600  * @cfg {Array} fields An array of field definition objects, or field name strings.
11601  * @cfg {Array} data The multi-dimensional array of data
11602  * @constructor
11603  * @param {Object} config
11604  */
11605 Roo.data.SimpleStore = function(config){
11606     Roo.data.SimpleStore.superclass.constructor.call(this, {
11607         isLocal : true,
11608         reader: new Roo.data.ArrayReader({
11609                 id: config.id
11610             },
11611             Roo.data.Record.create(config.fields)
11612         ),
11613         proxy : new Roo.data.MemoryProxy(config.data)
11614     });
11615     this.load();
11616 };
11617 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11618  * Based on:
11619  * Ext JS Library 1.1.1
11620  * Copyright(c) 2006-2007, Ext JS, LLC.
11621  *
11622  * Originally Released Under LGPL - original licence link has changed is not relivant.
11623  *
11624  * Fork - LGPL
11625  * <script type="text/javascript">
11626  */
11627
11628 /**
11629 /**
11630  * @extends Roo.data.Store
11631  * @class Roo.data.JsonStore
11632  * Small helper class to make creating Stores for JSON data easier. <br/>
11633 <pre><code>
11634 var store = new Roo.data.JsonStore({
11635     url: 'get-images.php',
11636     root: 'images',
11637     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11638 });
11639 </code></pre>
11640  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11641  * JsonReader and HttpProxy (unless inline data is provided).</b>
11642  * @cfg {Array} fields An array of field definition objects, or field name strings.
11643  * @constructor
11644  * @param {Object} config
11645  */
11646 Roo.data.JsonStore = function(c){
11647     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11648         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11649         reader: new Roo.data.JsonReader(c, c.fields)
11650     }));
11651 };
11652 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11653  * Based on:
11654  * Ext JS Library 1.1.1
11655  * Copyright(c) 2006-2007, Ext JS, LLC.
11656  *
11657  * Originally Released Under LGPL - original licence link has changed is not relivant.
11658  *
11659  * Fork - LGPL
11660  * <script type="text/javascript">
11661  */
11662
11663  
11664 Roo.data.Field = function(config){
11665     if(typeof config == "string"){
11666         config = {name: config};
11667     }
11668     Roo.apply(this, config);
11669     
11670     if(!this.type){
11671         this.type = "auto";
11672     }
11673     
11674     var st = Roo.data.SortTypes;
11675     // named sortTypes are supported, here we look them up
11676     if(typeof this.sortType == "string"){
11677         this.sortType = st[this.sortType];
11678     }
11679     
11680     // set default sortType for strings and dates
11681     if(!this.sortType){
11682         switch(this.type){
11683             case "string":
11684                 this.sortType = st.asUCString;
11685                 break;
11686             case "date":
11687                 this.sortType = st.asDate;
11688                 break;
11689             default:
11690                 this.sortType = st.none;
11691         }
11692     }
11693
11694     // define once
11695     var stripRe = /[\$,%]/g;
11696
11697     // prebuilt conversion function for this field, instead of
11698     // switching every time we're reading a value
11699     if(!this.convert){
11700         var cv, dateFormat = this.dateFormat;
11701         switch(this.type){
11702             case "":
11703             case "auto":
11704             case undefined:
11705                 cv = function(v){ return v; };
11706                 break;
11707             case "string":
11708                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11709                 break;
11710             case "int":
11711                 cv = function(v){
11712                     return v !== undefined && v !== null && v !== '' ?
11713                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11714                     };
11715                 break;
11716             case "float":
11717                 cv = function(v){
11718                     return v !== undefined && v !== null && v !== '' ?
11719                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11720                     };
11721                 break;
11722             case "bool":
11723             case "boolean":
11724                 cv = function(v){ return v === true || v === "true" || v == 1; };
11725                 break;
11726             case "date":
11727                 cv = function(v){
11728                     if(!v){
11729                         return '';
11730                     }
11731                     if(v instanceof Date){
11732                         return v;
11733                     }
11734                     if(dateFormat){
11735                         if(dateFormat == "timestamp"){
11736                             return new Date(v*1000);
11737                         }
11738                         return Date.parseDate(v, dateFormat);
11739                     }
11740                     var parsed = Date.parse(v);
11741                     return parsed ? new Date(parsed) : null;
11742                 };
11743              break;
11744             
11745         }
11746         this.convert = cv;
11747     }
11748 };
11749
11750 Roo.data.Field.prototype = {
11751     dateFormat: null,
11752     defaultValue: "",
11753     mapping: null,
11754     sortType : null,
11755     sortDir : "ASC"
11756 };/*
11757  * Based on:
11758  * Ext JS Library 1.1.1
11759  * Copyright(c) 2006-2007, Ext JS, LLC.
11760  *
11761  * Originally Released Under LGPL - original licence link has changed is not relivant.
11762  *
11763  * Fork - LGPL
11764  * <script type="text/javascript">
11765  */
11766  
11767 // Base class for reading structured data from a data source.  This class is intended to be
11768 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11769
11770 /**
11771  * @class Roo.data.DataReader
11772  * Base class for reading structured data from a data source.  This class is intended to be
11773  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11774  */
11775
11776 Roo.data.DataReader = function(meta, recordType){
11777     
11778     this.meta = meta;
11779     
11780     this.recordType = recordType instanceof Array ? 
11781         Roo.data.Record.create(recordType) : recordType;
11782 };
11783
11784 Roo.data.DataReader.prototype = {
11785      /**
11786      * Create an empty record
11787      * @param {Object} data (optional) - overlay some values
11788      * @return {Roo.data.Record} record created.
11789      */
11790     newRow :  function(d) {
11791         var da =  {};
11792         this.recordType.prototype.fields.each(function(c) {
11793             switch( c.type) {
11794                 case 'int' : da[c.name] = 0; break;
11795                 case 'date' : da[c.name] = new Date(); break;
11796                 case 'float' : da[c.name] = 0.0; break;
11797                 case 'boolean' : da[c.name] = false; break;
11798                 default : da[c.name] = ""; break;
11799             }
11800             
11801         });
11802         return new this.recordType(Roo.apply(da, d));
11803     }
11804     
11805 };/*
11806  * Based on:
11807  * Ext JS Library 1.1.1
11808  * Copyright(c) 2006-2007, Ext JS, LLC.
11809  *
11810  * Originally Released Under LGPL - original licence link has changed is not relivant.
11811  *
11812  * Fork - LGPL
11813  * <script type="text/javascript">
11814  */
11815
11816 /**
11817  * @class Roo.data.DataProxy
11818  * @extends Roo.data.Observable
11819  * This class is an abstract base class for implementations which provide retrieval of
11820  * unformatted data objects.<br>
11821  * <p>
11822  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11823  * (of the appropriate type which knows how to parse the data object) to provide a block of
11824  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11825  * <p>
11826  * Custom implementations must implement the load method as described in
11827  * {@link Roo.data.HttpProxy#load}.
11828  */
11829 Roo.data.DataProxy = function(){
11830     this.addEvents({
11831         /**
11832          * @event beforeload
11833          * Fires before a network request is made to retrieve a data object.
11834          * @param {Object} This DataProxy object.
11835          * @param {Object} params The params parameter to the load function.
11836          */
11837         beforeload : true,
11838         /**
11839          * @event load
11840          * Fires before the load method's callback is called.
11841          * @param {Object} This DataProxy object.
11842          * @param {Object} o The data object.
11843          * @param {Object} arg The callback argument object passed to the load function.
11844          */
11845         load : true,
11846         /**
11847          * @event loadexception
11848          * Fires if an Exception occurs during data retrieval.
11849          * @param {Object} This DataProxy object.
11850          * @param {Object} o The data object.
11851          * @param {Object} arg The callback argument object passed to the load function.
11852          * @param {Object} e The Exception.
11853          */
11854         loadexception : true
11855     });
11856     Roo.data.DataProxy.superclass.constructor.call(this);
11857 };
11858
11859 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11860
11861     /**
11862      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11863      */
11864 /*
11865  * Based on:
11866  * Ext JS Library 1.1.1
11867  * Copyright(c) 2006-2007, Ext JS, LLC.
11868  *
11869  * Originally Released Under LGPL - original licence link has changed is not relivant.
11870  *
11871  * Fork - LGPL
11872  * <script type="text/javascript">
11873  */
11874 /**
11875  * @class Roo.data.MemoryProxy
11876  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11877  * to the Reader when its load method is called.
11878  * @constructor
11879  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11880  */
11881 Roo.data.MemoryProxy = function(data){
11882     if (data.data) {
11883         data = data.data;
11884     }
11885     Roo.data.MemoryProxy.superclass.constructor.call(this);
11886     this.data = data;
11887 };
11888
11889 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11890     
11891     /**
11892      * Load data from the requested source (in this case an in-memory
11893      * data object passed to the constructor), read the data object into
11894      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11895      * process that block using the passed callback.
11896      * @param {Object} params This parameter is not used by the MemoryProxy class.
11897      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11898      * object into a block of Roo.data.Records.
11899      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11900      * The function must be passed <ul>
11901      * <li>The Record block object</li>
11902      * <li>The "arg" argument from the load function</li>
11903      * <li>A boolean success indicator</li>
11904      * </ul>
11905      * @param {Object} scope The scope in which to call the callback
11906      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11907      */
11908     load : function(params, reader, callback, scope, arg){
11909         params = params || {};
11910         var result;
11911         try {
11912             result = reader.readRecords(this.data);
11913         }catch(e){
11914             this.fireEvent("loadexception", this, arg, null, e);
11915             callback.call(scope, null, arg, false);
11916             return;
11917         }
11918         callback.call(scope, result, arg, true);
11919     },
11920     
11921     // private
11922     update : function(params, records){
11923         
11924     }
11925 });/*
11926  * Based on:
11927  * Ext JS Library 1.1.1
11928  * Copyright(c) 2006-2007, Ext JS, LLC.
11929  *
11930  * Originally Released Under LGPL - original licence link has changed is not relivant.
11931  *
11932  * Fork - LGPL
11933  * <script type="text/javascript">
11934  */
11935 /**
11936  * @class Roo.data.HttpProxy
11937  * @extends Roo.data.DataProxy
11938  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11939  * configured to reference a certain URL.<br><br>
11940  * <p>
11941  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11942  * from which the running page was served.<br><br>
11943  * <p>
11944  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11945  * <p>
11946  * Be aware that to enable the browser to parse an XML document, the server must set
11947  * the Content-Type header in the HTTP response to "text/xml".
11948  * @constructor
11949  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11950  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11951  * will be used to make the request.
11952  */
11953 Roo.data.HttpProxy = function(conn){
11954     Roo.data.HttpProxy.superclass.constructor.call(this);
11955     // is conn a conn config or a real conn?
11956     this.conn = conn;
11957     this.useAjax = !conn || !conn.events;
11958   
11959 };
11960
11961 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11962     // thse are take from connection...
11963     
11964     /**
11965      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11966      */
11967     /**
11968      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11969      * extra parameters to each request made by this object. (defaults to undefined)
11970      */
11971     /**
11972      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11973      *  to each request made by this object. (defaults to undefined)
11974      */
11975     /**
11976      * @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)
11977      */
11978     /**
11979      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11980      */
11981      /**
11982      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11983      * @type Boolean
11984      */
11985   
11986
11987     /**
11988      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11989      * @type Boolean
11990      */
11991     /**
11992      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11993      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11994      * a finer-grained basis than the DataProxy events.
11995      */
11996     getConnection : function(){
11997         return this.useAjax ? Roo.Ajax : this.conn;
11998     },
11999
12000     /**
12001      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12002      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12003      * process that block using the passed callback.
12004      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12005      * for the request to the remote server.
12006      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12007      * object into a block of Roo.data.Records.
12008      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12009      * The function must be passed <ul>
12010      * <li>The Record block object</li>
12011      * <li>The "arg" argument from the load function</li>
12012      * <li>A boolean success indicator</li>
12013      * </ul>
12014      * @param {Object} scope The scope in which to call the callback
12015      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12016      */
12017     load : function(params, reader, callback, scope, arg){
12018         if(this.fireEvent("beforeload", this, params) !== false){
12019             var  o = {
12020                 params : params || {},
12021                 request: {
12022                     callback : callback,
12023                     scope : scope,
12024                     arg : arg
12025                 },
12026                 reader: reader,
12027                 callback : this.loadResponse,
12028                 scope: this
12029             };
12030             if(this.useAjax){
12031                 Roo.applyIf(o, this.conn);
12032                 if(this.activeRequest){
12033                     Roo.Ajax.abort(this.activeRequest);
12034                 }
12035                 this.activeRequest = Roo.Ajax.request(o);
12036             }else{
12037                 this.conn.request(o);
12038             }
12039         }else{
12040             callback.call(scope||this, null, arg, false);
12041         }
12042     },
12043
12044     // private
12045     loadResponse : function(o, success, response){
12046         delete this.activeRequest;
12047         if(!success){
12048             this.fireEvent("loadexception", this, o, response);
12049             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12050             return;
12051         }
12052         var result;
12053         try {
12054             result = o.reader.read(response);
12055         }catch(e){
12056             this.fireEvent("loadexception", this, o, response, e);
12057             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12058             return;
12059         }
12060         
12061         this.fireEvent("load", this, o, o.request.arg);
12062         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12063     },
12064
12065     // private
12066     update : function(dataSet){
12067
12068     },
12069
12070     // private
12071     updateResponse : function(dataSet){
12072
12073     }
12074 });/*
12075  * Based on:
12076  * Ext JS Library 1.1.1
12077  * Copyright(c) 2006-2007, Ext JS, LLC.
12078  *
12079  * Originally Released Under LGPL - original licence link has changed is not relivant.
12080  *
12081  * Fork - LGPL
12082  * <script type="text/javascript">
12083  */
12084
12085 /**
12086  * @class Roo.data.ScriptTagProxy
12087  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12088  * other than the originating domain of the running page.<br><br>
12089  * <p>
12090  * <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
12091  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12092  * <p>
12093  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12094  * source code that is used as the source inside a &lt;script> tag.<br><br>
12095  * <p>
12096  * In order for the browser to process the returned data, the server must wrap the data object
12097  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12098  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12099  * depending on whether the callback name was passed:
12100  * <p>
12101  * <pre><code>
12102 boolean scriptTag = false;
12103 String cb = request.getParameter("callback");
12104 if (cb != null) {
12105     scriptTag = true;
12106     response.setContentType("text/javascript");
12107 } else {
12108     response.setContentType("application/x-json");
12109 }
12110 Writer out = response.getWriter();
12111 if (scriptTag) {
12112     out.write(cb + "(");
12113 }
12114 out.print(dataBlock.toJsonString());
12115 if (scriptTag) {
12116     out.write(");");
12117 }
12118 </pre></code>
12119  *
12120  * @constructor
12121  * @param {Object} config A configuration object.
12122  */
12123 Roo.data.ScriptTagProxy = function(config){
12124     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12125     Roo.apply(this, config);
12126     this.head = document.getElementsByTagName("head")[0];
12127 };
12128
12129 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12130
12131 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12132     /**
12133      * @cfg {String} url The URL from which to request the data object.
12134      */
12135     /**
12136      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12137      */
12138     timeout : 30000,
12139     /**
12140      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12141      * the server the name of the callback function set up by the load call to process the returned data object.
12142      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12143      * javascript output which calls this named function passing the data object as its only parameter.
12144      */
12145     callbackParam : "callback",
12146     /**
12147      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12148      * name to the request.
12149      */
12150     nocache : true,
12151
12152     /**
12153      * Load data from the configured URL, read the data object into
12154      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12155      * process that block using the passed callback.
12156      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12157      * for the request to the remote server.
12158      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12159      * object into a block of Roo.data.Records.
12160      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12161      * The function must be passed <ul>
12162      * <li>The Record block object</li>
12163      * <li>The "arg" argument from the load function</li>
12164      * <li>A boolean success indicator</li>
12165      * </ul>
12166      * @param {Object} scope The scope in which to call the callback
12167      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12168      */
12169     load : function(params, reader, callback, scope, arg){
12170         if(this.fireEvent("beforeload", this, params) !== false){
12171
12172             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12173
12174             var url = this.url;
12175             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12176             if(this.nocache){
12177                 url += "&_dc=" + (new Date().getTime());
12178             }
12179             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12180             var trans = {
12181                 id : transId,
12182                 cb : "stcCallback"+transId,
12183                 scriptId : "stcScript"+transId,
12184                 params : params,
12185                 arg : arg,
12186                 url : url,
12187                 callback : callback,
12188                 scope : scope,
12189                 reader : reader
12190             };
12191             var conn = this;
12192
12193             window[trans.cb] = function(o){
12194                 conn.handleResponse(o, trans);
12195             };
12196
12197             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12198
12199             if(this.autoAbort !== false){
12200                 this.abort();
12201             }
12202
12203             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12204
12205             var script = document.createElement("script");
12206             script.setAttribute("src", url);
12207             script.setAttribute("type", "text/javascript");
12208             script.setAttribute("id", trans.scriptId);
12209             this.head.appendChild(script);
12210
12211             this.trans = trans;
12212         }else{
12213             callback.call(scope||this, null, arg, false);
12214         }
12215     },
12216
12217     // private
12218     isLoading : function(){
12219         return this.trans ? true : false;
12220     },
12221
12222     /**
12223      * Abort the current server request.
12224      */
12225     abort : function(){
12226         if(this.isLoading()){
12227             this.destroyTrans(this.trans);
12228         }
12229     },
12230
12231     // private
12232     destroyTrans : function(trans, isLoaded){
12233         this.head.removeChild(document.getElementById(trans.scriptId));
12234         clearTimeout(trans.timeoutId);
12235         if(isLoaded){
12236             window[trans.cb] = undefined;
12237             try{
12238                 delete window[trans.cb];
12239             }catch(e){}
12240         }else{
12241             // if hasn't been loaded, wait for load to remove it to prevent script error
12242             window[trans.cb] = function(){
12243                 window[trans.cb] = undefined;
12244                 try{
12245                     delete window[trans.cb];
12246                 }catch(e){}
12247             };
12248         }
12249     },
12250
12251     // private
12252     handleResponse : function(o, trans){
12253         this.trans = false;
12254         this.destroyTrans(trans, true);
12255         var result;
12256         try {
12257             result = trans.reader.readRecords(o);
12258         }catch(e){
12259             this.fireEvent("loadexception", this, o, trans.arg, e);
12260             trans.callback.call(trans.scope||window, null, trans.arg, false);
12261             return;
12262         }
12263         this.fireEvent("load", this, o, trans.arg);
12264         trans.callback.call(trans.scope||window, result, trans.arg, true);
12265     },
12266
12267     // private
12268     handleFailure : function(trans){
12269         this.trans = false;
12270         this.destroyTrans(trans, false);
12271         this.fireEvent("loadexception", this, null, trans.arg);
12272         trans.callback.call(trans.scope||window, null, trans.arg, false);
12273     }
12274 });/*
12275  * Based on:
12276  * Ext JS Library 1.1.1
12277  * Copyright(c) 2006-2007, Ext JS, LLC.
12278  *
12279  * Originally Released Under LGPL - original licence link has changed is not relivant.
12280  *
12281  * Fork - LGPL
12282  * <script type="text/javascript">
12283  */
12284
12285 /**
12286  * @class Roo.data.JsonReader
12287  * @extends Roo.data.DataReader
12288  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12289  * based on mappings in a provided Roo.data.Record constructor.
12290  * 
12291  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12292  * in the reply previously. 
12293  * 
12294  * <p>
12295  * Example code:
12296  * <pre><code>
12297 var RecordDef = Roo.data.Record.create([
12298     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12299     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12300 ]);
12301 var myReader = new Roo.data.JsonReader({
12302     totalProperty: "results",    // The property which contains the total dataset size (optional)
12303     root: "rows",                // The property which contains an Array of row objects
12304     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12305 }, RecordDef);
12306 </code></pre>
12307  * <p>
12308  * This would consume a JSON file like this:
12309  * <pre><code>
12310 { 'results': 2, 'rows': [
12311     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12312     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12313 }
12314 </code></pre>
12315  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12316  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12317  * paged from the remote server.
12318  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12319  * @cfg {String} root name of the property which contains the Array of row objects.
12320  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12321  * @cfg {Array} fields Array of field definition objects
12322  * @constructor
12323  * Create a new JsonReader
12324  * @param {Object} meta Metadata configuration options
12325  * @param {Object} recordType Either an Array of field definition objects,
12326  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12327  */
12328 Roo.data.JsonReader = function(meta, recordType){
12329     
12330     meta = meta || {};
12331     // set some defaults:
12332     Roo.applyIf(meta, {
12333         totalProperty: 'total',
12334         successProperty : 'success',
12335         root : 'data',
12336         id : 'id'
12337     });
12338     
12339     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12340 };
12341 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12342     
12343     /**
12344      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12345      * Used by Store query builder to append _requestMeta to params.
12346      * 
12347      */
12348     metaFromRemote : false,
12349     /**
12350      * This method is only used by a DataProxy which has retrieved data from a remote server.
12351      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12352      * @return {Object} data A data block which is used by an Roo.data.Store object as
12353      * a cache of Roo.data.Records.
12354      */
12355     read : function(response){
12356         var json = response.responseText;
12357        
12358         var o = /* eval:var:o */ eval("("+json+")");
12359         if(!o) {
12360             throw {message: "JsonReader.read: Json object not found"};
12361         }
12362         
12363         if(o.metaData){
12364             
12365             delete this.ef;
12366             this.metaFromRemote = true;
12367             this.meta = o.metaData;
12368             this.recordType = Roo.data.Record.create(o.metaData.fields);
12369             this.onMetaChange(this.meta, this.recordType, o);
12370         }
12371         return this.readRecords(o);
12372     },
12373
12374     // private function a store will implement
12375     onMetaChange : function(meta, recordType, o){
12376
12377     },
12378
12379     /**
12380          * @ignore
12381          */
12382     simpleAccess: function(obj, subsc) {
12383         return obj[subsc];
12384     },
12385
12386         /**
12387          * @ignore
12388          */
12389     getJsonAccessor: function(){
12390         var re = /[\[\.]/;
12391         return function(expr) {
12392             try {
12393                 return(re.test(expr))
12394                     ? new Function("obj", "return obj." + expr)
12395                     : function(obj){
12396                         return obj[expr];
12397                     };
12398             } catch(e){}
12399             return Roo.emptyFn;
12400         };
12401     }(),
12402
12403     /**
12404      * Create a data block containing Roo.data.Records from an XML document.
12405      * @param {Object} o An object which contains an Array of row objects in the property specified
12406      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12407      * which contains the total size of the dataset.
12408      * @return {Object} data A data block which is used by an Roo.data.Store object as
12409      * a cache of Roo.data.Records.
12410      */
12411     readRecords : function(o){
12412         /**
12413          * After any data loads, the raw JSON data is available for further custom processing.
12414          * @type Object
12415          */
12416         this.o = o;
12417         var s = this.meta, Record = this.recordType,
12418             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12419
12420 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12421         if (!this.ef) {
12422             if(s.totalProperty) {
12423                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12424                 }
12425                 if(s.successProperty) {
12426                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12427                 }
12428                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12429                 if (s.id) {
12430                         var g = this.getJsonAccessor(s.id);
12431                         this.getId = function(rec) {
12432                                 var r = g(rec);  
12433                                 return (r === undefined || r === "") ? null : r;
12434                         };
12435                 } else {
12436                         this.getId = function(){return null;};
12437                 }
12438             this.ef = [];
12439             for(var jj = 0; jj < fl; jj++){
12440                 f = fi[jj];
12441                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12442                 this.ef[jj] = this.getJsonAccessor(map);
12443             }
12444         }
12445
12446         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12447         if(s.totalProperty){
12448             var vt = parseInt(this.getTotal(o), 10);
12449             if(!isNaN(vt)){
12450                 totalRecords = vt;
12451             }
12452         }
12453         if(s.successProperty){
12454             var vs = this.getSuccess(o);
12455             if(vs === false || vs === 'false'){
12456                 success = false;
12457             }
12458         }
12459         var records = [];
12460         for(var i = 0; i < c; i++){
12461                 var n = root[i];
12462             var values = {};
12463             var id = this.getId(n);
12464             for(var j = 0; j < fl; j++){
12465                 f = fi[j];
12466             var v = this.ef[j](n);
12467             if (!f.convert) {
12468                 Roo.log('missing convert for ' + f.name);
12469                 Roo.log(f);
12470                 continue;
12471             }
12472             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12473             }
12474             var record = new Record(values, id);
12475             record.json = n;
12476             records[i] = record;
12477         }
12478         return {
12479             raw : o,
12480             success : success,
12481             records : records,
12482             totalRecords : totalRecords
12483         };
12484     }
12485 });/*
12486  * Based on:
12487  * Ext JS Library 1.1.1
12488  * Copyright(c) 2006-2007, Ext JS, LLC.
12489  *
12490  * Originally Released Under LGPL - original licence link has changed is not relivant.
12491  *
12492  * Fork - LGPL
12493  * <script type="text/javascript">
12494  */
12495
12496 /**
12497  * @class Roo.data.ArrayReader
12498  * @extends Roo.data.DataReader
12499  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12500  * Each element of that Array represents a row of data fields. The
12501  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12502  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12503  * <p>
12504  * Example code:.
12505  * <pre><code>
12506 var RecordDef = Roo.data.Record.create([
12507     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12508     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12509 ]);
12510 var myReader = new Roo.data.ArrayReader({
12511     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12512 }, RecordDef);
12513 </code></pre>
12514  * <p>
12515  * This would consume an Array like this:
12516  * <pre><code>
12517 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12518   </code></pre>
12519  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12520  * @constructor
12521  * Create a new JsonReader
12522  * @param {Object} meta Metadata configuration options.
12523  * @param {Object} recordType Either an Array of field definition objects
12524  * as specified to {@link Roo.data.Record#create},
12525  * or an {@link Roo.data.Record} object
12526  * created using {@link Roo.data.Record#create}.
12527  */
12528 Roo.data.ArrayReader = function(meta, recordType){
12529     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12530 };
12531
12532 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12533     /**
12534      * Create a data block containing Roo.data.Records from an XML document.
12535      * @param {Object} o An Array of row objects which represents the dataset.
12536      * @return {Object} data A data block which is used by an Roo.data.Store object as
12537      * a cache of Roo.data.Records.
12538      */
12539     readRecords : function(o){
12540         var sid = this.meta ? this.meta.id : null;
12541         var recordType = this.recordType, fields = recordType.prototype.fields;
12542         var records = [];
12543         var root = o;
12544             for(var i = 0; i < root.length; i++){
12545                     var n = root[i];
12546                 var values = {};
12547                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12548                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12549                 var f = fields.items[j];
12550                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12551                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12552                 v = f.convert(v);
12553                 values[f.name] = v;
12554             }
12555                 var record = new recordType(values, id);
12556                 record.json = n;
12557                 records[records.length] = record;
12558             }
12559             return {
12560                 records : records,
12561                 totalRecords : records.length
12562             };
12563     }
12564 });/*
12565  * - LGPL
12566  * * 
12567  */
12568
12569 /**
12570  * @class Roo.bootstrap.ComboBox
12571  * @extends Roo.bootstrap.TriggerField
12572  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12573  * @cfg {Boolean} append (true|false) default false
12574  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12575  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12576  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12577  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12578  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12579  * @cfg {Boolean} animate default true
12580  * @cfg {Boolean} emptyResultText only for touch device
12581  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12582  * @cfg {String} emptyTitle default ''
12583  * @constructor
12584  * Create a new ComboBox.
12585  * @param {Object} config Configuration options
12586  */
12587 Roo.bootstrap.ComboBox = function(config){
12588     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12589     this.addEvents({
12590         /**
12591          * @event expand
12592          * Fires when the dropdown list is expanded
12593         * @param {Roo.bootstrap.ComboBox} combo This combo box
12594         */
12595         'expand' : true,
12596         /**
12597          * @event collapse
12598          * Fires when the dropdown list is collapsed
12599         * @param {Roo.bootstrap.ComboBox} combo This combo box
12600         */
12601         'collapse' : true,
12602         /**
12603          * @event beforeselect
12604          * Fires before a list item is selected. Return false to cancel the selection.
12605         * @param {Roo.bootstrap.ComboBox} combo This combo box
12606         * @param {Roo.data.Record} record The data record returned from the underlying store
12607         * @param {Number} index The index of the selected item in the dropdown list
12608         */
12609         'beforeselect' : true,
12610         /**
12611          * @event select
12612          * Fires when a list item is selected
12613         * @param {Roo.bootstrap.ComboBox} combo This combo box
12614         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12615         * @param {Number} index The index of the selected item in the dropdown list
12616         */
12617         'select' : true,
12618         /**
12619          * @event beforequery
12620          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12621          * The event object passed has these properties:
12622         * @param {Roo.bootstrap.ComboBox} combo This combo box
12623         * @param {String} query The query
12624         * @param {Boolean} forceAll true to force "all" query
12625         * @param {Boolean} cancel true to cancel the query
12626         * @param {Object} e The query event object
12627         */
12628         'beforequery': true,
12629          /**
12630          * @event add
12631          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12632         * @param {Roo.bootstrap.ComboBox} combo This combo box
12633         */
12634         'add' : true,
12635         /**
12636          * @event edit
12637          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12638         * @param {Roo.bootstrap.ComboBox} combo This combo box
12639         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12640         */
12641         'edit' : true,
12642         /**
12643          * @event remove
12644          * Fires when the remove value from the combobox array
12645         * @param {Roo.bootstrap.ComboBox} combo This combo box
12646         */
12647         'remove' : true,
12648         /**
12649          * @event afterremove
12650          * Fires when the remove value from the combobox array
12651         * @param {Roo.bootstrap.ComboBox} combo This combo box
12652         */
12653         'afterremove' : true,
12654         /**
12655          * @event specialfilter
12656          * Fires when specialfilter
12657             * @param {Roo.bootstrap.ComboBox} combo This combo box
12658             */
12659         'specialfilter' : true,
12660         /**
12661          * @event tick
12662          * Fires when tick the element
12663             * @param {Roo.bootstrap.ComboBox} combo This combo box
12664             */
12665         'tick' : true,
12666         /**
12667          * @event touchviewdisplay
12668          * Fires when touch view require special display (default is using displayField)
12669             * @param {Roo.bootstrap.ComboBox} combo This combo box
12670             * @param {Object} cfg set html .
12671             */
12672         'touchviewdisplay' : true
12673         
12674     });
12675     
12676     this.item = [];
12677     this.tickItems = [];
12678     
12679     this.selectedIndex = -1;
12680     if(this.mode == 'local'){
12681         if(config.queryDelay === undefined){
12682             this.queryDelay = 10;
12683         }
12684         if(config.minChars === undefined){
12685             this.minChars = 0;
12686         }
12687     }
12688 };
12689
12690 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12691      
12692     /**
12693      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12694      * rendering into an Roo.Editor, defaults to false)
12695      */
12696     /**
12697      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12698      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12699      */
12700     /**
12701      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12702      */
12703     /**
12704      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12705      * the dropdown list (defaults to undefined, with no header element)
12706      */
12707
12708      /**
12709      * @cfg {String/Roo.Template} tpl The template to use to render the output
12710      */
12711      
12712      /**
12713      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12714      */
12715     listWidth: undefined,
12716     /**
12717      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12718      * mode = 'remote' or 'text' if mode = 'local')
12719      */
12720     displayField: undefined,
12721     
12722     /**
12723      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12724      * mode = 'remote' or 'value' if mode = 'local'). 
12725      * Note: use of a valueField requires the user make a selection
12726      * in order for a value to be mapped.
12727      */
12728     valueField: undefined,
12729     /**
12730      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12731      */
12732     modalTitle : '',
12733     
12734     /**
12735      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12736      * field's data value (defaults to the underlying DOM element's name)
12737      */
12738     hiddenName: undefined,
12739     /**
12740      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12741      */
12742     listClass: '',
12743     /**
12744      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12745      */
12746     selectedClass: 'active',
12747     
12748     /**
12749      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12750      */
12751     shadow:'sides',
12752     /**
12753      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12754      * anchor positions (defaults to 'tl-bl')
12755      */
12756     listAlign: 'tl-bl?',
12757     /**
12758      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12759      */
12760     maxHeight: 300,
12761     /**
12762      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12763      * query specified by the allQuery config option (defaults to 'query')
12764      */
12765     triggerAction: 'query',
12766     /**
12767      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12768      * (defaults to 4, does not apply if editable = false)
12769      */
12770     minChars : 4,
12771     /**
12772      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12773      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12774      */
12775     typeAhead: false,
12776     /**
12777      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12778      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12779      */
12780     queryDelay: 500,
12781     /**
12782      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12783      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12784      */
12785     pageSize: 0,
12786     /**
12787      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12788      * when editable = true (defaults to false)
12789      */
12790     selectOnFocus:false,
12791     /**
12792      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12793      */
12794     queryParam: 'query',
12795     /**
12796      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12797      * when mode = 'remote' (defaults to 'Loading...')
12798      */
12799     loadingText: 'Loading...',
12800     /**
12801      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12802      */
12803     resizable: false,
12804     /**
12805      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12806      */
12807     handleHeight : 8,
12808     /**
12809      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12810      * traditional select (defaults to true)
12811      */
12812     editable: true,
12813     /**
12814      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12815      */
12816     allQuery: '',
12817     /**
12818      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12819      */
12820     mode: 'remote',
12821     /**
12822      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12823      * listWidth has a higher value)
12824      */
12825     minListWidth : 70,
12826     /**
12827      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12828      * allow the user to set arbitrary text into the field (defaults to false)
12829      */
12830     forceSelection:false,
12831     /**
12832      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12833      * if typeAhead = true (defaults to 250)
12834      */
12835     typeAheadDelay : 250,
12836     /**
12837      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12838      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12839      */
12840     valueNotFoundText : undefined,
12841     /**
12842      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12843      */
12844     blockFocus : false,
12845     
12846     /**
12847      * @cfg {Boolean} disableClear Disable showing of clear button.
12848      */
12849     disableClear : false,
12850     /**
12851      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12852      */
12853     alwaysQuery : false,
12854     
12855     /**
12856      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12857      */
12858     multiple : false,
12859     
12860     /**
12861      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12862      */
12863     invalidClass : "has-warning",
12864     
12865     /**
12866      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12867      */
12868     validClass : "has-success",
12869     
12870     /**
12871      * @cfg {Boolean} specialFilter (true|false) special filter default false
12872      */
12873     specialFilter : false,
12874     
12875     /**
12876      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12877      */
12878     mobileTouchView : true,
12879     
12880     /**
12881      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12882      */
12883     useNativeIOS : false,
12884     
12885     ios_options : false,
12886     
12887     //private
12888     addicon : false,
12889     editicon: false,
12890     
12891     page: 0,
12892     hasQuery: false,
12893     append: false,
12894     loadNext: false,
12895     autoFocus : true,
12896     tickable : false,
12897     btnPosition : 'right',
12898     triggerList : true,
12899     showToggleBtn : true,
12900     animate : true,
12901     emptyResultText: 'Empty',
12902     triggerText : 'Select',
12903     emptyTitle : '',
12904     
12905     // element that contains real text value.. (when hidden is used..)
12906     
12907     getAutoCreate : function()
12908     {   
12909         var cfg = false;
12910         //render
12911         /*
12912          * Render classic select for iso
12913          */
12914         
12915         if(Roo.isIOS && this.useNativeIOS){
12916             cfg = this.getAutoCreateNativeIOS();
12917             return cfg;
12918         }
12919         
12920         /*
12921          * Touch Devices
12922          */
12923         
12924         if(Roo.isTouch && this.mobileTouchView){
12925             cfg = this.getAutoCreateTouchView();
12926             return cfg;;
12927         }
12928         
12929         /*
12930          *  Normal ComboBox
12931          */
12932         if(!this.tickable){
12933             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12934             return cfg;
12935         }
12936         
12937         /*
12938          *  ComboBox with tickable selections
12939          */
12940              
12941         var align = this.labelAlign || this.parentLabelAlign();
12942         
12943         cfg = {
12944             cls : 'form-group roo-combobox-tickable' //input-group
12945         };
12946         
12947         var btn_text_select = '';
12948         var btn_text_done = '';
12949         var btn_text_cancel = '';
12950         
12951         if (this.btn_text_show) {
12952             btn_text_select = 'Select';
12953             btn_text_done = 'Done';
12954             btn_text_cancel = 'Cancel'; 
12955         }
12956         
12957         var buttons = {
12958             tag : 'div',
12959             cls : 'tickable-buttons',
12960             cn : [
12961                 {
12962                     tag : 'button',
12963                     type : 'button',
12964                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12965                     //html : this.triggerText
12966                     html: btn_text_select
12967                 },
12968                 {
12969                     tag : 'button',
12970                     type : 'button',
12971                     name : 'ok',
12972                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12973                     //html : 'Done'
12974                     html: btn_text_done
12975                 },
12976                 {
12977                     tag : 'button',
12978                     type : 'button',
12979                     name : 'cancel',
12980                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12981                     //html : 'Cancel'
12982                     html: btn_text_cancel
12983                 }
12984             ]
12985         };
12986         
12987         if(this.editable){
12988             buttons.cn.unshift({
12989                 tag: 'input',
12990                 cls: 'roo-select2-search-field-input'
12991             });
12992         }
12993         
12994         var _this = this;
12995         
12996         Roo.each(buttons.cn, function(c){
12997             if (_this.size) {
12998                 c.cls += ' btn-' + _this.size;
12999             }
13000
13001             if (_this.disabled) {
13002                 c.disabled = true;
13003             }
13004         });
13005         
13006         var box = {
13007             tag: 'div',
13008             cn: [
13009                 {
13010                     tag: 'input',
13011                     type : 'hidden',
13012                     cls: 'form-hidden-field'
13013                 },
13014                 {
13015                     tag: 'ul',
13016                     cls: 'roo-select2-choices',
13017                     cn:[
13018                         {
13019                             tag: 'li',
13020                             cls: 'roo-select2-search-field',
13021                             cn: [
13022                                 buttons
13023                             ]
13024                         }
13025                     ]
13026                 }
13027             ]
13028         };
13029         
13030         var combobox = {
13031             cls: 'roo-select2-container input-group roo-select2-container-multi',
13032             cn: [
13033                 box
13034 //                {
13035 //                    tag: 'ul',
13036 //                    cls: 'typeahead typeahead-long dropdown-menu',
13037 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13038 //                }
13039             ]
13040         };
13041         
13042         if(this.hasFeedback && !this.allowBlank){
13043             
13044             var feedback = {
13045                 tag: 'span',
13046                 cls: 'glyphicon form-control-feedback'
13047             };
13048
13049             combobox.cn.push(feedback);
13050         }
13051         
13052         
13053         if (align ==='left' && this.fieldLabel.length) {
13054             
13055             cfg.cls += ' roo-form-group-label-left';
13056             
13057             cfg.cn = [
13058                 {
13059                     tag : 'i',
13060                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13061                     tooltip : 'This field is required'
13062                 },
13063                 {
13064                     tag: 'label',
13065                     'for' :  id,
13066                     cls : 'control-label',
13067                     html : this.fieldLabel
13068
13069                 },
13070                 {
13071                     cls : "", 
13072                     cn: [
13073                         combobox
13074                     ]
13075                 }
13076
13077             ];
13078             
13079             var labelCfg = cfg.cn[1];
13080             var contentCfg = cfg.cn[2];
13081             
13082
13083             if(this.indicatorpos == 'right'){
13084                 
13085                 cfg.cn = [
13086                     {
13087                         tag: 'label',
13088                         'for' :  id,
13089                         cls : 'control-label',
13090                         cn : [
13091                             {
13092                                 tag : 'span',
13093                                 html : this.fieldLabel
13094                             },
13095                             {
13096                                 tag : 'i',
13097                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13098                                 tooltip : 'This field is required'
13099                             }
13100                         ]
13101                     },
13102                     {
13103                         cls : "",
13104                         cn: [
13105                             combobox
13106                         ]
13107                     }
13108
13109                 ];
13110                 
13111                 
13112                 
13113                 labelCfg = cfg.cn[0];
13114                 contentCfg = cfg.cn[1];
13115             
13116             }
13117             
13118             if(this.labelWidth > 12){
13119                 labelCfg.style = "width: " + this.labelWidth + 'px';
13120             }
13121             
13122             if(this.labelWidth < 13 && this.labelmd == 0){
13123                 this.labelmd = this.labelWidth;
13124             }
13125             
13126             if(this.labellg > 0){
13127                 labelCfg.cls += ' col-lg-' + this.labellg;
13128                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13129             }
13130             
13131             if(this.labelmd > 0){
13132                 labelCfg.cls += ' col-md-' + this.labelmd;
13133                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13134             }
13135             
13136             if(this.labelsm > 0){
13137                 labelCfg.cls += ' col-sm-' + this.labelsm;
13138                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13139             }
13140             
13141             if(this.labelxs > 0){
13142                 labelCfg.cls += ' col-xs-' + this.labelxs;
13143                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13144             }
13145                 
13146                 
13147         } else if ( this.fieldLabel.length) {
13148 //                Roo.log(" label");
13149                  cfg.cn = [
13150                     {
13151                         tag : 'i',
13152                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13153                         tooltip : 'This field is required'
13154                     },
13155                     {
13156                         tag: 'label',
13157                         //cls : 'input-group-addon',
13158                         html : this.fieldLabel
13159                     },
13160                     combobox
13161                 ];
13162                 
13163                 if(this.indicatorpos == 'right'){
13164                     cfg.cn = [
13165                         {
13166                             tag: 'label',
13167                             //cls : 'input-group-addon',
13168                             html : this.fieldLabel
13169                         },
13170                         {
13171                             tag : 'i',
13172                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13173                             tooltip : 'This field is required'
13174                         },
13175                         combobox
13176                     ];
13177                     
13178                 }
13179
13180         } else {
13181             
13182 //                Roo.log(" no label && no align");
13183                 cfg = combobox
13184                      
13185                 
13186         }
13187          
13188         var settings=this;
13189         ['xs','sm','md','lg'].map(function(size){
13190             if (settings[size]) {
13191                 cfg.cls += ' col-' + size + '-' + settings[size];
13192             }
13193         });
13194         
13195         return cfg;
13196         
13197     },
13198     
13199     _initEventsCalled : false,
13200     
13201     // private
13202     initEvents: function()
13203     {   
13204         if (this._initEventsCalled) { // as we call render... prevent looping...
13205             return;
13206         }
13207         this._initEventsCalled = true;
13208         
13209         if (!this.store) {
13210             throw "can not find store for combo";
13211         }
13212         
13213         this.indicator = this.indicatorEl();
13214         
13215         this.store = Roo.factory(this.store, Roo.data);
13216         this.store.parent = this;
13217         
13218         // if we are building from html. then this element is so complex, that we can not really
13219         // use the rendered HTML.
13220         // so we have to trash and replace the previous code.
13221         if (Roo.XComponent.build_from_html) {
13222             // remove this element....
13223             var e = this.el.dom, k=0;
13224             while (e ) { e = e.previousSibling;  ++k;}
13225
13226             this.el.remove();
13227             
13228             this.el=false;
13229             this.rendered = false;
13230             
13231             this.render(this.parent().getChildContainer(true), k);
13232         }
13233         
13234         if(Roo.isIOS && this.useNativeIOS){
13235             this.initIOSView();
13236             return;
13237         }
13238         
13239         /*
13240          * Touch Devices
13241          */
13242         
13243         if(Roo.isTouch && this.mobileTouchView){
13244             this.initTouchView();
13245             return;
13246         }
13247         
13248         if(this.tickable){
13249             this.initTickableEvents();
13250             return;
13251         }
13252         
13253         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13254         
13255         if(this.hiddenName){
13256             
13257             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13258             
13259             this.hiddenField.dom.value =
13260                 this.hiddenValue !== undefined ? this.hiddenValue :
13261                 this.value !== undefined ? this.value : '';
13262
13263             // prevent input submission
13264             this.el.dom.removeAttribute('name');
13265             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13266              
13267              
13268         }
13269         //if(Roo.isGecko){
13270         //    this.el.dom.setAttribute('autocomplete', 'off');
13271         //}
13272         
13273         var cls = 'x-combo-list';
13274         
13275         //this.list = new Roo.Layer({
13276         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13277         //});
13278         
13279         var _this = this;
13280         
13281         (function(){
13282             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13283             _this.list.setWidth(lw);
13284         }).defer(100);
13285         
13286         this.list.on('mouseover', this.onViewOver, this);
13287         this.list.on('mousemove', this.onViewMove, this);
13288         this.list.on('scroll', this.onViewScroll, this);
13289         
13290         /*
13291         this.list.swallowEvent('mousewheel');
13292         this.assetHeight = 0;
13293
13294         if(this.title){
13295             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13296             this.assetHeight += this.header.getHeight();
13297         }
13298
13299         this.innerList = this.list.createChild({cls:cls+'-inner'});
13300         this.innerList.on('mouseover', this.onViewOver, this);
13301         this.innerList.on('mousemove', this.onViewMove, this);
13302         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13303         
13304         if(this.allowBlank && !this.pageSize && !this.disableClear){
13305             this.footer = this.list.createChild({cls:cls+'-ft'});
13306             this.pageTb = new Roo.Toolbar(this.footer);
13307            
13308         }
13309         if(this.pageSize){
13310             this.footer = this.list.createChild({cls:cls+'-ft'});
13311             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13312                     {pageSize: this.pageSize});
13313             
13314         }
13315         
13316         if (this.pageTb && this.allowBlank && !this.disableClear) {
13317             var _this = this;
13318             this.pageTb.add(new Roo.Toolbar.Fill(), {
13319                 cls: 'x-btn-icon x-btn-clear',
13320                 text: '&#160;',
13321                 handler: function()
13322                 {
13323                     _this.collapse();
13324                     _this.clearValue();
13325                     _this.onSelect(false, -1);
13326                 }
13327             });
13328         }
13329         if (this.footer) {
13330             this.assetHeight += this.footer.getHeight();
13331         }
13332         */
13333             
13334         if(!this.tpl){
13335             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13336         }
13337
13338         this.view = new Roo.View(this.list, this.tpl, {
13339             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13340         });
13341         //this.view.wrapEl.setDisplayed(false);
13342         this.view.on('click', this.onViewClick, this);
13343         
13344         
13345         this.store.on('beforeload', this.onBeforeLoad, this);
13346         this.store.on('load', this.onLoad, this);
13347         this.store.on('loadexception', this.onLoadException, this);
13348         /*
13349         if(this.resizable){
13350             this.resizer = new Roo.Resizable(this.list,  {
13351                pinned:true, handles:'se'
13352             });
13353             this.resizer.on('resize', function(r, w, h){
13354                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13355                 this.listWidth = w;
13356                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13357                 this.restrictHeight();
13358             }, this);
13359             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13360         }
13361         */
13362         if(!this.editable){
13363             this.editable = true;
13364             this.setEditable(false);
13365         }
13366         
13367         /*
13368         
13369         if (typeof(this.events.add.listeners) != 'undefined') {
13370             
13371             this.addicon = this.wrap.createChild(
13372                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13373        
13374             this.addicon.on('click', function(e) {
13375                 this.fireEvent('add', this);
13376             }, this);
13377         }
13378         if (typeof(this.events.edit.listeners) != 'undefined') {
13379             
13380             this.editicon = this.wrap.createChild(
13381                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13382             if (this.addicon) {
13383                 this.editicon.setStyle('margin-left', '40px');
13384             }
13385             this.editicon.on('click', function(e) {
13386                 
13387                 // we fire even  if inothing is selected..
13388                 this.fireEvent('edit', this, this.lastData );
13389                 
13390             }, this);
13391         }
13392         */
13393         
13394         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13395             "up" : function(e){
13396                 this.inKeyMode = true;
13397                 this.selectPrev();
13398             },
13399
13400             "down" : function(e){
13401                 if(!this.isExpanded()){
13402                     this.onTriggerClick();
13403                 }else{
13404                     this.inKeyMode = true;
13405                     this.selectNext();
13406                 }
13407             },
13408
13409             "enter" : function(e){
13410 //                this.onViewClick();
13411                 //return true;
13412                 this.collapse();
13413                 
13414                 if(this.fireEvent("specialkey", this, e)){
13415                     this.onViewClick(false);
13416                 }
13417                 
13418                 return true;
13419             },
13420
13421             "esc" : function(e){
13422                 this.collapse();
13423             },
13424
13425             "tab" : function(e){
13426                 this.collapse();
13427                 
13428                 if(this.fireEvent("specialkey", this, e)){
13429                     this.onViewClick(false);
13430                 }
13431                 
13432                 return true;
13433             },
13434
13435             scope : this,
13436
13437             doRelay : function(foo, bar, hname){
13438                 if(hname == 'down' || this.scope.isExpanded()){
13439                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13440                 }
13441                 return true;
13442             },
13443
13444             forceKeyDown: true
13445         });
13446         
13447         
13448         this.queryDelay = Math.max(this.queryDelay || 10,
13449                 this.mode == 'local' ? 10 : 250);
13450         
13451         
13452         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13453         
13454         if(this.typeAhead){
13455             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13456         }
13457         if(this.editable !== false){
13458             this.inputEl().on("keyup", this.onKeyUp, this);
13459         }
13460         if(this.forceSelection){
13461             this.inputEl().on('blur', this.doForce, this);
13462         }
13463         
13464         if(this.multiple){
13465             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13466             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13467         }
13468     },
13469     
13470     initTickableEvents: function()
13471     {   
13472         this.createList();
13473         
13474         if(this.hiddenName){
13475             
13476             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13477             
13478             this.hiddenField.dom.value =
13479                 this.hiddenValue !== undefined ? this.hiddenValue :
13480                 this.value !== undefined ? this.value : '';
13481
13482             // prevent input submission
13483             this.el.dom.removeAttribute('name');
13484             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13485              
13486              
13487         }
13488         
13489 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13490         
13491         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13492         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13493         if(this.triggerList){
13494             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13495         }
13496          
13497         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13498         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13499         
13500         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13501         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13502         
13503         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13504         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13505         
13506         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13507         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13508         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13509         
13510         this.okBtn.hide();
13511         this.cancelBtn.hide();
13512         
13513         var _this = this;
13514         
13515         (function(){
13516             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13517             _this.list.setWidth(lw);
13518         }).defer(100);
13519         
13520         this.list.on('mouseover', this.onViewOver, this);
13521         this.list.on('mousemove', this.onViewMove, this);
13522         
13523         this.list.on('scroll', this.onViewScroll, this);
13524         
13525         if(!this.tpl){
13526             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>';
13527         }
13528
13529         this.view = new Roo.View(this.list, this.tpl, {
13530             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13531         });
13532         
13533         //this.view.wrapEl.setDisplayed(false);
13534         this.view.on('click', this.onViewClick, this);
13535         
13536         
13537         
13538         this.store.on('beforeload', this.onBeforeLoad, this);
13539         this.store.on('load', this.onLoad, this);
13540         this.store.on('loadexception', this.onLoadException, this);
13541         
13542         if(this.editable){
13543             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13544                 "up" : function(e){
13545                     this.inKeyMode = true;
13546                     this.selectPrev();
13547                 },
13548
13549                 "down" : function(e){
13550                     this.inKeyMode = true;
13551                     this.selectNext();
13552                 },
13553
13554                 "enter" : function(e){
13555                     if(this.fireEvent("specialkey", this, e)){
13556                         this.onViewClick(false);
13557                     }
13558                     
13559                     return true;
13560                 },
13561
13562                 "esc" : function(e){
13563                     this.onTickableFooterButtonClick(e, false, false);
13564                 },
13565
13566                 "tab" : function(e){
13567                     this.fireEvent("specialkey", this, e);
13568                     
13569                     this.onTickableFooterButtonClick(e, false, false);
13570                     
13571                     return true;
13572                 },
13573
13574                 scope : this,
13575
13576                 doRelay : function(e, fn, key){
13577                     if(this.scope.isExpanded()){
13578                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13579                     }
13580                     return true;
13581                 },
13582
13583                 forceKeyDown: true
13584             });
13585         }
13586         
13587         this.queryDelay = Math.max(this.queryDelay || 10,
13588                 this.mode == 'local' ? 10 : 250);
13589         
13590         
13591         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13592         
13593         if(this.typeAhead){
13594             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13595         }
13596         
13597         if(this.editable !== false){
13598             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13599         }
13600         
13601         this.indicator = this.indicatorEl();
13602         
13603         if(this.indicator){
13604             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13605             this.indicator.hide();
13606         }
13607         
13608     },
13609
13610     onDestroy : function(){
13611         if(this.view){
13612             this.view.setStore(null);
13613             this.view.el.removeAllListeners();
13614             this.view.el.remove();
13615             this.view.purgeListeners();
13616         }
13617         if(this.list){
13618             this.list.dom.innerHTML  = '';
13619         }
13620         
13621         if(this.store){
13622             this.store.un('beforeload', this.onBeforeLoad, this);
13623             this.store.un('load', this.onLoad, this);
13624             this.store.un('loadexception', this.onLoadException, this);
13625         }
13626         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13627     },
13628
13629     // private
13630     fireKey : function(e){
13631         if(e.isNavKeyPress() && !this.list.isVisible()){
13632             this.fireEvent("specialkey", this, e);
13633         }
13634     },
13635
13636     // private
13637     onResize: function(w, h){
13638 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13639 //        
13640 //        if(typeof w != 'number'){
13641 //            // we do not handle it!?!?
13642 //            return;
13643 //        }
13644 //        var tw = this.trigger.getWidth();
13645 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13646 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13647 //        var x = w - tw;
13648 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13649 //            
13650 //        //this.trigger.setStyle('left', x+'px');
13651 //        
13652 //        if(this.list && this.listWidth === undefined){
13653 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13654 //            this.list.setWidth(lw);
13655 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13656 //        }
13657         
13658     
13659         
13660     },
13661
13662     /**
13663      * Allow or prevent the user from directly editing the field text.  If false is passed,
13664      * the user will only be able to select from the items defined in the dropdown list.  This method
13665      * is the runtime equivalent of setting the 'editable' config option at config time.
13666      * @param {Boolean} value True to allow the user to directly edit the field text
13667      */
13668     setEditable : function(value){
13669         if(value == this.editable){
13670             return;
13671         }
13672         this.editable = value;
13673         if(!value){
13674             this.inputEl().dom.setAttribute('readOnly', true);
13675             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13676             this.inputEl().addClass('x-combo-noedit');
13677         }else{
13678             this.inputEl().dom.setAttribute('readOnly', false);
13679             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13680             this.inputEl().removeClass('x-combo-noedit');
13681         }
13682     },
13683
13684     // private
13685     
13686     onBeforeLoad : function(combo,opts){
13687         if(!this.hasFocus){
13688             return;
13689         }
13690          if (!opts.add) {
13691             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13692          }
13693         this.restrictHeight();
13694         this.selectedIndex = -1;
13695     },
13696
13697     // private
13698     onLoad : function(){
13699         
13700         this.hasQuery = false;
13701         
13702         if(!this.hasFocus){
13703             return;
13704         }
13705         
13706         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13707             this.loading.hide();
13708         }
13709         
13710         if(this.store.getCount() > 0){
13711             
13712             this.expand();
13713             this.restrictHeight();
13714             if(this.lastQuery == this.allQuery){
13715                 if(this.editable && !this.tickable){
13716                     this.inputEl().dom.select();
13717                 }
13718                 
13719                 if(
13720                     !this.selectByValue(this.value, true) &&
13721                     this.autoFocus && 
13722                     (
13723                         !this.store.lastOptions ||
13724                         typeof(this.store.lastOptions.add) == 'undefined' || 
13725                         this.store.lastOptions.add != true
13726                     )
13727                 ){
13728                     this.select(0, true);
13729                 }
13730             }else{
13731                 if(this.autoFocus){
13732                     this.selectNext();
13733                 }
13734                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13735                     this.taTask.delay(this.typeAheadDelay);
13736                 }
13737             }
13738         }else{
13739             this.onEmptyResults();
13740         }
13741         
13742         //this.el.focus();
13743     },
13744     // private
13745     onLoadException : function()
13746     {
13747         this.hasQuery = false;
13748         
13749         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13750             this.loading.hide();
13751         }
13752         
13753         if(this.tickable && this.editable){
13754             return;
13755         }
13756         
13757         this.collapse();
13758         // only causes errors at present
13759         //Roo.log(this.store.reader.jsonData);
13760         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13761             // fixme
13762             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13763         //}
13764         
13765         
13766     },
13767     // private
13768     onTypeAhead : function(){
13769         if(this.store.getCount() > 0){
13770             var r = this.store.getAt(0);
13771             var newValue = r.data[this.displayField];
13772             var len = newValue.length;
13773             var selStart = this.getRawValue().length;
13774             
13775             if(selStart != len){
13776                 this.setRawValue(newValue);
13777                 this.selectText(selStart, newValue.length);
13778             }
13779         }
13780     },
13781
13782     // private
13783     onSelect : function(record, index){
13784         
13785         if(this.fireEvent('beforeselect', this, record, index) !== false){
13786         
13787             this.setFromData(index > -1 ? record.data : false);
13788             
13789             this.collapse();
13790             this.fireEvent('select', this, record, index);
13791         }
13792     },
13793
13794     /**
13795      * Returns the currently selected field value or empty string if no value is set.
13796      * @return {String} value The selected value
13797      */
13798     getValue : function()
13799     {
13800         if(Roo.isIOS && this.useNativeIOS){
13801             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13802         }
13803         
13804         if(this.multiple){
13805             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13806         }
13807         
13808         if(this.valueField){
13809             return typeof this.value != 'undefined' ? this.value : '';
13810         }else{
13811             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13812         }
13813     },
13814     
13815     getRawValue : function()
13816     {
13817         if(Roo.isIOS && this.useNativeIOS){
13818             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13819         }
13820         
13821         var v = this.inputEl().getValue();
13822         
13823         return v;
13824     },
13825
13826     /**
13827      * Clears any text/value currently set in the field
13828      */
13829     clearValue : function(){
13830         
13831         if(this.hiddenField){
13832             this.hiddenField.dom.value = '';
13833         }
13834         this.value = '';
13835         this.setRawValue('');
13836         this.lastSelectionText = '';
13837         this.lastData = false;
13838         
13839         var close = this.closeTriggerEl();
13840         
13841         if(close){
13842             close.hide();
13843         }
13844         
13845         this.validate();
13846         
13847     },
13848
13849     /**
13850      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13851      * will be displayed in the field.  If the value does not match the data value of an existing item,
13852      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13853      * Otherwise the field will be blank (although the value will still be set).
13854      * @param {String} value The value to match
13855      */
13856     setValue : function(v)
13857     {
13858         if(Roo.isIOS && this.useNativeIOS){
13859             this.setIOSValue(v);
13860             return;
13861         }
13862         
13863         if(this.multiple){
13864             this.syncValue();
13865             return;
13866         }
13867         
13868         var text = v;
13869         if(this.valueField){
13870             var r = this.findRecord(this.valueField, v);
13871             if(r){
13872                 text = r.data[this.displayField];
13873             }else if(this.valueNotFoundText !== undefined){
13874                 text = this.valueNotFoundText;
13875             }
13876         }
13877         this.lastSelectionText = text;
13878         if(this.hiddenField){
13879             this.hiddenField.dom.value = v;
13880         }
13881         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13882         this.value = v;
13883         
13884         var close = this.closeTriggerEl();
13885         
13886         if(close){
13887             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13888         }
13889         
13890         this.validate();
13891     },
13892     /**
13893      * @property {Object} the last set data for the element
13894      */
13895     
13896     lastData : false,
13897     /**
13898      * Sets the value of the field based on a object which is related to the record format for the store.
13899      * @param {Object} value the value to set as. or false on reset?
13900      */
13901     setFromData : function(o){
13902         
13903         if(this.multiple){
13904             this.addItem(o);
13905             return;
13906         }
13907             
13908         var dv = ''; // display value
13909         var vv = ''; // value value..
13910         this.lastData = o;
13911         if (this.displayField) {
13912             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13913         } else {
13914             // this is an error condition!!!
13915             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13916         }
13917         
13918         if(this.valueField){
13919             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13920         }
13921         
13922         var close = this.closeTriggerEl();
13923         
13924         if(close){
13925             if(dv.length || vv * 1 > 0){
13926                 close.show() ;
13927                 this.blockFocus=true;
13928             } else {
13929                 close.hide();
13930             }             
13931         }
13932         
13933         if(this.hiddenField){
13934             this.hiddenField.dom.value = vv;
13935             
13936             this.lastSelectionText = dv;
13937             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13938             this.value = vv;
13939             return;
13940         }
13941         // no hidden field.. - we store the value in 'value', but still display
13942         // display field!!!!
13943         this.lastSelectionText = dv;
13944         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13945         this.value = vv;
13946         
13947         
13948         
13949     },
13950     // private
13951     reset : function(){
13952         // overridden so that last data is reset..
13953         
13954         if(this.multiple){
13955             this.clearItem();
13956             return;
13957         }
13958         
13959         this.setValue(this.originalValue);
13960         //this.clearInvalid();
13961         this.lastData = false;
13962         if (this.view) {
13963             this.view.clearSelections();
13964         }
13965         
13966         this.validate();
13967     },
13968     // private
13969     findRecord : function(prop, value){
13970         var record;
13971         if(this.store.getCount() > 0){
13972             this.store.each(function(r){
13973                 if(r.data[prop] == value){
13974                     record = r;
13975                     return false;
13976                 }
13977                 return true;
13978             });
13979         }
13980         return record;
13981     },
13982     
13983     getName: function()
13984     {
13985         // returns hidden if it's set..
13986         if (!this.rendered) {return ''};
13987         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13988         
13989     },
13990     // private
13991     onViewMove : function(e, t){
13992         this.inKeyMode = false;
13993     },
13994
13995     // private
13996     onViewOver : function(e, t){
13997         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13998             return;
13999         }
14000         var item = this.view.findItemFromChild(t);
14001         
14002         if(item){
14003             var index = this.view.indexOf(item);
14004             this.select(index, false);
14005         }
14006     },
14007
14008     // private
14009     onViewClick : function(view, doFocus, el, e)
14010     {
14011         var index = this.view.getSelectedIndexes()[0];
14012         
14013         var r = this.store.getAt(index);
14014         
14015         if(this.tickable){
14016             
14017             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14018                 return;
14019             }
14020             
14021             var rm = false;
14022             var _this = this;
14023             
14024             Roo.each(this.tickItems, function(v,k){
14025                 
14026                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14027                     Roo.log(v);
14028                     _this.tickItems.splice(k, 1);
14029                     
14030                     if(typeof(e) == 'undefined' && view == false){
14031                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14032                     }
14033                     
14034                     rm = true;
14035                     return;
14036                 }
14037             });
14038             
14039             if(rm){
14040                 return;
14041             }
14042             
14043             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14044                 this.tickItems.push(r.data);
14045             }
14046             
14047             if(typeof(e) == 'undefined' && view == false){
14048                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14049             }
14050                     
14051             return;
14052         }
14053         
14054         if(r){
14055             this.onSelect(r, index);
14056         }
14057         if(doFocus !== false && !this.blockFocus){
14058             this.inputEl().focus();
14059         }
14060     },
14061
14062     // private
14063     restrictHeight : function(){
14064         //this.innerList.dom.style.height = '';
14065         //var inner = this.innerList.dom;
14066         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14067         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14068         //this.list.beginUpdate();
14069         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14070         this.list.alignTo(this.inputEl(), this.listAlign);
14071         this.list.alignTo(this.inputEl(), this.listAlign);
14072         //this.list.endUpdate();
14073     },
14074
14075     // private
14076     onEmptyResults : function(){
14077         
14078         if(this.tickable && this.editable){
14079             this.hasFocus = false;
14080             this.restrictHeight();
14081             return;
14082         }
14083         
14084         this.collapse();
14085     },
14086
14087     /**
14088      * Returns true if the dropdown list is expanded, else false.
14089      */
14090     isExpanded : function(){
14091         return this.list.isVisible();
14092     },
14093
14094     /**
14095      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14096      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14097      * @param {String} value The data value of the item to select
14098      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14099      * selected item if it is not currently in view (defaults to true)
14100      * @return {Boolean} True if the value matched an item in the list, else false
14101      */
14102     selectByValue : function(v, scrollIntoView){
14103         if(v !== undefined && v !== null){
14104             var r = this.findRecord(this.valueField || this.displayField, v);
14105             if(r){
14106                 this.select(this.store.indexOf(r), scrollIntoView);
14107                 return true;
14108             }
14109         }
14110         return false;
14111     },
14112
14113     /**
14114      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14115      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14116      * @param {Number} index The zero-based index of the list item to select
14117      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14118      * selected item if it is not currently in view (defaults to true)
14119      */
14120     select : function(index, scrollIntoView){
14121         this.selectedIndex = index;
14122         this.view.select(index);
14123         if(scrollIntoView !== false){
14124             var el = this.view.getNode(index);
14125             /*
14126              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14127              */
14128             if(el){
14129                 this.list.scrollChildIntoView(el, false);
14130             }
14131         }
14132     },
14133
14134     // private
14135     selectNext : function(){
14136         var ct = this.store.getCount();
14137         if(ct > 0){
14138             if(this.selectedIndex == -1){
14139                 this.select(0);
14140             }else if(this.selectedIndex < ct-1){
14141                 this.select(this.selectedIndex+1);
14142             }
14143         }
14144     },
14145
14146     // private
14147     selectPrev : function(){
14148         var ct = this.store.getCount();
14149         if(ct > 0){
14150             if(this.selectedIndex == -1){
14151                 this.select(0);
14152             }else if(this.selectedIndex != 0){
14153                 this.select(this.selectedIndex-1);
14154             }
14155         }
14156     },
14157
14158     // private
14159     onKeyUp : function(e){
14160         if(this.editable !== false && !e.isSpecialKey()){
14161             this.lastKey = e.getKey();
14162             this.dqTask.delay(this.queryDelay);
14163         }
14164     },
14165
14166     // private
14167     validateBlur : function(){
14168         return !this.list || !this.list.isVisible();   
14169     },
14170
14171     // private
14172     initQuery : function(){
14173         
14174         var v = this.getRawValue();
14175         
14176         if(this.tickable && this.editable){
14177             v = this.tickableInputEl().getValue();
14178         }
14179         
14180         this.doQuery(v);
14181     },
14182
14183     // private
14184     doForce : function(){
14185         if(this.inputEl().dom.value.length > 0){
14186             this.inputEl().dom.value =
14187                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14188              
14189         }
14190     },
14191
14192     /**
14193      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14194      * query allowing the query action to be canceled if needed.
14195      * @param {String} query The SQL query to execute
14196      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14197      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14198      * saved in the current store (defaults to false)
14199      */
14200     doQuery : function(q, forceAll){
14201         
14202         if(q === undefined || q === null){
14203             q = '';
14204         }
14205         var qe = {
14206             query: q,
14207             forceAll: forceAll,
14208             combo: this,
14209             cancel:false
14210         };
14211         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14212             return false;
14213         }
14214         q = qe.query;
14215         
14216         forceAll = qe.forceAll;
14217         if(forceAll === true || (q.length >= this.minChars)){
14218             
14219             this.hasQuery = true;
14220             
14221             if(this.lastQuery != q || this.alwaysQuery){
14222                 this.lastQuery = q;
14223                 if(this.mode == 'local'){
14224                     this.selectedIndex = -1;
14225                     if(forceAll){
14226                         this.store.clearFilter();
14227                     }else{
14228                         
14229                         if(this.specialFilter){
14230                             this.fireEvent('specialfilter', this);
14231                             this.onLoad();
14232                             return;
14233                         }
14234                         
14235                         this.store.filter(this.displayField, q);
14236                     }
14237                     
14238                     this.store.fireEvent("datachanged", this.store);
14239                     
14240                     this.onLoad();
14241                     
14242                     
14243                 }else{
14244                     
14245                     this.store.baseParams[this.queryParam] = q;
14246                     
14247                     var options = {params : this.getParams(q)};
14248                     
14249                     if(this.loadNext){
14250                         options.add = true;
14251                         options.params.start = this.page * this.pageSize;
14252                     }
14253                     
14254                     this.store.load(options);
14255                     
14256                     /*
14257                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14258                      *  we should expand the list on onLoad
14259                      *  so command out it
14260                      */
14261 //                    this.expand();
14262                 }
14263             }else{
14264                 this.selectedIndex = -1;
14265                 this.onLoad();   
14266             }
14267         }
14268         
14269         this.loadNext = false;
14270     },
14271     
14272     // private
14273     getParams : function(q){
14274         var p = {};
14275         //p[this.queryParam] = q;
14276         
14277         if(this.pageSize){
14278             p.start = 0;
14279             p.limit = this.pageSize;
14280         }
14281         return p;
14282     },
14283
14284     /**
14285      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14286      */
14287     collapse : function(){
14288         if(!this.isExpanded()){
14289             return;
14290         }
14291         
14292         this.list.hide();
14293         
14294         this.hasFocus = false;
14295         
14296         if(this.tickable){
14297             this.okBtn.hide();
14298             this.cancelBtn.hide();
14299             this.trigger.show();
14300             
14301             if(this.editable){
14302                 this.tickableInputEl().dom.value = '';
14303                 this.tickableInputEl().blur();
14304             }
14305             
14306         }
14307         
14308         Roo.get(document).un('mousedown', this.collapseIf, this);
14309         Roo.get(document).un('mousewheel', this.collapseIf, this);
14310         if (!this.editable) {
14311             Roo.get(document).un('keydown', this.listKeyPress, this);
14312         }
14313         this.fireEvent('collapse', this);
14314         
14315         this.validate();
14316     },
14317
14318     // private
14319     collapseIf : function(e){
14320         var in_combo  = e.within(this.el);
14321         var in_list =  e.within(this.list);
14322         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14323         
14324         if (in_combo || in_list || is_list) {
14325             //e.stopPropagation();
14326             return;
14327         }
14328         
14329         if(this.tickable){
14330             this.onTickableFooterButtonClick(e, false, false);
14331         }
14332
14333         this.collapse();
14334         
14335     },
14336
14337     /**
14338      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14339      */
14340     expand : function(){
14341        
14342         if(this.isExpanded() || !this.hasFocus){
14343             return;
14344         }
14345         
14346         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14347         this.list.setWidth(lw);
14348         
14349         Roo.log('expand');
14350         
14351         this.list.show();
14352         
14353         this.restrictHeight();
14354         
14355         if(this.tickable){
14356             
14357             this.tickItems = Roo.apply([], this.item);
14358             
14359             this.okBtn.show();
14360             this.cancelBtn.show();
14361             this.trigger.hide();
14362             
14363             if(this.editable){
14364                 this.tickableInputEl().focus();
14365             }
14366             
14367         }
14368         
14369         Roo.get(document).on('mousedown', this.collapseIf, this);
14370         Roo.get(document).on('mousewheel', this.collapseIf, this);
14371         if (!this.editable) {
14372             Roo.get(document).on('keydown', this.listKeyPress, this);
14373         }
14374         
14375         this.fireEvent('expand', this);
14376     },
14377
14378     // private
14379     // Implements the default empty TriggerField.onTriggerClick function
14380     onTriggerClick : function(e)
14381     {
14382         Roo.log('trigger click');
14383         
14384         if(this.disabled || !this.triggerList){
14385             return;
14386         }
14387         
14388         this.page = 0;
14389         this.loadNext = false;
14390         
14391         if(this.isExpanded()){
14392             this.collapse();
14393             if (!this.blockFocus) {
14394                 this.inputEl().focus();
14395             }
14396             
14397         }else {
14398             this.hasFocus = true;
14399             if(this.triggerAction == 'all') {
14400                 this.doQuery(this.allQuery, true);
14401             } else {
14402                 this.doQuery(this.getRawValue());
14403             }
14404             if (!this.blockFocus) {
14405                 this.inputEl().focus();
14406             }
14407         }
14408     },
14409     
14410     onTickableTriggerClick : function(e)
14411     {
14412         if(this.disabled){
14413             return;
14414         }
14415         
14416         this.page = 0;
14417         this.loadNext = false;
14418         this.hasFocus = true;
14419         
14420         if(this.triggerAction == 'all') {
14421             this.doQuery(this.allQuery, true);
14422         } else {
14423             this.doQuery(this.getRawValue());
14424         }
14425     },
14426     
14427     onSearchFieldClick : function(e)
14428     {
14429         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14430             this.onTickableFooterButtonClick(e, false, false);
14431             return;
14432         }
14433         
14434         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14435             return;
14436         }
14437         
14438         this.page = 0;
14439         this.loadNext = false;
14440         this.hasFocus = true;
14441         
14442         if(this.triggerAction == 'all') {
14443             this.doQuery(this.allQuery, true);
14444         } else {
14445             this.doQuery(this.getRawValue());
14446         }
14447     },
14448     
14449     listKeyPress : function(e)
14450     {
14451         //Roo.log('listkeypress');
14452         // scroll to first matching element based on key pres..
14453         if (e.isSpecialKey()) {
14454             return false;
14455         }
14456         var k = String.fromCharCode(e.getKey()).toUpperCase();
14457         //Roo.log(k);
14458         var match  = false;
14459         var csel = this.view.getSelectedNodes();
14460         var cselitem = false;
14461         if (csel.length) {
14462             var ix = this.view.indexOf(csel[0]);
14463             cselitem  = this.store.getAt(ix);
14464             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14465                 cselitem = false;
14466             }
14467             
14468         }
14469         
14470         this.store.each(function(v) { 
14471             if (cselitem) {
14472                 // start at existing selection.
14473                 if (cselitem.id == v.id) {
14474                     cselitem = false;
14475                 }
14476                 return true;
14477             }
14478                 
14479             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14480                 match = this.store.indexOf(v);
14481                 return false;
14482             }
14483             return true;
14484         }, this);
14485         
14486         if (match === false) {
14487             return true; // no more action?
14488         }
14489         // scroll to?
14490         this.view.select(match);
14491         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14492         sn.scrollIntoView(sn.dom.parentNode, false);
14493     },
14494     
14495     onViewScroll : function(e, t){
14496         
14497         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){
14498             return;
14499         }
14500         
14501         this.hasQuery = true;
14502         
14503         this.loading = this.list.select('.loading', true).first();
14504         
14505         if(this.loading === null){
14506             this.list.createChild({
14507                 tag: 'div',
14508                 cls: 'loading roo-select2-more-results roo-select2-active',
14509                 html: 'Loading more results...'
14510             });
14511             
14512             this.loading = this.list.select('.loading', true).first();
14513             
14514             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14515             
14516             this.loading.hide();
14517         }
14518         
14519         this.loading.show();
14520         
14521         var _combo = this;
14522         
14523         this.page++;
14524         this.loadNext = true;
14525         
14526         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14527         
14528         return;
14529     },
14530     
14531     addItem : function(o)
14532     {   
14533         var dv = ''; // display value
14534         
14535         if (this.displayField) {
14536             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14537         } else {
14538             // this is an error condition!!!
14539             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14540         }
14541         
14542         if(!dv.length){
14543             return;
14544         }
14545         
14546         var choice = this.choices.createChild({
14547             tag: 'li',
14548             cls: 'roo-select2-search-choice',
14549             cn: [
14550                 {
14551                     tag: 'div',
14552                     html: dv
14553                 },
14554                 {
14555                     tag: 'a',
14556                     href: '#',
14557                     cls: 'roo-select2-search-choice-close fa fa-times',
14558                     tabindex: '-1'
14559                 }
14560             ]
14561             
14562         }, this.searchField);
14563         
14564         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14565         
14566         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14567         
14568         this.item.push(o);
14569         
14570         this.lastData = o;
14571         
14572         this.syncValue();
14573         
14574         this.inputEl().dom.value = '';
14575         
14576         this.validate();
14577     },
14578     
14579     onRemoveItem : function(e, _self, o)
14580     {
14581         e.preventDefault();
14582         
14583         this.lastItem = Roo.apply([], this.item);
14584         
14585         var index = this.item.indexOf(o.data) * 1;
14586         
14587         if( index < 0){
14588             Roo.log('not this item?!');
14589             return;
14590         }
14591         
14592         this.item.splice(index, 1);
14593         o.item.remove();
14594         
14595         this.syncValue();
14596         
14597         this.fireEvent('remove', this, e);
14598         
14599         this.validate();
14600         
14601     },
14602     
14603     syncValue : function()
14604     {
14605         if(!this.item.length){
14606             this.clearValue();
14607             return;
14608         }
14609             
14610         var value = [];
14611         var _this = this;
14612         Roo.each(this.item, function(i){
14613             if(_this.valueField){
14614                 value.push(i[_this.valueField]);
14615                 return;
14616             }
14617
14618             value.push(i);
14619         });
14620
14621         this.value = value.join(',');
14622
14623         if(this.hiddenField){
14624             this.hiddenField.dom.value = this.value;
14625         }
14626         
14627         this.store.fireEvent("datachanged", this.store);
14628         
14629         this.validate();
14630     },
14631     
14632     clearItem : function()
14633     {
14634         if(!this.multiple){
14635             return;
14636         }
14637         
14638         this.item = [];
14639         
14640         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14641            c.remove();
14642         });
14643         
14644         this.syncValue();
14645         
14646         this.validate();
14647         
14648         if(this.tickable && !Roo.isTouch){
14649             this.view.refresh();
14650         }
14651     },
14652     
14653     inputEl: function ()
14654     {
14655         if(Roo.isIOS && this.useNativeIOS){
14656             return this.el.select('select.roo-ios-select', true).first();
14657         }
14658         
14659         if(Roo.isTouch && this.mobileTouchView){
14660             return this.el.select('input.form-control',true).first();
14661         }
14662         
14663         if(this.tickable){
14664             return this.searchField;
14665         }
14666         
14667         return this.el.select('input.form-control',true).first();
14668     },
14669     
14670     onTickableFooterButtonClick : function(e, btn, el)
14671     {
14672         e.preventDefault();
14673         
14674         this.lastItem = Roo.apply([], this.item);
14675         
14676         if(btn && btn.name == 'cancel'){
14677             this.tickItems = Roo.apply([], this.item);
14678             this.collapse();
14679             return;
14680         }
14681         
14682         this.clearItem();
14683         
14684         var _this = this;
14685         
14686         Roo.each(this.tickItems, function(o){
14687             _this.addItem(o);
14688         });
14689         
14690         this.collapse();
14691         
14692     },
14693     
14694     validate : function()
14695     {
14696         if(this.getVisibilityEl().hasClass('hidden')){
14697             return true;
14698         }
14699         
14700         var v = this.getRawValue();
14701         
14702         if(this.multiple){
14703             v = this.getValue();
14704         }
14705         
14706         if(this.disabled || this.allowBlank || v.length){
14707             this.markValid();
14708             return true;
14709         }
14710         
14711         this.markInvalid();
14712         return false;
14713     },
14714     
14715     tickableInputEl : function()
14716     {
14717         if(!this.tickable || !this.editable){
14718             return this.inputEl();
14719         }
14720         
14721         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14722     },
14723     
14724     
14725     getAutoCreateTouchView : function()
14726     {
14727         var id = Roo.id();
14728         
14729         var cfg = {
14730             cls: 'form-group' //input-group
14731         };
14732         
14733         var input =  {
14734             tag: 'input',
14735             id : id,
14736             type : this.inputType,
14737             cls : 'form-control x-combo-noedit',
14738             autocomplete: 'new-password',
14739             placeholder : this.placeholder || '',
14740             readonly : true
14741         };
14742         
14743         if (this.name) {
14744             input.name = this.name;
14745         }
14746         
14747         if (this.size) {
14748             input.cls += ' input-' + this.size;
14749         }
14750         
14751         if (this.disabled) {
14752             input.disabled = true;
14753         }
14754         
14755         var inputblock = {
14756             cls : '',
14757             cn : [
14758                 input
14759             ]
14760         };
14761         
14762         if(this.before){
14763             inputblock.cls += ' input-group';
14764             
14765             inputblock.cn.unshift({
14766                 tag :'span',
14767                 cls : 'input-group-addon',
14768                 html : this.before
14769             });
14770         }
14771         
14772         if(this.removable && !this.multiple){
14773             inputblock.cls += ' roo-removable';
14774             
14775             inputblock.cn.push({
14776                 tag: 'button',
14777                 html : 'x',
14778                 cls : 'roo-combo-removable-btn close'
14779             });
14780         }
14781
14782         if(this.hasFeedback && !this.allowBlank){
14783             
14784             inputblock.cls += ' has-feedback';
14785             
14786             inputblock.cn.push({
14787                 tag: 'span',
14788                 cls: 'glyphicon form-control-feedback'
14789             });
14790             
14791         }
14792         
14793         if (this.after) {
14794             
14795             inputblock.cls += (this.before) ? '' : ' input-group';
14796             
14797             inputblock.cn.push({
14798                 tag :'span',
14799                 cls : 'input-group-addon',
14800                 html : this.after
14801             });
14802         }
14803
14804         var box = {
14805             tag: 'div',
14806             cn: [
14807                 {
14808                     tag: 'input',
14809                     type : 'hidden',
14810                     cls: 'form-hidden-field'
14811                 },
14812                 inputblock
14813             ]
14814             
14815         };
14816         
14817         if(this.multiple){
14818             box = {
14819                 tag: 'div',
14820                 cn: [
14821                     {
14822                         tag: 'input',
14823                         type : 'hidden',
14824                         cls: 'form-hidden-field'
14825                     },
14826                     {
14827                         tag: 'ul',
14828                         cls: 'roo-select2-choices',
14829                         cn:[
14830                             {
14831                                 tag: 'li',
14832                                 cls: 'roo-select2-search-field',
14833                                 cn: [
14834
14835                                     inputblock
14836                                 ]
14837                             }
14838                         ]
14839                     }
14840                 ]
14841             }
14842         };
14843         
14844         var combobox = {
14845             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14846             cn: [
14847                 box
14848             ]
14849         };
14850         
14851         if(!this.multiple && this.showToggleBtn){
14852             
14853             var caret = {
14854                         tag: 'span',
14855                         cls: 'caret'
14856             };
14857             
14858             if (this.caret != false) {
14859                 caret = {
14860                      tag: 'i',
14861                      cls: 'fa fa-' + this.caret
14862                 };
14863                 
14864             }
14865             
14866             combobox.cn.push({
14867                 tag :'span',
14868                 cls : 'input-group-addon btn dropdown-toggle',
14869                 cn : [
14870                     caret,
14871                     {
14872                         tag: 'span',
14873                         cls: 'combobox-clear',
14874                         cn  : [
14875                             {
14876                                 tag : 'i',
14877                                 cls: 'icon-remove'
14878                             }
14879                         ]
14880                     }
14881                 ]
14882
14883             })
14884         }
14885         
14886         if(this.multiple){
14887             combobox.cls += ' roo-select2-container-multi';
14888         }
14889         
14890         var align = this.labelAlign || this.parentLabelAlign();
14891         
14892         if (align ==='left' && this.fieldLabel.length) {
14893
14894             cfg.cn = [
14895                 {
14896                    tag : 'i',
14897                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14898                    tooltip : 'This field is required'
14899                 },
14900                 {
14901                     tag: 'label',
14902                     cls : 'control-label',
14903                     html : this.fieldLabel
14904
14905                 },
14906                 {
14907                     cls : '', 
14908                     cn: [
14909                         combobox
14910                     ]
14911                 }
14912             ];
14913             
14914             var labelCfg = cfg.cn[1];
14915             var contentCfg = cfg.cn[2];
14916             
14917
14918             if(this.indicatorpos == 'right'){
14919                 cfg.cn = [
14920                     {
14921                         tag: 'label',
14922                         'for' :  id,
14923                         cls : 'control-label',
14924                         cn : [
14925                             {
14926                                 tag : 'span',
14927                                 html : this.fieldLabel
14928                             },
14929                             {
14930                                 tag : 'i',
14931                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14932                                 tooltip : 'This field is required'
14933                             }
14934                         ]
14935                     },
14936                     {
14937                         cls : "",
14938                         cn: [
14939                             combobox
14940                         ]
14941                     }
14942
14943                 ];
14944                 
14945                 labelCfg = cfg.cn[0];
14946                 contentCfg = cfg.cn[1];
14947             }
14948             
14949            
14950             
14951             if(this.labelWidth > 12){
14952                 labelCfg.style = "width: " + this.labelWidth + 'px';
14953             }
14954             
14955             if(this.labelWidth < 13 && this.labelmd == 0){
14956                 this.labelmd = this.labelWidth;
14957             }
14958             
14959             if(this.labellg > 0){
14960                 labelCfg.cls += ' col-lg-' + this.labellg;
14961                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14962             }
14963             
14964             if(this.labelmd > 0){
14965                 labelCfg.cls += ' col-md-' + this.labelmd;
14966                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14967             }
14968             
14969             if(this.labelsm > 0){
14970                 labelCfg.cls += ' col-sm-' + this.labelsm;
14971                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14972             }
14973             
14974             if(this.labelxs > 0){
14975                 labelCfg.cls += ' col-xs-' + this.labelxs;
14976                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14977             }
14978                 
14979                 
14980         } else if ( this.fieldLabel.length) {
14981             cfg.cn = [
14982                 {
14983                    tag : 'i',
14984                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14985                    tooltip : 'This field is required'
14986                 },
14987                 {
14988                     tag: 'label',
14989                     cls : 'control-label',
14990                     html : this.fieldLabel
14991
14992                 },
14993                 {
14994                     cls : '', 
14995                     cn: [
14996                         combobox
14997                     ]
14998                 }
14999             ];
15000             
15001             if(this.indicatorpos == 'right'){
15002                 cfg.cn = [
15003                     {
15004                         tag: 'label',
15005                         cls : 'control-label',
15006                         html : this.fieldLabel,
15007                         cn : [
15008                             {
15009                                tag : 'i',
15010                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15011                                tooltip : 'This field is required'
15012                             }
15013                         ]
15014                     },
15015                     {
15016                         cls : '', 
15017                         cn: [
15018                             combobox
15019                         ]
15020                     }
15021                 ];
15022             }
15023         } else {
15024             cfg.cn = combobox;    
15025         }
15026         
15027         
15028         var settings = this;
15029         
15030         ['xs','sm','md','lg'].map(function(size){
15031             if (settings[size]) {
15032                 cfg.cls += ' col-' + size + '-' + settings[size];
15033             }
15034         });
15035         
15036         return cfg;
15037     },
15038     
15039     initTouchView : function()
15040     {
15041         this.renderTouchView();
15042         
15043         this.touchViewEl.on('scroll', function(){
15044             this.el.dom.scrollTop = 0;
15045         }, this);
15046         
15047         this.originalValue = this.getValue();
15048         
15049         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15050         
15051         this.inputEl().on("click", this.showTouchView, this);
15052         if (this.triggerEl) {
15053             this.triggerEl.on("click", this.showTouchView, this);
15054         }
15055         
15056         
15057         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15058         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15059         
15060         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15061         
15062         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15063         this.store.on('load', this.onTouchViewLoad, this);
15064         this.store.on('loadexception', this.onTouchViewLoadException, this);
15065         
15066         if(this.hiddenName){
15067             
15068             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15069             
15070             this.hiddenField.dom.value =
15071                 this.hiddenValue !== undefined ? this.hiddenValue :
15072                 this.value !== undefined ? this.value : '';
15073         
15074             this.el.dom.removeAttribute('name');
15075             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15076         }
15077         
15078         if(this.multiple){
15079             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15080             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15081         }
15082         
15083         if(this.removable && !this.multiple){
15084             var close = this.closeTriggerEl();
15085             if(close){
15086                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15087                 close.on('click', this.removeBtnClick, this, close);
15088             }
15089         }
15090         /*
15091          * fix the bug in Safari iOS8
15092          */
15093         this.inputEl().on("focus", function(e){
15094             document.activeElement.blur();
15095         }, this);
15096         
15097         return;
15098         
15099         
15100     },
15101     
15102     renderTouchView : function()
15103     {
15104         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15105         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15106         
15107         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15108         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15109         
15110         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15111         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15112         this.touchViewBodyEl.setStyle('overflow', 'auto');
15113         
15114         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15115         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15116         
15117         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15118         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15119         
15120     },
15121     
15122     showTouchView : function()
15123     {
15124         if(this.disabled){
15125             return;
15126         }
15127         
15128         this.touchViewHeaderEl.hide();
15129
15130         if(this.modalTitle.length){
15131             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15132             this.touchViewHeaderEl.show();
15133         }
15134
15135         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15136         this.touchViewEl.show();
15137
15138         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15139         
15140         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15141         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15142
15143         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15144
15145         if(this.modalTitle.length){
15146             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15147         }
15148         
15149         this.touchViewBodyEl.setHeight(bodyHeight);
15150
15151         if(this.animate){
15152             var _this = this;
15153             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15154         }else{
15155             this.touchViewEl.addClass('in');
15156         }
15157
15158         this.doTouchViewQuery();
15159         
15160     },
15161     
15162     hideTouchView : function()
15163     {
15164         this.touchViewEl.removeClass('in');
15165
15166         if(this.animate){
15167             var _this = this;
15168             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15169         }else{
15170             this.touchViewEl.setStyle('display', 'none');
15171         }
15172         
15173     },
15174     
15175     setTouchViewValue : function()
15176     {
15177         if(this.multiple){
15178             this.clearItem();
15179         
15180             var _this = this;
15181
15182             Roo.each(this.tickItems, function(o){
15183                 this.addItem(o);
15184             }, this);
15185         }
15186         
15187         this.hideTouchView();
15188     },
15189     
15190     doTouchViewQuery : function()
15191     {
15192         var qe = {
15193             query: '',
15194             forceAll: true,
15195             combo: this,
15196             cancel:false
15197         };
15198         
15199         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15200             return false;
15201         }
15202         
15203         if(!this.alwaysQuery || this.mode == 'local'){
15204             this.onTouchViewLoad();
15205             return;
15206         }
15207         
15208         this.store.load();
15209     },
15210     
15211     onTouchViewBeforeLoad : function(combo,opts)
15212     {
15213         return;
15214     },
15215
15216     // private
15217     onTouchViewLoad : function()
15218     {
15219         if(this.store.getCount() < 1){
15220             this.onTouchViewEmptyResults();
15221             return;
15222         }
15223         
15224         this.clearTouchView();
15225         
15226         var rawValue = this.getRawValue();
15227         
15228         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15229         
15230         this.tickItems = [];
15231         
15232         this.store.data.each(function(d, rowIndex){
15233             var row = this.touchViewListGroup.createChild(template);
15234             
15235             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15236                 row.addClass(d.data.cls);
15237             }
15238             
15239             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15240                 var cfg = {
15241                     data : d.data,
15242                     html : d.data[this.displayField]
15243                 };
15244                 
15245                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15246                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15247                 }
15248             }
15249             row.removeClass('selected');
15250             if(!this.multiple && this.valueField &&
15251                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15252             {
15253                 // radio buttons..
15254                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15255                 row.addClass('selected');
15256             }
15257             
15258             if(this.multiple && this.valueField &&
15259                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15260             {
15261                 
15262                 // checkboxes...
15263                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15264                 this.tickItems.push(d.data);
15265             }
15266             
15267             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15268             
15269         }, this);
15270         
15271         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15272         
15273         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15274
15275         if(this.modalTitle.length){
15276             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15277         }
15278
15279         var listHeight = this.touchViewListGroup.getHeight();
15280         
15281         var _this = this;
15282         
15283         if(firstChecked && listHeight > bodyHeight){
15284             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15285         }
15286         
15287     },
15288     
15289     onTouchViewLoadException : function()
15290     {
15291         this.hideTouchView();
15292     },
15293     
15294     onTouchViewEmptyResults : function()
15295     {
15296         this.clearTouchView();
15297         
15298         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15299         
15300         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15301         
15302     },
15303     
15304     clearTouchView : function()
15305     {
15306         this.touchViewListGroup.dom.innerHTML = '';
15307     },
15308     
15309     onTouchViewClick : function(e, el, o)
15310     {
15311         e.preventDefault();
15312         
15313         var row = o.row;
15314         var rowIndex = o.rowIndex;
15315         
15316         var r = this.store.getAt(rowIndex);
15317         
15318         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15319             
15320             if(!this.multiple){
15321                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15322                     c.dom.removeAttribute('checked');
15323                 }, this);
15324
15325                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15326
15327                 this.setFromData(r.data);
15328
15329                 var close = this.closeTriggerEl();
15330
15331                 if(close){
15332                     close.show();
15333                 }
15334
15335                 this.hideTouchView();
15336
15337                 this.fireEvent('select', this, r, rowIndex);
15338
15339                 return;
15340             }
15341
15342             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15343                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15344                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15345                 return;
15346             }
15347
15348             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15349             this.addItem(r.data);
15350             this.tickItems.push(r.data);
15351         }
15352     },
15353     
15354     getAutoCreateNativeIOS : function()
15355     {
15356         var cfg = {
15357             cls: 'form-group' //input-group,
15358         };
15359         
15360         var combobox =  {
15361             tag: 'select',
15362             cls : 'roo-ios-select'
15363         };
15364         
15365         if (this.name) {
15366             combobox.name = this.name;
15367         }
15368         
15369         if (this.disabled) {
15370             combobox.disabled = true;
15371         }
15372         
15373         var settings = this;
15374         
15375         ['xs','sm','md','lg'].map(function(size){
15376             if (settings[size]) {
15377                 cfg.cls += ' col-' + size + '-' + settings[size];
15378             }
15379         });
15380         
15381         cfg.cn = combobox;
15382         
15383         return cfg;
15384         
15385     },
15386     
15387     initIOSView : function()
15388     {
15389         this.store.on('load', this.onIOSViewLoad, this);
15390         
15391         return;
15392     },
15393     
15394     onIOSViewLoad : function()
15395     {
15396         if(this.store.getCount() < 1){
15397             return;
15398         }
15399         
15400         this.clearIOSView();
15401         
15402         if(this.allowBlank) {
15403             
15404             var default_text = '-- SELECT --';
15405             
15406             if(this.placeholder.length){
15407                 default_text = this.placeholder;
15408             }
15409             
15410             if(this.emptyTitle.length){
15411                 default_text += ' - ' + this.emptyTitle + ' -';
15412             }
15413             
15414             var opt = this.inputEl().createChild({
15415                 tag: 'option',
15416                 value : 0,
15417                 html : default_text
15418             });
15419             
15420             var o = {};
15421             o[this.valueField] = 0;
15422             o[this.displayField] = default_text;
15423             
15424             this.ios_options.push({
15425                 data : o,
15426                 el : opt
15427             });
15428             
15429         }
15430         
15431         this.store.data.each(function(d, rowIndex){
15432             
15433             var html = '';
15434             
15435             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15436                 html = d.data[this.displayField];
15437             }
15438             
15439             var value = '';
15440             
15441             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15442                 value = d.data[this.valueField];
15443             }
15444             
15445             var option = {
15446                 tag: 'option',
15447                 value : value,
15448                 html : html
15449             };
15450             
15451             if(this.value == d.data[this.valueField]){
15452                 option['selected'] = true;
15453             }
15454             
15455             var opt = this.inputEl().createChild(option);
15456             
15457             this.ios_options.push({
15458                 data : d.data,
15459                 el : opt
15460             });
15461             
15462         }, this);
15463         
15464         this.inputEl().on('change', function(){
15465            this.fireEvent('select', this);
15466         }, this);
15467         
15468     },
15469     
15470     clearIOSView: function()
15471     {
15472         this.inputEl().dom.innerHTML = '';
15473         
15474         this.ios_options = [];
15475     },
15476     
15477     setIOSValue: function(v)
15478     {
15479         this.value = v;
15480         
15481         if(!this.ios_options){
15482             return;
15483         }
15484         
15485         Roo.each(this.ios_options, function(opts){
15486            
15487            opts.el.dom.removeAttribute('selected');
15488            
15489            if(opts.data[this.valueField] != v){
15490                return;
15491            }
15492            
15493            opts.el.dom.setAttribute('selected', true);
15494            
15495         }, this);
15496     }
15497
15498     /** 
15499     * @cfg {Boolean} grow 
15500     * @hide 
15501     */
15502     /** 
15503     * @cfg {Number} growMin 
15504     * @hide 
15505     */
15506     /** 
15507     * @cfg {Number} growMax 
15508     * @hide 
15509     */
15510     /**
15511      * @hide
15512      * @method autoSize
15513      */
15514 });
15515
15516 Roo.apply(Roo.bootstrap.ComboBox,  {
15517     
15518     header : {
15519         tag: 'div',
15520         cls: 'modal-header',
15521         cn: [
15522             {
15523                 tag: 'h4',
15524                 cls: 'modal-title'
15525             }
15526         ]
15527     },
15528     
15529     body : {
15530         tag: 'div',
15531         cls: 'modal-body',
15532         cn: [
15533             {
15534                 tag: 'ul',
15535                 cls: 'list-group'
15536             }
15537         ]
15538     },
15539     
15540     listItemRadio : {
15541         tag: 'li',
15542         cls: 'list-group-item',
15543         cn: [
15544             {
15545                 tag: 'span',
15546                 cls: 'roo-combobox-list-group-item-value'
15547             },
15548             {
15549                 tag: 'div',
15550                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15551                 cn: [
15552                     {
15553                         tag: 'input',
15554                         type: 'radio'
15555                     },
15556                     {
15557                         tag: 'label'
15558                     }
15559                 ]
15560             }
15561         ]
15562     },
15563     
15564     listItemCheckbox : {
15565         tag: 'li',
15566         cls: 'list-group-item',
15567         cn: [
15568             {
15569                 tag: 'span',
15570                 cls: 'roo-combobox-list-group-item-value'
15571             },
15572             {
15573                 tag: 'div',
15574                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15575                 cn: [
15576                     {
15577                         tag: 'input',
15578                         type: 'checkbox'
15579                     },
15580                     {
15581                         tag: 'label'
15582                     }
15583                 ]
15584             }
15585         ]
15586     },
15587     
15588     emptyResult : {
15589         tag: 'div',
15590         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15591     },
15592     
15593     footer : {
15594         tag: 'div',
15595         cls: 'modal-footer',
15596         cn: [
15597             {
15598                 tag: 'div',
15599                 cls: 'row',
15600                 cn: [
15601                     {
15602                         tag: 'div',
15603                         cls: 'col-xs-6 text-left',
15604                         cn: {
15605                             tag: 'button',
15606                             cls: 'btn btn-danger roo-touch-view-cancel',
15607                             html: 'Cancel'
15608                         }
15609                     },
15610                     {
15611                         tag: 'div',
15612                         cls: 'col-xs-6 text-right',
15613                         cn: {
15614                             tag: 'button',
15615                             cls: 'btn btn-success roo-touch-view-ok',
15616                             html: 'OK'
15617                         }
15618                     }
15619                 ]
15620             }
15621         ]
15622         
15623     }
15624 });
15625
15626 Roo.apply(Roo.bootstrap.ComboBox,  {
15627     
15628     touchViewTemplate : {
15629         tag: 'div',
15630         cls: 'modal fade roo-combobox-touch-view',
15631         cn: [
15632             {
15633                 tag: 'div',
15634                 cls: 'modal-dialog',
15635                 style : 'position:fixed', // we have to fix position....
15636                 cn: [
15637                     {
15638                         tag: 'div',
15639                         cls: 'modal-content',
15640                         cn: [
15641                             Roo.bootstrap.ComboBox.header,
15642                             Roo.bootstrap.ComboBox.body,
15643                             Roo.bootstrap.ComboBox.footer
15644                         ]
15645                     }
15646                 ]
15647             }
15648         ]
15649     }
15650 });/*
15651  * Based on:
15652  * Ext JS Library 1.1.1
15653  * Copyright(c) 2006-2007, Ext JS, LLC.
15654  *
15655  * Originally Released Under LGPL - original licence link has changed is not relivant.
15656  *
15657  * Fork - LGPL
15658  * <script type="text/javascript">
15659  */
15660
15661 /**
15662  * @class Roo.View
15663  * @extends Roo.util.Observable
15664  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15665  * This class also supports single and multi selection modes. <br>
15666  * Create a data model bound view:
15667  <pre><code>
15668  var store = new Roo.data.Store(...);
15669
15670  var view = new Roo.View({
15671     el : "my-element",
15672     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15673  
15674     singleSelect: true,
15675     selectedClass: "ydataview-selected",
15676     store: store
15677  });
15678
15679  // listen for node click?
15680  view.on("click", function(vw, index, node, e){
15681  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15682  });
15683
15684  // load XML data
15685  dataModel.load("foobar.xml");
15686  </code></pre>
15687  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15688  * <br><br>
15689  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15690  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15691  * 
15692  * Note: old style constructor is still suported (container, template, config)
15693  * 
15694  * @constructor
15695  * Create a new View
15696  * @param {Object} config The config object
15697  * 
15698  */
15699 Roo.View = function(config, depreciated_tpl, depreciated_config){
15700     
15701     this.parent = false;
15702     
15703     if (typeof(depreciated_tpl) == 'undefined') {
15704         // new way.. - universal constructor.
15705         Roo.apply(this, config);
15706         this.el  = Roo.get(this.el);
15707     } else {
15708         // old format..
15709         this.el  = Roo.get(config);
15710         this.tpl = depreciated_tpl;
15711         Roo.apply(this, depreciated_config);
15712     }
15713     this.wrapEl  = this.el.wrap().wrap();
15714     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15715     
15716     
15717     if(typeof(this.tpl) == "string"){
15718         this.tpl = new Roo.Template(this.tpl);
15719     } else {
15720         // support xtype ctors..
15721         this.tpl = new Roo.factory(this.tpl, Roo);
15722     }
15723     
15724     
15725     this.tpl.compile();
15726     
15727     /** @private */
15728     this.addEvents({
15729         /**
15730          * @event beforeclick
15731          * Fires before a click is processed. Returns false to cancel the default action.
15732          * @param {Roo.View} this
15733          * @param {Number} index The index of the target node
15734          * @param {HTMLElement} node The target node
15735          * @param {Roo.EventObject} e The raw event object
15736          */
15737             "beforeclick" : true,
15738         /**
15739          * @event click
15740          * Fires when a template node is clicked.
15741          * @param {Roo.View} this
15742          * @param {Number} index The index of the target node
15743          * @param {HTMLElement} node The target node
15744          * @param {Roo.EventObject} e The raw event object
15745          */
15746             "click" : true,
15747         /**
15748          * @event dblclick
15749          * Fires when a template node is double clicked.
15750          * @param {Roo.View} this
15751          * @param {Number} index The index of the target node
15752          * @param {HTMLElement} node The target node
15753          * @param {Roo.EventObject} e The raw event object
15754          */
15755             "dblclick" : true,
15756         /**
15757          * @event contextmenu
15758          * Fires when a template node is right clicked.
15759          * @param {Roo.View} this
15760          * @param {Number} index The index of the target node
15761          * @param {HTMLElement} node The target node
15762          * @param {Roo.EventObject} e The raw event object
15763          */
15764             "contextmenu" : true,
15765         /**
15766          * @event selectionchange
15767          * Fires when the selected nodes change.
15768          * @param {Roo.View} this
15769          * @param {Array} selections Array of the selected nodes
15770          */
15771             "selectionchange" : true,
15772     
15773         /**
15774          * @event beforeselect
15775          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15776          * @param {Roo.View} this
15777          * @param {HTMLElement} node The node to be selected
15778          * @param {Array} selections Array of currently selected nodes
15779          */
15780             "beforeselect" : true,
15781         /**
15782          * @event preparedata
15783          * Fires on every row to render, to allow you to change the data.
15784          * @param {Roo.View} this
15785          * @param {Object} data to be rendered (change this)
15786          */
15787           "preparedata" : true
15788           
15789           
15790         });
15791
15792
15793
15794     this.el.on({
15795         "click": this.onClick,
15796         "dblclick": this.onDblClick,
15797         "contextmenu": this.onContextMenu,
15798         scope:this
15799     });
15800
15801     this.selections = [];
15802     this.nodes = [];
15803     this.cmp = new Roo.CompositeElementLite([]);
15804     if(this.store){
15805         this.store = Roo.factory(this.store, Roo.data);
15806         this.setStore(this.store, true);
15807     }
15808     
15809     if ( this.footer && this.footer.xtype) {
15810            
15811          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15812         
15813         this.footer.dataSource = this.store;
15814         this.footer.container = fctr;
15815         this.footer = Roo.factory(this.footer, Roo);
15816         fctr.insertFirst(this.el);
15817         
15818         // this is a bit insane - as the paging toolbar seems to detach the el..
15819 //        dom.parentNode.parentNode.parentNode
15820          // they get detached?
15821     }
15822     
15823     
15824     Roo.View.superclass.constructor.call(this);
15825     
15826     
15827 };
15828
15829 Roo.extend(Roo.View, Roo.util.Observable, {
15830     
15831      /**
15832      * @cfg {Roo.data.Store} store Data store to load data from.
15833      */
15834     store : false,
15835     
15836     /**
15837      * @cfg {String|Roo.Element} el The container element.
15838      */
15839     el : '',
15840     
15841     /**
15842      * @cfg {String|Roo.Template} tpl The template used by this View 
15843      */
15844     tpl : false,
15845     /**
15846      * @cfg {String} dataName the named area of the template to use as the data area
15847      *                          Works with domtemplates roo-name="name"
15848      */
15849     dataName: false,
15850     /**
15851      * @cfg {String} selectedClass The css class to add to selected nodes
15852      */
15853     selectedClass : "x-view-selected",
15854      /**
15855      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15856      */
15857     emptyText : "",
15858     
15859     /**
15860      * @cfg {String} text to display on mask (default Loading)
15861      */
15862     mask : false,
15863     /**
15864      * @cfg {Boolean} multiSelect Allow multiple selection
15865      */
15866     multiSelect : false,
15867     /**
15868      * @cfg {Boolean} singleSelect Allow single selection
15869      */
15870     singleSelect:  false,
15871     
15872     /**
15873      * @cfg {Boolean} toggleSelect - selecting 
15874      */
15875     toggleSelect : false,
15876     
15877     /**
15878      * @cfg {Boolean} tickable - selecting 
15879      */
15880     tickable : false,
15881     
15882     /**
15883      * Returns the element this view is bound to.
15884      * @return {Roo.Element}
15885      */
15886     getEl : function(){
15887         return this.wrapEl;
15888     },
15889     
15890     
15891
15892     /**
15893      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15894      */
15895     refresh : function(){
15896         //Roo.log('refresh');
15897         var t = this.tpl;
15898         
15899         // if we are using something like 'domtemplate', then
15900         // the what gets used is:
15901         // t.applySubtemplate(NAME, data, wrapping data..)
15902         // the outer template then get' applied with
15903         //     the store 'extra data'
15904         // and the body get's added to the
15905         //      roo-name="data" node?
15906         //      <span class='roo-tpl-{name}'></span> ?????
15907         
15908         
15909         
15910         this.clearSelections();
15911         this.el.update("");
15912         var html = [];
15913         var records = this.store.getRange();
15914         if(records.length < 1) {
15915             
15916             // is this valid??  = should it render a template??
15917             
15918             this.el.update(this.emptyText);
15919             return;
15920         }
15921         var el = this.el;
15922         if (this.dataName) {
15923             this.el.update(t.apply(this.store.meta)); //????
15924             el = this.el.child('.roo-tpl-' + this.dataName);
15925         }
15926         
15927         for(var i = 0, len = records.length; i < len; i++){
15928             var data = this.prepareData(records[i].data, i, records[i]);
15929             this.fireEvent("preparedata", this, data, i, records[i]);
15930             
15931             var d = Roo.apply({}, data);
15932             
15933             if(this.tickable){
15934                 Roo.apply(d, {'roo-id' : Roo.id()});
15935                 
15936                 var _this = this;
15937             
15938                 Roo.each(this.parent.item, function(item){
15939                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15940                         return;
15941                     }
15942                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15943                 });
15944             }
15945             
15946             html[html.length] = Roo.util.Format.trim(
15947                 this.dataName ?
15948                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15949                     t.apply(d)
15950             );
15951         }
15952         
15953         
15954         
15955         el.update(html.join(""));
15956         this.nodes = el.dom.childNodes;
15957         this.updateIndexes(0);
15958     },
15959     
15960
15961     /**
15962      * Function to override to reformat the data that is sent to
15963      * the template for each node.
15964      * DEPRICATED - use the preparedata event handler.
15965      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15966      * a JSON object for an UpdateManager bound view).
15967      */
15968     prepareData : function(data, index, record)
15969     {
15970         this.fireEvent("preparedata", this, data, index, record);
15971         return data;
15972     },
15973
15974     onUpdate : function(ds, record){
15975         // Roo.log('on update');   
15976         this.clearSelections();
15977         var index = this.store.indexOf(record);
15978         var n = this.nodes[index];
15979         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15980         n.parentNode.removeChild(n);
15981         this.updateIndexes(index, index);
15982     },
15983
15984     
15985     
15986 // --------- FIXME     
15987     onAdd : function(ds, records, index)
15988     {
15989         //Roo.log(['on Add', ds, records, index] );        
15990         this.clearSelections();
15991         if(this.nodes.length == 0){
15992             this.refresh();
15993             return;
15994         }
15995         var n = this.nodes[index];
15996         for(var i = 0, len = records.length; i < len; i++){
15997             var d = this.prepareData(records[i].data, i, records[i]);
15998             if(n){
15999                 this.tpl.insertBefore(n, d);
16000             }else{
16001                 
16002                 this.tpl.append(this.el, d);
16003             }
16004         }
16005         this.updateIndexes(index);
16006     },
16007
16008     onRemove : function(ds, record, index){
16009        // Roo.log('onRemove');
16010         this.clearSelections();
16011         var el = this.dataName  ?
16012             this.el.child('.roo-tpl-' + this.dataName) :
16013             this.el; 
16014         
16015         el.dom.removeChild(this.nodes[index]);
16016         this.updateIndexes(index);
16017     },
16018
16019     /**
16020      * Refresh an individual node.
16021      * @param {Number} index
16022      */
16023     refreshNode : function(index){
16024         this.onUpdate(this.store, this.store.getAt(index));
16025     },
16026
16027     updateIndexes : function(startIndex, endIndex){
16028         var ns = this.nodes;
16029         startIndex = startIndex || 0;
16030         endIndex = endIndex || ns.length - 1;
16031         for(var i = startIndex; i <= endIndex; i++){
16032             ns[i].nodeIndex = i;
16033         }
16034     },
16035
16036     /**
16037      * Changes the data store this view uses and refresh the view.
16038      * @param {Store} store
16039      */
16040     setStore : function(store, initial){
16041         if(!initial && this.store){
16042             this.store.un("datachanged", this.refresh);
16043             this.store.un("add", this.onAdd);
16044             this.store.un("remove", this.onRemove);
16045             this.store.un("update", this.onUpdate);
16046             this.store.un("clear", this.refresh);
16047             this.store.un("beforeload", this.onBeforeLoad);
16048             this.store.un("load", this.onLoad);
16049             this.store.un("loadexception", this.onLoad);
16050         }
16051         if(store){
16052           
16053             store.on("datachanged", this.refresh, this);
16054             store.on("add", this.onAdd, this);
16055             store.on("remove", this.onRemove, this);
16056             store.on("update", this.onUpdate, this);
16057             store.on("clear", this.refresh, this);
16058             store.on("beforeload", this.onBeforeLoad, this);
16059             store.on("load", this.onLoad, this);
16060             store.on("loadexception", this.onLoad, this);
16061         }
16062         
16063         if(store){
16064             this.refresh();
16065         }
16066     },
16067     /**
16068      * onbeforeLoad - masks the loading area.
16069      *
16070      */
16071     onBeforeLoad : function(store,opts)
16072     {
16073          //Roo.log('onBeforeLoad');   
16074         if (!opts.add) {
16075             this.el.update("");
16076         }
16077         this.el.mask(this.mask ? this.mask : "Loading" ); 
16078     },
16079     onLoad : function ()
16080     {
16081         this.el.unmask();
16082     },
16083     
16084
16085     /**
16086      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16087      * @param {HTMLElement} node
16088      * @return {HTMLElement} The template node
16089      */
16090     findItemFromChild : function(node){
16091         var el = this.dataName  ?
16092             this.el.child('.roo-tpl-' + this.dataName,true) :
16093             this.el.dom; 
16094         
16095         if(!node || node.parentNode == el){
16096                     return node;
16097             }
16098             var p = node.parentNode;
16099             while(p && p != el){
16100             if(p.parentNode == el){
16101                 return p;
16102             }
16103             p = p.parentNode;
16104         }
16105             return null;
16106     },
16107
16108     /** @ignore */
16109     onClick : function(e){
16110         var item = this.findItemFromChild(e.getTarget());
16111         if(item){
16112             var index = this.indexOf(item);
16113             if(this.onItemClick(item, index, e) !== false){
16114                 this.fireEvent("click", this, index, item, e);
16115             }
16116         }else{
16117             this.clearSelections();
16118         }
16119     },
16120
16121     /** @ignore */
16122     onContextMenu : function(e){
16123         var item = this.findItemFromChild(e.getTarget());
16124         if(item){
16125             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16126         }
16127     },
16128
16129     /** @ignore */
16130     onDblClick : function(e){
16131         var item = this.findItemFromChild(e.getTarget());
16132         if(item){
16133             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16134         }
16135     },
16136
16137     onItemClick : function(item, index, e)
16138     {
16139         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16140             return false;
16141         }
16142         if (this.toggleSelect) {
16143             var m = this.isSelected(item) ? 'unselect' : 'select';
16144             //Roo.log(m);
16145             var _t = this;
16146             _t[m](item, true, false);
16147             return true;
16148         }
16149         if(this.multiSelect || this.singleSelect){
16150             if(this.multiSelect && e.shiftKey && this.lastSelection){
16151                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16152             }else{
16153                 this.select(item, this.multiSelect && e.ctrlKey);
16154                 this.lastSelection = item;
16155             }
16156             
16157             if(!this.tickable){
16158                 e.preventDefault();
16159             }
16160             
16161         }
16162         return true;
16163     },
16164
16165     /**
16166      * Get the number of selected nodes.
16167      * @return {Number}
16168      */
16169     getSelectionCount : function(){
16170         return this.selections.length;
16171     },
16172
16173     /**
16174      * Get the currently selected nodes.
16175      * @return {Array} An array of HTMLElements
16176      */
16177     getSelectedNodes : function(){
16178         return this.selections;
16179     },
16180
16181     /**
16182      * Get the indexes of the selected nodes.
16183      * @return {Array}
16184      */
16185     getSelectedIndexes : function(){
16186         var indexes = [], s = this.selections;
16187         for(var i = 0, len = s.length; i < len; i++){
16188             indexes.push(s[i].nodeIndex);
16189         }
16190         return indexes;
16191     },
16192
16193     /**
16194      * Clear all selections
16195      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16196      */
16197     clearSelections : function(suppressEvent){
16198         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16199             this.cmp.elements = this.selections;
16200             this.cmp.removeClass(this.selectedClass);
16201             this.selections = [];
16202             if(!suppressEvent){
16203                 this.fireEvent("selectionchange", this, this.selections);
16204             }
16205         }
16206     },
16207
16208     /**
16209      * Returns true if the passed node is selected
16210      * @param {HTMLElement/Number} node The node or node index
16211      * @return {Boolean}
16212      */
16213     isSelected : function(node){
16214         var s = this.selections;
16215         if(s.length < 1){
16216             return false;
16217         }
16218         node = this.getNode(node);
16219         return s.indexOf(node) !== -1;
16220     },
16221
16222     /**
16223      * Selects nodes.
16224      * @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
16225      * @param {Boolean} keepExisting (optional) true to keep existing selections
16226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16227      */
16228     select : function(nodeInfo, keepExisting, suppressEvent){
16229         if(nodeInfo instanceof Array){
16230             if(!keepExisting){
16231                 this.clearSelections(true);
16232             }
16233             for(var i = 0, len = nodeInfo.length; i < len; i++){
16234                 this.select(nodeInfo[i], true, true);
16235             }
16236             return;
16237         } 
16238         var node = this.getNode(nodeInfo);
16239         if(!node || this.isSelected(node)){
16240             return; // already selected.
16241         }
16242         if(!keepExisting){
16243             this.clearSelections(true);
16244         }
16245         
16246         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16247             Roo.fly(node).addClass(this.selectedClass);
16248             this.selections.push(node);
16249             if(!suppressEvent){
16250                 this.fireEvent("selectionchange", this, this.selections);
16251             }
16252         }
16253         
16254         
16255     },
16256       /**
16257      * Unselects nodes.
16258      * @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
16259      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16260      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16261      */
16262     unselect : function(nodeInfo, keepExisting, suppressEvent)
16263     {
16264         if(nodeInfo instanceof Array){
16265             Roo.each(this.selections, function(s) {
16266                 this.unselect(s, nodeInfo);
16267             }, this);
16268             return;
16269         }
16270         var node = this.getNode(nodeInfo);
16271         if(!node || !this.isSelected(node)){
16272             //Roo.log("not selected");
16273             return; // not selected.
16274         }
16275         // fireevent???
16276         var ns = [];
16277         Roo.each(this.selections, function(s) {
16278             if (s == node ) {
16279                 Roo.fly(node).removeClass(this.selectedClass);
16280
16281                 return;
16282             }
16283             ns.push(s);
16284         },this);
16285         
16286         this.selections= ns;
16287         this.fireEvent("selectionchange", this, this.selections);
16288     },
16289
16290     /**
16291      * Gets a template node.
16292      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16293      * @return {HTMLElement} The node or null if it wasn't found
16294      */
16295     getNode : function(nodeInfo){
16296         if(typeof nodeInfo == "string"){
16297             return document.getElementById(nodeInfo);
16298         }else if(typeof nodeInfo == "number"){
16299             return this.nodes[nodeInfo];
16300         }
16301         return nodeInfo;
16302     },
16303
16304     /**
16305      * Gets a range template nodes.
16306      * @param {Number} startIndex
16307      * @param {Number} endIndex
16308      * @return {Array} An array of nodes
16309      */
16310     getNodes : function(start, end){
16311         var ns = this.nodes;
16312         start = start || 0;
16313         end = typeof end == "undefined" ? ns.length - 1 : end;
16314         var nodes = [];
16315         if(start <= end){
16316             for(var i = start; i <= end; i++){
16317                 nodes.push(ns[i]);
16318             }
16319         } else{
16320             for(var i = start; i >= end; i--){
16321                 nodes.push(ns[i]);
16322             }
16323         }
16324         return nodes;
16325     },
16326
16327     /**
16328      * Finds the index of the passed node
16329      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16330      * @return {Number} The index of the node or -1
16331      */
16332     indexOf : function(node){
16333         node = this.getNode(node);
16334         if(typeof node.nodeIndex == "number"){
16335             return node.nodeIndex;
16336         }
16337         var ns = this.nodes;
16338         for(var i = 0, len = ns.length; i < len; i++){
16339             if(ns[i] == node){
16340                 return i;
16341             }
16342         }
16343         return -1;
16344     }
16345 });
16346 /*
16347  * - LGPL
16348  *
16349  * based on jquery fullcalendar
16350  * 
16351  */
16352
16353 Roo.bootstrap = Roo.bootstrap || {};
16354 /**
16355  * @class Roo.bootstrap.Calendar
16356  * @extends Roo.bootstrap.Component
16357  * Bootstrap Calendar class
16358  * @cfg {Boolean} loadMask (true|false) default false
16359  * @cfg {Object} header generate the user specific header of the calendar, default false
16360
16361  * @constructor
16362  * Create a new Container
16363  * @param {Object} config The config object
16364  */
16365
16366
16367
16368 Roo.bootstrap.Calendar = function(config){
16369     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16370      this.addEvents({
16371         /**
16372              * @event select
16373              * Fires when a date is selected
16374              * @param {DatePicker} this
16375              * @param {Date} date The selected date
16376              */
16377         'select': true,
16378         /**
16379              * @event monthchange
16380              * Fires when the displayed month changes 
16381              * @param {DatePicker} this
16382              * @param {Date} date The selected month
16383              */
16384         'monthchange': true,
16385         /**
16386              * @event evententer
16387              * Fires when mouse over an event
16388              * @param {Calendar} this
16389              * @param {event} Event
16390              */
16391         'evententer': true,
16392         /**
16393              * @event eventleave
16394              * Fires when the mouse leaves an
16395              * @param {Calendar} this
16396              * @param {event}
16397              */
16398         'eventleave': true,
16399         /**
16400              * @event eventclick
16401              * Fires when the mouse click an
16402              * @param {Calendar} this
16403              * @param {event}
16404              */
16405         'eventclick': true
16406         
16407     });
16408
16409 };
16410
16411 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16412     
16413      /**
16414      * @cfg {Number} startDay
16415      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16416      */
16417     startDay : 0,
16418     
16419     loadMask : false,
16420     
16421     header : false,
16422       
16423     getAutoCreate : function(){
16424         
16425         
16426         var fc_button = function(name, corner, style, content ) {
16427             return Roo.apply({},{
16428                 tag : 'span',
16429                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16430                          (corner.length ?
16431                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16432                             ''
16433                         ),
16434                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16435                 unselectable: 'on'
16436             });
16437         };
16438         
16439         var header = {};
16440         
16441         if(!this.header){
16442             header = {
16443                 tag : 'table',
16444                 cls : 'fc-header',
16445                 style : 'width:100%',
16446                 cn : [
16447                     {
16448                         tag: 'tr',
16449                         cn : [
16450                             {
16451                                 tag : 'td',
16452                                 cls : 'fc-header-left',
16453                                 cn : [
16454                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16455                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16456                                     { tag: 'span', cls: 'fc-header-space' },
16457                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16458
16459
16460                                 ]
16461                             },
16462
16463                             {
16464                                 tag : 'td',
16465                                 cls : 'fc-header-center',
16466                                 cn : [
16467                                     {
16468                                         tag: 'span',
16469                                         cls: 'fc-header-title',
16470                                         cn : {
16471                                             tag: 'H2',
16472                                             html : 'month / year'
16473                                         }
16474                                     }
16475
16476                                 ]
16477                             },
16478                             {
16479                                 tag : 'td',
16480                                 cls : 'fc-header-right',
16481                                 cn : [
16482                               /*      fc_button('month', 'left', '', 'month' ),
16483                                     fc_button('week', '', '', 'week' ),
16484                                     fc_button('day', 'right', '', 'day' )
16485                                 */    
16486
16487                                 ]
16488                             }
16489
16490                         ]
16491                     }
16492                 ]
16493             };
16494         }
16495         
16496         header = this.header;
16497         
16498        
16499         var cal_heads = function() {
16500             var ret = [];
16501             // fixme - handle this.
16502             
16503             for (var i =0; i < Date.dayNames.length; i++) {
16504                 var d = Date.dayNames[i];
16505                 ret.push({
16506                     tag: 'th',
16507                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16508                     html : d.substring(0,3)
16509                 });
16510                 
16511             }
16512             ret[0].cls += ' fc-first';
16513             ret[6].cls += ' fc-last';
16514             return ret;
16515         };
16516         var cal_cell = function(n) {
16517             return  {
16518                 tag: 'td',
16519                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16520                 cn : [
16521                     {
16522                         cn : [
16523                             {
16524                                 cls: 'fc-day-number',
16525                                 html: 'D'
16526                             },
16527                             {
16528                                 cls: 'fc-day-content',
16529                              
16530                                 cn : [
16531                                      {
16532                                         style: 'position: relative;' // height: 17px;
16533                                     }
16534                                 ]
16535                             }
16536                             
16537                             
16538                         ]
16539                     }
16540                 ]
16541                 
16542             }
16543         };
16544         var cal_rows = function() {
16545             
16546             var ret = [];
16547             for (var r = 0; r < 6; r++) {
16548                 var row= {
16549                     tag : 'tr',
16550                     cls : 'fc-week',
16551                     cn : []
16552                 };
16553                 
16554                 for (var i =0; i < Date.dayNames.length; i++) {
16555                     var d = Date.dayNames[i];
16556                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16557
16558                 }
16559                 row.cn[0].cls+=' fc-first';
16560                 row.cn[0].cn[0].style = 'min-height:90px';
16561                 row.cn[6].cls+=' fc-last';
16562                 ret.push(row);
16563                 
16564             }
16565             ret[0].cls += ' fc-first';
16566             ret[4].cls += ' fc-prev-last';
16567             ret[5].cls += ' fc-last';
16568             return ret;
16569             
16570         };
16571         
16572         var cal_table = {
16573             tag: 'table',
16574             cls: 'fc-border-separate',
16575             style : 'width:100%',
16576             cellspacing  : 0,
16577             cn : [
16578                 { 
16579                     tag: 'thead',
16580                     cn : [
16581                         { 
16582                             tag: 'tr',
16583                             cls : 'fc-first fc-last',
16584                             cn : cal_heads()
16585                         }
16586                     ]
16587                 },
16588                 { 
16589                     tag: 'tbody',
16590                     cn : cal_rows()
16591                 }
16592                   
16593             ]
16594         };
16595          
16596          var cfg = {
16597             cls : 'fc fc-ltr',
16598             cn : [
16599                 header,
16600                 {
16601                     cls : 'fc-content',
16602                     style : "position: relative;",
16603                     cn : [
16604                         {
16605                             cls : 'fc-view fc-view-month fc-grid',
16606                             style : 'position: relative',
16607                             unselectable : 'on',
16608                             cn : [
16609                                 {
16610                                     cls : 'fc-event-container',
16611                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16612                                 },
16613                                 cal_table
16614                             ]
16615                         }
16616                     ]
16617     
16618                 }
16619            ] 
16620             
16621         };
16622         
16623          
16624         
16625         return cfg;
16626     },
16627     
16628     
16629     initEvents : function()
16630     {
16631         if(!this.store){
16632             throw "can not find store for calendar";
16633         }
16634         
16635         var mark = {
16636             tag: "div",
16637             cls:"x-dlg-mask",
16638             style: "text-align:center",
16639             cn: [
16640                 {
16641                     tag: "div",
16642                     style: "background-color:white;width:50%;margin:250 auto",
16643                     cn: [
16644                         {
16645                             tag: "img",
16646                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16647                         },
16648                         {
16649                             tag: "span",
16650                             html: "Loading"
16651                         }
16652                         
16653                     ]
16654                 }
16655             ]
16656         };
16657         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16658         
16659         var size = this.el.select('.fc-content', true).first().getSize();
16660         this.maskEl.setSize(size.width, size.height);
16661         this.maskEl.enableDisplayMode("block");
16662         if(!this.loadMask){
16663             this.maskEl.hide();
16664         }
16665         
16666         this.store = Roo.factory(this.store, Roo.data);
16667         this.store.on('load', this.onLoad, this);
16668         this.store.on('beforeload', this.onBeforeLoad, this);
16669         
16670         this.resize();
16671         
16672         this.cells = this.el.select('.fc-day',true);
16673         //Roo.log(this.cells);
16674         this.textNodes = this.el.query('.fc-day-number');
16675         this.cells.addClassOnOver('fc-state-hover');
16676         
16677         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16678         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16679         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16680         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16681         
16682         this.on('monthchange', this.onMonthChange, this);
16683         
16684         this.update(new Date().clearTime());
16685     },
16686     
16687     resize : function() {
16688         var sz  = this.el.getSize();
16689         
16690         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16691         this.el.select('.fc-day-content div',true).setHeight(34);
16692     },
16693     
16694     
16695     // private
16696     showPrevMonth : function(e){
16697         this.update(this.activeDate.add("mo", -1));
16698     },
16699     showToday : function(e){
16700         this.update(new Date().clearTime());
16701     },
16702     // private
16703     showNextMonth : function(e){
16704         this.update(this.activeDate.add("mo", 1));
16705     },
16706
16707     // private
16708     showPrevYear : function(){
16709         this.update(this.activeDate.add("y", -1));
16710     },
16711
16712     // private
16713     showNextYear : function(){
16714         this.update(this.activeDate.add("y", 1));
16715     },
16716
16717     
16718    // private
16719     update : function(date)
16720     {
16721         var vd = this.activeDate;
16722         this.activeDate = date;
16723 //        if(vd && this.el){
16724 //            var t = date.getTime();
16725 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16726 //                Roo.log('using add remove');
16727 //                
16728 //                this.fireEvent('monthchange', this, date);
16729 //                
16730 //                this.cells.removeClass("fc-state-highlight");
16731 //                this.cells.each(function(c){
16732 //                   if(c.dateValue == t){
16733 //                       c.addClass("fc-state-highlight");
16734 //                       setTimeout(function(){
16735 //                            try{c.dom.firstChild.focus();}catch(e){}
16736 //                       }, 50);
16737 //                       return false;
16738 //                   }
16739 //                   return true;
16740 //                });
16741 //                return;
16742 //            }
16743 //        }
16744         
16745         var days = date.getDaysInMonth();
16746         
16747         var firstOfMonth = date.getFirstDateOfMonth();
16748         var startingPos = firstOfMonth.getDay()-this.startDay;
16749         
16750         if(startingPos < this.startDay){
16751             startingPos += 7;
16752         }
16753         
16754         var pm = date.add(Date.MONTH, -1);
16755         var prevStart = pm.getDaysInMonth()-startingPos;
16756 //        
16757         this.cells = this.el.select('.fc-day',true);
16758         this.textNodes = this.el.query('.fc-day-number');
16759         this.cells.addClassOnOver('fc-state-hover');
16760         
16761         var cells = this.cells.elements;
16762         var textEls = this.textNodes;
16763         
16764         Roo.each(cells, function(cell){
16765             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16766         });
16767         
16768         days += startingPos;
16769
16770         // convert everything to numbers so it's fast
16771         var day = 86400000;
16772         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16773         //Roo.log(d);
16774         //Roo.log(pm);
16775         //Roo.log(prevStart);
16776         
16777         var today = new Date().clearTime().getTime();
16778         var sel = date.clearTime().getTime();
16779         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16780         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16781         var ddMatch = this.disabledDatesRE;
16782         var ddText = this.disabledDatesText;
16783         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16784         var ddaysText = this.disabledDaysText;
16785         var format = this.format;
16786         
16787         var setCellClass = function(cal, cell){
16788             cell.row = 0;
16789             cell.events = [];
16790             cell.more = [];
16791             //Roo.log('set Cell Class');
16792             cell.title = "";
16793             var t = d.getTime();
16794             
16795             //Roo.log(d);
16796             
16797             cell.dateValue = t;
16798             if(t == today){
16799                 cell.className += " fc-today";
16800                 cell.className += " fc-state-highlight";
16801                 cell.title = cal.todayText;
16802             }
16803             if(t == sel){
16804                 // disable highlight in other month..
16805                 //cell.className += " fc-state-highlight";
16806                 
16807             }
16808             // disabling
16809             if(t < min) {
16810                 cell.className = " fc-state-disabled";
16811                 cell.title = cal.minText;
16812                 return;
16813             }
16814             if(t > max) {
16815                 cell.className = " fc-state-disabled";
16816                 cell.title = cal.maxText;
16817                 return;
16818             }
16819             if(ddays){
16820                 if(ddays.indexOf(d.getDay()) != -1){
16821                     cell.title = ddaysText;
16822                     cell.className = " fc-state-disabled";
16823                 }
16824             }
16825             if(ddMatch && format){
16826                 var fvalue = d.dateFormat(format);
16827                 if(ddMatch.test(fvalue)){
16828                     cell.title = ddText.replace("%0", fvalue);
16829                     cell.className = " fc-state-disabled";
16830                 }
16831             }
16832             
16833             if (!cell.initialClassName) {
16834                 cell.initialClassName = cell.dom.className;
16835             }
16836             
16837             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16838         };
16839
16840         var i = 0;
16841         
16842         for(; i < startingPos; i++) {
16843             textEls[i].innerHTML = (++prevStart);
16844             d.setDate(d.getDate()+1);
16845             
16846             cells[i].className = "fc-past fc-other-month";
16847             setCellClass(this, cells[i]);
16848         }
16849         
16850         var intDay = 0;
16851         
16852         for(; i < days; i++){
16853             intDay = i - startingPos + 1;
16854             textEls[i].innerHTML = (intDay);
16855             d.setDate(d.getDate()+1);
16856             
16857             cells[i].className = ''; // "x-date-active";
16858             setCellClass(this, cells[i]);
16859         }
16860         var extraDays = 0;
16861         
16862         for(; i < 42; i++) {
16863             textEls[i].innerHTML = (++extraDays);
16864             d.setDate(d.getDate()+1);
16865             
16866             cells[i].className = "fc-future fc-other-month";
16867             setCellClass(this, cells[i]);
16868         }
16869         
16870         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16871         
16872         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16873         
16874         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16875         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16876         
16877         if(totalRows != 6){
16878             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16879             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16880         }
16881         
16882         this.fireEvent('monthchange', this, date);
16883         
16884         
16885         /*
16886         if(!this.internalRender){
16887             var main = this.el.dom.firstChild;
16888             var w = main.offsetWidth;
16889             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16890             Roo.fly(main).setWidth(w);
16891             this.internalRender = true;
16892             // opera does not respect the auto grow header center column
16893             // then, after it gets a width opera refuses to recalculate
16894             // without a second pass
16895             if(Roo.isOpera && !this.secondPass){
16896                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16897                 this.secondPass = true;
16898                 this.update.defer(10, this, [date]);
16899             }
16900         }
16901         */
16902         
16903     },
16904     
16905     findCell : function(dt) {
16906         dt = dt.clearTime().getTime();
16907         var ret = false;
16908         this.cells.each(function(c){
16909             //Roo.log("check " +c.dateValue + '?=' + dt);
16910             if(c.dateValue == dt){
16911                 ret = c;
16912                 return false;
16913             }
16914             return true;
16915         });
16916         
16917         return ret;
16918     },
16919     
16920     findCells : function(ev) {
16921         var s = ev.start.clone().clearTime().getTime();
16922        // Roo.log(s);
16923         var e= ev.end.clone().clearTime().getTime();
16924        // Roo.log(e);
16925         var ret = [];
16926         this.cells.each(function(c){
16927              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16928             
16929             if(c.dateValue > e){
16930                 return ;
16931             }
16932             if(c.dateValue < s){
16933                 return ;
16934             }
16935             ret.push(c);
16936         });
16937         
16938         return ret;    
16939     },
16940     
16941 //    findBestRow: function(cells)
16942 //    {
16943 //        var ret = 0;
16944 //        
16945 //        for (var i =0 ; i < cells.length;i++) {
16946 //            ret  = Math.max(cells[i].rows || 0,ret);
16947 //        }
16948 //        return ret;
16949 //        
16950 //    },
16951     
16952     
16953     addItem : function(ev)
16954     {
16955         // look for vertical location slot in
16956         var cells = this.findCells(ev);
16957         
16958 //        ev.row = this.findBestRow(cells);
16959         
16960         // work out the location.
16961         
16962         var crow = false;
16963         var rows = [];
16964         for(var i =0; i < cells.length; i++) {
16965             
16966             cells[i].row = cells[0].row;
16967             
16968             if(i == 0){
16969                 cells[i].row = cells[i].row + 1;
16970             }
16971             
16972             if (!crow) {
16973                 crow = {
16974                     start : cells[i],
16975                     end :  cells[i]
16976                 };
16977                 continue;
16978             }
16979             if (crow.start.getY() == cells[i].getY()) {
16980                 // on same row.
16981                 crow.end = cells[i];
16982                 continue;
16983             }
16984             // different row.
16985             rows.push(crow);
16986             crow = {
16987                 start: cells[i],
16988                 end : cells[i]
16989             };
16990             
16991         }
16992         
16993         rows.push(crow);
16994         ev.els = [];
16995         ev.rows = rows;
16996         ev.cells = cells;
16997         
16998         cells[0].events.push(ev);
16999         
17000         this.calevents.push(ev);
17001     },
17002     
17003     clearEvents: function() {
17004         
17005         if(!this.calevents){
17006             return;
17007         }
17008         
17009         Roo.each(this.cells.elements, function(c){
17010             c.row = 0;
17011             c.events = [];
17012             c.more = [];
17013         });
17014         
17015         Roo.each(this.calevents, function(e) {
17016             Roo.each(e.els, function(el) {
17017                 el.un('mouseenter' ,this.onEventEnter, this);
17018                 el.un('mouseleave' ,this.onEventLeave, this);
17019                 el.remove();
17020             },this);
17021         },this);
17022         
17023         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17024             e.remove();
17025         });
17026         
17027     },
17028     
17029     renderEvents: function()
17030     {   
17031         var _this = this;
17032         
17033         this.cells.each(function(c) {
17034             
17035             if(c.row < 5){
17036                 return;
17037             }
17038             
17039             var ev = c.events;
17040             
17041             var r = 4;
17042             if(c.row != c.events.length){
17043                 r = 4 - (4 - (c.row - c.events.length));
17044             }
17045             
17046             c.events = ev.slice(0, r);
17047             c.more = ev.slice(r);
17048             
17049             if(c.more.length && c.more.length == 1){
17050                 c.events.push(c.more.pop());
17051             }
17052             
17053             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17054             
17055         });
17056             
17057         this.cells.each(function(c) {
17058             
17059             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17060             
17061             
17062             for (var e = 0; e < c.events.length; e++){
17063                 var ev = c.events[e];
17064                 var rows = ev.rows;
17065                 
17066                 for(var i = 0; i < rows.length; i++) {
17067                 
17068                     // how many rows should it span..
17069
17070                     var  cfg = {
17071                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17072                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17073
17074                         unselectable : "on",
17075                         cn : [
17076                             {
17077                                 cls: 'fc-event-inner',
17078                                 cn : [
17079     //                                {
17080     //                                  tag:'span',
17081     //                                  cls: 'fc-event-time',
17082     //                                  html : cells.length > 1 ? '' : ev.time
17083     //                                },
17084                                     {
17085                                       tag:'span',
17086                                       cls: 'fc-event-title',
17087                                       html : String.format('{0}', ev.title)
17088                                     }
17089
17090
17091                                 ]
17092                             },
17093                             {
17094                                 cls: 'ui-resizable-handle ui-resizable-e',
17095                                 html : '&nbsp;&nbsp;&nbsp'
17096                             }
17097
17098                         ]
17099                     };
17100
17101                     if (i == 0) {
17102                         cfg.cls += ' fc-event-start';
17103                     }
17104                     if ((i+1) == rows.length) {
17105                         cfg.cls += ' fc-event-end';
17106                     }
17107
17108                     var ctr = _this.el.select('.fc-event-container',true).first();
17109                     var cg = ctr.createChild(cfg);
17110
17111                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17112                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17113
17114                     var r = (c.more.length) ? 1 : 0;
17115                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17116                     cg.setWidth(ebox.right - sbox.x -2);
17117
17118                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17119                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17120                     cg.on('click', _this.onEventClick, _this, ev);
17121
17122                     ev.els.push(cg);
17123                     
17124                 }
17125                 
17126             }
17127             
17128             
17129             if(c.more.length){
17130                 var  cfg = {
17131                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17132                     style : 'position: absolute',
17133                     unselectable : "on",
17134                     cn : [
17135                         {
17136                             cls: 'fc-event-inner',
17137                             cn : [
17138                                 {
17139                                   tag:'span',
17140                                   cls: 'fc-event-title',
17141                                   html : 'More'
17142                                 }
17143
17144
17145                             ]
17146                         },
17147                         {
17148                             cls: 'ui-resizable-handle ui-resizable-e',
17149                             html : '&nbsp;&nbsp;&nbsp'
17150                         }
17151
17152                     ]
17153                 };
17154
17155                 var ctr = _this.el.select('.fc-event-container',true).first();
17156                 var cg = ctr.createChild(cfg);
17157
17158                 var sbox = c.select('.fc-day-content',true).first().getBox();
17159                 var ebox = c.select('.fc-day-content',true).first().getBox();
17160                 //Roo.log(cg);
17161                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17162                 cg.setWidth(ebox.right - sbox.x -2);
17163
17164                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17165                 
17166             }
17167             
17168         });
17169         
17170         
17171         
17172     },
17173     
17174     onEventEnter: function (e, el,event,d) {
17175         this.fireEvent('evententer', this, el, event);
17176     },
17177     
17178     onEventLeave: function (e, el,event,d) {
17179         this.fireEvent('eventleave', this, el, event);
17180     },
17181     
17182     onEventClick: function (e, el,event,d) {
17183         this.fireEvent('eventclick', this, el, event);
17184     },
17185     
17186     onMonthChange: function () {
17187         this.store.load();
17188     },
17189     
17190     onMoreEventClick: function(e, el, more)
17191     {
17192         var _this = this;
17193         
17194         this.calpopover.placement = 'right';
17195         this.calpopover.setTitle('More');
17196         
17197         this.calpopover.setContent('');
17198         
17199         var ctr = this.calpopover.el.select('.popover-content', true).first();
17200         
17201         Roo.each(more, function(m){
17202             var cfg = {
17203                 cls : 'fc-event-hori fc-event-draggable',
17204                 html : m.title
17205             };
17206             var cg = ctr.createChild(cfg);
17207             
17208             cg.on('click', _this.onEventClick, _this, m);
17209         });
17210         
17211         this.calpopover.show(el);
17212         
17213         
17214     },
17215     
17216     onLoad: function () 
17217     {   
17218         this.calevents = [];
17219         var cal = this;
17220         
17221         if(this.store.getCount() > 0){
17222             this.store.data.each(function(d){
17223                cal.addItem({
17224                     id : d.data.id,
17225                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17226                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17227                     time : d.data.start_time,
17228                     title : d.data.title,
17229                     description : d.data.description,
17230                     venue : d.data.venue
17231                 });
17232             });
17233         }
17234         
17235         this.renderEvents();
17236         
17237         if(this.calevents.length && this.loadMask){
17238             this.maskEl.hide();
17239         }
17240     },
17241     
17242     onBeforeLoad: function()
17243     {
17244         this.clearEvents();
17245         if(this.loadMask){
17246             this.maskEl.show();
17247         }
17248     }
17249 });
17250
17251  
17252  /*
17253  * - LGPL
17254  *
17255  * element
17256  * 
17257  */
17258
17259 /**
17260  * @class Roo.bootstrap.Popover
17261  * @extends Roo.bootstrap.Component
17262  * Bootstrap Popover class
17263  * @cfg {String} html contents of the popover   (or false to use children..)
17264  * @cfg {String} title of popover (or false to hide)
17265  * @cfg {String} placement how it is placed
17266  * @cfg {String} trigger click || hover (or false to trigger manually)
17267  * @cfg {String} over what (parent or false to trigger manually.)
17268  * @cfg {Number} delay - delay before showing
17269  
17270  * @constructor
17271  * Create a new Popover
17272  * @param {Object} config The config object
17273  */
17274
17275 Roo.bootstrap.Popover = function(config){
17276     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17277     
17278     this.addEvents({
17279         // raw events
17280          /**
17281          * @event show
17282          * After the popover show
17283          * 
17284          * @param {Roo.bootstrap.Popover} this
17285          */
17286         "show" : true,
17287         /**
17288          * @event hide
17289          * After the popover hide
17290          * 
17291          * @param {Roo.bootstrap.Popover} this
17292          */
17293         "hide" : true
17294     });
17295 };
17296
17297 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17298     
17299     title: 'Fill in a title',
17300     html: false,
17301     
17302     placement : 'right',
17303     trigger : 'hover', // hover
17304     
17305     delay : 0,
17306     
17307     over: 'parent',
17308     
17309     can_build_overlaid : false,
17310     
17311     getChildContainer : function()
17312     {
17313         return this.el.select('.popover-content',true).first();
17314     },
17315     
17316     getAutoCreate : function(){
17317          
17318         var cfg = {
17319            cls : 'popover roo-dynamic',
17320            style: 'display:block',
17321            cn : [
17322                 {
17323                     cls : 'arrow'
17324                 },
17325                 {
17326                     cls : 'popover-inner',
17327                     cn : [
17328                         {
17329                             tag: 'h3',
17330                             cls: 'popover-title',
17331                             html : this.title
17332                         },
17333                         {
17334                             cls : 'popover-content',
17335                             html : this.html
17336                         }
17337                     ]
17338                     
17339                 }
17340            ]
17341         };
17342         
17343         return cfg;
17344     },
17345     setTitle: function(str)
17346     {
17347         this.title = str;
17348         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17349     },
17350     setContent: function(str)
17351     {
17352         this.html = str;
17353         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17354     },
17355     // as it get's added to the bottom of the page.
17356     onRender : function(ct, position)
17357     {
17358         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17359         if(!this.el){
17360             var cfg = Roo.apply({},  this.getAutoCreate());
17361             cfg.id = Roo.id();
17362             
17363             if (this.cls) {
17364                 cfg.cls += ' ' + this.cls;
17365             }
17366             if (this.style) {
17367                 cfg.style = this.style;
17368             }
17369             //Roo.log("adding to ");
17370             this.el = Roo.get(document.body).createChild(cfg, position);
17371 //            Roo.log(this.el);
17372         }
17373         this.initEvents();
17374     },
17375     
17376     initEvents : function()
17377     {
17378         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17379         this.el.enableDisplayMode('block');
17380         this.el.hide();
17381         if (this.over === false) {
17382             return; 
17383         }
17384         if (this.triggers === false) {
17385             return;
17386         }
17387         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17388         var triggers = this.trigger ? this.trigger.split(' ') : [];
17389         Roo.each(triggers, function(trigger) {
17390         
17391             if (trigger == 'click') {
17392                 on_el.on('click', this.toggle, this);
17393             } else if (trigger != 'manual') {
17394                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17395                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17396       
17397                 on_el.on(eventIn  ,this.enter, this);
17398                 on_el.on(eventOut, this.leave, this);
17399             }
17400         }, this);
17401         
17402     },
17403     
17404     
17405     // private
17406     timeout : null,
17407     hoverState : null,
17408     
17409     toggle : function () {
17410         this.hoverState == 'in' ? this.leave() : this.enter();
17411     },
17412     
17413     enter : function () {
17414         
17415         clearTimeout(this.timeout);
17416     
17417         this.hoverState = 'in';
17418     
17419         if (!this.delay || !this.delay.show) {
17420             this.show();
17421             return;
17422         }
17423         var _t = this;
17424         this.timeout = setTimeout(function () {
17425             if (_t.hoverState == 'in') {
17426                 _t.show();
17427             }
17428         }, this.delay.show)
17429     },
17430     
17431     leave : function() {
17432         clearTimeout(this.timeout);
17433     
17434         this.hoverState = 'out';
17435     
17436         if (!this.delay || !this.delay.hide) {
17437             this.hide();
17438             return;
17439         }
17440         var _t = this;
17441         this.timeout = setTimeout(function () {
17442             if (_t.hoverState == 'out') {
17443                 _t.hide();
17444             }
17445         }, this.delay.hide)
17446     },
17447     
17448     show : function (on_el)
17449     {
17450         if (!on_el) {
17451             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17452         }
17453         
17454         // set content.
17455         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17456         if (this.html !== false) {
17457             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17458         }
17459         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17460         if (!this.title.length) {
17461             this.el.select('.popover-title',true).hide();
17462         }
17463         
17464         var placement = typeof this.placement == 'function' ?
17465             this.placement.call(this, this.el, on_el) :
17466             this.placement;
17467             
17468         var autoToken = /\s?auto?\s?/i;
17469         var autoPlace = autoToken.test(placement);
17470         if (autoPlace) {
17471             placement = placement.replace(autoToken, '') || 'top';
17472         }
17473         
17474         //this.el.detach()
17475         //this.el.setXY([0,0]);
17476         this.el.show();
17477         this.el.dom.style.display='block';
17478         this.el.addClass(placement);
17479         
17480         //this.el.appendTo(on_el);
17481         
17482         var p = this.getPosition();
17483         var box = this.el.getBox();
17484         
17485         if (autoPlace) {
17486             // fixme..
17487         }
17488         var align = Roo.bootstrap.Popover.alignment[placement];
17489         
17490 //        Roo.log(align);
17491         this.el.alignTo(on_el, align[0],align[1]);
17492         //var arrow = this.el.select('.arrow',true).first();
17493         //arrow.set(align[2], 
17494         
17495         this.el.addClass('in');
17496         
17497         
17498         if (this.el.hasClass('fade')) {
17499             // fade it?
17500         }
17501         
17502         this.hoverState = 'in';
17503         
17504         this.fireEvent('show', this);
17505         
17506     },
17507     hide : function()
17508     {
17509         this.el.setXY([0,0]);
17510         this.el.removeClass('in');
17511         this.el.hide();
17512         this.hoverState = null;
17513         
17514         this.fireEvent('hide', this);
17515     }
17516     
17517 });
17518
17519 Roo.bootstrap.Popover.alignment = {
17520     'left' : ['r-l', [-10,0], 'right'],
17521     'right' : ['l-r', [10,0], 'left'],
17522     'bottom' : ['t-b', [0,10], 'top'],
17523     'top' : [ 'b-t', [0,-10], 'bottom']
17524 };
17525
17526  /*
17527  * - LGPL
17528  *
17529  * Progress
17530  * 
17531  */
17532
17533 /**
17534  * @class Roo.bootstrap.Progress
17535  * @extends Roo.bootstrap.Component
17536  * Bootstrap Progress class
17537  * @cfg {Boolean} striped striped of the progress bar
17538  * @cfg {Boolean} active animated of the progress bar
17539  * 
17540  * 
17541  * @constructor
17542  * Create a new Progress
17543  * @param {Object} config The config object
17544  */
17545
17546 Roo.bootstrap.Progress = function(config){
17547     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17548 };
17549
17550 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17551     
17552     striped : false,
17553     active: false,
17554     
17555     getAutoCreate : function(){
17556         var cfg = {
17557             tag: 'div',
17558             cls: 'progress'
17559         };
17560         
17561         
17562         if(this.striped){
17563             cfg.cls += ' progress-striped';
17564         }
17565       
17566         if(this.active){
17567             cfg.cls += ' active';
17568         }
17569         
17570         
17571         return cfg;
17572     }
17573    
17574 });
17575
17576  
17577
17578  /*
17579  * - LGPL
17580  *
17581  * ProgressBar
17582  * 
17583  */
17584
17585 /**
17586  * @class Roo.bootstrap.ProgressBar
17587  * @extends Roo.bootstrap.Component
17588  * Bootstrap ProgressBar class
17589  * @cfg {Number} aria_valuenow aria-value now
17590  * @cfg {Number} aria_valuemin aria-value min
17591  * @cfg {Number} aria_valuemax aria-value max
17592  * @cfg {String} label label for the progress bar
17593  * @cfg {String} panel (success | info | warning | danger )
17594  * @cfg {String} role role of the progress bar
17595  * @cfg {String} sr_only text
17596  * 
17597  * 
17598  * @constructor
17599  * Create a new ProgressBar
17600  * @param {Object} config The config object
17601  */
17602
17603 Roo.bootstrap.ProgressBar = function(config){
17604     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17605 };
17606
17607 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17608     
17609     aria_valuenow : 0,
17610     aria_valuemin : 0,
17611     aria_valuemax : 100,
17612     label : false,
17613     panel : false,
17614     role : false,
17615     sr_only: false,
17616     
17617     getAutoCreate : function()
17618     {
17619         
17620         var cfg = {
17621             tag: 'div',
17622             cls: 'progress-bar',
17623             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17624         };
17625         
17626         if(this.sr_only){
17627             cfg.cn = {
17628                 tag: 'span',
17629                 cls: 'sr-only',
17630                 html: this.sr_only
17631             }
17632         }
17633         
17634         if(this.role){
17635             cfg.role = this.role;
17636         }
17637         
17638         if(this.aria_valuenow){
17639             cfg['aria-valuenow'] = this.aria_valuenow;
17640         }
17641         
17642         if(this.aria_valuemin){
17643             cfg['aria-valuemin'] = this.aria_valuemin;
17644         }
17645         
17646         if(this.aria_valuemax){
17647             cfg['aria-valuemax'] = this.aria_valuemax;
17648         }
17649         
17650         if(this.label && !this.sr_only){
17651             cfg.html = this.label;
17652         }
17653         
17654         if(this.panel){
17655             cfg.cls += ' progress-bar-' + this.panel;
17656         }
17657         
17658         return cfg;
17659     },
17660     
17661     update : function(aria_valuenow)
17662     {
17663         this.aria_valuenow = aria_valuenow;
17664         
17665         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17666     }
17667    
17668 });
17669
17670  
17671
17672  /*
17673  * - LGPL
17674  *
17675  * column
17676  * 
17677  */
17678
17679 /**
17680  * @class Roo.bootstrap.TabGroup
17681  * @extends Roo.bootstrap.Column
17682  * Bootstrap Column class
17683  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17684  * @cfg {Boolean} carousel true to make the group behave like a carousel
17685  * @cfg {Boolean} bullets show bullets for the panels
17686  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17687  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17688  * @cfg {Boolean} showarrow (true|false) show arrow default true
17689  * 
17690  * @constructor
17691  * Create a new TabGroup
17692  * @param {Object} config The config object
17693  */
17694
17695 Roo.bootstrap.TabGroup = function(config){
17696     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17697     if (!this.navId) {
17698         this.navId = Roo.id();
17699     }
17700     this.tabs = [];
17701     Roo.bootstrap.TabGroup.register(this);
17702     
17703 };
17704
17705 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17706     
17707     carousel : false,
17708     transition : false,
17709     bullets : 0,
17710     timer : 0,
17711     autoslide : false,
17712     slideFn : false,
17713     slideOnTouch : false,
17714     showarrow : true,
17715     
17716     getAutoCreate : function()
17717     {
17718         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17719         
17720         cfg.cls += ' tab-content';
17721         
17722         if (this.carousel) {
17723             cfg.cls += ' carousel slide';
17724             
17725             cfg.cn = [{
17726                cls : 'carousel-inner',
17727                cn : []
17728             }];
17729         
17730             if(this.bullets  && !Roo.isTouch){
17731                 
17732                 var bullets = {
17733                     cls : 'carousel-bullets',
17734                     cn : []
17735                 };
17736                
17737                 if(this.bullets_cls){
17738                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17739                 }
17740                 
17741                 bullets.cn.push({
17742                     cls : 'clear'
17743                 });
17744                 
17745                 cfg.cn[0].cn.push(bullets);
17746             }
17747             
17748             if(this.showarrow){
17749                 cfg.cn[0].cn.push({
17750                     tag : 'div',
17751                     class : 'carousel-arrow',
17752                     cn : [
17753                         {
17754                             tag : 'div',
17755                             class : 'carousel-prev',
17756                             cn : [
17757                                 {
17758                                     tag : 'i',
17759                                     class : 'fa fa-chevron-left'
17760                                 }
17761                             ]
17762                         },
17763                         {
17764                             tag : 'div',
17765                             class : 'carousel-next',
17766                             cn : [
17767                                 {
17768                                     tag : 'i',
17769                                     class : 'fa fa-chevron-right'
17770                                 }
17771                             ]
17772                         }
17773                     ]
17774                 });
17775             }
17776             
17777         }
17778         
17779         return cfg;
17780     },
17781     
17782     initEvents:  function()
17783     {
17784 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17785 //            this.el.on("touchstart", this.onTouchStart, this);
17786 //        }
17787         
17788         if(this.autoslide){
17789             var _this = this;
17790             
17791             this.slideFn = window.setInterval(function() {
17792                 _this.showPanelNext();
17793             }, this.timer);
17794         }
17795         
17796         if(this.showarrow){
17797             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17798             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17799         }
17800         
17801         
17802     },
17803     
17804 //    onTouchStart : function(e, el, o)
17805 //    {
17806 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17807 //            return;
17808 //        }
17809 //        
17810 //        this.showPanelNext();
17811 //    },
17812     
17813     
17814     getChildContainer : function()
17815     {
17816         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17817     },
17818     
17819     /**
17820     * register a Navigation item
17821     * @param {Roo.bootstrap.NavItem} the navitem to add
17822     */
17823     register : function(item)
17824     {
17825         this.tabs.push( item);
17826         item.navId = this.navId; // not really needed..
17827         this.addBullet();
17828     
17829     },
17830     
17831     getActivePanel : function()
17832     {
17833         var r = false;
17834         Roo.each(this.tabs, function(t) {
17835             if (t.active) {
17836                 r = t;
17837                 return false;
17838             }
17839             return null;
17840         });
17841         return r;
17842         
17843     },
17844     getPanelByName : function(n)
17845     {
17846         var r = false;
17847         Roo.each(this.tabs, function(t) {
17848             if (t.tabId == n) {
17849                 r = t;
17850                 return false;
17851             }
17852             return null;
17853         });
17854         return r;
17855     },
17856     indexOfPanel : function(p)
17857     {
17858         var r = false;
17859         Roo.each(this.tabs, function(t,i) {
17860             if (t.tabId == p.tabId) {
17861                 r = i;
17862                 return false;
17863             }
17864             return null;
17865         });
17866         return r;
17867     },
17868     /**
17869      * show a specific panel
17870      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17871      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17872      */
17873     showPanel : function (pan)
17874     {
17875         if(this.transition || typeof(pan) == 'undefined'){
17876             Roo.log("waiting for the transitionend");
17877             return;
17878         }
17879         
17880         if (typeof(pan) == 'number') {
17881             pan = this.tabs[pan];
17882         }
17883         
17884         if (typeof(pan) == 'string') {
17885             pan = this.getPanelByName(pan);
17886         }
17887         
17888         var cur = this.getActivePanel();
17889         
17890         if(!pan || !cur){
17891             Roo.log('pan or acitve pan is undefined');
17892             return false;
17893         }
17894         
17895         if (pan.tabId == this.getActivePanel().tabId) {
17896             return true;
17897         }
17898         
17899         if (false === cur.fireEvent('beforedeactivate')) {
17900             return false;
17901         }
17902         
17903         if(this.bullets > 0 && !Roo.isTouch){
17904             this.setActiveBullet(this.indexOfPanel(pan));
17905         }
17906         
17907         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17908             
17909             this.transition = true;
17910             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17911             var lr = dir == 'next' ? 'left' : 'right';
17912             pan.el.addClass(dir); // or prev
17913             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17914             cur.el.addClass(lr); // or right
17915             pan.el.addClass(lr);
17916             
17917             var _this = this;
17918             cur.el.on('transitionend', function() {
17919                 Roo.log("trans end?");
17920                 
17921                 pan.el.removeClass([lr,dir]);
17922                 pan.setActive(true);
17923                 
17924                 cur.el.removeClass([lr]);
17925                 cur.setActive(false);
17926                 
17927                 _this.transition = false;
17928                 
17929             }, this, { single:  true } );
17930             
17931             return true;
17932         }
17933         
17934         cur.setActive(false);
17935         pan.setActive(true);
17936         
17937         return true;
17938         
17939     },
17940     showPanelNext : function()
17941     {
17942         var i = this.indexOfPanel(this.getActivePanel());
17943         
17944         if (i >= this.tabs.length - 1 && !this.autoslide) {
17945             return;
17946         }
17947         
17948         if (i >= this.tabs.length - 1 && this.autoslide) {
17949             i = -1;
17950         }
17951         
17952         this.showPanel(this.tabs[i+1]);
17953     },
17954     
17955     showPanelPrev : function()
17956     {
17957         var i = this.indexOfPanel(this.getActivePanel());
17958         
17959         if (i  < 1 && !this.autoslide) {
17960             return;
17961         }
17962         
17963         if (i < 1 && this.autoslide) {
17964             i = this.tabs.length;
17965         }
17966         
17967         this.showPanel(this.tabs[i-1]);
17968     },
17969     
17970     
17971     addBullet: function()
17972     {
17973         if(!this.bullets || Roo.isTouch){
17974             return;
17975         }
17976         var ctr = this.el.select('.carousel-bullets',true).first();
17977         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17978         var bullet = ctr.createChild({
17979             cls : 'bullet bullet-' + i
17980         },ctr.dom.lastChild);
17981         
17982         
17983         var _this = this;
17984         
17985         bullet.on('click', (function(e, el, o, ii, t){
17986
17987             e.preventDefault();
17988
17989             this.showPanel(ii);
17990
17991             if(this.autoslide && this.slideFn){
17992                 clearInterval(this.slideFn);
17993                 this.slideFn = window.setInterval(function() {
17994                     _this.showPanelNext();
17995                 }, this.timer);
17996             }
17997
17998         }).createDelegate(this, [i, bullet], true));
17999                 
18000         
18001     },
18002      
18003     setActiveBullet : function(i)
18004     {
18005         if(Roo.isTouch){
18006             return;
18007         }
18008         
18009         Roo.each(this.el.select('.bullet', true).elements, function(el){
18010             el.removeClass('selected');
18011         });
18012
18013         var bullet = this.el.select('.bullet-' + i, true).first();
18014         
18015         if(!bullet){
18016             return;
18017         }
18018         
18019         bullet.addClass('selected');
18020     }
18021     
18022     
18023   
18024 });
18025
18026  
18027
18028  
18029  
18030 Roo.apply(Roo.bootstrap.TabGroup, {
18031     
18032     groups: {},
18033      /**
18034     * register a Navigation Group
18035     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18036     */
18037     register : function(navgrp)
18038     {
18039         this.groups[navgrp.navId] = navgrp;
18040         
18041     },
18042     /**
18043     * fetch a Navigation Group based on the navigation ID
18044     * if one does not exist , it will get created.
18045     * @param {string} the navgroup to add
18046     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18047     */
18048     get: function(navId) {
18049         if (typeof(this.groups[navId]) == 'undefined') {
18050             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18051         }
18052         return this.groups[navId] ;
18053     }
18054     
18055     
18056     
18057 });
18058
18059  /*
18060  * - LGPL
18061  *
18062  * TabPanel
18063  * 
18064  */
18065
18066 /**
18067  * @class Roo.bootstrap.TabPanel
18068  * @extends Roo.bootstrap.Component
18069  * Bootstrap TabPanel class
18070  * @cfg {Boolean} active panel active
18071  * @cfg {String} html panel content
18072  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18073  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18074  * @cfg {String} href click to link..
18075  * 
18076  * 
18077  * @constructor
18078  * Create a new TabPanel
18079  * @param {Object} config The config object
18080  */
18081
18082 Roo.bootstrap.TabPanel = function(config){
18083     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18084     this.addEvents({
18085         /**
18086              * @event changed
18087              * Fires when the active status changes
18088              * @param {Roo.bootstrap.TabPanel} this
18089              * @param {Boolean} state the new state
18090             
18091          */
18092         'changed': true,
18093         /**
18094              * @event beforedeactivate
18095              * Fires before a tab is de-activated - can be used to do validation on a form.
18096              * @param {Roo.bootstrap.TabPanel} this
18097              * @return {Boolean} false if there is an error
18098             
18099          */
18100         'beforedeactivate': true
18101      });
18102     
18103     this.tabId = this.tabId || Roo.id();
18104   
18105 };
18106
18107 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18108     
18109     active: false,
18110     html: false,
18111     tabId: false,
18112     navId : false,
18113     href : '',
18114     
18115     getAutoCreate : function(){
18116         var cfg = {
18117             tag: 'div',
18118             // item is needed for carousel - not sure if it has any effect otherwise
18119             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18120             html: this.html || ''
18121         };
18122         
18123         if(this.active){
18124             cfg.cls += ' active';
18125         }
18126         
18127         if(this.tabId){
18128             cfg.tabId = this.tabId;
18129         }
18130         
18131         
18132         return cfg;
18133     },
18134     
18135     initEvents:  function()
18136     {
18137         var p = this.parent();
18138         
18139         this.navId = this.navId || p.navId;
18140         
18141         if (typeof(this.navId) != 'undefined') {
18142             // not really needed.. but just in case.. parent should be a NavGroup.
18143             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18144             
18145             tg.register(this);
18146             
18147             var i = tg.tabs.length - 1;
18148             
18149             if(this.active && tg.bullets > 0 && i < tg.bullets){
18150                 tg.setActiveBullet(i);
18151             }
18152         }
18153         
18154         this.el.on('click', this.onClick, this);
18155         
18156         if(Roo.isTouch){
18157             this.el.on("touchstart", this.onTouchStart, this);
18158             this.el.on("touchmove", this.onTouchMove, this);
18159             this.el.on("touchend", this.onTouchEnd, this);
18160         }
18161         
18162     },
18163     
18164     onRender : function(ct, position)
18165     {
18166         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18167     },
18168     
18169     setActive : function(state)
18170     {
18171         Roo.log("panel - set active " + this.tabId + "=" + state);
18172         
18173         this.active = state;
18174         if (!state) {
18175             this.el.removeClass('active');
18176             
18177         } else  if (!this.el.hasClass('active')) {
18178             this.el.addClass('active');
18179         }
18180         
18181         this.fireEvent('changed', this, state);
18182     },
18183     
18184     onClick : function(e)
18185     {
18186         e.preventDefault();
18187         
18188         if(!this.href.length){
18189             return;
18190         }
18191         
18192         window.location.href = this.href;
18193     },
18194     
18195     startX : 0,
18196     startY : 0,
18197     endX : 0,
18198     endY : 0,
18199     swiping : false,
18200     
18201     onTouchStart : function(e)
18202     {
18203         this.swiping = false;
18204         
18205         this.startX = e.browserEvent.touches[0].clientX;
18206         this.startY = e.browserEvent.touches[0].clientY;
18207     },
18208     
18209     onTouchMove : function(e)
18210     {
18211         this.swiping = true;
18212         
18213         this.endX = e.browserEvent.touches[0].clientX;
18214         this.endY = e.browserEvent.touches[0].clientY;
18215     },
18216     
18217     onTouchEnd : function(e)
18218     {
18219         if(!this.swiping){
18220             this.onClick(e);
18221             return;
18222         }
18223         
18224         var tabGroup = this.parent();
18225         
18226         if(this.endX > this.startX){ // swiping right
18227             tabGroup.showPanelPrev();
18228             return;
18229         }
18230         
18231         if(this.startX > this.endX){ // swiping left
18232             tabGroup.showPanelNext();
18233             return;
18234         }
18235     }
18236     
18237     
18238 });
18239  
18240
18241  
18242
18243  /*
18244  * - LGPL
18245  *
18246  * DateField
18247  * 
18248  */
18249
18250 /**
18251  * @class Roo.bootstrap.DateField
18252  * @extends Roo.bootstrap.Input
18253  * Bootstrap DateField class
18254  * @cfg {Number} weekStart default 0
18255  * @cfg {String} viewMode default empty, (months|years)
18256  * @cfg {String} minViewMode default empty, (months|years)
18257  * @cfg {Number} startDate default -Infinity
18258  * @cfg {Number} endDate default Infinity
18259  * @cfg {Boolean} todayHighlight default false
18260  * @cfg {Boolean} todayBtn default false
18261  * @cfg {Boolean} calendarWeeks default false
18262  * @cfg {Object} daysOfWeekDisabled default empty
18263  * @cfg {Boolean} singleMode default false (true | false)
18264  * 
18265  * @cfg {Boolean} keyboardNavigation default true
18266  * @cfg {String} language default en
18267  * 
18268  * @constructor
18269  * Create a new DateField
18270  * @param {Object} config The config object
18271  */
18272
18273 Roo.bootstrap.DateField = function(config){
18274     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18275      this.addEvents({
18276             /**
18277              * @event show
18278              * Fires when this field show.
18279              * @param {Roo.bootstrap.DateField} this
18280              * @param {Mixed} date The date value
18281              */
18282             show : true,
18283             /**
18284              * @event show
18285              * Fires when this field hide.
18286              * @param {Roo.bootstrap.DateField} this
18287              * @param {Mixed} date The date value
18288              */
18289             hide : true,
18290             /**
18291              * @event select
18292              * Fires when select a date.
18293              * @param {Roo.bootstrap.DateField} this
18294              * @param {Mixed} date The date value
18295              */
18296             select : true,
18297             /**
18298              * @event beforeselect
18299              * Fires when before select a date.
18300              * @param {Roo.bootstrap.DateField} this
18301              * @param {Mixed} date The date value
18302              */
18303             beforeselect : true
18304         });
18305 };
18306
18307 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18308     
18309     /**
18310      * @cfg {String} format
18311      * The default date format string which can be overriden for localization support.  The format must be
18312      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18313      */
18314     format : "m/d/y",
18315     /**
18316      * @cfg {String} altFormats
18317      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18318      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18319      */
18320     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18321     
18322     weekStart : 0,
18323     
18324     viewMode : '',
18325     
18326     minViewMode : '',
18327     
18328     todayHighlight : false,
18329     
18330     todayBtn: false,
18331     
18332     language: 'en',
18333     
18334     keyboardNavigation: true,
18335     
18336     calendarWeeks: false,
18337     
18338     startDate: -Infinity,
18339     
18340     endDate: Infinity,
18341     
18342     daysOfWeekDisabled: [],
18343     
18344     _events: [],
18345     
18346     singleMode : false,
18347     
18348     UTCDate: function()
18349     {
18350         return new Date(Date.UTC.apply(Date, arguments));
18351     },
18352     
18353     UTCToday: function()
18354     {
18355         var today = new Date();
18356         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18357     },
18358     
18359     getDate: function() {
18360             var d = this.getUTCDate();
18361             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18362     },
18363     
18364     getUTCDate: function() {
18365             return this.date;
18366     },
18367     
18368     setDate: function(d) {
18369             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18370     },
18371     
18372     setUTCDate: function(d) {
18373             this.date = d;
18374             this.setValue(this.formatDate(this.date));
18375     },
18376         
18377     onRender: function(ct, position)
18378     {
18379         
18380         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18381         
18382         this.language = this.language || 'en';
18383         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18384         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18385         
18386         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18387         this.format = this.format || 'm/d/y';
18388         this.isInline = false;
18389         this.isInput = true;
18390         this.component = this.el.select('.add-on', true).first() || false;
18391         this.component = (this.component && this.component.length === 0) ? false : this.component;
18392         this.hasInput = this.component && this.inputEl().length;
18393         
18394         if (typeof(this.minViewMode === 'string')) {
18395             switch (this.minViewMode) {
18396                 case 'months':
18397                     this.minViewMode = 1;
18398                     break;
18399                 case 'years':
18400                     this.minViewMode = 2;
18401                     break;
18402                 default:
18403                     this.minViewMode = 0;
18404                     break;
18405             }
18406         }
18407         
18408         if (typeof(this.viewMode === 'string')) {
18409             switch (this.viewMode) {
18410                 case 'months':
18411                     this.viewMode = 1;
18412                     break;
18413                 case 'years':
18414                     this.viewMode = 2;
18415                     break;
18416                 default:
18417                     this.viewMode = 0;
18418                     break;
18419             }
18420         }
18421                 
18422         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18423         
18424 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18425         
18426         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18427         
18428         this.picker().on('mousedown', this.onMousedown, this);
18429         this.picker().on('click', this.onClick, this);
18430         
18431         this.picker().addClass('datepicker-dropdown');
18432         
18433         this.startViewMode = this.viewMode;
18434         
18435         if(this.singleMode){
18436             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18437                 v.setVisibilityMode(Roo.Element.DISPLAY);
18438                 v.hide();
18439             });
18440             
18441             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18442                 v.setStyle('width', '189px');
18443             });
18444         }
18445         
18446         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18447             if(!this.calendarWeeks){
18448                 v.remove();
18449                 return;
18450             }
18451             
18452             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18453             v.attr('colspan', function(i, val){
18454                 return parseInt(val) + 1;
18455             });
18456         });
18457                         
18458         
18459         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18460         
18461         this.setStartDate(this.startDate);
18462         this.setEndDate(this.endDate);
18463         
18464         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18465         
18466         this.fillDow();
18467         this.fillMonths();
18468         this.update();
18469         this.showMode();
18470         
18471         if(this.isInline) {
18472             this.show();
18473         }
18474     },
18475     
18476     picker : function()
18477     {
18478         return this.pickerEl;
18479 //        return this.el.select('.datepicker', true).first();
18480     },
18481     
18482     fillDow: function()
18483     {
18484         var dowCnt = this.weekStart;
18485         
18486         var dow = {
18487             tag: 'tr',
18488             cn: [
18489                 
18490             ]
18491         };
18492         
18493         if(this.calendarWeeks){
18494             dow.cn.push({
18495                 tag: 'th',
18496                 cls: 'cw',
18497                 html: '&nbsp;'
18498             })
18499         }
18500         
18501         while (dowCnt < this.weekStart + 7) {
18502             dow.cn.push({
18503                 tag: 'th',
18504                 cls: 'dow',
18505                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18506             });
18507         }
18508         
18509         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18510     },
18511     
18512     fillMonths: function()
18513     {    
18514         var i = 0;
18515         var months = this.picker().select('>.datepicker-months td', true).first();
18516         
18517         months.dom.innerHTML = '';
18518         
18519         while (i < 12) {
18520             var month = {
18521                 tag: 'span',
18522                 cls: 'month',
18523                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18524             };
18525             
18526             months.createChild(month);
18527         }
18528         
18529     },
18530     
18531     update: function()
18532     {
18533         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;
18534         
18535         if (this.date < this.startDate) {
18536             this.viewDate = new Date(this.startDate);
18537         } else if (this.date > this.endDate) {
18538             this.viewDate = new Date(this.endDate);
18539         } else {
18540             this.viewDate = new Date(this.date);
18541         }
18542         
18543         this.fill();
18544     },
18545     
18546     fill: function() 
18547     {
18548         var d = new Date(this.viewDate),
18549                 year = d.getUTCFullYear(),
18550                 month = d.getUTCMonth(),
18551                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18552                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18553                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18554                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18555                 currentDate = this.date && this.date.valueOf(),
18556                 today = this.UTCToday();
18557         
18558         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18559         
18560 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18561         
18562 //        this.picker.select('>tfoot th.today').
18563 //                                              .text(dates[this.language].today)
18564 //                                              .toggle(this.todayBtn !== false);
18565     
18566         this.updateNavArrows();
18567         this.fillMonths();
18568                                                 
18569         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18570         
18571         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18572          
18573         prevMonth.setUTCDate(day);
18574         
18575         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18576         
18577         var nextMonth = new Date(prevMonth);
18578         
18579         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18580         
18581         nextMonth = nextMonth.valueOf();
18582         
18583         var fillMonths = false;
18584         
18585         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18586         
18587         while(prevMonth.valueOf() < nextMonth) {
18588             var clsName = '';
18589             
18590             if (prevMonth.getUTCDay() === this.weekStart) {
18591                 if(fillMonths){
18592                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18593                 }
18594                     
18595                 fillMonths = {
18596                     tag: 'tr',
18597                     cn: []
18598                 };
18599                 
18600                 if(this.calendarWeeks){
18601                     // ISO 8601: First week contains first thursday.
18602                     // ISO also states week starts on Monday, but we can be more abstract here.
18603                     var
18604                     // Start of current week: based on weekstart/current date
18605                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18606                     // Thursday of this week
18607                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18608                     // First Thursday of year, year from thursday
18609                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18610                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18611                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18612                     
18613                     fillMonths.cn.push({
18614                         tag: 'td',
18615                         cls: 'cw',
18616                         html: calWeek
18617                     });
18618                 }
18619             }
18620             
18621             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18622                 clsName += ' old';
18623             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18624                 clsName += ' new';
18625             }
18626             if (this.todayHighlight &&
18627                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18628                 prevMonth.getUTCMonth() == today.getMonth() &&
18629                 prevMonth.getUTCDate() == today.getDate()) {
18630                 clsName += ' today';
18631             }
18632             
18633             if (currentDate && prevMonth.valueOf() === currentDate) {
18634                 clsName += ' active';
18635             }
18636             
18637             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18638                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18639                     clsName += ' disabled';
18640             }
18641             
18642             fillMonths.cn.push({
18643                 tag: 'td',
18644                 cls: 'day ' + clsName,
18645                 html: prevMonth.getDate()
18646             });
18647             
18648             prevMonth.setDate(prevMonth.getDate()+1);
18649         }
18650           
18651         var currentYear = this.date && this.date.getUTCFullYear();
18652         var currentMonth = this.date && this.date.getUTCMonth();
18653         
18654         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18655         
18656         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18657             v.removeClass('active');
18658             
18659             if(currentYear === year && k === currentMonth){
18660                 v.addClass('active');
18661             }
18662             
18663             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18664                 v.addClass('disabled');
18665             }
18666             
18667         });
18668         
18669         
18670         year = parseInt(year/10, 10) * 10;
18671         
18672         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18673         
18674         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18675         
18676         year -= 1;
18677         for (var i = -1; i < 11; i++) {
18678             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18679                 tag: 'span',
18680                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18681                 html: year
18682             });
18683             
18684             year += 1;
18685         }
18686     },
18687     
18688     showMode: function(dir) 
18689     {
18690         if (dir) {
18691             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18692         }
18693         
18694         Roo.each(this.picker().select('>div',true).elements, function(v){
18695             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18696             v.hide();
18697         });
18698         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18699     },
18700     
18701     place: function()
18702     {
18703         if(this.isInline) {
18704             return;
18705         }
18706         
18707         this.picker().removeClass(['bottom', 'top']);
18708         
18709         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18710             /*
18711              * place to the top of element!
18712              *
18713              */
18714             
18715             this.picker().addClass('top');
18716             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18717             
18718             return;
18719         }
18720         
18721         this.picker().addClass('bottom');
18722         
18723         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18724     },
18725     
18726     parseDate : function(value)
18727     {
18728         if(!value || value instanceof Date){
18729             return value;
18730         }
18731         var v = Date.parseDate(value, this.format);
18732         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18733             v = Date.parseDate(value, 'Y-m-d');
18734         }
18735         if(!v && this.altFormats){
18736             if(!this.altFormatsArray){
18737                 this.altFormatsArray = this.altFormats.split("|");
18738             }
18739             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18740                 v = Date.parseDate(value, this.altFormatsArray[i]);
18741             }
18742         }
18743         return v;
18744     },
18745     
18746     formatDate : function(date, fmt)
18747     {   
18748         return (!date || !(date instanceof Date)) ?
18749         date : date.dateFormat(fmt || this.format);
18750     },
18751     
18752     onFocus : function()
18753     {
18754         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18755         this.show();
18756     },
18757     
18758     onBlur : function()
18759     {
18760         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18761         
18762         var d = this.inputEl().getValue();
18763         
18764         this.setValue(d);
18765                 
18766         this.hide();
18767     },
18768     
18769     show : function()
18770     {
18771         this.picker().show();
18772         this.update();
18773         this.place();
18774         
18775         this.fireEvent('show', this, this.date);
18776     },
18777     
18778     hide : function()
18779     {
18780         if(this.isInline) {
18781             return;
18782         }
18783         this.picker().hide();
18784         this.viewMode = this.startViewMode;
18785         this.showMode();
18786         
18787         this.fireEvent('hide', this, this.date);
18788         
18789     },
18790     
18791     onMousedown: function(e)
18792     {
18793         e.stopPropagation();
18794         e.preventDefault();
18795     },
18796     
18797     keyup: function(e)
18798     {
18799         Roo.bootstrap.DateField.superclass.keyup.call(this);
18800         this.update();
18801     },
18802
18803     setValue: function(v)
18804     {
18805         if(this.fireEvent('beforeselect', this, v) !== false){
18806             var d = new Date(this.parseDate(v) ).clearTime();
18807         
18808             if(isNaN(d.getTime())){
18809                 this.date = this.viewDate = '';
18810                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18811                 return;
18812             }
18813
18814             v = this.formatDate(d);
18815
18816             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18817
18818             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18819
18820             this.update();
18821
18822             this.fireEvent('select', this, this.date);
18823         }
18824     },
18825     
18826     getValue: function()
18827     {
18828         return this.formatDate(this.date);
18829     },
18830     
18831     fireKey: function(e)
18832     {
18833         if (!this.picker().isVisible()){
18834             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18835                 this.show();
18836             }
18837             return;
18838         }
18839         
18840         var dateChanged = false,
18841         dir, day, month,
18842         newDate, newViewDate;
18843         
18844         switch(e.keyCode){
18845             case 27: // escape
18846                 this.hide();
18847                 e.preventDefault();
18848                 break;
18849             case 37: // left
18850             case 39: // right
18851                 if (!this.keyboardNavigation) {
18852                     break;
18853                 }
18854                 dir = e.keyCode == 37 ? -1 : 1;
18855                 
18856                 if (e.ctrlKey){
18857                     newDate = this.moveYear(this.date, dir);
18858                     newViewDate = this.moveYear(this.viewDate, dir);
18859                 } else if (e.shiftKey){
18860                     newDate = this.moveMonth(this.date, dir);
18861                     newViewDate = this.moveMonth(this.viewDate, dir);
18862                 } else {
18863                     newDate = new Date(this.date);
18864                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18865                     newViewDate = new Date(this.viewDate);
18866                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18867                 }
18868                 if (this.dateWithinRange(newDate)){
18869                     this.date = newDate;
18870                     this.viewDate = newViewDate;
18871                     this.setValue(this.formatDate(this.date));
18872 //                    this.update();
18873                     e.preventDefault();
18874                     dateChanged = true;
18875                 }
18876                 break;
18877             case 38: // up
18878             case 40: // down
18879                 if (!this.keyboardNavigation) {
18880                     break;
18881                 }
18882                 dir = e.keyCode == 38 ? -1 : 1;
18883                 if (e.ctrlKey){
18884                     newDate = this.moveYear(this.date, dir);
18885                     newViewDate = this.moveYear(this.viewDate, dir);
18886                 } else if (e.shiftKey){
18887                     newDate = this.moveMonth(this.date, dir);
18888                     newViewDate = this.moveMonth(this.viewDate, dir);
18889                 } else {
18890                     newDate = new Date(this.date);
18891                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18892                     newViewDate = new Date(this.viewDate);
18893                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18894                 }
18895                 if (this.dateWithinRange(newDate)){
18896                     this.date = newDate;
18897                     this.viewDate = newViewDate;
18898                     this.setValue(this.formatDate(this.date));
18899 //                    this.update();
18900                     e.preventDefault();
18901                     dateChanged = true;
18902                 }
18903                 break;
18904             case 13: // enter
18905                 this.setValue(this.formatDate(this.date));
18906                 this.hide();
18907                 e.preventDefault();
18908                 break;
18909             case 9: // tab
18910                 this.setValue(this.formatDate(this.date));
18911                 this.hide();
18912                 break;
18913             case 16: // shift
18914             case 17: // ctrl
18915             case 18: // alt
18916                 break;
18917             default :
18918                 this.hide();
18919                 
18920         }
18921     },
18922     
18923     
18924     onClick: function(e) 
18925     {
18926         e.stopPropagation();
18927         e.preventDefault();
18928         
18929         var target = e.getTarget();
18930         
18931         if(target.nodeName.toLowerCase() === 'i'){
18932             target = Roo.get(target).dom.parentNode;
18933         }
18934         
18935         var nodeName = target.nodeName;
18936         var className = target.className;
18937         var html = target.innerHTML;
18938         //Roo.log(nodeName);
18939         
18940         switch(nodeName.toLowerCase()) {
18941             case 'th':
18942                 switch(className) {
18943                     case 'switch':
18944                         this.showMode(1);
18945                         break;
18946                     case 'prev':
18947                     case 'next':
18948                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18949                         switch(this.viewMode){
18950                                 case 0:
18951                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18952                                         break;
18953                                 case 1:
18954                                 case 2:
18955                                         this.viewDate = this.moveYear(this.viewDate, dir);
18956                                         break;
18957                         }
18958                         this.fill();
18959                         break;
18960                     case 'today':
18961                         var date = new Date();
18962                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18963 //                        this.fill()
18964                         this.setValue(this.formatDate(this.date));
18965                         
18966                         this.hide();
18967                         break;
18968                 }
18969                 break;
18970             case 'span':
18971                 if (className.indexOf('disabled') < 0) {
18972                     this.viewDate.setUTCDate(1);
18973                     if (className.indexOf('month') > -1) {
18974                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18975                     } else {
18976                         var year = parseInt(html, 10) || 0;
18977                         this.viewDate.setUTCFullYear(year);
18978                         
18979                     }
18980                     
18981                     if(this.singleMode){
18982                         this.setValue(this.formatDate(this.viewDate));
18983                         this.hide();
18984                         return;
18985                     }
18986                     
18987                     this.showMode(-1);
18988                     this.fill();
18989                 }
18990                 break;
18991                 
18992             case 'td':
18993                 //Roo.log(className);
18994                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18995                     var day = parseInt(html, 10) || 1;
18996                     var year = this.viewDate.getUTCFullYear(),
18997                         month = this.viewDate.getUTCMonth();
18998
18999                     if (className.indexOf('old') > -1) {
19000                         if(month === 0 ){
19001                             month = 11;
19002                             year -= 1;
19003                         }else{
19004                             month -= 1;
19005                         }
19006                     } else if (className.indexOf('new') > -1) {
19007                         if (month == 11) {
19008                             month = 0;
19009                             year += 1;
19010                         } else {
19011                             month += 1;
19012                         }
19013                     }
19014                     //Roo.log([year,month,day]);
19015                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19016                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19017 //                    this.fill();
19018                     //Roo.log(this.formatDate(this.date));
19019                     this.setValue(this.formatDate(this.date));
19020                     this.hide();
19021                 }
19022                 break;
19023         }
19024     },
19025     
19026     setStartDate: function(startDate)
19027     {
19028         this.startDate = startDate || -Infinity;
19029         if (this.startDate !== -Infinity) {
19030             this.startDate = this.parseDate(this.startDate);
19031         }
19032         this.update();
19033         this.updateNavArrows();
19034     },
19035
19036     setEndDate: function(endDate)
19037     {
19038         this.endDate = endDate || Infinity;
19039         if (this.endDate !== Infinity) {
19040             this.endDate = this.parseDate(this.endDate);
19041         }
19042         this.update();
19043         this.updateNavArrows();
19044     },
19045     
19046     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19047     {
19048         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19049         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19050             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19051         }
19052         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19053             return parseInt(d, 10);
19054         });
19055         this.update();
19056         this.updateNavArrows();
19057     },
19058     
19059     updateNavArrows: function() 
19060     {
19061         if(this.singleMode){
19062             return;
19063         }
19064         
19065         var d = new Date(this.viewDate),
19066         year = d.getUTCFullYear(),
19067         month = d.getUTCMonth();
19068         
19069         Roo.each(this.picker().select('.prev', true).elements, function(v){
19070             v.show();
19071             switch (this.viewMode) {
19072                 case 0:
19073
19074                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19075                         v.hide();
19076                     }
19077                     break;
19078                 case 1:
19079                 case 2:
19080                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19081                         v.hide();
19082                     }
19083                     break;
19084             }
19085         });
19086         
19087         Roo.each(this.picker().select('.next', true).elements, function(v){
19088             v.show();
19089             switch (this.viewMode) {
19090                 case 0:
19091
19092                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19093                         v.hide();
19094                     }
19095                     break;
19096                 case 1:
19097                 case 2:
19098                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19099                         v.hide();
19100                     }
19101                     break;
19102             }
19103         })
19104     },
19105     
19106     moveMonth: function(date, dir)
19107     {
19108         if (!dir) {
19109             return date;
19110         }
19111         var new_date = new Date(date.valueOf()),
19112         day = new_date.getUTCDate(),
19113         month = new_date.getUTCMonth(),
19114         mag = Math.abs(dir),
19115         new_month, test;
19116         dir = dir > 0 ? 1 : -1;
19117         if (mag == 1){
19118             test = dir == -1
19119             // If going back one month, make sure month is not current month
19120             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19121             ? function(){
19122                 return new_date.getUTCMonth() == month;
19123             }
19124             // If going forward one month, make sure month is as expected
19125             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19126             : function(){
19127                 return new_date.getUTCMonth() != new_month;
19128             };
19129             new_month = month + dir;
19130             new_date.setUTCMonth(new_month);
19131             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19132             if (new_month < 0 || new_month > 11) {
19133                 new_month = (new_month + 12) % 12;
19134             }
19135         } else {
19136             // For magnitudes >1, move one month at a time...
19137             for (var i=0; i<mag; i++) {
19138                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19139                 new_date = this.moveMonth(new_date, dir);
19140             }
19141             // ...then reset the day, keeping it in the new month
19142             new_month = new_date.getUTCMonth();
19143             new_date.setUTCDate(day);
19144             test = function(){
19145                 return new_month != new_date.getUTCMonth();
19146             };
19147         }
19148         // Common date-resetting loop -- if date is beyond end of month, make it
19149         // end of month
19150         while (test()){
19151             new_date.setUTCDate(--day);
19152             new_date.setUTCMonth(new_month);
19153         }
19154         return new_date;
19155     },
19156
19157     moveYear: function(date, dir)
19158     {
19159         return this.moveMonth(date, dir*12);
19160     },
19161
19162     dateWithinRange: function(date)
19163     {
19164         return date >= this.startDate && date <= this.endDate;
19165     },
19166
19167     
19168     remove: function() 
19169     {
19170         this.picker().remove();
19171     },
19172     
19173     validateValue : function(value)
19174     {
19175         if(this.getVisibilityEl().hasClass('hidden')){
19176             return true;
19177         }
19178         
19179         if(value.length < 1)  {
19180             if(this.allowBlank){
19181                 return true;
19182             }
19183             return false;
19184         }
19185         
19186         if(value.length < this.minLength){
19187             return false;
19188         }
19189         if(value.length > this.maxLength){
19190             return false;
19191         }
19192         if(this.vtype){
19193             var vt = Roo.form.VTypes;
19194             if(!vt[this.vtype](value, this)){
19195                 return false;
19196             }
19197         }
19198         if(typeof this.validator == "function"){
19199             var msg = this.validator(value);
19200             if(msg !== true){
19201                 return false;
19202             }
19203         }
19204         
19205         if(this.regex && !this.regex.test(value)){
19206             return false;
19207         }
19208         
19209         if(typeof(this.parseDate(value)) == 'undefined'){
19210             return false;
19211         }
19212         
19213         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19214             return false;
19215         }      
19216         
19217         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19218             return false;
19219         } 
19220         
19221         
19222         return true;
19223     },
19224     
19225     setVisible : function(visible)
19226     {
19227         if(!this.getEl()){
19228             return;
19229         }
19230         
19231         this.getEl().removeClass('hidden');
19232         
19233         if(visible){
19234             return;
19235         }
19236         
19237         this.getEl().addClass('hidden');
19238     }
19239    
19240 });
19241
19242 Roo.apply(Roo.bootstrap.DateField,  {
19243     
19244     head : {
19245         tag: 'thead',
19246         cn: [
19247         {
19248             tag: 'tr',
19249             cn: [
19250             {
19251                 tag: 'th',
19252                 cls: 'prev',
19253                 html: '<i class="fa fa-arrow-left"/>'
19254             },
19255             {
19256                 tag: 'th',
19257                 cls: 'switch',
19258                 colspan: '5'
19259             },
19260             {
19261                 tag: 'th',
19262                 cls: 'next',
19263                 html: '<i class="fa fa-arrow-right"/>'
19264             }
19265
19266             ]
19267         }
19268         ]
19269     },
19270     
19271     content : {
19272         tag: 'tbody',
19273         cn: [
19274         {
19275             tag: 'tr',
19276             cn: [
19277             {
19278                 tag: 'td',
19279                 colspan: '7'
19280             }
19281             ]
19282         }
19283         ]
19284     },
19285     
19286     footer : {
19287         tag: 'tfoot',
19288         cn: [
19289         {
19290             tag: 'tr',
19291             cn: [
19292             {
19293                 tag: 'th',
19294                 colspan: '7',
19295                 cls: 'today'
19296             }
19297                     
19298             ]
19299         }
19300         ]
19301     },
19302     
19303     dates:{
19304         en: {
19305             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19306             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19307             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19308             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19309             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19310             today: "Today"
19311         }
19312     },
19313     
19314     modes: [
19315     {
19316         clsName: 'days',
19317         navFnc: 'Month',
19318         navStep: 1
19319     },
19320     {
19321         clsName: 'months',
19322         navFnc: 'FullYear',
19323         navStep: 1
19324     },
19325     {
19326         clsName: 'years',
19327         navFnc: 'FullYear',
19328         navStep: 10
19329     }]
19330 });
19331
19332 Roo.apply(Roo.bootstrap.DateField,  {
19333   
19334     template : {
19335         tag: 'div',
19336         cls: 'datepicker dropdown-menu roo-dynamic',
19337         cn: [
19338         {
19339             tag: 'div',
19340             cls: 'datepicker-days',
19341             cn: [
19342             {
19343                 tag: 'table',
19344                 cls: 'table-condensed',
19345                 cn:[
19346                 Roo.bootstrap.DateField.head,
19347                 {
19348                     tag: 'tbody'
19349                 },
19350                 Roo.bootstrap.DateField.footer
19351                 ]
19352             }
19353             ]
19354         },
19355         {
19356             tag: 'div',
19357             cls: 'datepicker-months',
19358             cn: [
19359             {
19360                 tag: 'table',
19361                 cls: 'table-condensed',
19362                 cn:[
19363                 Roo.bootstrap.DateField.head,
19364                 Roo.bootstrap.DateField.content,
19365                 Roo.bootstrap.DateField.footer
19366                 ]
19367             }
19368             ]
19369         },
19370         {
19371             tag: 'div',
19372             cls: 'datepicker-years',
19373             cn: [
19374             {
19375                 tag: 'table',
19376                 cls: 'table-condensed',
19377                 cn:[
19378                 Roo.bootstrap.DateField.head,
19379                 Roo.bootstrap.DateField.content,
19380                 Roo.bootstrap.DateField.footer
19381                 ]
19382             }
19383             ]
19384         }
19385         ]
19386     }
19387 });
19388
19389  
19390
19391  /*
19392  * - LGPL
19393  *
19394  * TimeField
19395  * 
19396  */
19397
19398 /**
19399  * @class Roo.bootstrap.TimeField
19400  * @extends Roo.bootstrap.Input
19401  * Bootstrap DateField class
19402  * 
19403  * 
19404  * @constructor
19405  * Create a new TimeField
19406  * @param {Object} config The config object
19407  */
19408
19409 Roo.bootstrap.TimeField = function(config){
19410     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19411     this.addEvents({
19412             /**
19413              * @event show
19414              * Fires when this field show.
19415              * @param {Roo.bootstrap.DateField} thisthis
19416              * @param {Mixed} date The date value
19417              */
19418             show : true,
19419             /**
19420              * @event show
19421              * Fires when this field hide.
19422              * @param {Roo.bootstrap.DateField} this
19423              * @param {Mixed} date The date value
19424              */
19425             hide : true,
19426             /**
19427              * @event select
19428              * Fires when select a date.
19429              * @param {Roo.bootstrap.DateField} this
19430              * @param {Mixed} date The date value
19431              */
19432             select : true
19433         });
19434 };
19435
19436 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19437     
19438     /**
19439      * @cfg {String} format
19440      * The default time format string which can be overriden for localization support.  The format must be
19441      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19442      */
19443     format : "H:i",
19444        
19445     onRender: function(ct, position)
19446     {
19447         
19448         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19449                 
19450         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19451         
19452         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19453         
19454         this.pop = this.picker().select('>.datepicker-time',true).first();
19455         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19456         
19457         this.picker().on('mousedown', this.onMousedown, this);
19458         this.picker().on('click', this.onClick, this);
19459         
19460         this.picker().addClass('datepicker-dropdown');
19461     
19462         this.fillTime();
19463         this.update();
19464             
19465         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19466         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19467         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19468         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19469         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19470         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19471
19472     },
19473     
19474     fireKey: function(e){
19475         if (!this.picker().isVisible()){
19476             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19477                 this.show();
19478             }
19479             return;
19480         }
19481
19482         e.preventDefault();
19483         
19484         switch(e.keyCode){
19485             case 27: // escape
19486                 this.hide();
19487                 break;
19488             case 37: // left
19489             case 39: // right
19490                 this.onTogglePeriod();
19491                 break;
19492             case 38: // up
19493                 this.onIncrementMinutes();
19494                 break;
19495             case 40: // down
19496                 this.onDecrementMinutes();
19497                 break;
19498             case 13: // enter
19499             case 9: // tab
19500                 this.setTime();
19501                 break;
19502         }
19503     },
19504     
19505     onClick: function(e) {
19506         e.stopPropagation();
19507         e.preventDefault();
19508     },
19509     
19510     picker : function()
19511     {
19512         return this.el.select('.datepicker', true).first();
19513     },
19514     
19515     fillTime: function()
19516     {    
19517         var time = this.pop.select('tbody', true).first();
19518         
19519         time.dom.innerHTML = '';
19520         
19521         time.createChild({
19522             tag: 'tr',
19523             cn: [
19524                 {
19525                     tag: 'td',
19526                     cn: [
19527                         {
19528                             tag: 'a',
19529                             href: '#',
19530                             cls: 'btn',
19531                             cn: [
19532                                 {
19533                                     tag: 'span',
19534                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19535                                 }
19536                             ]
19537                         } 
19538                     ]
19539                 },
19540                 {
19541                     tag: 'td',
19542                     cls: 'separator'
19543                 },
19544                 {
19545                     tag: 'td',
19546                     cn: [
19547                         {
19548                             tag: 'a',
19549                             href: '#',
19550                             cls: 'btn',
19551                             cn: [
19552                                 {
19553                                     tag: 'span',
19554                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19555                                 }
19556                             ]
19557                         }
19558                     ]
19559                 },
19560                 {
19561                     tag: 'td',
19562                     cls: 'separator'
19563                 }
19564             ]
19565         });
19566         
19567         time.createChild({
19568             tag: 'tr',
19569             cn: [
19570                 {
19571                     tag: 'td',
19572                     cn: [
19573                         {
19574                             tag: 'span',
19575                             cls: 'timepicker-hour',
19576                             html: '00'
19577                         }  
19578                     ]
19579                 },
19580                 {
19581                     tag: 'td',
19582                     cls: 'separator',
19583                     html: ':'
19584                 },
19585                 {
19586                     tag: 'td',
19587                     cn: [
19588                         {
19589                             tag: 'span',
19590                             cls: 'timepicker-minute',
19591                             html: '00'
19592                         }  
19593                     ]
19594                 },
19595                 {
19596                     tag: 'td',
19597                     cls: 'separator'
19598                 },
19599                 {
19600                     tag: 'td',
19601                     cn: [
19602                         {
19603                             tag: 'button',
19604                             type: 'button',
19605                             cls: 'btn btn-primary period',
19606                             html: 'AM'
19607                             
19608                         }
19609                     ]
19610                 }
19611             ]
19612         });
19613         
19614         time.createChild({
19615             tag: 'tr',
19616             cn: [
19617                 {
19618                     tag: 'td',
19619                     cn: [
19620                         {
19621                             tag: 'a',
19622                             href: '#',
19623                             cls: 'btn',
19624                             cn: [
19625                                 {
19626                                     tag: 'span',
19627                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19628                                 }
19629                             ]
19630                         }
19631                     ]
19632                 },
19633                 {
19634                     tag: 'td',
19635                     cls: 'separator'
19636                 },
19637                 {
19638                     tag: 'td',
19639                     cn: [
19640                         {
19641                             tag: 'a',
19642                             href: '#',
19643                             cls: 'btn',
19644                             cn: [
19645                                 {
19646                                     tag: 'span',
19647                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19648                                 }
19649                             ]
19650                         }
19651                     ]
19652                 },
19653                 {
19654                     tag: 'td',
19655                     cls: 'separator'
19656                 }
19657             ]
19658         });
19659         
19660     },
19661     
19662     update: function()
19663     {
19664         
19665         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19666         
19667         this.fill();
19668     },
19669     
19670     fill: function() 
19671     {
19672         var hours = this.time.getHours();
19673         var minutes = this.time.getMinutes();
19674         var period = 'AM';
19675         
19676         if(hours > 11){
19677             period = 'PM';
19678         }
19679         
19680         if(hours == 0){
19681             hours = 12;
19682         }
19683         
19684         
19685         if(hours > 12){
19686             hours = hours - 12;
19687         }
19688         
19689         if(hours < 10){
19690             hours = '0' + hours;
19691         }
19692         
19693         if(minutes < 10){
19694             minutes = '0' + minutes;
19695         }
19696         
19697         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19698         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19699         this.pop.select('button', true).first().dom.innerHTML = period;
19700         
19701     },
19702     
19703     place: function()
19704     {   
19705         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19706         
19707         var cls = ['bottom'];
19708         
19709         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19710             cls.pop();
19711             cls.push('top');
19712         }
19713         
19714         cls.push('right');
19715         
19716         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19717             cls.pop();
19718             cls.push('left');
19719         }
19720         
19721         this.picker().addClass(cls.join('-'));
19722         
19723         var _this = this;
19724         
19725         Roo.each(cls, function(c){
19726             if(c == 'bottom'){
19727                 _this.picker().setTop(_this.inputEl().getHeight());
19728                 return;
19729             }
19730             if(c == 'top'){
19731                 _this.picker().setTop(0 - _this.picker().getHeight());
19732                 return;
19733             }
19734             
19735             if(c == 'left'){
19736                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19737                 return;
19738             }
19739             if(c == 'right'){
19740                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19741                 return;
19742             }
19743         });
19744         
19745     },
19746   
19747     onFocus : function()
19748     {
19749         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19750         this.show();
19751     },
19752     
19753     onBlur : function()
19754     {
19755         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19756         this.hide();
19757     },
19758     
19759     show : function()
19760     {
19761         this.picker().show();
19762         this.pop.show();
19763         this.update();
19764         this.place();
19765         
19766         this.fireEvent('show', this, this.date);
19767     },
19768     
19769     hide : function()
19770     {
19771         this.picker().hide();
19772         this.pop.hide();
19773         
19774         this.fireEvent('hide', this, this.date);
19775     },
19776     
19777     setTime : function()
19778     {
19779         this.hide();
19780         this.setValue(this.time.format(this.format));
19781         
19782         this.fireEvent('select', this, this.date);
19783         
19784         
19785     },
19786     
19787     onMousedown: function(e){
19788         e.stopPropagation();
19789         e.preventDefault();
19790     },
19791     
19792     onIncrementHours: function()
19793     {
19794         Roo.log('onIncrementHours');
19795         this.time = this.time.add(Date.HOUR, 1);
19796         this.update();
19797         
19798     },
19799     
19800     onDecrementHours: function()
19801     {
19802         Roo.log('onDecrementHours');
19803         this.time = this.time.add(Date.HOUR, -1);
19804         this.update();
19805     },
19806     
19807     onIncrementMinutes: function()
19808     {
19809         Roo.log('onIncrementMinutes');
19810         this.time = this.time.add(Date.MINUTE, 1);
19811         this.update();
19812     },
19813     
19814     onDecrementMinutes: function()
19815     {
19816         Roo.log('onDecrementMinutes');
19817         this.time = this.time.add(Date.MINUTE, -1);
19818         this.update();
19819     },
19820     
19821     onTogglePeriod: function()
19822     {
19823         Roo.log('onTogglePeriod');
19824         this.time = this.time.add(Date.HOUR, 12);
19825         this.update();
19826     }
19827     
19828    
19829 });
19830
19831 Roo.apply(Roo.bootstrap.TimeField,  {
19832     
19833     content : {
19834         tag: 'tbody',
19835         cn: [
19836             {
19837                 tag: 'tr',
19838                 cn: [
19839                 {
19840                     tag: 'td',
19841                     colspan: '7'
19842                 }
19843                 ]
19844             }
19845         ]
19846     },
19847     
19848     footer : {
19849         tag: 'tfoot',
19850         cn: [
19851             {
19852                 tag: 'tr',
19853                 cn: [
19854                 {
19855                     tag: 'th',
19856                     colspan: '7',
19857                     cls: '',
19858                     cn: [
19859                         {
19860                             tag: 'button',
19861                             cls: 'btn btn-info ok',
19862                             html: 'OK'
19863                         }
19864                     ]
19865                 }
19866
19867                 ]
19868             }
19869         ]
19870     }
19871 });
19872
19873 Roo.apply(Roo.bootstrap.TimeField,  {
19874   
19875     template : {
19876         tag: 'div',
19877         cls: 'datepicker dropdown-menu',
19878         cn: [
19879             {
19880                 tag: 'div',
19881                 cls: 'datepicker-time',
19882                 cn: [
19883                 {
19884                     tag: 'table',
19885                     cls: 'table-condensed',
19886                     cn:[
19887                     Roo.bootstrap.TimeField.content,
19888                     Roo.bootstrap.TimeField.footer
19889                     ]
19890                 }
19891                 ]
19892             }
19893         ]
19894     }
19895 });
19896
19897  
19898
19899  /*
19900  * - LGPL
19901  *
19902  * MonthField
19903  * 
19904  */
19905
19906 /**
19907  * @class Roo.bootstrap.MonthField
19908  * @extends Roo.bootstrap.Input
19909  * Bootstrap MonthField class
19910  * 
19911  * @cfg {String} language default en
19912  * 
19913  * @constructor
19914  * Create a new MonthField
19915  * @param {Object} config The config object
19916  */
19917
19918 Roo.bootstrap.MonthField = function(config){
19919     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19920     
19921     this.addEvents({
19922         /**
19923          * @event show
19924          * Fires when this field show.
19925          * @param {Roo.bootstrap.MonthField} this
19926          * @param {Mixed} date The date value
19927          */
19928         show : true,
19929         /**
19930          * @event show
19931          * Fires when this field hide.
19932          * @param {Roo.bootstrap.MonthField} this
19933          * @param {Mixed} date The date value
19934          */
19935         hide : true,
19936         /**
19937          * @event select
19938          * Fires when select a date.
19939          * @param {Roo.bootstrap.MonthField} this
19940          * @param {String} oldvalue The old value
19941          * @param {String} newvalue The new value
19942          */
19943         select : true
19944     });
19945 };
19946
19947 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19948     
19949     onRender: function(ct, position)
19950     {
19951         
19952         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19953         
19954         this.language = this.language || 'en';
19955         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19956         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19957         
19958         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19959         this.isInline = false;
19960         this.isInput = true;
19961         this.component = this.el.select('.add-on', true).first() || false;
19962         this.component = (this.component && this.component.length === 0) ? false : this.component;
19963         this.hasInput = this.component && this.inputEL().length;
19964         
19965         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19966         
19967         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19968         
19969         this.picker().on('mousedown', this.onMousedown, this);
19970         this.picker().on('click', this.onClick, this);
19971         
19972         this.picker().addClass('datepicker-dropdown');
19973         
19974         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19975             v.setStyle('width', '189px');
19976         });
19977         
19978         this.fillMonths();
19979         
19980         this.update();
19981         
19982         if(this.isInline) {
19983             this.show();
19984         }
19985         
19986     },
19987     
19988     setValue: function(v, suppressEvent)
19989     {   
19990         var o = this.getValue();
19991         
19992         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19993         
19994         this.update();
19995
19996         if(suppressEvent !== true){
19997             this.fireEvent('select', this, o, v);
19998         }
19999         
20000     },
20001     
20002     getValue: function()
20003     {
20004         return this.value;
20005     },
20006     
20007     onClick: function(e) 
20008     {
20009         e.stopPropagation();
20010         e.preventDefault();
20011         
20012         var target = e.getTarget();
20013         
20014         if(target.nodeName.toLowerCase() === 'i'){
20015             target = Roo.get(target).dom.parentNode;
20016         }
20017         
20018         var nodeName = target.nodeName;
20019         var className = target.className;
20020         var html = target.innerHTML;
20021         
20022         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20023             return;
20024         }
20025         
20026         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20027         
20028         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20029         
20030         this.hide();
20031                         
20032     },
20033     
20034     picker : function()
20035     {
20036         return this.pickerEl;
20037     },
20038     
20039     fillMonths: function()
20040     {    
20041         var i = 0;
20042         var months = this.picker().select('>.datepicker-months td', true).first();
20043         
20044         months.dom.innerHTML = '';
20045         
20046         while (i < 12) {
20047             var month = {
20048                 tag: 'span',
20049                 cls: 'month',
20050                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20051             };
20052             
20053             months.createChild(month);
20054         }
20055         
20056     },
20057     
20058     update: function()
20059     {
20060         var _this = this;
20061         
20062         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20063             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20064         }
20065         
20066         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20067             e.removeClass('active');
20068             
20069             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20070                 e.addClass('active');
20071             }
20072         })
20073     },
20074     
20075     place: function()
20076     {
20077         if(this.isInline) {
20078             return;
20079         }
20080         
20081         this.picker().removeClass(['bottom', 'top']);
20082         
20083         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20084             /*
20085              * place to the top of element!
20086              *
20087              */
20088             
20089             this.picker().addClass('top');
20090             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20091             
20092             return;
20093         }
20094         
20095         this.picker().addClass('bottom');
20096         
20097         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20098     },
20099     
20100     onFocus : function()
20101     {
20102         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20103         this.show();
20104     },
20105     
20106     onBlur : function()
20107     {
20108         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20109         
20110         var d = this.inputEl().getValue();
20111         
20112         this.setValue(d);
20113                 
20114         this.hide();
20115     },
20116     
20117     show : function()
20118     {
20119         this.picker().show();
20120         this.picker().select('>.datepicker-months', true).first().show();
20121         this.update();
20122         this.place();
20123         
20124         this.fireEvent('show', this, this.date);
20125     },
20126     
20127     hide : function()
20128     {
20129         if(this.isInline) {
20130             return;
20131         }
20132         this.picker().hide();
20133         this.fireEvent('hide', this, this.date);
20134         
20135     },
20136     
20137     onMousedown: function(e)
20138     {
20139         e.stopPropagation();
20140         e.preventDefault();
20141     },
20142     
20143     keyup: function(e)
20144     {
20145         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20146         this.update();
20147     },
20148
20149     fireKey: function(e)
20150     {
20151         if (!this.picker().isVisible()){
20152             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20153                 this.show();
20154             }
20155             return;
20156         }
20157         
20158         var dir;
20159         
20160         switch(e.keyCode){
20161             case 27: // escape
20162                 this.hide();
20163                 e.preventDefault();
20164                 break;
20165             case 37: // left
20166             case 39: // right
20167                 dir = e.keyCode == 37 ? -1 : 1;
20168                 
20169                 this.vIndex = this.vIndex + dir;
20170                 
20171                 if(this.vIndex < 0){
20172                     this.vIndex = 0;
20173                 }
20174                 
20175                 if(this.vIndex > 11){
20176                     this.vIndex = 11;
20177                 }
20178                 
20179                 if(isNaN(this.vIndex)){
20180                     this.vIndex = 0;
20181                 }
20182                 
20183                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20184                 
20185                 break;
20186             case 38: // up
20187             case 40: // down
20188                 
20189                 dir = e.keyCode == 38 ? -1 : 1;
20190                 
20191                 this.vIndex = this.vIndex + dir * 4;
20192                 
20193                 if(this.vIndex < 0){
20194                     this.vIndex = 0;
20195                 }
20196                 
20197                 if(this.vIndex > 11){
20198                     this.vIndex = 11;
20199                 }
20200                 
20201                 if(isNaN(this.vIndex)){
20202                     this.vIndex = 0;
20203                 }
20204                 
20205                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20206                 break;
20207                 
20208             case 13: // enter
20209                 
20210                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20211                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20212                 }
20213                 
20214                 this.hide();
20215                 e.preventDefault();
20216                 break;
20217             case 9: // tab
20218                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20219                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20220                 }
20221                 this.hide();
20222                 break;
20223             case 16: // shift
20224             case 17: // ctrl
20225             case 18: // alt
20226                 break;
20227             default :
20228                 this.hide();
20229                 
20230         }
20231     },
20232     
20233     remove: function() 
20234     {
20235         this.picker().remove();
20236     }
20237    
20238 });
20239
20240 Roo.apply(Roo.bootstrap.MonthField,  {
20241     
20242     content : {
20243         tag: 'tbody',
20244         cn: [
20245         {
20246             tag: 'tr',
20247             cn: [
20248             {
20249                 tag: 'td',
20250                 colspan: '7'
20251             }
20252             ]
20253         }
20254         ]
20255     },
20256     
20257     dates:{
20258         en: {
20259             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20260             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20261         }
20262     }
20263 });
20264
20265 Roo.apply(Roo.bootstrap.MonthField,  {
20266   
20267     template : {
20268         tag: 'div',
20269         cls: 'datepicker dropdown-menu roo-dynamic',
20270         cn: [
20271             {
20272                 tag: 'div',
20273                 cls: 'datepicker-months',
20274                 cn: [
20275                 {
20276                     tag: 'table',
20277                     cls: 'table-condensed',
20278                     cn:[
20279                         Roo.bootstrap.DateField.content
20280                     ]
20281                 }
20282                 ]
20283             }
20284         ]
20285     }
20286 });
20287
20288  
20289
20290  
20291  /*
20292  * - LGPL
20293  *
20294  * CheckBox
20295  * 
20296  */
20297
20298 /**
20299  * @class Roo.bootstrap.CheckBox
20300  * @extends Roo.bootstrap.Input
20301  * Bootstrap CheckBox class
20302  * 
20303  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20304  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20305  * @cfg {String} boxLabel The text that appears beside the checkbox
20306  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20307  * @cfg {Boolean} checked initnal the element
20308  * @cfg {Boolean} inline inline the element (default false)
20309  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20310  * @cfg {String} tooltip label tooltip
20311  * 
20312  * @constructor
20313  * Create a new CheckBox
20314  * @param {Object} config The config object
20315  */
20316
20317 Roo.bootstrap.CheckBox = function(config){
20318     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20319    
20320     this.addEvents({
20321         /**
20322         * @event check
20323         * Fires when the element is checked or unchecked.
20324         * @param {Roo.bootstrap.CheckBox} this This input
20325         * @param {Boolean} checked The new checked value
20326         */
20327        check : true,
20328        /**
20329         * @event click
20330         * Fires when the element is click.
20331         * @param {Roo.bootstrap.CheckBox} this This input
20332         */
20333        click : true
20334     });
20335     
20336 };
20337
20338 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20339   
20340     inputType: 'checkbox',
20341     inputValue: 1,
20342     valueOff: 0,
20343     boxLabel: false,
20344     checked: false,
20345     weight : false,
20346     inline: false,
20347     tooltip : '',
20348     
20349     getAutoCreate : function()
20350     {
20351         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20352         
20353         var id = Roo.id();
20354         
20355         var cfg = {};
20356         
20357         cfg.cls = 'form-group ' + this.inputType; //input-group
20358         
20359         if(this.inline){
20360             cfg.cls += ' ' + this.inputType + '-inline';
20361         }
20362         
20363         var input =  {
20364             tag: 'input',
20365             id : id,
20366             type : this.inputType,
20367             value : this.inputValue,
20368             cls : 'roo-' + this.inputType, //'form-box',
20369             placeholder : this.placeholder || ''
20370             
20371         };
20372         
20373         if(this.inputType != 'radio'){
20374             var hidden =  {
20375                 tag: 'input',
20376                 type : 'hidden',
20377                 cls : 'roo-hidden-value',
20378                 value : this.checked ? this.inputValue : this.valueOff
20379             };
20380         }
20381         
20382             
20383         if (this.weight) { // Validity check?
20384             cfg.cls += " " + this.inputType + "-" + this.weight;
20385         }
20386         
20387         if (this.disabled) {
20388             input.disabled=true;
20389         }
20390         
20391         if(this.checked){
20392             input.checked = this.checked;
20393         }
20394         
20395         if (this.name) {
20396             
20397             input.name = this.name;
20398             
20399             if(this.inputType != 'radio'){
20400                 hidden.name = this.name;
20401                 input.name = '_hidden_' + this.name;
20402             }
20403         }
20404         
20405         if (this.size) {
20406             input.cls += ' input-' + this.size;
20407         }
20408         
20409         var settings=this;
20410         
20411         ['xs','sm','md','lg'].map(function(size){
20412             if (settings[size]) {
20413                 cfg.cls += ' col-' + size + '-' + settings[size];
20414             }
20415         });
20416         
20417         var inputblock = input;
20418          
20419         if (this.before || this.after) {
20420             
20421             inputblock = {
20422                 cls : 'input-group',
20423                 cn :  [] 
20424             };
20425             
20426             if (this.before) {
20427                 inputblock.cn.push({
20428                     tag :'span',
20429                     cls : 'input-group-addon',
20430                     html : this.before
20431                 });
20432             }
20433             
20434             inputblock.cn.push(input);
20435             
20436             if(this.inputType != 'radio'){
20437                 inputblock.cn.push(hidden);
20438             }
20439             
20440             if (this.after) {
20441                 inputblock.cn.push({
20442                     tag :'span',
20443                     cls : 'input-group-addon',
20444                     html : this.after
20445                 });
20446             }
20447             
20448         }
20449         
20450         if (align ==='left' && this.fieldLabel.length) {
20451 //                Roo.log("left and has label");
20452             cfg.cn = [
20453                 {
20454                     tag: 'label',
20455                     'for' :  id,
20456                     cls : 'control-label',
20457                     html : this.fieldLabel
20458                 },
20459                 {
20460                     cls : "", 
20461                     cn: [
20462                         inputblock
20463                     ]
20464                 }
20465             ];
20466             
20467             if(this.labelWidth > 12){
20468                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20469             }
20470             
20471             if(this.labelWidth < 13 && this.labelmd == 0){
20472                 this.labelmd = this.labelWidth;
20473             }
20474             
20475             if(this.labellg > 0){
20476                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20477                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20478             }
20479             
20480             if(this.labelmd > 0){
20481                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20482                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20483             }
20484             
20485             if(this.labelsm > 0){
20486                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20487                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20488             }
20489             
20490             if(this.labelxs > 0){
20491                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20492                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20493             }
20494             
20495         } else if ( this.fieldLabel.length) {
20496 //                Roo.log(" label");
20497                 cfg.cn = [
20498                    
20499                     {
20500                         tag: this.boxLabel ? 'span' : 'label',
20501                         'for': id,
20502                         cls: 'control-label box-input-label',
20503                         //cls : 'input-group-addon',
20504                         html : this.fieldLabel
20505                     },
20506                     
20507                     inputblock
20508                     
20509                 ];
20510
20511         } else {
20512             
20513 //                Roo.log(" no label && no align");
20514                 cfg.cn = [  inputblock ] ;
20515                 
20516                 
20517         }
20518         
20519         if(this.boxLabel){
20520              var boxLabelCfg = {
20521                 tag: 'label',
20522                 //'for': id, // box label is handled by onclick - so no for...
20523                 cls: 'box-label',
20524                 html: this.boxLabel
20525             };
20526             
20527             if(this.tooltip){
20528                 boxLabelCfg.tooltip = this.tooltip;
20529             }
20530              
20531             cfg.cn.push(boxLabelCfg);
20532         }
20533         
20534         if(this.inputType != 'radio'){
20535             cfg.cn.push(hidden);
20536         }
20537         
20538         return cfg;
20539         
20540     },
20541     
20542     /**
20543      * return the real input element.
20544      */
20545     inputEl: function ()
20546     {
20547         return this.el.select('input.roo-' + this.inputType,true).first();
20548     },
20549     hiddenEl: function ()
20550     {
20551         return this.el.select('input.roo-hidden-value',true).first();
20552     },
20553     
20554     labelEl: function()
20555     {
20556         return this.el.select('label.control-label',true).first();
20557     },
20558     /* depricated... */
20559     
20560     label: function()
20561     {
20562         return this.labelEl();
20563     },
20564     
20565     boxLabelEl: function()
20566     {
20567         return this.el.select('label.box-label',true).first();
20568     },
20569     
20570     initEvents : function()
20571     {
20572 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20573         
20574         this.inputEl().on('click', this.onClick,  this);
20575         
20576         if (this.boxLabel) { 
20577             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20578         }
20579         
20580         this.startValue = this.getValue();
20581         
20582         if(this.groupId){
20583             Roo.bootstrap.CheckBox.register(this);
20584         }
20585     },
20586     
20587     onClick : function(e)
20588     {   
20589         if(this.fireEvent('click', this, e) !== false){
20590             this.setChecked(!this.checked);
20591         }
20592         
20593     },
20594     
20595     setChecked : function(state,suppressEvent)
20596     {
20597         this.startValue = this.getValue();
20598
20599         if(this.inputType == 'radio'){
20600             
20601             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20602                 e.dom.checked = false;
20603             });
20604             
20605             this.inputEl().dom.checked = true;
20606             
20607             this.inputEl().dom.value = this.inputValue;
20608             
20609             if(suppressEvent !== true){
20610                 this.fireEvent('check', this, true);
20611             }
20612             
20613             this.validate();
20614             
20615             return;
20616         }
20617         
20618         this.checked = state;
20619         
20620         this.inputEl().dom.checked = state;
20621         
20622         
20623         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20624         
20625         if(suppressEvent !== true){
20626             this.fireEvent('check', this, state);
20627         }
20628         
20629         this.validate();
20630     },
20631     
20632     getValue : function()
20633     {
20634         if(this.inputType == 'radio'){
20635             return this.getGroupValue();
20636         }
20637         
20638         return this.hiddenEl().dom.value;
20639         
20640     },
20641     
20642     getGroupValue : function()
20643     {
20644         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20645             return '';
20646         }
20647         
20648         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20649     },
20650     
20651     setValue : function(v,suppressEvent)
20652     {
20653         if(this.inputType == 'radio'){
20654             this.setGroupValue(v, suppressEvent);
20655             return;
20656         }
20657         
20658         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20659         
20660         this.validate();
20661     },
20662     
20663     setGroupValue : function(v, suppressEvent)
20664     {
20665         this.startValue = this.getValue();
20666         
20667         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20668             e.dom.checked = false;
20669             
20670             if(e.dom.value == v){
20671                 e.dom.checked = true;
20672             }
20673         });
20674         
20675         if(suppressEvent !== true){
20676             this.fireEvent('check', this, true);
20677         }
20678
20679         this.validate();
20680         
20681         return;
20682     },
20683     
20684     validate : function()
20685     {
20686         if(this.getVisibilityEl().hasClass('hidden')){
20687             return true;
20688         }
20689         
20690         if(
20691                 this.disabled || 
20692                 (this.inputType == 'radio' && this.validateRadio()) ||
20693                 (this.inputType == 'checkbox' && this.validateCheckbox())
20694         ){
20695             this.markValid();
20696             return true;
20697         }
20698         
20699         this.markInvalid();
20700         return false;
20701     },
20702     
20703     validateRadio : function()
20704     {
20705         if(this.getVisibilityEl().hasClass('hidden')){
20706             return true;
20707         }
20708         
20709         if(this.allowBlank){
20710             return true;
20711         }
20712         
20713         var valid = false;
20714         
20715         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20716             if(!e.dom.checked){
20717                 return;
20718             }
20719             
20720             valid = true;
20721             
20722             return false;
20723         });
20724         
20725         return valid;
20726     },
20727     
20728     validateCheckbox : function()
20729     {
20730         if(!this.groupId){
20731             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20732             //return (this.getValue() == this.inputValue) ? true : false;
20733         }
20734         
20735         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20736         
20737         if(!group){
20738             return false;
20739         }
20740         
20741         var r = false;
20742         
20743         for(var i in group){
20744             if(group[i].el.isVisible(true)){
20745                 r = false;
20746                 break;
20747             }
20748             
20749             r = true;
20750         }
20751         
20752         for(var i in group){
20753             if(r){
20754                 break;
20755             }
20756             
20757             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20758         }
20759         
20760         return r;
20761     },
20762     
20763     /**
20764      * Mark this field as valid
20765      */
20766     markValid : function()
20767     {
20768         var _this = this;
20769         
20770         this.fireEvent('valid', this);
20771         
20772         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20773         
20774         if(this.groupId){
20775             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20776         }
20777         
20778         if(label){
20779             label.markValid();
20780         }
20781
20782         if(this.inputType == 'radio'){
20783             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20784                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20785                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20786             });
20787             
20788             return;
20789         }
20790
20791         if(!this.groupId){
20792             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20793             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20794             return;
20795         }
20796         
20797         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20798         
20799         if(!group){
20800             return;
20801         }
20802         
20803         for(var i in group){
20804             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20805             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20806         }
20807     },
20808     
20809      /**
20810      * Mark this field as invalid
20811      * @param {String} msg The validation message
20812      */
20813     markInvalid : function(msg)
20814     {
20815         if(this.allowBlank){
20816             return;
20817         }
20818         
20819         var _this = this;
20820         
20821         this.fireEvent('invalid', this, msg);
20822         
20823         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20824         
20825         if(this.groupId){
20826             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20827         }
20828         
20829         if(label){
20830             label.markInvalid();
20831         }
20832             
20833         if(this.inputType == 'radio'){
20834             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20835                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20836                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20837             });
20838             
20839             return;
20840         }
20841         
20842         if(!this.groupId){
20843             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20844             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20845             return;
20846         }
20847         
20848         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20849         
20850         if(!group){
20851             return;
20852         }
20853         
20854         for(var i in group){
20855             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20856             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20857         }
20858         
20859     },
20860     
20861     clearInvalid : function()
20862     {
20863         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20864         
20865         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20866         
20867         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20868         
20869         if (label && label.iconEl) {
20870             label.iconEl.removeClass(label.validClass);
20871             label.iconEl.removeClass(label.invalidClass);
20872         }
20873     },
20874     
20875     disable : function()
20876     {
20877         if(this.inputType != 'radio'){
20878             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20879             return;
20880         }
20881         
20882         var _this = this;
20883         
20884         if(this.rendered){
20885             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20886                 _this.getActionEl().addClass(this.disabledClass);
20887                 e.dom.disabled = true;
20888             });
20889         }
20890         
20891         this.disabled = true;
20892         this.fireEvent("disable", this);
20893         return this;
20894     },
20895
20896     enable : function()
20897     {
20898         if(this.inputType != 'radio'){
20899             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20900             return;
20901         }
20902         
20903         var _this = this;
20904         
20905         if(this.rendered){
20906             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20907                 _this.getActionEl().removeClass(this.disabledClass);
20908                 e.dom.disabled = false;
20909             });
20910         }
20911         
20912         this.disabled = false;
20913         this.fireEvent("enable", this);
20914         return this;
20915     },
20916     
20917     setBoxLabel : function(v)
20918     {
20919         this.boxLabel = v;
20920         
20921         if(this.rendered){
20922             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20923         }
20924     }
20925
20926 });
20927
20928 Roo.apply(Roo.bootstrap.CheckBox, {
20929     
20930     groups: {},
20931     
20932      /**
20933     * register a CheckBox Group
20934     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20935     */
20936     register : function(checkbox)
20937     {
20938         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20939             this.groups[checkbox.groupId] = {};
20940         }
20941         
20942         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20943             return;
20944         }
20945         
20946         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20947         
20948     },
20949     /**
20950     * fetch a CheckBox Group based on the group ID
20951     * @param {string} the group ID
20952     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20953     */
20954     get: function(groupId) {
20955         if (typeof(this.groups[groupId]) == 'undefined') {
20956             return false;
20957         }
20958         
20959         return this.groups[groupId] ;
20960     }
20961     
20962     
20963 });
20964 /*
20965  * - LGPL
20966  *
20967  * RadioItem
20968  * 
20969  */
20970
20971 /**
20972  * @class Roo.bootstrap.Radio
20973  * @extends Roo.bootstrap.Component
20974  * Bootstrap Radio class
20975  * @cfg {String} boxLabel - the label associated
20976  * @cfg {String} value - the value of radio
20977  * 
20978  * @constructor
20979  * Create a new Radio
20980  * @param {Object} config The config object
20981  */
20982 Roo.bootstrap.Radio = function(config){
20983     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20984     
20985 };
20986
20987 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20988     
20989     boxLabel : '',
20990     
20991     value : '',
20992     
20993     getAutoCreate : function()
20994     {
20995         var cfg = {
20996             tag : 'div',
20997             cls : 'form-group radio',
20998             cn : [
20999                 {
21000                     tag : 'label',
21001                     cls : 'box-label',
21002                     html : this.boxLabel
21003                 }
21004             ]
21005         };
21006         
21007         return cfg;
21008     },
21009     
21010     initEvents : function() 
21011     {
21012         this.parent().register(this);
21013         
21014         this.el.on('click', this.onClick, this);
21015         
21016     },
21017     
21018     onClick : function(e)
21019     {
21020         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21021             this.setChecked(true);
21022         }
21023     },
21024     
21025     setChecked : function(state, suppressEvent)
21026     {
21027         this.parent().setValue(this.value, suppressEvent);
21028         
21029     },
21030     
21031     setBoxLabel : function(v)
21032     {
21033         this.boxLabel = v;
21034         
21035         if(this.rendered){
21036             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21037         }
21038     }
21039     
21040 });
21041  
21042
21043  /*
21044  * - LGPL
21045  *
21046  * Input
21047  * 
21048  */
21049
21050 /**
21051  * @class Roo.bootstrap.SecurePass
21052  * @extends Roo.bootstrap.Input
21053  * Bootstrap SecurePass class
21054  *
21055  * 
21056  * @constructor
21057  * Create a new SecurePass
21058  * @param {Object} config The config object
21059  */
21060  
21061 Roo.bootstrap.SecurePass = function (config) {
21062     // these go here, so the translation tool can replace them..
21063     this.errors = {
21064         PwdEmpty: "Please type a password, and then retype it to confirm.",
21065         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21066         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21067         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21068         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21069         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21070         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21071         TooWeak: "Your password is Too Weak."
21072     },
21073     this.meterLabel = "Password strength:";
21074     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21075     this.meterClass = [
21076         "roo-password-meter-tooweak", 
21077         "roo-password-meter-weak", 
21078         "roo-password-meter-medium", 
21079         "roo-password-meter-strong", 
21080         "roo-password-meter-grey"
21081     ];
21082     
21083     this.errors = {};
21084     
21085     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21086 }
21087
21088 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21089     /**
21090      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21091      * {
21092      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21093      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21094      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21095      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21096      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21097      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21098      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21099      * })
21100      */
21101     // private
21102     
21103     meterWidth: 300,
21104     errorMsg :'',    
21105     errors: false,
21106     imageRoot: '/',
21107     /**
21108      * @cfg {String/Object} Label for the strength meter (defaults to
21109      * 'Password strength:')
21110      */
21111     // private
21112     meterLabel: '',
21113     /**
21114      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21115      * ['Weak', 'Medium', 'Strong'])
21116      */
21117     // private    
21118     pwdStrengths: false,    
21119     // private
21120     strength: 0,
21121     // private
21122     _lastPwd: null,
21123     // private
21124     kCapitalLetter: 0,
21125     kSmallLetter: 1,
21126     kDigit: 2,
21127     kPunctuation: 3,
21128     
21129     insecure: false,
21130     // private
21131     initEvents: function ()
21132     {
21133         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21134
21135         if (this.el.is('input[type=password]') && Roo.isSafari) {
21136             this.el.on('keydown', this.SafariOnKeyDown, this);
21137         }
21138
21139         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21140     },
21141     // private
21142     onRender: function (ct, position)
21143     {
21144         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21145         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21146         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21147
21148         this.trigger.createChild({
21149                    cn: [
21150                     {
21151                     //id: 'PwdMeter',
21152                     tag: 'div',
21153                     cls: 'roo-password-meter-grey col-xs-12',
21154                     style: {
21155                         //width: 0,
21156                         //width: this.meterWidth + 'px'                                                
21157                         }
21158                     },
21159                     {                            
21160                          cls: 'roo-password-meter-text'                          
21161                     }
21162                 ]            
21163         });
21164
21165          
21166         if (this.hideTrigger) {
21167             this.trigger.setDisplayed(false);
21168         }
21169         this.setSize(this.width || '', this.height || '');
21170     },
21171     // private
21172     onDestroy: function ()
21173     {
21174         if (this.trigger) {
21175             this.trigger.removeAllListeners();
21176             this.trigger.remove();
21177         }
21178         if (this.wrap) {
21179             this.wrap.remove();
21180         }
21181         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21182     },
21183     // private
21184     checkStrength: function ()
21185     {
21186         var pwd = this.inputEl().getValue();
21187         if (pwd == this._lastPwd) {
21188             return;
21189         }
21190
21191         var strength;
21192         if (this.ClientSideStrongPassword(pwd)) {
21193             strength = 3;
21194         } else if (this.ClientSideMediumPassword(pwd)) {
21195             strength = 2;
21196         } else if (this.ClientSideWeakPassword(pwd)) {
21197             strength = 1;
21198         } else {
21199             strength = 0;
21200         }
21201         
21202         Roo.log('strength1: ' + strength);
21203         
21204         //var pm = this.trigger.child('div/div/div').dom;
21205         var pm = this.trigger.child('div/div');
21206         pm.removeClass(this.meterClass);
21207         pm.addClass(this.meterClass[strength]);
21208                 
21209         
21210         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21211                 
21212         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21213         
21214         this._lastPwd = pwd;
21215     },
21216     reset: function ()
21217     {
21218         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21219         
21220         this._lastPwd = '';
21221         
21222         var pm = this.trigger.child('div/div');
21223         pm.removeClass(this.meterClass);
21224         pm.addClass('roo-password-meter-grey');        
21225         
21226         
21227         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21228         
21229         pt.innerHTML = '';
21230         this.inputEl().dom.type='password';
21231     },
21232     // private
21233     validateValue: function (value)
21234     {
21235         
21236         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21237             return false;
21238         }
21239         if (value.length == 0) {
21240             if (this.allowBlank) {
21241                 this.clearInvalid();
21242                 return true;
21243             }
21244
21245             this.markInvalid(this.errors.PwdEmpty);
21246             this.errorMsg = this.errors.PwdEmpty;
21247             return false;
21248         }
21249         
21250         if(this.insecure){
21251             return true;
21252         }
21253         
21254         if ('[\x21-\x7e]*'.match(value)) {
21255             this.markInvalid(this.errors.PwdBadChar);
21256             this.errorMsg = this.errors.PwdBadChar;
21257             return false;
21258         }
21259         if (value.length < 6) {
21260             this.markInvalid(this.errors.PwdShort);
21261             this.errorMsg = this.errors.PwdShort;
21262             return false;
21263         }
21264         if (value.length > 16) {
21265             this.markInvalid(this.errors.PwdLong);
21266             this.errorMsg = this.errors.PwdLong;
21267             return false;
21268         }
21269         var strength;
21270         if (this.ClientSideStrongPassword(value)) {
21271             strength = 3;
21272         } else if (this.ClientSideMediumPassword(value)) {
21273             strength = 2;
21274         } else if (this.ClientSideWeakPassword(value)) {
21275             strength = 1;
21276         } else {
21277             strength = 0;
21278         }
21279
21280         
21281         if (strength < 2) {
21282             //this.markInvalid(this.errors.TooWeak);
21283             this.errorMsg = this.errors.TooWeak;
21284             //return false;
21285         }
21286         
21287         
21288         console.log('strength2: ' + strength);
21289         
21290         //var pm = this.trigger.child('div/div/div').dom;
21291         
21292         var pm = this.trigger.child('div/div');
21293         pm.removeClass(this.meterClass);
21294         pm.addClass(this.meterClass[strength]);
21295                 
21296         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21297                 
21298         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21299         
21300         this.errorMsg = ''; 
21301         return true;
21302     },
21303     // private
21304     CharacterSetChecks: function (type)
21305     {
21306         this.type = type;
21307         this.fResult = false;
21308     },
21309     // private
21310     isctype: function (character, type)
21311     {
21312         switch (type) {  
21313             case this.kCapitalLetter:
21314                 if (character >= 'A' && character <= 'Z') {
21315                     return true;
21316                 }
21317                 break;
21318             
21319             case this.kSmallLetter:
21320                 if (character >= 'a' && character <= 'z') {
21321                     return true;
21322                 }
21323                 break;
21324             
21325             case this.kDigit:
21326                 if (character >= '0' && character <= '9') {
21327                     return true;
21328                 }
21329                 break;
21330             
21331             case this.kPunctuation:
21332                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21333                     return true;
21334                 }
21335                 break;
21336             
21337             default:
21338                 return false;
21339         }
21340
21341     },
21342     // private
21343     IsLongEnough: function (pwd, size)
21344     {
21345         return !(pwd == null || isNaN(size) || pwd.length < size);
21346     },
21347     // private
21348     SpansEnoughCharacterSets: function (word, nb)
21349     {
21350         if (!this.IsLongEnough(word, nb))
21351         {
21352             return false;
21353         }
21354
21355         var characterSetChecks = new Array(
21356             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21357             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21358         );
21359         
21360         for (var index = 0; index < word.length; ++index) {
21361             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21362                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21363                     characterSetChecks[nCharSet].fResult = true;
21364                     break;
21365                 }
21366             }
21367         }
21368
21369         var nCharSets = 0;
21370         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21371             if (characterSetChecks[nCharSet].fResult) {
21372                 ++nCharSets;
21373             }
21374         }
21375
21376         if (nCharSets < nb) {
21377             return false;
21378         }
21379         return true;
21380     },
21381     // private
21382     ClientSideStrongPassword: function (pwd)
21383     {
21384         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21385     },
21386     // private
21387     ClientSideMediumPassword: function (pwd)
21388     {
21389         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21390     },
21391     // private
21392     ClientSideWeakPassword: function (pwd)
21393     {
21394         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21395     }
21396           
21397 })//<script type="text/javascript">
21398
21399 /*
21400  * Based  Ext JS Library 1.1.1
21401  * Copyright(c) 2006-2007, Ext JS, LLC.
21402  * LGPL
21403  *
21404  */
21405  
21406 /**
21407  * @class Roo.HtmlEditorCore
21408  * @extends Roo.Component
21409  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21410  *
21411  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21412  */
21413
21414 Roo.HtmlEditorCore = function(config){
21415     
21416     
21417     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21418     
21419     
21420     this.addEvents({
21421         /**
21422          * @event initialize
21423          * Fires when the editor is fully initialized (including the iframe)
21424          * @param {Roo.HtmlEditorCore} this
21425          */
21426         initialize: true,
21427         /**
21428          * @event activate
21429          * Fires when the editor is first receives the focus. Any insertion must wait
21430          * until after this event.
21431          * @param {Roo.HtmlEditorCore} this
21432          */
21433         activate: true,
21434          /**
21435          * @event beforesync
21436          * Fires before the textarea is updated with content from the editor iframe. Return false
21437          * to cancel the sync.
21438          * @param {Roo.HtmlEditorCore} this
21439          * @param {String} html
21440          */
21441         beforesync: true,
21442          /**
21443          * @event beforepush
21444          * Fires before the iframe editor is updated with content from the textarea. Return false
21445          * to cancel the push.
21446          * @param {Roo.HtmlEditorCore} this
21447          * @param {String} html
21448          */
21449         beforepush: true,
21450          /**
21451          * @event sync
21452          * Fires when the textarea is updated with content from the editor iframe.
21453          * @param {Roo.HtmlEditorCore} this
21454          * @param {String} html
21455          */
21456         sync: true,
21457          /**
21458          * @event push
21459          * Fires when the iframe editor is updated with content from the textarea.
21460          * @param {Roo.HtmlEditorCore} this
21461          * @param {String} html
21462          */
21463         push: true,
21464         
21465         /**
21466          * @event editorevent
21467          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21468          * @param {Roo.HtmlEditorCore} this
21469          */
21470         editorevent: true
21471         
21472     });
21473     
21474     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21475     
21476     // defaults : white / black...
21477     this.applyBlacklists();
21478     
21479     
21480     
21481 };
21482
21483
21484 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21485
21486
21487      /**
21488      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21489      */
21490     
21491     owner : false,
21492     
21493      /**
21494      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21495      *                        Roo.resizable.
21496      */
21497     resizable : false,
21498      /**
21499      * @cfg {Number} height (in pixels)
21500      */   
21501     height: 300,
21502    /**
21503      * @cfg {Number} width (in pixels)
21504      */   
21505     width: 500,
21506     
21507     /**
21508      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21509      * 
21510      */
21511     stylesheets: false,
21512     
21513     // id of frame..
21514     frameId: false,
21515     
21516     // private properties
21517     validationEvent : false,
21518     deferHeight: true,
21519     initialized : false,
21520     activated : false,
21521     sourceEditMode : false,
21522     onFocus : Roo.emptyFn,
21523     iframePad:3,
21524     hideMode:'offsets',
21525     
21526     clearUp: true,
21527     
21528     // blacklist + whitelisted elements..
21529     black: false,
21530     white: false,
21531      
21532     bodyCls : '',
21533
21534     /**
21535      * Protected method that will not generally be called directly. It
21536      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21537      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21538      */
21539     getDocMarkup : function(){
21540         // body styles..
21541         var st = '';
21542         
21543         // inherit styels from page...?? 
21544         if (this.stylesheets === false) {
21545             
21546             Roo.get(document.head).select('style').each(function(node) {
21547                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21548             });
21549             
21550             Roo.get(document.head).select('link').each(function(node) { 
21551                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21552             });
21553             
21554         } else if (!this.stylesheets.length) {
21555                 // simple..
21556                 st = '<style type="text/css">' +
21557                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21558                    '</style>';
21559         } else { 
21560             st = '<style type="text/css">' +
21561                     this.stylesheets +
21562                 '</style>';
21563         }
21564         
21565         st +=  '<style type="text/css">' +
21566             'IMG { cursor: pointer } ' +
21567         '</style>';
21568
21569         var cls = 'roo-htmleditor-body';
21570         
21571         if(this.bodyCls.length){
21572             cls += ' ' + this.bodyCls;
21573         }
21574         
21575         return '<html><head>' + st  +
21576             //<style type="text/css">' +
21577             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21578             //'</style>' +
21579             ' </head><body class="' +  cls + '"></body></html>';
21580     },
21581
21582     // private
21583     onRender : function(ct, position)
21584     {
21585         var _t = this;
21586         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21587         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21588         
21589         
21590         this.el.dom.style.border = '0 none';
21591         this.el.dom.setAttribute('tabIndex', -1);
21592         this.el.addClass('x-hidden hide');
21593         
21594         
21595         
21596         if(Roo.isIE){ // fix IE 1px bogus margin
21597             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21598         }
21599        
21600         
21601         this.frameId = Roo.id();
21602         
21603          
21604         
21605         var iframe = this.owner.wrap.createChild({
21606             tag: 'iframe',
21607             cls: 'form-control', // bootstrap..
21608             id: this.frameId,
21609             name: this.frameId,
21610             frameBorder : 'no',
21611             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21612         }, this.el
21613         );
21614         
21615         
21616         this.iframe = iframe.dom;
21617
21618          this.assignDocWin();
21619         
21620         this.doc.designMode = 'on';
21621        
21622         this.doc.open();
21623         this.doc.write(this.getDocMarkup());
21624         this.doc.close();
21625
21626         
21627         var task = { // must defer to wait for browser to be ready
21628             run : function(){
21629                 //console.log("run task?" + this.doc.readyState);
21630                 this.assignDocWin();
21631                 if(this.doc.body || this.doc.readyState == 'complete'){
21632                     try {
21633                         this.doc.designMode="on";
21634                     } catch (e) {
21635                         return;
21636                     }
21637                     Roo.TaskMgr.stop(task);
21638                     this.initEditor.defer(10, this);
21639                 }
21640             },
21641             interval : 10,
21642             duration: 10000,
21643             scope: this
21644         };
21645         Roo.TaskMgr.start(task);
21646
21647     },
21648
21649     // private
21650     onResize : function(w, h)
21651     {
21652          Roo.log('resize: ' +w + ',' + h );
21653         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21654         if(!this.iframe){
21655             return;
21656         }
21657         if(typeof w == 'number'){
21658             
21659             this.iframe.style.width = w + 'px';
21660         }
21661         if(typeof h == 'number'){
21662             
21663             this.iframe.style.height = h + 'px';
21664             if(this.doc){
21665                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21666             }
21667         }
21668         
21669     },
21670
21671     /**
21672      * Toggles the editor between standard and source edit mode.
21673      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21674      */
21675     toggleSourceEdit : function(sourceEditMode){
21676         
21677         this.sourceEditMode = sourceEditMode === true;
21678         
21679         if(this.sourceEditMode){
21680  
21681             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21682             
21683         }else{
21684             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21685             //this.iframe.className = '';
21686             this.deferFocus();
21687         }
21688         //this.setSize(this.owner.wrap.getSize());
21689         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21690     },
21691
21692     
21693   
21694
21695     /**
21696      * Protected method that will not generally be called directly. If you need/want
21697      * custom HTML cleanup, this is the method you should override.
21698      * @param {String} html The HTML to be cleaned
21699      * return {String} The cleaned HTML
21700      */
21701     cleanHtml : function(html){
21702         html = String(html);
21703         if(html.length > 5){
21704             if(Roo.isSafari){ // strip safari nonsense
21705                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21706             }
21707         }
21708         if(html == '&nbsp;'){
21709             html = '';
21710         }
21711         return html;
21712     },
21713
21714     /**
21715      * HTML Editor -> Textarea
21716      * Protected method that will not generally be called directly. Syncs the contents
21717      * of the editor iframe with the textarea.
21718      */
21719     syncValue : function(){
21720         if(this.initialized){
21721             var bd = (this.doc.body || this.doc.documentElement);
21722             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21723             var html = bd.innerHTML;
21724             if(Roo.isSafari){
21725                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21726                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21727                 if(m && m[1]){
21728                     html = '<div style="'+m[0]+'">' + html + '</div>';
21729                 }
21730             }
21731             html = this.cleanHtml(html);
21732             // fix up the special chars.. normaly like back quotes in word...
21733             // however we do not want to do this with chinese..
21734             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21735                 var cc = b.charCodeAt();
21736                 if (
21737                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21738                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21739                     (cc >= 0xf900 && cc < 0xfb00 )
21740                 ) {
21741                         return b;
21742                 }
21743                 return "&#"+cc+";" 
21744             });
21745             if(this.owner.fireEvent('beforesync', this, html) !== false){
21746                 this.el.dom.value = html;
21747                 this.owner.fireEvent('sync', this, html);
21748             }
21749         }
21750     },
21751
21752     /**
21753      * Protected method that will not generally be called directly. Pushes the value of the textarea
21754      * into the iframe editor.
21755      */
21756     pushValue : function(){
21757         if(this.initialized){
21758             var v = this.el.dom.value.trim();
21759             
21760 //            if(v.length < 1){
21761 //                v = '&#160;';
21762 //            }
21763             
21764             if(this.owner.fireEvent('beforepush', this, v) !== false){
21765                 var d = (this.doc.body || this.doc.documentElement);
21766                 d.innerHTML = v;
21767                 this.cleanUpPaste();
21768                 this.el.dom.value = d.innerHTML;
21769                 this.owner.fireEvent('push', this, v);
21770             }
21771         }
21772     },
21773
21774     // private
21775     deferFocus : function(){
21776         this.focus.defer(10, this);
21777     },
21778
21779     // doc'ed in Field
21780     focus : function(){
21781         if(this.win && !this.sourceEditMode){
21782             this.win.focus();
21783         }else{
21784             this.el.focus();
21785         }
21786     },
21787     
21788     assignDocWin: function()
21789     {
21790         var iframe = this.iframe;
21791         
21792          if(Roo.isIE){
21793             this.doc = iframe.contentWindow.document;
21794             this.win = iframe.contentWindow;
21795         } else {
21796 //            if (!Roo.get(this.frameId)) {
21797 //                return;
21798 //            }
21799 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21800 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21801             
21802             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21803                 return;
21804             }
21805             
21806             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21807             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21808         }
21809     },
21810     
21811     // private
21812     initEditor : function(){
21813         //console.log("INIT EDITOR");
21814         this.assignDocWin();
21815         
21816         
21817         
21818         this.doc.designMode="on";
21819         this.doc.open();
21820         this.doc.write(this.getDocMarkup());
21821         this.doc.close();
21822         
21823         var dbody = (this.doc.body || this.doc.documentElement);
21824         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21825         // this copies styles from the containing element into thsi one..
21826         // not sure why we need all of this..
21827         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21828         
21829         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21830         //ss['background-attachment'] = 'fixed'; // w3c
21831         dbody.bgProperties = 'fixed'; // ie
21832         //Roo.DomHelper.applyStyles(dbody, ss);
21833         Roo.EventManager.on(this.doc, {
21834             //'mousedown': this.onEditorEvent,
21835             'mouseup': this.onEditorEvent,
21836             'dblclick': this.onEditorEvent,
21837             'click': this.onEditorEvent,
21838             'keyup': this.onEditorEvent,
21839             buffer:100,
21840             scope: this
21841         });
21842         if(Roo.isGecko){
21843             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21844         }
21845         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21846             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21847         }
21848         this.initialized = true;
21849
21850         this.owner.fireEvent('initialize', this);
21851         this.pushValue();
21852     },
21853
21854     // private
21855     onDestroy : function(){
21856         
21857         
21858         
21859         if(this.rendered){
21860             
21861             //for (var i =0; i < this.toolbars.length;i++) {
21862             //    // fixme - ask toolbars for heights?
21863             //    this.toolbars[i].onDestroy();
21864            // }
21865             
21866             //this.wrap.dom.innerHTML = '';
21867             //this.wrap.remove();
21868         }
21869     },
21870
21871     // private
21872     onFirstFocus : function(){
21873         
21874         this.assignDocWin();
21875         
21876         
21877         this.activated = true;
21878          
21879     
21880         if(Roo.isGecko){ // prevent silly gecko errors
21881             this.win.focus();
21882             var s = this.win.getSelection();
21883             if(!s.focusNode || s.focusNode.nodeType != 3){
21884                 var r = s.getRangeAt(0);
21885                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21886                 r.collapse(true);
21887                 this.deferFocus();
21888             }
21889             try{
21890                 this.execCmd('useCSS', true);
21891                 this.execCmd('styleWithCSS', false);
21892             }catch(e){}
21893         }
21894         this.owner.fireEvent('activate', this);
21895     },
21896
21897     // private
21898     adjustFont: function(btn){
21899         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21900         //if(Roo.isSafari){ // safari
21901         //    adjust *= 2;
21902        // }
21903         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21904         if(Roo.isSafari){ // safari
21905             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21906             v =  (v < 10) ? 10 : v;
21907             v =  (v > 48) ? 48 : v;
21908             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21909             
21910         }
21911         
21912         
21913         v = Math.max(1, v+adjust);
21914         
21915         this.execCmd('FontSize', v  );
21916     },
21917
21918     onEditorEvent : function(e)
21919     {
21920         this.owner.fireEvent('editorevent', this, e);
21921       //  this.updateToolbar();
21922         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21923     },
21924
21925     insertTag : function(tg)
21926     {
21927         // could be a bit smarter... -> wrap the current selected tRoo..
21928         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21929             
21930             range = this.createRange(this.getSelection());
21931             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21932             wrappingNode.appendChild(range.extractContents());
21933             range.insertNode(wrappingNode);
21934
21935             return;
21936             
21937             
21938             
21939         }
21940         this.execCmd("formatblock",   tg);
21941         
21942     },
21943     
21944     insertText : function(txt)
21945     {
21946         
21947         
21948         var range = this.createRange();
21949         range.deleteContents();
21950                //alert(Sender.getAttribute('label'));
21951                
21952         range.insertNode(this.doc.createTextNode(txt));
21953     } ,
21954     
21955      
21956
21957     /**
21958      * Executes a Midas editor command on the editor document and performs necessary focus and
21959      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21960      * @param {String} cmd The Midas command
21961      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21962      */
21963     relayCmd : function(cmd, value){
21964         this.win.focus();
21965         this.execCmd(cmd, value);
21966         this.owner.fireEvent('editorevent', this);
21967         //this.updateToolbar();
21968         this.owner.deferFocus();
21969     },
21970
21971     /**
21972      * Executes a Midas editor command directly on the editor document.
21973      * For visual commands, you should use {@link #relayCmd} instead.
21974      * <b>This should only be called after the editor is initialized.</b>
21975      * @param {String} cmd The Midas command
21976      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21977      */
21978     execCmd : function(cmd, value){
21979         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21980         this.syncValue();
21981     },
21982  
21983  
21984    
21985     /**
21986      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21987      * to insert tRoo.
21988      * @param {String} text | dom node.. 
21989      */
21990     insertAtCursor : function(text)
21991     {
21992         
21993         if(!this.activated){
21994             return;
21995         }
21996         /*
21997         if(Roo.isIE){
21998             this.win.focus();
21999             var r = this.doc.selection.createRange();
22000             if(r){
22001                 r.collapse(true);
22002                 r.pasteHTML(text);
22003                 this.syncValue();
22004                 this.deferFocus();
22005             
22006             }
22007             return;
22008         }
22009         */
22010         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22011             this.win.focus();
22012             
22013             
22014             // from jquery ui (MIT licenced)
22015             var range, node;
22016             var win = this.win;
22017             
22018             if (win.getSelection && win.getSelection().getRangeAt) {
22019                 range = win.getSelection().getRangeAt(0);
22020                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22021                 range.insertNode(node);
22022             } else if (win.document.selection && win.document.selection.createRange) {
22023                 // no firefox support
22024                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22025                 win.document.selection.createRange().pasteHTML(txt);
22026             } else {
22027                 // no firefox support
22028                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22029                 this.execCmd('InsertHTML', txt);
22030             } 
22031             
22032             this.syncValue();
22033             
22034             this.deferFocus();
22035         }
22036     },
22037  // private
22038     mozKeyPress : function(e){
22039         if(e.ctrlKey){
22040             var c = e.getCharCode(), cmd;
22041           
22042             if(c > 0){
22043                 c = String.fromCharCode(c).toLowerCase();
22044                 switch(c){
22045                     case 'b':
22046                         cmd = 'bold';
22047                         break;
22048                     case 'i':
22049                         cmd = 'italic';
22050                         break;
22051                     
22052                     case 'u':
22053                         cmd = 'underline';
22054                         break;
22055                     
22056                     case 'v':
22057                         this.cleanUpPaste.defer(100, this);
22058                         return;
22059                         
22060                 }
22061                 if(cmd){
22062                     this.win.focus();
22063                     this.execCmd(cmd);
22064                     this.deferFocus();
22065                     e.preventDefault();
22066                 }
22067                 
22068             }
22069         }
22070     },
22071
22072     // private
22073     fixKeys : function(){ // load time branching for fastest keydown performance
22074         if(Roo.isIE){
22075             return function(e){
22076                 var k = e.getKey(), r;
22077                 if(k == e.TAB){
22078                     e.stopEvent();
22079                     r = this.doc.selection.createRange();
22080                     if(r){
22081                         r.collapse(true);
22082                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22083                         this.deferFocus();
22084                     }
22085                     return;
22086                 }
22087                 
22088                 if(k == e.ENTER){
22089                     r = this.doc.selection.createRange();
22090                     if(r){
22091                         var target = r.parentElement();
22092                         if(!target || target.tagName.toLowerCase() != 'li'){
22093                             e.stopEvent();
22094                             r.pasteHTML('<br />');
22095                             r.collapse(false);
22096                             r.select();
22097                         }
22098                     }
22099                 }
22100                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22101                     this.cleanUpPaste.defer(100, this);
22102                     return;
22103                 }
22104                 
22105                 
22106             };
22107         }else if(Roo.isOpera){
22108             return function(e){
22109                 var k = e.getKey();
22110                 if(k == e.TAB){
22111                     e.stopEvent();
22112                     this.win.focus();
22113                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22114                     this.deferFocus();
22115                 }
22116                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22117                     this.cleanUpPaste.defer(100, this);
22118                     return;
22119                 }
22120                 
22121             };
22122         }else if(Roo.isSafari){
22123             return function(e){
22124                 var k = e.getKey();
22125                 
22126                 if(k == e.TAB){
22127                     e.stopEvent();
22128                     this.execCmd('InsertText','\t');
22129                     this.deferFocus();
22130                     return;
22131                 }
22132                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22133                     this.cleanUpPaste.defer(100, this);
22134                     return;
22135                 }
22136                 
22137              };
22138         }
22139     }(),
22140     
22141     getAllAncestors: function()
22142     {
22143         var p = this.getSelectedNode();
22144         var a = [];
22145         if (!p) {
22146             a.push(p); // push blank onto stack..
22147             p = this.getParentElement();
22148         }
22149         
22150         
22151         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22152             a.push(p);
22153             p = p.parentNode;
22154         }
22155         a.push(this.doc.body);
22156         return a;
22157     },
22158     lastSel : false,
22159     lastSelNode : false,
22160     
22161     
22162     getSelection : function() 
22163     {
22164         this.assignDocWin();
22165         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22166     },
22167     
22168     getSelectedNode: function() 
22169     {
22170         // this may only work on Gecko!!!
22171         
22172         // should we cache this!!!!
22173         
22174         
22175         
22176          
22177         var range = this.createRange(this.getSelection()).cloneRange();
22178         
22179         if (Roo.isIE) {
22180             var parent = range.parentElement();
22181             while (true) {
22182                 var testRange = range.duplicate();
22183                 testRange.moveToElementText(parent);
22184                 if (testRange.inRange(range)) {
22185                     break;
22186                 }
22187                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22188                     break;
22189                 }
22190                 parent = parent.parentElement;
22191             }
22192             return parent;
22193         }
22194         
22195         // is ancestor a text element.
22196         var ac =  range.commonAncestorContainer;
22197         if (ac.nodeType == 3) {
22198             ac = ac.parentNode;
22199         }
22200         
22201         var ar = ac.childNodes;
22202          
22203         var nodes = [];
22204         var other_nodes = [];
22205         var has_other_nodes = false;
22206         for (var i=0;i<ar.length;i++) {
22207             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22208                 continue;
22209             }
22210             // fullly contained node.
22211             
22212             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22213                 nodes.push(ar[i]);
22214                 continue;
22215             }
22216             
22217             // probably selected..
22218             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22219                 other_nodes.push(ar[i]);
22220                 continue;
22221             }
22222             // outer..
22223             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22224                 continue;
22225             }
22226             
22227             
22228             has_other_nodes = true;
22229         }
22230         if (!nodes.length && other_nodes.length) {
22231             nodes= other_nodes;
22232         }
22233         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22234             return false;
22235         }
22236         
22237         return nodes[0];
22238     },
22239     createRange: function(sel)
22240     {
22241         // this has strange effects when using with 
22242         // top toolbar - not sure if it's a great idea.
22243         //this.editor.contentWindow.focus();
22244         if (typeof sel != "undefined") {
22245             try {
22246                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22247             } catch(e) {
22248                 return this.doc.createRange();
22249             }
22250         } else {
22251             return this.doc.createRange();
22252         }
22253     },
22254     getParentElement: function()
22255     {
22256         
22257         this.assignDocWin();
22258         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22259         
22260         var range = this.createRange(sel);
22261          
22262         try {
22263             var p = range.commonAncestorContainer;
22264             while (p.nodeType == 3) { // text node
22265                 p = p.parentNode;
22266             }
22267             return p;
22268         } catch (e) {
22269             return null;
22270         }
22271     
22272     },
22273     /***
22274      *
22275      * Range intersection.. the hard stuff...
22276      *  '-1' = before
22277      *  '0' = hits..
22278      *  '1' = after.
22279      *         [ -- selected range --- ]
22280      *   [fail]                        [fail]
22281      *
22282      *    basically..
22283      *      if end is before start or  hits it. fail.
22284      *      if start is after end or hits it fail.
22285      *
22286      *   if either hits (but other is outside. - then it's not 
22287      *   
22288      *    
22289      **/
22290     
22291     
22292     // @see http://www.thismuchiknow.co.uk/?p=64.
22293     rangeIntersectsNode : function(range, node)
22294     {
22295         var nodeRange = node.ownerDocument.createRange();
22296         try {
22297             nodeRange.selectNode(node);
22298         } catch (e) {
22299             nodeRange.selectNodeContents(node);
22300         }
22301     
22302         var rangeStartRange = range.cloneRange();
22303         rangeStartRange.collapse(true);
22304     
22305         var rangeEndRange = range.cloneRange();
22306         rangeEndRange.collapse(false);
22307     
22308         var nodeStartRange = nodeRange.cloneRange();
22309         nodeStartRange.collapse(true);
22310     
22311         var nodeEndRange = nodeRange.cloneRange();
22312         nodeEndRange.collapse(false);
22313     
22314         return rangeStartRange.compareBoundaryPoints(
22315                  Range.START_TO_START, nodeEndRange) == -1 &&
22316                rangeEndRange.compareBoundaryPoints(
22317                  Range.START_TO_START, nodeStartRange) == 1;
22318         
22319          
22320     },
22321     rangeCompareNode : function(range, node)
22322     {
22323         var nodeRange = node.ownerDocument.createRange();
22324         try {
22325             nodeRange.selectNode(node);
22326         } catch (e) {
22327             nodeRange.selectNodeContents(node);
22328         }
22329         
22330         
22331         range.collapse(true);
22332     
22333         nodeRange.collapse(true);
22334      
22335         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22336         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22337          
22338         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22339         
22340         var nodeIsBefore   =  ss == 1;
22341         var nodeIsAfter    = ee == -1;
22342         
22343         if (nodeIsBefore && nodeIsAfter) {
22344             return 0; // outer
22345         }
22346         if (!nodeIsBefore && nodeIsAfter) {
22347             return 1; //right trailed.
22348         }
22349         
22350         if (nodeIsBefore && !nodeIsAfter) {
22351             return 2;  // left trailed.
22352         }
22353         // fully contined.
22354         return 3;
22355     },
22356
22357     // private? - in a new class?
22358     cleanUpPaste :  function()
22359     {
22360         // cleans up the whole document..
22361         Roo.log('cleanuppaste');
22362         
22363         this.cleanUpChildren(this.doc.body);
22364         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22365         if (clean != this.doc.body.innerHTML) {
22366             this.doc.body.innerHTML = clean;
22367         }
22368         
22369     },
22370     
22371     cleanWordChars : function(input) {// change the chars to hex code
22372         var he = Roo.HtmlEditorCore;
22373         
22374         var output = input;
22375         Roo.each(he.swapCodes, function(sw) { 
22376             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22377             
22378             output = output.replace(swapper, sw[1]);
22379         });
22380         
22381         return output;
22382     },
22383     
22384     
22385     cleanUpChildren : function (n)
22386     {
22387         if (!n.childNodes.length) {
22388             return;
22389         }
22390         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22391            this.cleanUpChild(n.childNodes[i]);
22392         }
22393     },
22394     
22395     
22396         
22397     
22398     cleanUpChild : function (node)
22399     {
22400         var ed = this;
22401         //console.log(node);
22402         if (node.nodeName == "#text") {
22403             // clean up silly Windows -- stuff?
22404             return; 
22405         }
22406         if (node.nodeName == "#comment") {
22407             node.parentNode.removeChild(node);
22408             // clean up silly Windows -- stuff?
22409             return; 
22410         }
22411         var lcname = node.tagName.toLowerCase();
22412         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22413         // whitelist of tags..
22414         
22415         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22416             // remove node.
22417             node.parentNode.removeChild(node);
22418             return;
22419             
22420         }
22421         
22422         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22423         
22424         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22425         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22426         
22427         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22428         //    remove_keep_children = true;
22429         //}
22430         
22431         if (remove_keep_children) {
22432             this.cleanUpChildren(node);
22433             // inserts everything just before this node...
22434             while (node.childNodes.length) {
22435                 var cn = node.childNodes[0];
22436                 node.removeChild(cn);
22437                 node.parentNode.insertBefore(cn, node);
22438             }
22439             node.parentNode.removeChild(node);
22440             return;
22441         }
22442         
22443         if (!node.attributes || !node.attributes.length) {
22444             this.cleanUpChildren(node);
22445             return;
22446         }
22447         
22448         function cleanAttr(n,v)
22449         {
22450             
22451             if (v.match(/^\./) || v.match(/^\//)) {
22452                 return;
22453             }
22454             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22455                 return;
22456             }
22457             if (v.match(/^#/)) {
22458                 return;
22459             }
22460 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22461             node.removeAttribute(n);
22462             
22463         }
22464         
22465         var cwhite = this.cwhite;
22466         var cblack = this.cblack;
22467             
22468         function cleanStyle(n,v)
22469         {
22470             if (v.match(/expression/)) { //XSS?? should we even bother..
22471                 node.removeAttribute(n);
22472                 return;
22473             }
22474             
22475             var parts = v.split(/;/);
22476             var clean = [];
22477             
22478             Roo.each(parts, function(p) {
22479                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22480                 if (!p.length) {
22481                     return true;
22482                 }
22483                 var l = p.split(':').shift().replace(/\s+/g,'');
22484                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22485                 
22486                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22487 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22488                     //node.removeAttribute(n);
22489                     return true;
22490                 }
22491                 //Roo.log()
22492                 // only allow 'c whitelisted system attributes'
22493                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22494 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22495                     //node.removeAttribute(n);
22496                     return true;
22497                 }
22498                 
22499                 
22500                  
22501                 
22502                 clean.push(p);
22503                 return true;
22504             });
22505             if (clean.length) { 
22506                 node.setAttribute(n, clean.join(';'));
22507             } else {
22508                 node.removeAttribute(n);
22509             }
22510             
22511         }
22512         
22513         
22514         for (var i = node.attributes.length-1; i > -1 ; i--) {
22515             var a = node.attributes[i];
22516             //console.log(a);
22517             
22518             if (a.name.toLowerCase().substr(0,2)=='on')  {
22519                 node.removeAttribute(a.name);
22520                 continue;
22521             }
22522             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22523                 node.removeAttribute(a.name);
22524                 continue;
22525             }
22526             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22527                 cleanAttr(a.name,a.value); // fixme..
22528                 continue;
22529             }
22530             if (a.name == 'style') {
22531                 cleanStyle(a.name,a.value);
22532                 continue;
22533             }
22534             /// clean up MS crap..
22535             // tecnically this should be a list of valid class'es..
22536             
22537             
22538             if (a.name == 'class') {
22539                 if (a.value.match(/^Mso/)) {
22540                     node.className = '';
22541                 }
22542                 
22543                 if (a.value.match(/^body$/)) {
22544                     node.className = '';
22545                 }
22546                 continue;
22547             }
22548             
22549             // style cleanup!?
22550             // class cleanup?
22551             
22552         }
22553         
22554         
22555         this.cleanUpChildren(node);
22556         
22557         
22558     },
22559     
22560     /**
22561      * Clean up MS wordisms...
22562      */
22563     cleanWord : function(node)
22564     {
22565         
22566         
22567         if (!node) {
22568             this.cleanWord(this.doc.body);
22569             return;
22570         }
22571         if (node.nodeName == "#text") {
22572             // clean up silly Windows -- stuff?
22573             return; 
22574         }
22575         if (node.nodeName == "#comment") {
22576             node.parentNode.removeChild(node);
22577             // clean up silly Windows -- stuff?
22578             return; 
22579         }
22580         
22581         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22582             node.parentNode.removeChild(node);
22583             return;
22584         }
22585         
22586         // remove - but keep children..
22587         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22588             while (node.childNodes.length) {
22589                 var cn = node.childNodes[0];
22590                 node.removeChild(cn);
22591                 node.parentNode.insertBefore(cn, node);
22592             }
22593             node.parentNode.removeChild(node);
22594             this.iterateChildren(node, this.cleanWord);
22595             return;
22596         }
22597         // clean styles
22598         if (node.className.length) {
22599             
22600             var cn = node.className.split(/\W+/);
22601             var cna = [];
22602             Roo.each(cn, function(cls) {
22603                 if (cls.match(/Mso[a-zA-Z]+/)) {
22604                     return;
22605                 }
22606                 cna.push(cls);
22607             });
22608             node.className = cna.length ? cna.join(' ') : '';
22609             if (!cna.length) {
22610                 node.removeAttribute("class");
22611             }
22612         }
22613         
22614         if (node.hasAttribute("lang")) {
22615             node.removeAttribute("lang");
22616         }
22617         
22618         if (node.hasAttribute("style")) {
22619             
22620             var styles = node.getAttribute("style").split(";");
22621             var nstyle = [];
22622             Roo.each(styles, function(s) {
22623                 if (!s.match(/:/)) {
22624                     return;
22625                 }
22626                 var kv = s.split(":");
22627                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22628                     return;
22629                 }
22630                 // what ever is left... we allow.
22631                 nstyle.push(s);
22632             });
22633             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22634             if (!nstyle.length) {
22635                 node.removeAttribute('style');
22636             }
22637         }
22638         this.iterateChildren(node, this.cleanWord);
22639         
22640         
22641         
22642     },
22643     /**
22644      * iterateChildren of a Node, calling fn each time, using this as the scole..
22645      * @param {DomNode} node node to iterate children of.
22646      * @param {Function} fn method of this class to call on each item.
22647      */
22648     iterateChildren : function(node, fn)
22649     {
22650         if (!node.childNodes.length) {
22651                 return;
22652         }
22653         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22654            fn.call(this, node.childNodes[i])
22655         }
22656     },
22657     
22658     
22659     /**
22660      * cleanTableWidths.
22661      *
22662      * Quite often pasting from word etc.. results in tables with column and widths.
22663      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22664      *
22665      */
22666     cleanTableWidths : function(node)
22667     {
22668          
22669          
22670         if (!node) {
22671             this.cleanTableWidths(this.doc.body);
22672             return;
22673         }
22674         
22675         // ignore list...
22676         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22677             return; 
22678         }
22679         Roo.log(node.tagName);
22680         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22681             this.iterateChildren(node, this.cleanTableWidths);
22682             return;
22683         }
22684         if (node.hasAttribute('width')) {
22685             node.removeAttribute('width');
22686         }
22687         
22688          
22689         if (node.hasAttribute("style")) {
22690             // pretty basic...
22691             
22692             var styles = node.getAttribute("style").split(";");
22693             var nstyle = [];
22694             Roo.each(styles, function(s) {
22695                 if (!s.match(/:/)) {
22696                     return;
22697                 }
22698                 var kv = s.split(":");
22699                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22700                     return;
22701                 }
22702                 // what ever is left... we allow.
22703                 nstyle.push(s);
22704             });
22705             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22706             if (!nstyle.length) {
22707                 node.removeAttribute('style');
22708             }
22709         }
22710         
22711         this.iterateChildren(node, this.cleanTableWidths);
22712         
22713         
22714     },
22715     
22716     
22717     
22718     
22719     domToHTML : function(currentElement, depth, nopadtext) {
22720         
22721         depth = depth || 0;
22722         nopadtext = nopadtext || false;
22723     
22724         if (!currentElement) {
22725             return this.domToHTML(this.doc.body);
22726         }
22727         
22728         //Roo.log(currentElement);
22729         var j;
22730         var allText = false;
22731         var nodeName = currentElement.nodeName;
22732         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22733         
22734         if  (nodeName == '#text') {
22735             
22736             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22737         }
22738         
22739         
22740         var ret = '';
22741         if (nodeName != 'BODY') {
22742              
22743             var i = 0;
22744             // Prints the node tagName, such as <A>, <IMG>, etc
22745             if (tagName) {
22746                 var attr = [];
22747                 for(i = 0; i < currentElement.attributes.length;i++) {
22748                     // quoting?
22749                     var aname = currentElement.attributes.item(i).name;
22750                     if (!currentElement.attributes.item(i).value.length) {
22751                         continue;
22752                     }
22753                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22754                 }
22755                 
22756                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22757             } 
22758             else {
22759                 
22760                 // eack
22761             }
22762         } else {
22763             tagName = false;
22764         }
22765         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22766             return ret;
22767         }
22768         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22769             nopadtext = true;
22770         }
22771         
22772         
22773         // Traverse the tree
22774         i = 0;
22775         var currentElementChild = currentElement.childNodes.item(i);
22776         var allText = true;
22777         var innerHTML  = '';
22778         lastnode = '';
22779         while (currentElementChild) {
22780             // Formatting code (indent the tree so it looks nice on the screen)
22781             var nopad = nopadtext;
22782             if (lastnode == 'SPAN') {
22783                 nopad  = true;
22784             }
22785             // text
22786             if  (currentElementChild.nodeName == '#text') {
22787                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22788                 toadd = nopadtext ? toadd : toadd.trim();
22789                 if (!nopad && toadd.length > 80) {
22790                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22791                 }
22792                 innerHTML  += toadd;
22793                 
22794                 i++;
22795                 currentElementChild = currentElement.childNodes.item(i);
22796                 lastNode = '';
22797                 continue;
22798             }
22799             allText = false;
22800             
22801             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22802                 
22803             // Recursively traverse the tree structure of the child node
22804             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22805             lastnode = currentElementChild.nodeName;
22806             i++;
22807             currentElementChild=currentElement.childNodes.item(i);
22808         }
22809         
22810         ret += innerHTML;
22811         
22812         if (!allText) {
22813                 // The remaining code is mostly for formatting the tree
22814             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22815         }
22816         
22817         
22818         if (tagName) {
22819             ret+= "</"+tagName+">";
22820         }
22821         return ret;
22822         
22823     },
22824         
22825     applyBlacklists : function()
22826     {
22827         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22828         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22829         
22830         this.white = [];
22831         this.black = [];
22832         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22833             if (b.indexOf(tag) > -1) {
22834                 return;
22835             }
22836             this.white.push(tag);
22837             
22838         }, this);
22839         
22840         Roo.each(w, function(tag) {
22841             if (b.indexOf(tag) > -1) {
22842                 return;
22843             }
22844             if (this.white.indexOf(tag) > -1) {
22845                 return;
22846             }
22847             this.white.push(tag);
22848             
22849         }, this);
22850         
22851         
22852         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22853             if (w.indexOf(tag) > -1) {
22854                 return;
22855             }
22856             this.black.push(tag);
22857             
22858         }, this);
22859         
22860         Roo.each(b, function(tag) {
22861             if (w.indexOf(tag) > -1) {
22862                 return;
22863             }
22864             if (this.black.indexOf(tag) > -1) {
22865                 return;
22866             }
22867             this.black.push(tag);
22868             
22869         }, this);
22870         
22871         
22872         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22873         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22874         
22875         this.cwhite = [];
22876         this.cblack = [];
22877         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22878             if (b.indexOf(tag) > -1) {
22879                 return;
22880             }
22881             this.cwhite.push(tag);
22882             
22883         }, this);
22884         
22885         Roo.each(w, function(tag) {
22886             if (b.indexOf(tag) > -1) {
22887                 return;
22888             }
22889             if (this.cwhite.indexOf(tag) > -1) {
22890                 return;
22891             }
22892             this.cwhite.push(tag);
22893             
22894         }, this);
22895         
22896         
22897         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22898             if (w.indexOf(tag) > -1) {
22899                 return;
22900             }
22901             this.cblack.push(tag);
22902             
22903         }, this);
22904         
22905         Roo.each(b, function(tag) {
22906             if (w.indexOf(tag) > -1) {
22907                 return;
22908             }
22909             if (this.cblack.indexOf(tag) > -1) {
22910                 return;
22911             }
22912             this.cblack.push(tag);
22913             
22914         }, this);
22915     },
22916     
22917     setStylesheets : function(stylesheets)
22918     {
22919         if(typeof(stylesheets) == 'string'){
22920             Roo.get(this.iframe.contentDocument.head).createChild({
22921                 tag : 'link',
22922                 rel : 'stylesheet',
22923                 type : 'text/css',
22924                 href : stylesheets
22925             });
22926             
22927             return;
22928         }
22929         var _this = this;
22930      
22931         Roo.each(stylesheets, function(s) {
22932             if(!s.length){
22933                 return;
22934             }
22935             
22936             Roo.get(_this.iframe.contentDocument.head).createChild({
22937                 tag : 'link',
22938                 rel : 'stylesheet',
22939                 type : 'text/css',
22940                 href : s
22941             });
22942         });
22943
22944         
22945     },
22946     
22947     removeStylesheets : function()
22948     {
22949         var _this = this;
22950         
22951         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22952             s.remove();
22953         });
22954     },
22955     
22956     setStyle : function(style)
22957     {
22958         Roo.get(this.iframe.contentDocument.head).createChild({
22959             tag : 'style',
22960             type : 'text/css',
22961             html : style
22962         });
22963
22964         return;
22965     }
22966     
22967     // hide stuff that is not compatible
22968     /**
22969      * @event blur
22970      * @hide
22971      */
22972     /**
22973      * @event change
22974      * @hide
22975      */
22976     /**
22977      * @event focus
22978      * @hide
22979      */
22980     /**
22981      * @event specialkey
22982      * @hide
22983      */
22984     /**
22985      * @cfg {String} fieldClass @hide
22986      */
22987     /**
22988      * @cfg {String} focusClass @hide
22989      */
22990     /**
22991      * @cfg {String} autoCreate @hide
22992      */
22993     /**
22994      * @cfg {String} inputType @hide
22995      */
22996     /**
22997      * @cfg {String} invalidClass @hide
22998      */
22999     /**
23000      * @cfg {String} invalidText @hide
23001      */
23002     /**
23003      * @cfg {String} msgFx @hide
23004      */
23005     /**
23006      * @cfg {String} validateOnBlur @hide
23007      */
23008 });
23009
23010 Roo.HtmlEditorCore.white = [
23011         'area', 'br', 'img', 'input', 'hr', 'wbr',
23012         
23013        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23014        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23015        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23016        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23017        'table',   'ul',         'xmp', 
23018        
23019        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23020       'thead',   'tr', 
23021      
23022       'dir', 'menu', 'ol', 'ul', 'dl',
23023        
23024       'embed',  'object'
23025 ];
23026
23027
23028 Roo.HtmlEditorCore.black = [
23029     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23030         'applet', // 
23031         'base',   'basefont', 'bgsound', 'blink',  'body', 
23032         'frame',  'frameset', 'head',    'html',   'ilayer', 
23033         'iframe', 'layer',  'link',     'meta',    'object',   
23034         'script', 'style' ,'title',  'xml' // clean later..
23035 ];
23036 Roo.HtmlEditorCore.clean = [
23037     'script', 'style', 'title', 'xml'
23038 ];
23039 Roo.HtmlEditorCore.remove = [
23040     'font'
23041 ];
23042 // attributes..
23043
23044 Roo.HtmlEditorCore.ablack = [
23045     'on'
23046 ];
23047     
23048 Roo.HtmlEditorCore.aclean = [ 
23049     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23050 ];
23051
23052 // protocols..
23053 Roo.HtmlEditorCore.pwhite= [
23054         'http',  'https',  'mailto'
23055 ];
23056
23057 // white listed style attributes.
23058 Roo.HtmlEditorCore.cwhite= [
23059       //  'text-align', /// default is to allow most things..
23060       
23061          
23062 //        'font-size'//??
23063 ];
23064
23065 // black listed style attributes.
23066 Roo.HtmlEditorCore.cblack= [
23067       //  'font-size' -- this can be set by the project 
23068 ];
23069
23070
23071 Roo.HtmlEditorCore.swapCodes   =[ 
23072     [    8211, "--" ], 
23073     [    8212, "--" ], 
23074     [    8216,  "'" ],  
23075     [    8217, "'" ],  
23076     [    8220, '"' ],  
23077     [    8221, '"' ],  
23078     [    8226, "*" ],  
23079     [    8230, "..." ]
23080 ]; 
23081
23082     /*
23083  * - LGPL
23084  *
23085  * HtmlEditor
23086  * 
23087  */
23088
23089 /**
23090  * @class Roo.bootstrap.HtmlEditor
23091  * @extends Roo.bootstrap.TextArea
23092  * Bootstrap HtmlEditor class
23093
23094  * @constructor
23095  * Create a new HtmlEditor
23096  * @param {Object} config The config object
23097  */
23098
23099 Roo.bootstrap.HtmlEditor = function(config){
23100     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23101     if (!this.toolbars) {
23102         this.toolbars = [];
23103     }
23104     
23105     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23106     this.addEvents({
23107             /**
23108              * @event initialize
23109              * Fires when the editor is fully initialized (including the iframe)
23110              * @param {HtmlEditor} this
23111              */
23112             initialize: true,
23113             /**
23114              * @event activate
23115              * Fires when the editor is first receives the focus. Any insertion must wait
23116              * until after this event.
23117              * @param {HtmlEditor} this
23118              */
23119             activate: true,
23120              /**
23121              * @event beforesync
23122              * Fires before the textarea is updated with content from the editor iframe. Return false
23123              * to cancel the sync.
23124              * @param {HtmlEditor} this
23125              * @param {String} html
23126              */
23127             beforesync: true,
23128              /**
23129              * @event beforepush
23130              * Fires before the iframe editor is updated with content from the textarea. Return false
23131              * to cancel the push.
23132              * @param {HtmlEditor} this
23133              * @param {String} html
23134              */
23135             beforepush: true,
23136              /**
23137              * @event sync
23138              * Fires when the textarea is updated with content from the editor iframe.
23139              * @param {HtmlEditor} this
23140              * @param {String} html
23141              */
23142             sync: true,
23143              /**
23144              * @event push
23145              * Fires when the iframe editor is updated with content from the textarea.
23146              * @param {HtmlEditor} this
23147              * @param {String} html
23148              */
23149             push: true,
23150              /**
23151              * @event editmodechange
23152              * Fires when the editor switches edit modes
23153              * @param {HtmlEditor} this
23154              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23155              */
23156             editmodechange: true,
23157             /**
23158              * @event editorevent
23159              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23160              * @param {HtmlEditor} this
23161              */
23162             editorevent: true,
23163             /**
23164              * @event firstfocus
23165              * Fires when on first focus - needed by toolbars..
23166              * @param {HtmlEditor} this
23167              */
23168             firstfocus: true,
23169             /**
23170              * @event autosave
23171              * Auto save the htmlEditor value as a file into Events
23172              * @param {HtmlEditor} this
23173              */
23174             autosave: true,
23175             /**
23176              * @event savedpreview
23177              * preview the saved version of htmlEditor
23178              * @param {HtmlEditor} this
23179              */
23180             savedpreview: true
23181         });
23182 };
23183
23184
23185 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23186     
23187     
23188       /**
23189      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23190      */
23191     toolbars : false,
23192     
23193      /**
23194     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23195     */
23196     btns : [],
23197    
23198      /**
23199      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23200      *                        Roo.resizable.
23201      */
23202     resizable : false,
23203      /**
23204      * @cfg {Number} height (in pixels)
23205      */   
23206     height: 300,
23207    /**
23208      * @cfg {Number} width (in pixels)
23209      */   
23210     width: false,
23211     
23212     /**
23213      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23214      * 
23215      */
23216     stylesheets: false,
23217     
23218     // id of frame..
23219     frameId: false,
23220     
23221     // private properties
23222     validationEvent : false,
23223     deferHeight: true,
23224     initialized : false,
23225     activated : false,
23226     
23227     onFocus : Roo.emptyFn,
23228     iframePad:3,
23229     hideMode:'offsets',
23230     
23231     tbContainer : false,
23232     
23233     bodyCls : '',
23234     
23235     toolbarContainer :function() {
23236         return this.wrap.select('.x-html-editor-tb',true).first();
23237     },
23238
23239     /**
23240      * Protected method that will not generally be called directly. It
23241      * is called when the editor creates its toolbar. Override this method if you need to
23242      * add custom toolbar buttons.
23243      * @param {HtmlEditor} editor
23244      */
23245     createToolbar : function(){
23246         Roo.log('renewing');
23247         Roo.log("create toolbars");
23248         
23249         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23250         this.toolbars[0].render(this.toolbarContainer());
23251         
23252         return;
23253         
23254 //        if (!editor.toolbars || !editor.toolbars.length) {
23255 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23256 //        }
23257 //        
23258 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23259 //            editor.toolbars[i] = Roo.factory(
23260 //                    typeof(editor.toolbars[i]) == 'string' ?
23261 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23262 //                Roo.bootstrap.HtmlEditor);
23263 //            editor.toolbars[i].init(editor);
23264 //        }
23265     },
23266
23267      
23268     // private
23269     onRender : function(ct, position)
23270     {
23271        // Roo.log("Call onRender: " + this.xtype);
23272         var _t = this;
23273         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23274       
23275         this.wrap = this.inputEl().wrap({
23276             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23277         });
23278         
23279         this.editorcore.onRender(ct, position);
23280          
23281         if (this.resizable) {
23282             this.resizeEl = new Roo.Resizable(this.wrap, {
23283                 pinned : true,
23284                 wrap: true,
23285                 dynamic : true,
23286                 minHeight : this.height,
23287                 height: this.height,
23288                 handles : this.resizable,
23289                 width: this.width,
23290                 listeners : {
23291                     resize : function(r, w, h) {
23292                         _t.onResize(w,h); // -something
23293                     }
23294                 }
23295             });
23296             
23297         }
23298         this.createToolbar(this);
23299        
23300         
23301         if(!this.width && this.resizable){
23302             this.setSize(this.wrap.getSize());
23303         }
23304         if (this.resizeEl) {
23305             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23306             // should trigger onReize..
23307         }
23308         
23309     },
23310
23311     // private
23312     onResize : function(w, h)
23313     {
23314         Roo.log('resize: ' +w + ',' + h );
23315         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23316         var ew = false;
23317         var eh = false;
23318         
23319         if(this.inputEl() ){
23320             if(typeof w == 'number'){
23321                 var aw = w - this.wrap.getFrameWidth('lr');
23322                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23323                 ew = aw;
23324             }
23325             if(typeof h == 'number'){
23326                  var tbh = -11;  // fixme it needs to tool bar size!
23327                 for (var i =0; i < this.toolbars.length;i++) {
23328                     // fixme - ask toolbars for heights?
23329                     tbh += this.toolbars[i].el.getHeight();
23330                     //if (this.toolbars[i].footer) {
23331                     //    tbh += this.toolbars[i].footer.el.getHeight();
23332                     //}
23333                 }
23334               
23335                 
23336                 
23337                 
23338                 
23339                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23340                 ah -= 5; // knock a few pixes off for look..
23341                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23342                 var eh = ah;
23343             }
23344         }
23345         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23346         this.editorcore.onResize(ew,eh);
23347         
23348     },
23349
23350     /**
23351      * Toggles the editor between standard and source edit mode.
23352      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23353      */
23354     toggleSourceEdit : function(sourceEditMode)
23355     {
23356         this.editorcore.toggleSourceEdit(sourceEditMode);
23357         
23358         if(this.editorcore.sourceEditMode){
23359             Roo.log('editor - showing textarea');
23360             
23361 //            Roo.log('in');
23362 //            Roo.log(this.syncValue());
23363             this.syncValue();
23364             this.inputEl().removeClass(['hide', 'x-hidden']);
23365             this.inputEl().dom.removeAttribute('tabIndex');
23366             this.inputEl().focus();
23367         }else{
23368             Roo.log('editor - hiding textarea');
23369 //            Roo.log('out')
23370 //            Roo.log(this.pushValue()); 
23371             this.pushValue();
23372             
23373             this.inputEl().addClass(['hide', 'x-hidden']);
23374             this.inputEl().dom.setAttribute('tabIndex', -1);
23375             //this.deferFocus();
23376         }
23377          
23378         if(this.resizable){
23379             this.setSize(this.wrap.getSize());
23380         }
23381         
23382         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23383     },
23384  
23385     // private (for BoxComponent)
23386     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23387
23388     // private (for BoxComponent)
23389     getResizeEl : function(){
23390         return this.wrap;
23391     },
23392
23393     // private (for BoxComponent)
23394     getPositionEl : function(){
23395         return this.wrap;
23396     },
23397
23398     // private
23399     initEvents : function(){
23400         this.originalValue = this.getValue();
23401     },
23402
23403 //    /**
23404 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23405 //     * @method
23406 //     */
23407 //    markInvalid : Roo.emptyFn,
23408 //    /**
23409 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23410 //     * @method
23411 //     */
23412 //    clearInvalid : Roo.emptyFn,
23413
23414     setValue : function(v){
23415         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23416         this.editorcore.pushValue();
23417     },
23418
23419      
23420     // private
23421     deferFocus : function(){
23422         this.focus.defer(10, this);
23423     },
23424
23425     // doc'ed in Field
23426     focus : function(){
23427         this.editorcore.focus();
23428         
23429     },
23430       
23431
23432     // private
23433     onDestroy : function(){
23434         
23435         
23436         
23437         if(this.rendered){
23438             
23439             for (var i =0; i < this.toolbars.length;i++) {
23440                 // fixme - ask toolbars for heights?
23441                 this.toolbars[i].onDestroy();
23442             }
23443             
23444             this.wrap.dom.innerHTML = '';
23445             this.wrap.remove();
23446         }
23447     },
23448
23449     // private
23450     onFirstFocus : function(){
23451         //Roo.log("onFirstFocus");
23452         this.editorcore.onFirstFocus();
23453          for (var i =0; i < this.toolbars.length;i++) {
23454             this.toolbars[i].onFirstFocus();
23455         }
23456         
23457     },
23458     
23459     // private
23460     syncValue : function()
23461     {   
23462         this.editorcore.syncValue();
23463     },
23464     
23465     pushValue : function()
23466     {   
23467         this.editorcore.pushValue();
23468     }
23469      
23470     
23471     // hide stuff that is not compatible
23472     /**
23473      * @event blur
23474      * @hide
23475      */
23476     /**
23477      * @event change
23478      * @hide
23479      */
23480     /**
23481      * @event focus
23482      * @hide
23483      */
23484     /**
23485      * @event specialkey
23486      * @hide
23487      */
23488     /**
23489      * @cfg {String} fieldClass @hide
23490      */
23491     /**
23492      * @cfg {String} focusClass @hide
23493      */
23494     /**
23495      * @cfg {String} autoCreate @hide
23496      */
23497     /**
23498      * @cfg {String} inputType @hide
23499      */
23500     /**
23501      * @cfg {String} invalidClass @hide
23502      */
23503     /**
23504      * @cfg {String} invalidText @hide
23505      */
23506     /**
23507      * @cfg {String} msgFx @hide
23508      */
23509     /**
23510      * @cfg {String} validateOnBlur @hide
23511      */
23512 });
23513  
23514     
23515    
23516    
23517    
23518       
23519 Roo.namespace('Roo.bootstrap.htmleditor');
23520 /**
23521  * @class Roo.bootstrap.HtmlEditorToolbar1
23522  * Basic Toolbar
23523  * 
23524  * Usage:
23525  *
23526  new Roo.bootstrap.HtmlEditor({
23527     ....
23528     toolbars : [
23529         new Roo.bootstrap.HtmlEditorToolbar1({
23530             disable : { fonts: 1 , format: 1, ..., ... , ...],
23531             btns : [ .... ]
23532         })
23533     }
23534      
23535  * 
23536  * @cfg {Object} disable List of elements to disable..
23537  * @cfg {Array} btns List of additional buttons.
23538  * 
23539  * 
23540  * NEEDS Extra CSS? 
23541  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23542  */
23543  
23544 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23545 {
23546     
23547     Roo.apply(this, config);
23548     
23549     // default disabled, based on 'good practice'..
23550     this.disable = this.disable || {};
23551     Roo.applyIf(this.disable, {
23552         fontSize : true,
23553         colors : true,
23554         specialElements : true
23555     });
23556     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23557     
23558     this.editor = config.editor;
23559     this.editorcore = config.editor.editorcore;
23560     
23561     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23562     
23563     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23564     // dont call parent... till later.
23565 }
23566 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23567      
23568     bar : true,
23569     
23570     editor : false,
23571     editorcore : false,
23572     
23573     
23574     formats : [
23575         "p" ,  
23576         "h1","h2","h3","h4","h5","h6", 
23577         "pre", "code", 
23578         "abbr", "acronym", "address", "cite", "samp", "var",
23579         'div','span'
23580     ],
23581     
23582     onRender : function(ct, position)
23583     {
23584        // Roo.log("Call onRender: " + this.xtype);
23585         
23586        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23587        Roo.log(this.el);
23588        this.el.dom.style.marginBottom = '0';
23589        var _this = this;
23590        var editorcore = this.editorcore;
23591        var editor= this.editor;
23592        
23593        var children = [];
23594        var btn = function(id,cmd , toggle, handler, html){
23595        
23596             var  event = toggle ? 'toggle' : 'click';
23597        
23598             var a = {
23599                 size : 'sm',
23600                 xtype: 'Button',
23601                 xns: Roo.bootstrap,
23602                 glyphicon : id,
23603                 cmd : id || cmd,
23604                 enableToggle:toggle !== false,
23605                 html : html || '',
23606                 pressed : toggle ? false : null,
23607                 listeners : {}
23608             };
23609             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23610                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23611             };
23612             children.push(a);
23613             return a;
23614        }
23615        
23616     //    var cb_box = function...
23617         
23618         var style = {
23619                 xtype: 'Button',
23620                 size : 'sm',
23621                 xns: Roo.bootstrap,
23622                 glyphicon : 'font',
23623                 //html : 'submit'
23624                 menu : {
23625                     xtype: 'Menu',
23626                     xns: Roo.bootstrap,
23627                     items:  []
23628                 }
23629         };
23630         Roo.each(this.formats, function(f) {
23631             style.menu.items.push({
23632                 xtype :'MenuItem',
23633                 xns: Roo.bootstrap,
23634                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23635                 tagname : f,
23636                 listeners : {
23637                     click : function()
23638                     {
23639                         editorcore.insertTag(this.tagname);
23640                         editor.focus();
23641                     }
23642                 }
23643                 
23644             });
23645         });
23646         children.push(style);   
23647         
23648         btn('bold',false,true);
23649         btn('italic',false,true);
23650         btn('align-left', 'justifyleft',true);
23651         btn('align-center', 'justifycenter',true);
23652         btn('align-right' , 'justifyright',true);
23653         btn('link', false, false, function(btn) {
23654             //Roo.log("create link?");
23655             var url = prompt(this.createLinkText, this.defaultLinkValue);
23656             if(url && url != 'http:/'+'/'){
23657                 this.editorcore.relayCmd('createlink', url);
23658             }
23659         }),
23660         btn('list','insertunorderedlist',true);
23661         btn('pencil', false,true, function(btn){
23662                 Roo.log(this);
23663                 this.toggleSourceEdit(btn.pressed);
23664         });
23665         
23666         if (this.editor.btns.length > 0) {
23667             for (var i = 0; i<this.editor.btns.length; i++) {
23668                 children.push(this.editor.btns[i]);
23669             }
23670         }
23671         
23672         /*
23673         var cog = {
23674                 xtype: 'Button',
23675                 size : 'sm',
23676                 xns: Roo.bootstrap,
23677                 glyphicon : 'cog',
23678                 //html : 'submit'
23679                 menu : {
23680                     xtype: 'Menu',
23681                     xns: Roo.bootstrap,
23682                     items:  []
23683                 }
23684         };
23685         
23686         cog.menu.items.push({
23687             xtype :'MenuItem',
23688             xns: Roo.bootstrap,
23689             html : Clean styles,
23690             tagname : f,
23691             listeners : {
23692                 click : function()
23693                 {
23694                     editorcore.insertTag(this.tagname);
23695                     editor.focus();
23696                 }
23697             }
23698             
23699         });
23700        */
23701         
23702          
23703        this.xtype = 'NavSimplebar';
23704         
23705         for(var i=0;i< children.length;i++) {
23706             
23707             this.buttons.add(this.addxtypeChild(children[i]));
23708             
23709         }
23710         
23711         editor.on('editorevent', this.updateToolbar, this);
23712     },
23713     onBtnClick : function(id)
23714     {
23715        this.editorcore.relayCmd(id);
23716        this.editorcore.focus();
23717     },
23718     
23719     /**
23720      * Protected method that will not generally be called directly. It triggers
23721      * a toolbar update by reading the markup state of the current selection in the editor.
23722      */
23723     updateToolbar: function(){
23724
23725         if(!this.editorcore.activated){
23726             this.editor.onFirstFocus(); // is this neeed?
23727             return;
23728         }
23729
23730         var btns = this.buttons; 
23731         var doc = this.editorcore.doc;
23732         btns.get('bold').setActive(doc.queryCommandState('bold'));
23733         btns.get('italic').setActive(doc.queryCommandState('italic'));
23734         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23735         
23736         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23737         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23738         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23739         
23740         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23741         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23742          /*
23743         
23744         var ans = this.editorcore.getAllAncestors();
23745         if (this.formatCombo) {
23746             
23747             
23748             var store = this.formatCombo.store;
23749             this.formatCombo.setValue("");
23750             for (var i =0; i < ans.length;i++) {
23751                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23752                     // select it..
23753                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23754                     break;
23755                 }
23756             }
23757         }
23758         
23759         
23760         
23761         // hides menus... - so this cant be on a menu...
23762         Roo.bootstrap.MenuMgr.hideAll();
23763         */
23764         Roo.bootstrap.MenuMgr.hideAll();
23765         //this.editorsyncValue();
23766     },
23767     onFirstFocus: function() {
23768         this.buttons.each(function(item){
23769            item.enable();
23770         });
23771     },
23772     toggleSourceEdit : function(sourceEditMode){
23773         
23774           
23775         if(sourceEditMode){
23776             Roo.log("disabling buttons");
23777            this.buttons.each( function(item){
23778                 if(item.cmd != 'pencil'){
23779                     item.disable();
23780                 }
23781             });
23782           
23783         }else{
23784             Roo.log("enabling buttons");
23785             if(this.editorcore.initialized){
23786                 this.buttons.each( function(item){
23787                     item.enable();
23788                 });
23789             }
23790             
23791         }
23792         Roo.log("calling toggole on editor");
23793         // tell the editor that it's been pressed..
23794         this.editor.toggleSourceEdit(sourceEditMode);
23795        
23796     }
23797 });
23798
23799
23800
23801
23802
23803 /**
23804  * @class Roo.bootstrap.Table.AbstractSelectionModel
23805  * @extends Roo.util.Observable
23806  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23807  * implemented by descendant classes.  This class should not be directly instantiated.
23808  * @constructor
23809  */
23810 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23811     this.locked = false;
23812     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23813 };
23814
23815
23816 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23817     /** @ignore Called by the grid automatically. Do not call directly. */
23818     init : function(grid){
23819         this.grid = grid;
23820         this.initEvents();
23821     },
23822
23823     /**
23824      * Locks the selections.
23825      */
23826     lock : function(){
23827         this.locked = true;
23828     },
23829
23830     /**
23831      * Unlocks the selections.
23832      */
23833     unlock : function(){
23834         this.locked = false;
23835     },
23836
23837     /**
23838      * Returns true if the selections are locked.
23839      * @return {Boolean}
23840      */
23841     isLocked : function(){
23842         return this.locked;
23843     }
23844 });
23845 /**
23846  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23847  * @class Roo.bootstrap.Table.RowSelectionModel
23848  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23849  * It supports multiple selections and keyboard selection/navigation. 
23850  * @constructor
23851  * @param {Object} config
23852  */
23853
23854 Roo.bootstrap.Table.RowSelectionModel = function(config){
23855     Roo.apply(this, config);
23856     this.selections = new Roo.util.MixedCollection(false, function(o){
23857         return o.id;
23858     });
23859
23860     this.last = false;
23861     this.lastActive = false;
23862
23863     this.addEvents({
23864         /**
23865              * @event selectionchange
23866              * Fires when the selection changes
23867              * @param {SelectionModel} this
23868              */
23869             "selectionchange" : true,
23870         /**
23871              * @event afterselectionchange
23872              * Fires after the selection changes (eg. by key press or clicking)
23873              * @param {SelectionModel} this
23874              */
23875             "afterselectionchange" : true,
23876         /**
23877              * @event beforerowselect
23878              * Fires when a row is selected being selected, return false to cancel.
23879              * @param {SelectionModel} this
23880              * @param {Number} rowIndex The selected index
23881              * @param {Boolean} keepExisting False if other selections will be cleared
23882              */
23883             "beforerowselect" : true,
23884         /**
23885              * @event rowselect
23886              * Fires when a row is selected.
23887              * @param {SelectionModel} this
23888              * @param {Number} rowIndex The selected index
23889              * @param {Roo.data.Record} r The record
23890              */
23891             "rowselect" : true,
23892         /**
23893              * @event rowdeselect
23894              * Fires when a row is deselected.
23895              * @param {SelectionModel} this
23896              * @param {Number} rowIndex The selected index
23897              */
23898         "rowdeselect" : true
23899     });
23900     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23901     this.locked = false;
23902  };
23903
23904 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23905     /**
23906      * @cfg {Boolean} singleSelect
23907      * True to allow selection of only one row at a time (defaults to false)
23908      */
23909     singleSelect : false,
23910
23911     // private
23912     initEvents : function()
23913     {
23914
23915         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23916         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23917         //}else{ // allow click to work like normal
23918          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23919         //}
23920         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23921         this.grid.on("rowclick", this.handleMouseDown, this);
23922         
23923         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23924             "up" : function(e){
23925                 if(!e.shiftKey){
23926                     this.selectPrevious(e.shiftKey);
23927                 }else if(this.last !== false && this.lastActive !== false){
23928                     var last = this.last;
23929                     this.selectRange(this.last,  this.lastActive-1);
23930                     this.grid.getView().focusRow(this.lastActive);
23931                     if(last !== false){
23932                         this.last = last;
23933                     }
23934                 }else{
23935                     this.selectFirstRow();
23936                 }
23937                 this.fireEvent("afterselectionchange", this);
23938             },
23939             "down" : function(e){
23940                 if(!e.shiftKey){
23941                     this.selectNext(e.shiftKey);
23942                 }else if(this.last !== false && this.lastActive !== false){
23943                     var last = this.last;
23944                     this.selectRange(this.last,  this.lastActive+1);
23945                     this.grid.getView().focusRow(this.lastActive);
23946                     if(last !== false){
23947                         this.last = last;
23948                     }
23949                 }else{
23950                     this.selectFirstRow();
23951                 }
23952                 this.fireEvent("afterselectionchange", this);
23953             },
23954             scope: this
23955         });
23956         this.grid.store.on('load', function(){
23957             this.selections.clear();
23958         },this);
23959         /*
23960         var view = this.grid.view;
23961         view.on("refresh", this.onRefresh, this);
23962         view.on("rowupdated", this.onRowUpdated, this);
23963         view.on("rowremoved", this.onRemove, this);
23964         */
23965     },
23966
23967     // private
23968     onRefresh : function()
23969     {
23970         var ds = this.grid.store, i, v = this.grid.view;
23971         var s = this.selections;
23972         s.each(function(r){
23973             if((i = ds.indexOfId(r.id)) != -1){
23974                 v.onRowSelect(i);
23975             }else{
23976                 s.remove(r);
23977             }
23978         });
23979     },
23980
23981     // private
23982     onRemove : function(v, index, r){
23983         this.selections.remove(r);
23984     },
23985
23986     // private
23987     onRowUpdated : function(v, index, r){
23988         if(this.isSelected(r)){
23989             v.onRowSelect(index);
23990         }
23991     },
23992
23993     /**
23994      * Select records.
23995      * @param {Array} records The records to select
23996      * @param {Boolean} keepExisting (optional) True to keep existing selections
23997      */
23998     selectRecords : function(records, keepExisting)
23999     {
24000         if(!keepExisting){
24001             this.clearSelections();
24002         }
24003             var ds = this.grid.store;
24004         for(var i = 0, len = records.length; i < len; i++){
24005             this.selectRow(ds.indexOf(records[i]), true);
24006         }
24007     },
24008
24009     /**
24010      * Gets the number of selected rows.
24011      * @return {Number}
24012      */
24013     getCount : function(){
24014         return this.selections.length;
24015     },
24016
24017     /**
24018      * Selects the first row in the grid.
24019      */
24020     selectFirstRow : function(){
24021         this.selectRow(0);
24022     },
24023
24024     /**
24025      * Select the last row.
24026      * @param {Boolean} keepExisting (optional) True to keep existing selections
24027      */
24028     selectLastRow : function(keepExisting){
24029         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24030         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24031     },
24032
24033     /**
24034      * Selects the row immediately following the last selected row.
24035      * @param {Boolean} keepExisting (optional) True to keep existing selections
24036      */
24037     selectNext : function(keepExisting)
24038     {
24039             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24040             this.selectRow(this.last+1, keepExisting);
24041             this.grid.getView().focusRow(this.last);
24042         }
24043     },
24044
24045     /**
24046      * Selects the row that precedes the last selected row.
24047      * @param {Boolean} keepExisting (optional) True to keep existing selections
24048      */
24049     selectPrevious : function(keepExisting){
24050         if(this.last){
24051             this.selectRow(this.last-1, keepExisting);
24052             this.grid.getView().focusRow(this.last);
24053         }
24054     },
24055
24056     /**
24057      * Returns the selected records
24058      * @return {Array} Array of selected records
24059      */
24060     getSelections : function(){
24061         return [].concat(this.selections.items);
24062     },
24063
24064     /**
24065      * Returns the first selected record.
24066      * @return {Record}
24067      */
24068     getSelected : function(){
24069         return this.selections.itemAt(0);
24070     },
24071
24072
24073     /**
24074      * Clears all selections.
24075      */
24076     clearSelections : function(fast)
24077     {
24078         if(this.locked) {
24079             return;
24080         }
24081         if(fast !== true){
24082                 var ds = this.grid.store;
24083             var s = this.selections;
24084             s.each(function(r){
24085                 this.deselectRow(ds.indexOfId(r.id));
24086             }, this);
24087             s.clear();
24088         }else{
24089             this.selections.clear();
24090         }
24091         this.last = false;
24092     },
24093
24094
24095     /**
24096      * Selects all rows.
24097      */
24098     selectAll : function(){
24099         if(this.locked) {
24100             return;
24101         }
24102         this.selections.clear();
24103         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24104             this.selectRow(i, true);
24105         }
24106     },
24107
24108     /**
24109      * Returns True if there is a selection.
24110      * @return {Boolean}
24111      */
24112     hasSelection : function(){
24113         return this.selections.length > 0;
24114     },
24115
24116     /**
24117      * Returns True if the specified row is selected.
24118      * @param {Number/Record} record The record or index of the record to check
24119      * @return {Boolean}
24120      */
24121     isSelected : function(index){
24122             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24123         return (r && this.selections.key(r.id) ? true : false);
24124     },
24125
24126     /**
24127      * Returns True if the specified record id is selected.
24128      * @param {String} id The id of record to check
24129      * @return {Boolean}
24130      */
24131     isIdSelected : function(id){
24132         return (this.selections.key(id) ? true : false);
24133     },
24134
24135
24136     // private
24137     handleMouseDBClick : function(e, t){
24138         
24139     },
24140     // private
24141     handleMouseDown : function(e, t)
24142     {
24143             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24144         if(this.isLocked() || rowIndex < 0 ){
24145             return;
24146         };
24147         if(e.shiftKey && this.last !== false){
24148             var last = this.last;
24149             this.selectRange(last, rowIndex, e.ctrlKey);
24150             this.last = last; // reset the last
24151             t.focus();
24152     
24153         }else{
24154             var isSelected = this.isSelected(rowIndex);
24155             //Roo.log("select row:" + rowIndex);
24156             if(isSelected){
24157                 this.deselectRow(rowIndex);
24158             } else {
24159                         this.selectRow(rowIndex, true);
24160             }
24161     
24162             /*
24163                 if(e.button !== 0 && isSelected){
24164                 alert('rowIndex 2: ' + rowIndex);
24165                     view.focusRow(rowIndex);
24166                 }else if(e.ctrlKey && isSelected){
24167                     this.deselectRow(rowIndex);
24168                 }else if(!isSelected){
24169                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24170                     view.focusRow(rowIndex);
24171                 }
24172             */
24173         }
24174         this.fireEvent("afterselectionchange", this);
24175     },
24176     // private
24177     handleDragableRowClick :  function(grid, rowIndex, e) 
24178     {
24179         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24180             this.selectRow(rowIndex, false);
24181             grid.view.focusRow(rowIndex);
24182              this.fireEvent("afterselectionchange", this);
24183         }
24184     },
24185     
24186     /**
24187      * Selects multiple rows.
24188      * @param {Array} rows Array of the indexes of the row to select
24189      * @param {Boolean} keepExisting (optional) True to keep existing selections
24190      */
24191     selectRows : function(rows, keepExisting){
24192         if(!keepExisting){
24193             this.clearSelections();
24194         }
24195         for(var i = 0, len = rows.length; i < len; i++){
24196             this.selectRow(rows[i], true);
24197         }
24198     },
24199
24200     /**
24201      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24202      * @param {Number} startRow The index of the first row in the range
24203      * @param {Number} endRow The index of the last row in the range
24204      * @param {Boolean} keepExisting (optional) True to retain existing selections
24205      */
24206     selectRange : function(startRow, endRow, keepExisting){
24207         if(this.locked) {
24208             return;
24209         }
24210         if(!keepExisting){
24211             this.clearSelections();
24212         }
24213         if(startRow <= endRow){
24214             for(var i = startRow; i <= endRow; i++){
24215                 this.selectRow(i, true);
24216             }
24217         }else{
24218             for(var i = startRow; i >= endRow; i--){
24219                 this.selectRow(i, true);
24220             }
24221         }
24222     },
24223
24224     /**
24225      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24226      * @param {Number} startRow The index of the first row in the range
24227      * @param {Number} endRow The index of the last row in the range
24228      */
24229     deselectRange : function(startRow, endRow, preventViewNotify){
24230         if(this.locked) {
24231             return;
24232         }
24233         for(var i = startRow; i <= endRow; i++){
24234             this.deselectRow(i, preventViewNotify);
24235         }
24236     },
24237
24238     /**
24239      * Selects a row.
24240      * @param {Number} row The index of the row to select
24241      * @param {Boolean} keepExisting (optional) True to keep existing selections
24242      */
24243     selectRow : function(index, keepExisting, preventViewNotify)
24244     {
24245             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24246             return;
24247         }
24248         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24249             if(!keepExisting || this.singleSelect){
24250                 this.clearSelections();
24251             }
24252             
24253             var r = this.grid.store.getAt(index);
24254             //console.log('selectRow - record id :' + r.id);
24255             
24256             this.selections.add(r);
24257             this.last = this.lastActive = index;
24258             if(!preventViewNotify){
24259                 var proxy = new Roo.Element(
24260                                 this.grid.getRowDom(index)
24261                 );
24262                 proxy.addClass('bg-info info');
24263             }
24264             this.fireEvent("rowselect", this, index, r);
24265             this.fireEvent("selectionchange", this);
24266         }
24267     },
24268
24269     /**
24270      * Deselects a row.
24271      * @param {Number} row The index of the row to deselect
24272      */
24273     deselectRow : function(index, preventViewNotify)
24274     {
24275         if(this.locked) {
24276             return;
24277         }
24278         if(this.last == index){
24279             this.last = false;
24280         }
24281         if(this.lastActive == index){
24282             this.lastActive = false;
24283         }
24284         
24285         var r = this.grid.store.getAt(index);
24286         if (!r) {
24287             return;
24288         }
24289         
24290         this.selections.remove(r);
24291         //.console.log('deselectRow - record id :' + r.id);
24292         if(!preventViewNotify){
24293         
24294             var proxy = new Roo.Element(
24295                 this.grid.getRowDom(index)
24296             );
24297             proxy.removeClass('bg-info info');
24298         }
24299         this.fireEvent("rowdeselect", this, index);
24300         this.fireEvent("selectionchange", this);
24301     },
24302
24303     // private
24304     restoreLast : function(){
24305         if(this._last){
24306             this.last = this._last;
24307         }
24308     },
24309
24310     // private
24311     acceptsNav : function(row, col, cm){
24312         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24313     },
24314
24315     // private
24316     onEditorKey : function(field, e){
24317         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24318         if(k == e.TAB){
24319             e.stopEvent();
24320             ed.completeEdit();
24321             if(e.shiftKey){
24322                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24323             }else{
24324                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24325             }
24326         }else if(k == e.ENTER && !e.ctrlKey){
24327             e.stopEvent();
24328             ed.completeEdit();
24329             if(e.shiftKey){
24330                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24331             }else{
24332                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24333             }
24334         }else if(k == e.ESC){
24335             ed.cancelEdit();
24336         }
24337         if(newCell){
24338             g.startEditing(newCell[0], newCell[1]);
24339         }
24340     }
24341 });
24342 /*
24343  * Based on:
24344  * Ext JS Library 1.1.1
24345  * Copyright(c) 2006-2007, Ext JS, LLC.
24346  *
24347  * Originally Released Under LGPL - original licence link has changed is not relivant.
24348  *
24349  * Fork - LGPL
24350  * <script type="text/javascript">
24351  */
24352  
24353 /**
24354  * @class Roo.bootstrap.PagingToolbar
24355  * @extends Roo.bootstrap.NavSimplebar
24356  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24357  * @constructor
24358  * Create a new PagingToolbar
24359  * @param {Object} config The config object
24360  * @param {Roo.data.Store} store
24361  */
24362 Roo.bootstrap.PagingToolbar = function(config)
24363 {
24364     // old args format still supported... - xtype is prefered..
24365         // created from xtype...
24366     
24367     this.ds = config.dataSource;
24368     
24369     if (config.store && !this.ds) {
24370         this.store= Roo.factory(config.store, Roo.data);
24371         this.ds = this.store;
24372         this.ds.xmodule = this.xmodule || false;
24373     }
24374     
24375     this.toolbarItems = [];
24376     if (config.items) {
24377         this.toolbarItems = config.items;
24378     }
24379     
24380     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24381     
24382     this.cursor = 0;
24383     
24384     if (this.ds) { 
24385         this.bind(this.ds);
24386     }
24387     
24388     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24389     
24390 };
24391
24392 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24393     /**
24394      * @cfg {Roo.data.Store} dataSource
24395      * The underlying data store providing the paged data
24396      */
24397     /**
24398      * @cfg {String/HTMLElement/Element} container
24399      * container The id or element that will contain the toolbar
24400      */
24401     /**
24402      * @cfg {Boolean} displayInfo
24403      * True to display the displayMsg (defaults to false)
24404      */
24405     /**
24406      * @cfg {Number} pageSize
24407      * The number of records to display per page (defaults to 20)
24408      */
24409     pageSize: 20,
24410     /**
24411      * @cfg {String} displayMsg
24412      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24413      */
24414     displayMsg : 'Displaying {0} - {1} of {2}',
24415     /**
24416      * @cfg {String} emptyMsg
24417      * The message to display when no records are found (defaults to "No data to display")
24418      */
24419     emptyMsg : 'No data to display',
24420     /**
24421      * Customizable piece of the default paging text (defaults to "Page")
24422      * @type String
24423      */
24424     beforePageText : "Page",
24425     /**
24426      * Customizable piece of the default paging text (defaults to "of %0")
24427      * @type String
24428      */
24429     afterPageText : "of {0}",
24430     /**
24431      * Customizable piece of the default paging text (defaults to "First Page")
24432      * @type String
24433      */
24434     firstText : "First Page",
24435     /**
24436      * Customizable piece of the default paging text (defaults to "Previous Page")
24437      * @type String
24438      */
24439     prevText : "Previous Page",
24440     /**
24441      * Customizable piece of the default paging text (defaults to "Next Page")
24442      * @type String
24443      */
24444     nextText : "Next Page",
24445     /**
24446      * Customizable piece of the default paging text (defaults to "Last Page")
24447      * @type String
24448      */
24449     lastText : "Last Page",
24450     /**
24451      * Customizable piece of the default paging text (defaults to "Refresh")
24452      * @type String
24453      */
24454     refreshText : "Refresh",
24455
24456     buttons : false,
24457     // private
24458     onRender : function(ct, position) 
24459     {
24460         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24461         this.navgroup.parentId = this.id;
24462         this.navgroup.onRender(this.el, null);
24463         // add the buttons to the navgroup
24464         
24465         if(this.displayInfo){
24466             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24467             this.displayEl = this.el.select('.x-paging-info', true).first();
24468 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24469 //            this.displayEl = navel.el.select('span',true).first();
24470         }
24471         
24472         var _this = this;
24473         
24474         if(this.buttons){
24475             Roo.each(_this.buttons, function(e){ // this might need to use render????
24476                Roo.factory(e).onRender(_this.el, null);
24477             });
24478         }
24479             
24480         Roo.each(_this.toolbarItems, function(e) {
24481             _this.navgroup.addItem(e);
24482         });
24483         
24484         
24485         this.first = this.navgroup.addItem({
24486             tooltip: this.firstText,
24487             cls: "prev",
24488             icon : 'fa fa-backward',
24489             disabled: true,
24490             preventDefault: true,
24491             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24492         });
24493         
24494         this.prev =  this.navgroup.addItem({
24495             tooltip: this.prevText,
24496             cls: "prev",
24497             icon : 'fa fa-step-backward',
24498             disabled: true,
24499             preventDefault: true,
24500             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24501         });
24502     //this.addSeparator();
24503         
24504         
24505         var field = this.navgroup.addItem( {
24506             tagtype : 'span',
24507             cls : 'x-paging-position',
24508             
24509             html : this.beforePageText  +
24510                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24511                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24512          } ); //?? escaped?
24513         
24514         this.field = field.el.select('input', true).first();
24515         this.field.on("keydown", this.onPagingKeydown, this);
24516         this.field.on("focus", function(){this.dom.select();});
24517     
24518     
24519         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24520         //this.field.setHeight(18);
24521         //this.addSeparator();
24522         this.next = this.navgroup.addItem({
24523             tooltip: this.nextText,
24524             cls: "next",
24525             html : ' <i class="fa fa-step-forward">',
24526             disabled: true,
24527             preventDefault: true,
24528             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24529         });
24530         this.last = this.navgroup.addItem({
24531             tooltip: this.lastText,
24532             icon : 'fa fa-forward',
24533             cls: "next",
24534             disabled: true,
24535             preventDefault: true,
24536             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24537         });
24538     //this.addSeparator();
24539         this.loading = this.navgroup.addItem({
24540             tooltip: this.refreshText,
24541             icon: 'fa fa-refresh',
24542             preventDefault: true,
24543             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24544         });
24545         
24546     },
24547
24548     // private
24549     updateInfo : function(){
24550         if(this.displayEl){
24551             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24552             var msg = count == 0 ?
24553                 this.emptyMsg :
24554                 String.format(
24555                     this.displayMsg,
24556                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24557                 );
24558             this.displayEl.update(msg);
24559         }
24560     },
24561
24562     // private
24563     onLoad : function(ds, r, o)
24564     {
24565         this.cursor = o.params.start ? o.params.start : 0;
24566         
24567         var d = this.getPageData(),
24568             ap = d.activePage,
24569             ps = d.pages;
24570         
24571         
24572         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24573         this.field.dom.value = ap;
24574         this.first.setDisabled(ap == 1);
24575         this.prev.setDisabled(ap == 1);
24576         this.next.setDisabled(ap == ps);
24577         this.last.setDisabled(ap == ps);
24578         this.loading.enable();
24579         this.updateInfo();
24580     },
24581
24582     // private
24583     getPageData : function(){
24584         var total = this.ds.getTotalCount();
24585         return {
24586             total : total,
24587             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24588             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24589         };
24590     },
24591
24592     // private
24593     onLoadError : function(){
24594         this.loading.enable();
24595     },
24596
24597     // private
24598     onPagingKeydown : function(e){
24599         var k = e.getKey();
24600         var d = this.getPageData();
24601         if(k == e.RETURN){
24602             var v = this.field.dom.value, pageNum;
24603             if(!v || isNaN(pageNum = parseInt(v, 10))){
24604                 this.field.dom.value = d.activePage;
24605                 return;
24606             }
24607             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24608             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24609             e.stopEvent();
24610         }
24611         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))
24612         {
24613           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24614           this.field.dom.value = pageNum;
24615           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24616           e.stopEvent();
24617         }
24618         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24619         {
24620           var v = this.field.dom.value, pageNum; 
24621           var increment = (e.shiftKey) ? 10 : 1;
24622           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24623                 increment *= -1;
24624           }
24625           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24626             this.field.dom.value = d.activePage;
24627             return;
24628           }
24629           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24630           {
24631             this.field.dom.value = parseInt(v, 10) + increment;
24632             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24633             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24634           }
24635           e.stopEvent();
24636         }
24637     },
24638
24639     // private
24640     beforeLoad : function(){
24641         if(this.loading){
24642             this.loading.disable();
24643         }
24644     },
24645
24646     // private
24647     onClick : function(which){
24648         
24649         var ds = this.ds;
24650         if (!ds) {
24651             return;
24652         }
24653         
24654         switch(which){
24655             case "first":
24656                 ds.load({params:{start: 0, limit: this.pageSize}});
24657             break;
24658             case "prev":
24659                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24660             break;
24661             case "next":
24662                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24663             break;
24664             case "last":
24665                 var total = ds.getTotalCount();
24666                 var extra = total % this.pageSize;
24667                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24668                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24669             break;
24670             case "refresh":
24671                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24672             break;
24673         }
24674     },
24675
24676     /**
24677      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24678      * @param {Roo.data.Store} store The data store to unbind
24679      */
24680     unbind : function(ds){
24681         ds.un("beforeload", this.beforeLoad, this);
24682         ds.un("load", this.onLoad, this);
24683         ds.un("loadexception", this.onLoadError, this);
24684         ds.un("remove", this.updateInfo, this);
24685         ds.un("add", this.updateInfo, this);
24686         this.ds = undefined;
24687     },
24688
24689     /**
24690      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24691      * @param {Roo.data.Store} store The data store to bind
24692      */
24693     bind : function(ds){
24694         ds.on("beforeload", this.beforeLoad, this);
24695         ds.on("load", this.onLoad, this);
24696         ds.on("loadexception", this.onLoadError, this);
24697         ds.on("remove", this.updateInfo, this);
24698         ds.on("add", this.updateInfo, this);
24699         this.ds = ds;
24700     }
24701 });/*
24702  * - LGPL
24703  *
24704  * element
24705  * 
24706  */
24707
24708 /**
24709  * @class Roo.bootstrap.MessageBar
24710  * @extends Roo.bootstrap.Component
24711  * Bootstrap MessageBar class
24712  * @cfg {String} html contents of the MessageBar
24713  * @cfg {String} weight (info | success | warning | danger) default info
24714  * @cfg {String} beforeClass insert the bar before the given class
24715  * @cfg {Boolean} closable (true | false) default false
24716  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24717  * 
24718  * @constructor
24719  * Create a new Element
24720  * @param {Object} config The config object
24721  */
24722
24723 Roo.bootstrap.MessageBar = function(config){
24724     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24725 };
24726
24727 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24728     
24729     html: '',
24730     weight: 'info',
24731     closable: false,
24732     fixed: false,
24733     beforeClass: 'bootstrap-sticky-wrap',
24734     
24735     getAutoCreate : function(){
24736         
24737         var cfg = {
24738             tag: 'div',
24739             cls: 'alert alert-dismissable alert-' + this.weight,
24740             cn: [
24741                 {
24742                     tag: 'span',
24743                     cls: 'message',
24744                     html: this.html || ''
24745                 }
24746             ]
24747         };
24748         
24749         if(this.fixed){
24750             cfg.cls += ' alert-messages-fixed';
24751         }
24752         
24753         if(this.closable){
24754             cfg.cn.push({
24755                 tag: 'button',
24756                 cls: 'close',
24757                 html: 'x'
24758             });
24759         }
24760         
24761         return cfg;
24762     },
24763     
24764     onRender : function(ct, position)
24765     {
24766         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24767         
24768         if(!this.el){
24769             var cfg = Roo.apply({},  this.getAutoCreate());
24770             cfg.id = Roo.id();
24771             
24772             if (this.cls) {
24773                 cfg.cls += ' ' + this.cls;
24774             }
24775             if (this.style) {
24776                 cfg.style = this.style;
24777             }
24778             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24779             
24780             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24781         }
24782         
24783         this.el.select('>button.close').on('click', this.hide, this);
24784         
24785     },
24786     
24787     show : function()
24788     {
24789         if (!this.rendered) {
24790             this.render();
24791         }
24792         
24793         this.el.show();
24794         
24795         this.fireEvent('show', this);
24796         
24797     },
24798     
24799     hide : function()
24800     {
24801         if (!this.rendered) {
24802             this.render();
24803         }
24804         
24805         this.el.hide();
24806         
24807         this.fireEvent('hide', this);
24808     },
24809     
24810     update : function()
24811     {
24812 //        var e = this.el.dom.firstChild;
24813 //        
24814 //        if(this.closable){
24815 //            e = e.nextSibling;
24816 //        }
24817 //        
24818 //        e.data = this.html || '';
24819
24820         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24821     }
24822    
24823 });
24824
24825  
24826
24827      /*
24828  * - LGPL
24829  *
24830  * Graph
24831  * 
24832  */
24833
24834
24835 /**
24836  * @class Roo.bootstrap.Graph
24837  * @extends Roo.bootstrap.Component
24838  * Bootstrap Graph class
24839 > Prameters
24840  -sm {number} sm 4
24841  -md {number} md 5
24842  @cfg {String} graphtype  bar | vbar | pie
24843  @cfg {number} g_x coodinator | centre x (pie)
24844  @cfg {number} g_y coodinator | centre y (pie)
24845  @cfg {number} g_r radius (pie)
24846  @cfg {number} g_height height of the chart (respected by all elements in the set)
24847  @cfg {number} g_width width of the chart (respected by all elements in the set)
24848  @cfg {Object} title The title of the chart
24849     
24850  -{Array}  values
24851  -opts (object) options for the chart 
24852      o {
24853      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24854      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24855      o vgutter (number)
24856      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.
24857      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24858      o to
24859      o stretch (boolean)
24860      o }
24861  -opts (object) options for the pie
24862      o{
24863      o cut
24864      o startAngle (number)
24865      o endAngle (number)
24866      } 
24867  *
24868  * @constructor
24869  * Create a new Input
24870  * @param {Object} config The config object
24871  */
24872
24873 Roo.bootstrap.Graph = function(config){
24874     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24875     
24876     this.addEvents({
24877         // img events
24878         /**
24879          * @event click
24880          * The img click event for the img.
24881          * @param {Roo.EventObject} e
24882          */
24883         "click" : true
24884     });
24885 };
24886
24887 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24888     
24889     sm: 4,
24890     md: 5,
24891     graphtype: 'bar',
24892     g_height: 250,
24893     g_width: 400,
24894     g_x: 50,
24895     g_y: 50,
24896     g_r: 30,
24897     opts:{
24898         //g_colors: this.colors,
24899         g_type: 'soft',
24900         g_gutter: '20%'
24901
24902     },
24903     title : false,
24904
24905     getAutoCreate : function(){
24906         
24907         var cfg = {
24908             tag: 'div',
24909             html : null
24910         };
24911         
24912         
24913         return  cfg;
24914     },
24915
24916     onRender : function(ct,position){
24917         
24918         
24919         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24920         
24921         if (typeof(Raphael) == 'undefined') {
24922             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24923             return;
24924         }
24925         
24926         this.raphael = Raphael(this.el.dom);
24927         
24928                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24929                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24930                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24931                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24932                 /*
24933                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24934                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24935                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24936                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24937                 
24938                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24939                 r.barchart(330, 10, 300, 220, data1);
24940                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24941                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24942                 */
24943                 
24944                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24945                 // r.barchart(30, 30, 560, 250,  xdata, {
24946                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24947                 //     axis : "0 0 1 1",
24948                 //     axisxlabels :  xdata
24949                 //     //yvalues : cols,
24950                    
24951                 // });
24952 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24953 //        
24954 //        this.load(null,xdata,{
24955 //                axis : "0 0 1 1",
24956 //                axisxlabels :  xdata
24957 //                });
24958
24959     },
24960
24961     load : function(graphtype,xdata,opts)
24962     {
24963         this.raphael.clear();
24964         if(!graphtype) {
24965             graphtype = this.graphtype;
24966         }
24967         if(!opts){
24968             opts = this.opts;
24969         }
24970         var r = this.raphael,
24971             fin = function () {
24972                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24973             },
24974             fout = function () {
24975                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24976             },
24977             pfin = function() {
24978                 this.sector.stop();
24979                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24980
24981                 if (this.label) {
24982                     this.label[0].stop();
24983                     this.label[0].attr({ r: 7.5 });
24984                     this.label[1].attr({ "font-weight": 800 });
24985                 }
24986             },
24987             pfout = function() {
24988                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24989
24990                 if (this.label) {
24991                     this.label[0].animate({ r: 5 }, 500, "bounce");
24992                     this.label[1].attr({ "font-weight": 400 });
24993                 }
24994             };
24995
24996         switch(graphtype){
24997             case 'bar':
24998                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24999                 break;
25000             case 'hbar':
25001                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25002                 break;
25003             case 'pie':
25004 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25005 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25006 //            
25007                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25008                 
25009                 break;
25010
25011         }
25012         
25013         if(this.title){
25014             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25015         }
25016         
25017     },
25018     
25019     setTitle: function(o)
25020     {
25021         this.title = o;
25022     },
25023     
25024     initEvents: function() {
25025         
25026         if(!this.href){
25027             this.el.on('click', this.onClick, this);
25028         }
25029     },
25030     
25031     onClick : function(e)
25032     {
25033         Roo.log('img onclick');
25034         this.fireEvent('click', this, e);
25035     }
25036    
25037 });
25038
25039  
25040 /*
25041  * - LGPL
25042  *
25043  * numberBox
25044  * 
25045  */
25046 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25047
25048 /**
25049  * @class Roo.bootstrap.dash.NumberBox
25050  * @extends Roo.bootstrap.Component
25051  * Bootstrap NumberBox class
25052  * @cfg {String} headline Box headline
25053  * @cfg {String} content Box content
25054  * @cfg {String} icon Box icon
25055  * @cfg {String} footer Footer text
25056  * @cfg {String} fhref Footer href
25057  * 
25058  * @constructor
25059  * Create a new NumberBox
25060  * @param {Object} config The config object
25061  */
25062
25063
25064 Roo.bootstrap.dash.NumberBox = function(config){
25065     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25066     
25067 };
25068
25069 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25070     
25071     headline : '',
25072     content : '',
25073     icon : '',
25074     footer : '',
25075     fhref : '',
25076     ficon : '',
25077     
25078     getAutoCreate : function(){
25079         
25080         var cfg = {
25081             tag : 'div',
25082             cls : 'small-box ',
25083             cn : [
25084                 {
25085                     tag : 'div',
25086                     cls : 'inner',
25087                     cn :[
25088                         {
25089                             tag : 'h3',
25090                             cls : 'roo-headline',
25091                             html : this.headline
25092                         },
25093                         {
25094                             tag : 'p',
25095                             cls : 'roo-content',
25096                             html : this.content
25097                         }
25098                     ]
25099                 }
25100             ]
25101         };
25102         
25103         if(this.icon){
25104             cfg.cn.push({
25105                 tag : 'div',
25106                 cls : 'icon',
25107                 cn :[
25108                     {
25109                         tag : 'i',
25110                         cls : 'ion ' + this.icon
25111                     }
25112                 ]
25113             });
25114         }
25115         
25116         if(this.footer){
25117             var footer = {
25118                 tag : 'a',
25119                 cls : 'small-box-footer',
25120                 href : this.fhref || '#',
25121                 html : this.footer
25122             };
25123             
25124             cfg.cn.push(footer);
25125             
25126         }
25127         
25128         return  cfg;
25129     },
25130
25131     onRender : function(ct,position){
25132         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25133
25134
25135        
25136                 
25137     },
25138
25139     setHeadline: function (value)
25140     {
25141         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25142     },
25143     
25144     setFooter: function (value, href)
25145     {
25146         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25147         
25148         if(href){
25149             this.el.select('a.small-box-footer',true).first().attr('href', href);
25150         }
25151         
25152     },
25153
25154     setContent: function (value)
25155     {
25156         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25157     },
25158
25159     initEvents: function() 
25160     {   
25161         
25162     }
25163     
25164 });
25165
25166  
25167 /*
25168  * - LGPL
25169  *
25170  * TabBox
25171  * 
25172  */
25173 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25174
25175 /**
25176  * @class Roo.bootstrap.dash.TabBox
25177  * @extends Roo.bootstrap.Component
25178  * Bootstrap TabBox class
25179  * @cfg {String} title Title of the TabBox
25180  * @cfg {String} icon Icon of the TabBox
25181  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25182  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25183  * 
25184  * @constructor
25185  * Create a new TabBox
25186  * @param {Object} config The config object
25187  */
25188
25189
25190 Roo.bootstrap.dash.TabBox = function(config){
25191     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25192     this.addEvents({
25193         // raw events
25194         /**
25195          * @event addpane
25196          * When a pane is added
25197          * @param {Roo.bootstrap.dash.TabPane} pane
25198          */
25199         "addpane" : true,
25200         /**
25201          * @event activatepane
25202          * When a pane is activated
25203          * @param {Roo.bootstrap.dash.TabPane} pane
25204          */
25205         "activatepane" : true
25206         
25207          
25208     });
25209     
25210     this.panes = [];
25211 };
25212
25213 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25214
25215     title : '',
25216     icon : false,
25217     showtabs : true,
25218     tabScrollable : false,
25219     
25220     getChildContainer : function()
25221     {
25222         return this.el.select('.tab-content', true).first();
25223     },
25224     
25225     getAutoCreate : function(){
25226         
25227         var header = {
25228             tag: 'li',
25229             cls: 'pull-left header',
25230             html: this.title,
25231             cn : []
25232         };
25233         
25234         if(this.icon){
25235             header.cn.push({
25236                 tag: 'i',
25237                 cls: 'fa ' + this.icon
25238             });
25239         }
25240         
25241         var h = {
25242             tag: 'ul',
25243             cls: 'nav nav-tabs pull-right',
25244             cn: [
25245                 header
25246             ]
25247         };
25248         
25249         if(this.tabScrollable){
25250             h = {
25251                 tag: 'div',
25252                 cls: 'tab-header',
25253                 cn: [
25254                     {
25255                         tag: 'ul',
25256                         cls: 'nav nav-tabs pull-right',
25257                         cn: [
25258                             header
25259                         ]
25260                     }
25261                 ]
25262             };
25263         }
25264         
25265         var cfg = {
25266             tag: 'div',
25267             cls: 'nav-tabs-custom',
25268             cn: [
25269                 h,
25270                 {
25271                     tag: 'div',
25272                     cls: 'tab-content no-padding',
25273                     cn: []
25274                 }
25275             ]
25276         };
25277
25278         return  cfg;
25279     },
25280     initEvents : function()
25281     {
25282         //Roo.log('add add pane handler');
25283         this.on('addpane', this.onAddPane, this);
25284     },
25285      /**
25286      * Updates the box title
25287      * @param {String} html to set the title to.
25288      */
25289     setTitle : function(value)
25290     {
25291         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25292     },
25293     onAddPane : function(pane)
25294     {
25295         this.panes.push(pane);
25296         //Roo.log('addpane');
25297         //Roo.log(pane);
25298         // tabs are rendere left to right..
25299         if(!this.showtabs){
25300             return;
25301         }
25302         
25303         var ctr = this.el.select('.nav-tabs', true).first();
25304          
25305          
25306         var existing = ctr.select('.nav-tab',true);
25307         var qty = existing.getCount();;
25308         
25309         
25310         var tab = ctr.createChild({
25311             tag : 'li',
25312             cls : 'nav-tab' + (qty ? '' : ' active'),
25313             cn : [
25314                 {
25315                     tag : 'a',
25316                     href:'#',
25317                     html : pane.title
25318                 }
25319             ]
25320         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25321         pane.tab = tab;
25322         
25323         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25324         if (!qty) {
25325             pane.el.addClass('active');
25326         }
25327         
25328                 
25329     },
25330     onTabClick : function(ev,un,ob,pane)
25331     {
25332         //Roo.log('tab - prev default');
25333         ev.preventDefault();
25334         
25335         
25336         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25337         pane.tab.addClass('active');
25338         //Roo.log(pane.title);
25339         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25340         // technically we should have a deactivate event.. but maybe add later.
25341         // and it should not de-activate the selected tab...
25342         this.fireEvent('activatepane', pane);
25343         pane.el.addClass('active');
25344         pane.fireEvent('activate');
25345         
25346         
25347     },
25348     
25349     getActivePane : function()
25350     {
25351         var r = false;
25352         Roo.each(this.panes, function(p) {
25353             if(p.el.hasClass('active')){
25354                 r = p;
25355                 return false;
25356             }
25357             
25358             return;
25359         });
25360         
25361         return r;
25362     }
25363     
25364     
25365 });
25366
25367  
25368 /*
25369  * - LGPL
25370  *
25371  * Tab pane
25372  * 
25373  */
25374 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25375 /**
25376  * @class Roo.bootstrap.TabPane
25377  * @extends Roo.bootstrap.Component
25378  * Bootstrap TabPane class
25379  * @cfg {Boolean} active (false | true) Default false
25380  * @cfg {String} title title of panel
25381
25382  * 
25383  * @constructor
25384  * Create a new TabPane
25385  * @param {Object} config The config object
25386  */
25387
25388 Roo.bootstrap.dash.TabPane = function(config){
25389     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25390     
25391     this.addEvents({
25392         // raw events
25393         /**
25394          * @event activate
25395          * When a pane is activated
25396          * @param {Roo.bootstrap.dash.TabPane} pane
25397          */
25398         "activate" : true
25399          
25400     });
25401 };
25402
25403 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25404     
25405     active : false,
25406     title : '',
25407     
25408     // the tabBox that this is attached to.
25409     tab : false,
25410      
25411     getAutoCreate : function() 
25412     {
25413         var cfg = {
25414             tag: 'div',
25415             cls: 'tab-pane'
25416         };
25417         
25418         if(this.active){
25419             cfg.cls += ' active';
25420         }
25421         
25422         return cfg;
25423     },
25424     initEvents  : function()
25425     {
25426         //Roo.log('trigger add pane handler');
25427         this.parent().fireEvent('addpane', this)
25428     },
25429     
25430      /**
25431      * Updates the tab title 
25432      * @param {String} html to set the title to.
25433      */
25434     setTitle: function(str)
25435     {
25436         if (!this.tab) {
25437             return;
25438         }
25439         this.title = str;
25440         this.tab.select('a', true).first().dom.innerHTML = str;
25441         
25442     }
25443     
25444     
25445     
25446 });
25447
25448  
25449
25450
25451  /*
25452  * - LGPL
25453  *
25454  * menu
25455  * 
25456  */
25457 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25458
25459 /**
25460  * @class Roo.bootstrap.menu.Menu
25461  * @extends Roo.bootstrap.Component
25462  * Bootstrap Menu class - container for Menu
25463  * @cfg {String} html Text of the menu
25464  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25465  * @cfg {String} icon Font awesome icon
25466  * @cfg {String} pos Menu align to (top | bottom) default bottom
25467  * 
25468  * 
25469  * @constructor
25470  * Create a new Menu
25471  * @param {Object} config The config object
25472  */
25473
25474
25475 Roo.bootstrap.menu.Menu = function(config){
25476     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25477     
25478     this.addEvents({
25479         /**
25480          * @event beforeshow
25481          * Fires before this menu is displayed
25482          * @param {Roo.bootstrap.menu.Menu} this
25483          */
25484         beforeshow : true,
25485         /**
25486          * @event beforehide
25487          * Fires before this menu is hidden
25488          * @param {Roo.bootstrap.menu.Menu} this
25489          */
25490         beforehide : true,
25491         /**
25492          * @event show
25493          * Fires after this menu is displayed
25494          * @param {Roo.bootstrap.menu.Menu} this
25495          */
25496         show : true,
25497         /**
25498          * @event hide
25499          * Fires after this menu is hidden
25500          * @param {Roo.bootstrap.menu.Menu} this
25501          */
25502         hide : true,
25503         /**
25504          * @event click
25505          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25506          * @param {Roo.bootstrap.menu.Menu} this
25507          * @param {Roo.EventObject} e
25508          */
25509         click : true
25510     });
25511     
25512 };
25513
25514 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25515     
25516     submenu : false,
25517     html : '',
25518     weight : 'default',
25519     icon : false,
25520     pos : 'bottom',
25521     
25522     
25523     getChildContainer : function() {
25524         if(this.isSubMenu){
25525             return this.el;
25526         }
25527         
25528         return this.el.select('ul.dropdown-menu', true).first();  
25529     },
25530     
25531     getAutoCreate : function()
25532     {
25533         var text = [
25534             {
25535                 tag : 'span',
25536                 cls : 'roo-menu-text',
25537                 html : this.html
25538             }
25539         ];
25540         
25541         if(this.icon){
25542             text.unshift({
25543                 tag : 'i',
25544                 cls : 'fa ' + this.icon
25545             })
25546         }
25547         
25548         
25549         var cfg = {
25550             tag : 'div',
25551             cls : 'btn-group',
25552             cn : [
25553                 {
25554                     tag : 'button',
25555                     cls : 'dropdown-button btn btn-' + this.weight,
25556                     cn : text
25557                 },
25558                 {
25559                     tag : 'button',
25560                     cls : 'dropdown-toggle btn btn-' + this.weight,
25561                     cn : [
25562                         {
25563                             tag : 'span',
25564                             cls : 'caret'
25565                         }
25566                     ]
25567                 },
25568                 {
25569                     tag : 'ul',
25570                     cls : 'dropdown-menu'
25571                 }
25572             ]
25573             
25574         };
25575         
25576         if(this.pos == 'top'){
25577             cfg.cls += ' dropup';
25578         }
25579         
25580         if(this.isSubMenu){
25581             cfg = {
25582                 tag : 'ul',
25583                 cls : 'dropdown-menu'
25584             }
25585         }
25586         
25587         return cfg;
25588     },
25589     
25590     onRender : function(ct, position)
25591     {
25592         this.isSubMenu = ct.hasClass('dropdown-submenu');
25593         
25594         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25595     },
25596     
25597     initEvents : function() 
25598     {
25599         if(this.isSubMenu){
25600             return;
25601         }
25602         
25603         this.hidden = true;
25604         
25605         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25606         this.triggerEl.on('click', this.onTriggerPress, this);
25607         
25608         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25609         this.buttonEl.on('click', this.onClick, this);
25610         
25611     },
25612     
25613     list : function()
25614     {
25615         if(this.isSubMenu){
25616             return this.el;
25617         }
25618         
25619         return this.el.select('ul.dropdown-menu', true).first();
25620     },
25621     
25622     onClick : function(e)
25623     {
25624         this.fireEvent("click", this, e);
25625     },
25626     
25627     onTriggerPress  : function(e)
25628     {   
25629         if (this.isVisible()) {
25630             this.hide();
25631         } else {
25632             this.show();
25633         }
25634     },
25635     
25636     isVisible : function(){
25637         return !this.hidden;
25638     },
25639     
25640     show : function()
25641     {
25642         this.fireEvent("beforeshow", this);
25643         
25644         this.hidden = false;
25645         this.el.addClass('open');
25646         
25647         Roo.get(document).on("mouseup", this.onMouseUp, this);
25648         
25649         this.fireEvent("show", this);
25650         
25651         
25652     },
25653     
25654     hide : function()
25655     {
25656         this.fireEvent("beforehide", this);
25657         
25658         this.hidden = true;
25659         this.el.removeClass('open');
25660         
25661         Roo.get(document).un("mouseup", this.onMouseUp);
25662         
25663         this.fireEvent("hide", this);
25664     },
25665     
25666     onMouseUp : function()
25667     {
25668         this.hide();
25669     }
25670     
25671 });
25672
25673  
25674  /*
25675  * - LGPL
25676  *
25677  * menu item
25678  * 
25679  */
25680 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25681
25682 /**
25683  * @class Roo.bootstrap.menu.Item
25684  * @extends Roo.bootstrap.Component
25685  * Bootstrap MenuItem class
25686  * @cfg {Boolean} submenu (true | false) default false
25687  * @cfg {String} html text of the item
25688  * @cfg {String} href the link
25689  * @cfg {Boolean} disable (true | false) default false
25690  * @cfg {Boolean} preventDefault (true | false) default true
25691  * @cfg {String} icon Font awesome icon
25692  * @cfg {String} pos Submenu align to (left | right) default right 
25693  * 
25694  * 
25695  * @constructor
25696  * Create a new Item
25697  * @param {Object} config The config object
25698  */
25699
25700
25701 Roo.bootstrap.menu.Item = function(config){
25702     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25703     this.addEvents({
25704         /**
25705          * @event mouseover
25706          * Fires when the mouse is hovering over this menu
25707          * @param {Roo.bootstrap.menu.Item} this
25708          * @param {Roo.EventObject} e
25709          */
25710         mouseover : true,
25711         /**
25712          * @event mouseout
25713          * Fires when the mouse exits this menu
25714          * @param {Roo.bootstrap.menu.Item} this
25715          * @param {Roo.EventObject} e
25716          */
25717         mouseout : true,
25718         // raw events
25719         /**
25720          * @event click
25721          * The raw click event for the entire grid.
25722          * @param {Roo.EventObject} e
25723          */
25724         click : true
25725     });
25726 };
25727
25728 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25729     
25730     submenu : false,
25731     href : '',
25732     html : '',
25733     preventDefault: true,
25734     disable : false,
25735     icon : false,
25736     pos : 'right',
25737     
25738     getAutoCreate : function()
25739     {
25740         var text = [
25741             {
25742                 tag : 'span',
25743                 cls : 'roo-menu-item-text',
25744                 html : this.html
25745             }
25746         ];
25747         
25748         if(this.icon){
25749             text.unshift({
25750                 tag : 'i',
25751                 cls : 'fa ' + this.icon
25752             })
25753         }
25754         
25755         var cfg = {
25756             tag : 'li',
25757             cn : [
25758                 {
25759                     tag : 'a',
25760                     href : this.href || '#',
25761                     cn : text
25762                 }
25763             ]
25764         };
25765         
25766         if(this.disable){
25767             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25768         }
25769         
25770         if(this.submenu){
25771             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25772             
25773             if(this.pos == 'left'){
25774                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25775             }
25776         }
25777         
25778         return cfg;
25779     },
25780     
25781     initEvents : function() 
25782     {
25783         this.el.on('mouseover', this.onMouseOver, this);
25784         this.el.on('mouseout', this.onMouseOut, this);
25785         
25786         this.el.select('a', true).first().on('click', this.onClick, this);
25787         
25788     },
25789     
25790     onClick : function(e)
25791     {
25792         if(this.preventDefault){
25793             e.preventDefault();
25794         }
25795         
25796         this.fireEvent("click", this, e);
25797     },
25798     
25799     onMouseOver : function(e)
25800     {
25801         if(this.submenu && this.pos == 'left'){
25802             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25803         }
25804         
25805         this.fireEvent("mouseover", this, e);
25806     },
25807     
25808     onMouseOut : function(e)
25809     {
25810         this.fireEvent("mouseout", this, e);
25811     }
25812 });
25813
25814  
25815
25816  /*
25817  * - LGPL
25818  *
25819  * menu separator
25820  * 
25821  */
25822 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25823
25824 /**
25825  * @class Roo.bootstrap.menu.Separator
25826  * @extends Roo.bootstrap.Component
25827  * Bootstrap Separator class
25828  * 
25829  * @constructor
25830  * Create a new Separator
25831  * @param {Object} config The config object
25832  */
25833
25834
25835 Roo.bootstrap.menu.Separator = function(config){
25836     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25837 };
25838
25839 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25840     
25841     getAutoCreate : function(){
25842         var cfg = {
25843             tag : 'li',
25844             cls: 'divider'
25845         };
25846         
25847         return cfg;
25848     }
25849    
25850 });
25851
25852  
25853
25854  /*
25855  * - LGPL
25856  *
25857  * Tooltip
25858  * 
25859  */
25860
25861 /**
25862  * @class Roo.bootstrap.Tooltip
25863  * Bootstrap Tooltip class
25864  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25865  * to determine which dom element triggers the tooltip.
25866  * 
25867  * It needs to add support for additional attributes like tooltip-position
25868  * 
25869  * @constructor
25870  * Create a new Toolti
25871  * @param {Object} config The config object
25872  */
25873
25874 Roo.bootstrap.Tooltip = function(config){
25875     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25876     
25877     this.alignment = Roo.bootstrap.Tooltip.alignment;
25878     
25879     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25880         this.alignment = config.alignment;
25881     }
25882     
25883 };
25884
25885 Roo.apply(Roo.bootstrap.Tooltip, {
25886     /**
25887      * @function init initialize tooltip monitoring.
25888      * @static
25889      */
25890     currentEl : false,
25891     currentTip : false,
25892     currentRegion : false,
25893     
25894     //  init : delay?
25895     
25896     init : function()
25897     {
25898         Roo.get(document).on('mouseover', this.enter ,this);
25899         Roo.get(document).on('mouseout', this.leave, this);
25900          
25901         
25902         this.currentTip = new Roo.bootstrap.Tooltip();
25903     },
25904     
25905     enter : function(ev)
25906     {
25907         var dom = ev.getTarget();
25908         
25909         //Roo.log(['enter',dom]);
25910         var el = Roo.fly(dom);
25911         if (this.currentEl) {
25912             //Roo.log(dom);
25913             //Roo.log(this.currentEl);
25914             //Roo.log(this.currentEl.contains(dom));
25915             if (this.currentEl == el) {
25916                 return;
25917             }
25918             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25919                 return;
25920             }
25921
25922         }
25923         
25924         if (this.currentTip.el) {
25925             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25926         }    
25927         //Roo.log(ev);
25928         
25929         if(!el || el.dom == document){
25930             return;
25931         }
25932         
25933         var bindEl = el;
25934         
25935         // you can not look for children, as if el is the body.. then everythign is the child..
25936         if (!el.attr('tooltip')) { //
25937             if (!el.select("[tooltip]").elements.length) {
25938                 return;
25939             }
25940             // is the mouse over this child...?
25941             bindEl = el.select("[tooltip]").first();
25942             var xy = ev.getXY();
25943             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25944                 //Roo.log("not in region.");
25945                 return;
25946             }
25947             //Roo.log("child element over..");
25948             
25949         }
25950         this.currentEl = bindEl;
25951         this.currentTip.bind(bindEl);
25952         this.currentRegion = Roo.lib.Region.getRegion(dom);
25953         this.currentTip.enter();
25954         
25955     },
25956     leave : function(ev)
25957     {
25958         var dom = ev.getTarget();
25959         //Roo.log(['leave',dom]);
25960         if (!this.currentEl) {
25961             return;
25962         }
25963         
25964         
25965         if (dom != this.currentEl.dom) {
25966             return;
25967         }
25968         var xy = ev.getXY();
25969         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25970             return;
25971         }
25972         // only activate leave if mouse cursor is outside... bounding box..
25973         
25974         
25975         
25976         
25977         if (this.currentTip) {
25978             this.currentTip.leave();
25979         }
25980         //Roo.log('clear currentEl');
25981         this.currentEl = false;
25982         
25983         
25984     },
25985     alignment : {
25986         'left' : ['r-l', [-2,0], 'right'],
25987         'right' : ['l-r', [2,0], 'left'],
25988         'bottom' : ['t-b', [0,2], 'top'],
25989         'top' : [ 'b-t', [0,-2], 'bottom']
25990     }
25991     
25992 });
25993
25994
25995 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25996     
25997     
25998     bindEl : false,
25999     
26000     delay : null, // can be { show : 300 , hide: 500}
26001     
26002     timeout : null,
26003     
26004     hoverState : null, //???
26005     
26006     placement : 'bottom', 
26007     
26008     alignment : false,
26009     
26010     getAutoCreate : function(){
26011     
26012         var cfg = {
26013            cls : 'tooltip',
26014            role : 'tooltip',
26015            cn : [
26016                 {
26017                     cls : 'tooltip-arrow'
26018                 },
26019                 {
26020                     cls : 'tooltip-inner'
26021                 }
26022            ]
26023         };
26024         
26025         return cfg;
26026     },
26027     bind : function(el)
26028     {
26029         this.bindEl = el;
26030     },
26031       
26032     
26033     enter : function () {
26034        
26035         if (this.timeout != null) {
26036             clearTimeout(this.timeout);
26037         }
26038         
26039         this.hoverState = 'in';
26040          //Roo.log("enter - show");
26041         if (!this.delay || !this.delay.show) {
26042             this.show();
26043             return;
26044         }
26045         var _t = this;
26046         this.timeout = setTimeout(function () {
26047             if (_t.hoverState == 'in') {
26048                 _t.show();
26049             }
26050         }, this.delay.show);
26051     },
26052     leave : function()
26053     {
26054         clearTimeout(this.timeout);
26055     
26056         this.hoverState = 'out';
26057          if (!this.delay || !this.delay.hide) {
26058             this.hide();
26059             return;
26060         }
26061        
26062         var _t = this;
26063         this.timeout = setTimeout(function () {
26064             //Roo.log("leave - timeout");
26065             
26066             if (_t.hoverState == 'out') {
26067                 _t.hide();
26068                 Roo.bootstrap.Tooltip.currentEl = false;
26069             }
26070         }, delay);
26071     },
26072     
26073     show : function (msg)
26074     {
26075         if (!this.el) {
26076             this.render(document.body);
26077         }
26078         // set content.
26079         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26080         
26081         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26082         
26083         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26084         
26085         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26086         
26087         var placement = typeof this.placement == 'function' ?
26088             this.placement.call(this, this.el, on_el) :
26089             this.placement;
26090             
26091         var autoToken = /\s?auto?\s?/i;
26092         var autoPlace = autoToken.test(placement);
26093         if (autoPlace) {
26094             placement = placement.replace(autoToken, '') || 'top';
26095         }
26096         
26097         //this.el.detach()
26098         //this.el.setXY([0,0]);
26099         this.el.show();
26100         //this.el.dom.style.display='block';
26101         
26102         //this.el.appendTo(on_el);
26103         
26104         var p = this.getPosition();
26105         var box = this.el.getBox();
26106         
26107         if (autoPlace) {
26108             // fixme..
26109         }
26110         
26111         var align = this.alignment[placement];
26112         
26113         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26114         
26115         if(placement == 'top' || placement == 'bottom'){
26116             if(xy[0] < 0){
26117                 placement = 'right';
26118             }
26119             
26120             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26121                 placement = 'left';
26122             }
26123             
26124             var scroll = Roo.select('body', true).first().getScroll();
26125             
26126             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26127                 placement = 'top';
26128             }
26129             
26130         }
26131         
26132         this.el.alignTo(this.bindEl, align[0],align[1]);
26133         //var arrow = this.el.select('.arrow',true).first();
26134         //arrow.set(align[2], 
26135         
26136         this.el.addClass(placement);
26137         
26138         this.el.addClass('in fade');
26139         
26140         this.hoverState = null;
26141         
26142         if (this.el.hasClass('fade')) {
26143             // fade it?
26144         }
26145         
26146     },
26147     hide : function()
26148     {
26149          
26150         if (!this.el) {
26151             return;
26152         }
26153         //this.el.setXY([0,0]);
26154         this.el.removeClass('in');
26155         //this.el.hide();
26156         
26157     }
26158     
26159 });
26160  
26161
26162  /*
26163  * - LGPL
26164  *
26165  * Location Picker
26166  * 
26167  */
26168
26169 /**
26170  * @class Roo.bootstrap.LocationPicker
26171  * @extends Roo.bootstrap.Component
26172  * Bootstrap LocationPicker class
26173  * @cfg {Number} latitude Position when init default 0
26174  * @cfg {Number} longitude Position when init default 0
26175  * @cfg {Number} zoom default 15
26176  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26177  * @cfg {Boolean} mapTypeControl default false
26178  * @cfg {Boolean} disableDoubleClickZoom default false
26179  * @cfg {Boolean} scrollwheel default true
26180  * @cfg {Boolean} streetViewControl default false
26181  * @cfg {Number} radius default 0
26182  * @cfg {String} locationName
26183  * @cfg {Boolean} draggable default true
26184  * @cfg {Boolean} enableAutocomplete default false
26185  * @cfg {Boolean} enableReverseGeocode default true
26186  * @cfg {String} markerTitle
26187  * 
26188  * @constructor
26189  * Create a new LocationPicker
26190  * @param {Object} config The config object
26191  */
26192
26193
26194 Roo.bootstrap.LocationPicker = function(config){
26195     
26196     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26197     
26198     this.addEvents({
26199         /**
26200          * @event initial
26201          * Fires when the picker initialized.
26202          * @param {Roo.bootstrap.LocationPicker} this
26203          * @param {Google Location} location
26204          */
26205         initial : true,
26206         /**
26207          * @event positionchanged
26208          * Fires when the picker position changed.
26209          * @param {Roo.bootstrap.LocationPicker} this
26210          * @param {Google Location} location
26211          */
26212         positionchanged : true,
26213         /**
26214          * @event resize
26215          * Fires when the map resize.
26216          * @param {Roo.bootstrap.LocationPicker} this
26217          */
26218         resize : true,
26219         /**
26220          * @event show
26221          * Fires when the map show.
26222          * @param {Roo.bootstrap.LocationPicker} this
26223          */
26224         show : true,
26225         /**
26226          * @event hide
26227          * Fires when the map hide.
26228          * @param {Roo.bootstrap.LocationPicker} this
26229          */
26230         hide : true,
26231         /**
26232          * @event mapClick
26233          * Fires when click the map.
26234          * @param {Roo.bootstrap.LocationPicker} this
26235          * @param {Map event} e
26236          */
26237         mapClick : true,
26238         /**
26239          * @event mapRightClick
26240          * Fires when right click the map.
26241          * @param {Roo.bootstrap.LocationPicker} this
26242          * @param {Map event} e
26243          */
26244         mapRightClick : true,
26245         /**
26246          * @event markerClick
26247          * Fires when click the marker.
26248          * @param {Roo.bootstrap.LocationPicker} this
26249          * @param {Map event} e
26250          */
26251         markerClick : true,
26252         /**
26253          * @event markerRightClick
26254          * Fires when right click the marker.
26255          * @param {Roo.bootstrap.LocationPicker} this
26256          * @param {Map event} e
26257          */
26258         markerRightClick : true,
26259         /**
26260          * @event OverlayViewDraw
26261          * Fires when OverlayView Draw
26262          * @param {Roo.bootstrap.LocationPicker} this
26263          */
26264         OverlayViewDraw : true,
26265         /**
26266          * @event OverlayViewOnAdd
26267          * Fires when OverlayView Draw
26268          * @param {Roo.bootstrap.LocationPicker} this
26269          */
26270         OverlayViewOnAdd : true,
26271         /**
26272          * @event OverlayViewOnRemove
26273          * Fires when OverlayView Draw
26274          * @param {Roo.bootstrap.LocationPicker} this
26275          */
26276         OverlayViewOnRemove : true,
26277         /**
26278          * @event OverlayViewShow
26279          * Fires when OverlayView Draw
26280          * @param {Roo.bootstrap.LocationPicker} this
26281          * @param {Pixel} cpx
26282          */
26283         OverlayViewShow : true,
26284         /**
26285          * @event OverlayViewHide
26286          * Fires when OverlayView Draw
26287          * @param {Roo.bootstrap.LocationPicker} this
26288          */
26289         OverlayViewHide : true,
26290         /**
26291          * @event loadexception
26292          * Fires when load google lib failed.
26293          * @param {Roo.bootstrap.LocationPicker} this
26294          */
26295         loadexception : true
26296     });
26297         
26298 };
26299
26300 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26301     
26302     gMapContext: false,
26303     
26304     latitude: 0,
26305     longitude: 0,
26306     zoom: 15,
26307     mapTypeId: false,
26308     mapTypeControl: false,
26309     disableDoubleClickZoom: false,
26310     scrollwheel: true,
26311     streetViewControl: false,
26312     radius: 0,
26313     locationName: '',
26314     draggable: true,
26315     enableAutocomplete: false,
26316     enableReverseGeocode: true,
26317     markerTitle: '',
26318     
26319     getAutoCreate: function()
26320     {
26321
26322         var cfg = {
26323             tag: 'div',
26324             cls: 'roo-location-picker'
26325         };
26326         
26327         return cfg
26328     },
26329     
26330     initEvents: function(ct, position)
26331     {       
26332         if(!this.el.getWidth() || this.isApplied()){
26333             return;
26334         }
26335         
26336         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26337         
26338         this.initial();
26339     },
26340     
26341     initial: function()
26342     {
26343         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26344             this.fireEvent('loadexception', this);
26345             return;
26346         }
26347         
26348         if(!this.mapTypeId){
26349             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26350         }
26351         
26352         this.gMapContext = this.GMapContext();
26353         
26354         this.initOverlayView();
26355         
26356         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26357         
26358         var _this = this;
26359                 
26360         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26361             _this.setPosition(_this.gMapContext.marker.position);
26362         });
26363         
26364         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26365             _this.fireEvent('mapClick', this, event);
26366             
26367         });
26368
26369         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26370             _this.fireEvent('mapRightClick', this, event);
26371             
26372         });
26373         
26374         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26375             _this.fireEvent('markerClick', this, event);
26376             
26377         });
26378
26379         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26380             _this.fireEvent('markerRightClick', this, event);
26381             
26382         });
26383         
26384         this.setPosition(this.gMapContext.location);
26385         
26386         this.fireEvent('initial', this, this.gMapContext.location);
26387     },
26388     
26389     initOverlayView: function()
26390     {
26391         var _this = this;
26392         
26393         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26394             
26395             draw: function()
26396             {
26397                 _this.fireEvent('OverlayViewDraw', _this);
26398             },
26399             
26400             onAdd: function()
26401             {
26402                 _this.fireEvent('OverlayViewOnAdd', _this);
26403             },
26404             
26405             onRemove: function()
26406             {
26407                 _this.fireEvent('OverlayViewOnRemove', _this);
26408             },
26409             
26410             show: function(cpx)
26411             {
26412                 _this.fireEvent('OverlayViewShow', _this, cpx);
26413             },
26414             
26415             hide: function()
26416             {
26417                 _this.fireEvent('OverlayViewHide', _this);
26418             }
26419             
26420         });
26421     },
26422     
26423     fromLatLngToContainerPixel: function(event)
26424     {
26425         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26426     },
26427     
26428     isApplied: function() 
26429     {
26430         return this.getGmapContext() == false ? false : true;
26431     },
26432     
26433     getGmapContext: function() 
26434     {
26435         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26436     },
26437     
26438     GMapContext: function() 
26439     {
26440         var position = new google.maps.LatLng(this.latitude, this.longitude);
26441         
26442         var _map = new google.maps.Map(this.el.dom, {
26443             center: position,
26444             zoom: this.zoom,
26445             mapTypeId: this.mapTypeId,
26446             mapTypeControl: this.mapTypeControl,
26447             disableDoubleClickZoom: this.disableDoubleClickZoom,
26448             scrollwheel: this.scrollwheel,
26449             streetViewControl: this.streetViewControl,
26450             locationName: this.locationName,
26451             draggable: this.draggable,
26452             enableAutocomplete: this.enableAutocomplete,
26453             enableReverseGeocode: this.enableReverseGeocode
26454         });
26455         
26456         var _marker = new google.maps.Marker({
26457             position: position,
26458             map: _map,
26459             title: this.markerTitle,
26460             draggable: this.draggable
26461         });
26462         
26463         return {
26464             map: _map,
26465             marker: _marker,
26466             circle: null,
26467             location: position,
26468             radius: this.radius,
26469             locationName: this.locationName,
26470             addressComponents: {
26471                 formatted_address: null,
26472                 addressLine1: null,
26473                 addressLine2: null,
26474                 streetName: null,
26475                 streetNumber: null,
26476                 city: null,
26477                 district: null,
26478                 state: null,
26479                 stateOrProvince: null
26480             },
26481             settings: this,
26482             domContainer: this.el.dom,
26483             geodecoder: new google.maps.Geocoder()
26484         };
26485     },
26486     
26487     drawCircle: function(center, radius, options) 
26488     {
26489         if (this.gMapContext.circle != null) {
26490             this.gMapContext.circle.setMap(null);
26491         }
26492         if (radius > 0) {
26493             radius *= 1;
26494             options = Roo.apply({}, options, {
26495                 strokeColor: "#0000FF",
26496                 strokeOpacity: .35,
26497                 strokeWeight: 2,
26498                 fillColor: "#0000FF",
26499                 fillOpacity: .2
26500             });
26501             
26502             options.map = this.gMapContext.map;
26503             options.radius = radius;
26504             options.center = center;
26505             this.gMapContext.circle = new google.maps.Circle(options);
26506             return this.gMapContext.circle;
26507         }
26508         
26509         return null;
26510     },
26511     
26512     setPosition: function(location) 
26513     {
26514         this.gMapContext.location = location;
26515         this.gMapContext.marker.setPosition(location);
26516         this.gMapContext.map.panTo(location);
26517         this.drawCircle(location, this.gMapContext.radius, {});
26518         
26519         var _this = this;
26520         
26521         if (this.gMapContext.settings.enableReverseGeocode) {
26522             this.gMapContext.geodecoder.geocode({
26523                 latLng: this.gMapContext.location
26524             }, function(results, status) {
26525                 
26526                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26527                     _this.gMapContext.locationName = results[0].formatted_address;
26528                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26529                     
26530                     _this.fireEvent('positionchanged', this, location);
26531                 }
26532             });
26533             
26534             return;
26535         }
26536         
26537         this.fireEvent('positionchanged', this, location);
26538     },
26539     
26540     resize: function()
26541     {
26542         google.maps.event.trigger(this.gMapContext.map, "resize");
26543         
26544         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26545         
26546         this.fireEvent('resize', this);
26547     },
26548     
26549     setPositionByLatLng: function(latitude, longitude)
26550     {
26551         this.setPosition(new google.maps.LatLng(latitude, longitude));
26552     },
26553     
26554     getCurrentPosition: function() 
26555     {
26556         return {
26557             latitude: this.gMapContext.location.lat(),
26558             longitude: this.gMapContext.location.lng()
26559         };
26560     },
26561     
26562     getAddressName: function() 
26563     {
26564         return this.gMapContext.locationName;
26565     },
26566     
26567     getAddressComponents: function() 
26568     {
26569         return this.gMapContext.addressComponents;
26570     },
26571     
26572     address_component_from_google_geocode: function(address_components) 
26573     {
26574         var result = {};
26575         
26576         for (var i = 0; i < address_components.length; i++) {
26577             var component = address_components[i];
26578             if (component.types.indexOf("postal_code") >= 0) {
26579                 result.postalCode = component.short_name;
26580             } else if (component.types.indexOf("street_number") >= 0) {
26581                 result.streetNumber = component.short_name;
26582             } else if (component.types.indexOf("route") >= 0) {
26583                 result.streetName = component.short_name;
26584             } else if (component.types.indexOf("neighborhood") >= 0) {
26585                 result.city = component.short_name;
26586             } else if (component.types.indexOf("locality") >= 0) {
26587                 result.city = component.short_name;
26588             } else if (component.types.indexOf("sublocality") >= 0) {
26589                 result.district = component.short_name;
26590             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26591                 result.stateOrProvince = component.short_name;
26592             } else if (component.types.indexOf("country") >= 0) {
26593                 result.country = component.short_name;
26594             }
26595         }
26596         
26597         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26598         result.addressLine2 = "";
26599         return result;
26600     },
26601     
26602     setZoomLevel: function(zoom)
26603     {
26604         this.gMapContext.map.setZoom(zoom);
26605     },
26606     
26607     show: function()
26608     {
26609         if(!this.el){
26610             return;
26611         }
26612         
26613         this.el.show();
26614         
26615         this.resize();
26616         
26617         this.fireEvent('show', this);
26618     },
26619     
26620     hide: function()
26621     {
26622         if(!this.el){
26623             return;
26624         }
26625         
26626         this.el.hide();
26627         
26628         this.fireEvent('hide', this);
26629     }
26630     
26631 });
26632
26633 Roo.apply(Roo.bootstrap.LocationPicker, {
26634     
26635     OverlayView : function(map, options)
26636     {
26637         options = options || {};
26638         
26639         this.setMap(map);
26640     }
26641     
26642     
26643 });/*
26644  * - LGPL
26645  *
26646  * Alert
26647  * 
26648  */
26649
26650 /**
26651  * @class Roo.bootstrap.Alert
26652  * @extends Roo.bootstrap.Component
26653  * Bootstrap Alert class
26654  * @cfg {String} title The title of alert
26655  * @cfg {String} html The content of alert
26656  * @cfg {String} weight (  success | info | warning | danger )
26657  * @cfg {String} faicon font-awesomeicon
26658  * 
26659  * @constructor
26660  * Create a new alert
26661  * @param {Object} config The config object
26662  */
26663
26664
26665 Roo.bootstrap.Alert = function(config){
26666     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26667     
26668 };
26669
26670 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26671     
26672     title: '',
26673     html: '',
26674     weight: false,
26675     faicon: false,
26676     
26677     getAutoCreate : function()
26678     {
26679         
26680         var cfg = {
26681             tag : 'div',
26682             cls : 'alert',
26683             cn : [
26684                 {
26685                     tag : 'i',
26686                     cls : 'roo-alert-icon'
26687                     
26688                 },
26689                 {
26690                     tag : 'b',
26691                     cls : 'roo-alert-title',
26692                     html : this.title
26693                 },
26694                 {
26695                     tag : 'span',
26696                     cls : 'roo-alert-text',
26697                     html : this.html
26698                 }
26699             ]
26700         };
26701         
26702         if(this.faicon){
26703             cfg.cn[0].cls += ' fa ' + this.faicon;
26704         }
26705         
26706         if(this.weight){
26707             cfg.cls += ' alert-' + this.weight;
26708         }
26709         
26710         return cfg;
26711     },
26712     
26713     initEvents: function() 
26714     {
26715         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26716     },
26717     
26718     setTitle : function(str)
26719     {
26720         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26721     },
26722     
26723     setText : function(str)
26724     {
26725         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26726     },
26727     
26728     setWeight : function(weight)
26729     {
26730         if(this.weight){
26731             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26732         }
26733         
26734         this.weight = weight;
26735         
26736         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26737     },
26738     
26739     setIcon : function(icon)
26740     {
26741         if(this.faicon){
26742             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26743         }
26744         
26745         this.faicon = icon;
26746         
26747         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26748     },
26749     
26750     hide: function() 
26751     {
26752         this.el.hide();   
26753     },
26754     
26755     show: function() 
26756     {  
26757         this.el.show();   
26758     }
26759     
26760 });
26761
26762  
26763 /*
26764 * Licence: LGPL
26765 */
26766
26767 /**
26768  * @class Roo.bootstrap.UploadCropbox
26769  * @extends Roo.bootstrap.Component
26770  * Bootstrap UploadCropbox class
26771  * @cfg {String} emptyText show when image has been loaded
26772  * @cfg {String} rotateNotify show when image too small to rotate
26773  * @cfg {Number} errorTimeout default 3000
26774  * @cfg {Number} minWidth default 300
26775  * @cfg {Number} minHeight default 300
26776  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26777  * @cfg {Boolean} isDocument (true|false) default false
26778  * @cfg {String} url action url
26779  * @cfg {String} paramName default 'imageUpload'
26780  * @cfg {String} method default POST
26781  * @cfg {Boolean} loadMask (true|false) default true
26782  * @cfg {Boolean} loadingText default 'Loading...'
26783  * 
26784  * @constructor
26785  * Create a new UploadCropbox
26786  * @param {Object} config The config object
26787  */
26788
26789 Roo.bootstrap.UploadCropbox = function(config){
26790     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26791     
26792     this.addEvents({
26793         /**
26794          * @event beforeselectfile
26795          * Fire before select file
26796          * @param {Roo.bootstrap.UploadCropbox} this
26797          */
26798         "beforeselectfile" : true,
26799         /**
26800          * @event initial
26801          * Fire after initEvent
26802          * @param {Roo.bootstrap.UploadCropbox} this
26803          */
26804         "initial" : true,
26805         /**
26806          * @event crop
26807          * Fire after initEvent
26808          * @param {Roo.bootstrap.UploadCropbox} this
26809          * @param {String} data
26810          */
26811         "crop" : true,
26812         /**
26813          * @event prepare
26814          * Fire when preparing the file data
26815          * @param {Roo.bootstrap.UploadCropbox} this
26816          * @param {Object} file
26817          */
26818         "prepare" : true,
26819         /**
26820          * @event exception
26821          * Fire when get exception
26822          * @param {Roo.bootstrap.UploadCropbox} this
26823          * @param {XMLHttpRequest} xhr
26824          */
26825         "exception" : true,
26826         /**
26827          * @event beforeloadcanvas
26828          * Fire before load the canvas
26829          * @param {Roo.bootstrap.UploadCropbox} this
26830          * @param {String} src
26831          */
26832         "beforeloadcanvas" : true,
26833         /**
26834          * @event trash
26835          * Fire when trash image
26836          * @param {Roo.bootstrap.UploadCropbox} this
26837          */
26838         "trash" : true,
26839         /**
26840          * @event download
26841          * Fire when download the image
26842          * @param {Roo.bootstrap.UploadCropbox} this
26843          */
26844         "download" : true,
26845         /**
26846          * @event footerbuttonclick
26847          * Fire when footerbuttonclick
26848          * @param {Roo.bootstrap.UploadCropbox} this
26849          * @param {String} type
26850          */
26851         "footerbuttonclick" : true,
26852         /**
26853          * @event resize
26854          * Fire when resize
26855          * @param {Roo.bootstrap.UploadCropbox} this
26856          */
26857         "resize" : true,
26858         /**
26859          * @event rotate
26860          * Fire when rotate the image
26861          * @param {Roo.bootstrap.UploadCropbox} this
26862          * @param {String} pos
26863          */
26864         "rotate" : true,
26865         /**
26866          * @event inspect
26867          * Fire when inspect the file
26868          * @param {Roo.bootstrap.UploadCropbox} this
26869          * @param {Object} file
26870          */
26871         "inspect" : true,
26872         /**
26873          * @event upload
26874          * Fire when xhr upload the file
26875          * @param {Roo.bootstrap.UploadCropbox} this
26876          * @param {Object} data
26877          */
26878         "upload" : true,
26879         /**
26880          * @event arrange
26881          * Fire when arrange the file data
26882          * @param {Roo.bootstrap.UploadCropbox} this
26883          * @param {Object} formData
26884          */
26885         "arrange" : true
26886     });
26887     
26888     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26889 };
26890
26891 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26892     
26893     emptyText : 'Click to upload image',
26894     rotateNotify : 'Image is too small to rotate',
26895     errorTimeout : 3000,
26896     scale : 0,
26897     baseScale : 1,
26898     rotate : 0,
26899     dragable : false,
26900     pinching : false,
26901     mouseX : 0,
26902     mouseY : 0,
26903     cropData : false,
26904     minWidth : 300,
26905     minHeight : 300,
26906     file : false,
26907     exif : {},
26908     baseRotate : 1,
26909     cropType : 'image/jpeg',
26910     buttons : false,
26911     canvasLoaded : false,
26912     isDocument : false,
26913     method : 'POST',
26914     paramName : 'imageUpload',
26915     loadMask : true,
26916     loadingText : 'Loading...',
26917     maskEl : false,
26918     
26919     getAutoCreate : function()
26920     {
26921         var cfg = {
26922             tag : 'div',
26923             cls : 'roo-upload-cropbox',
26924             cn : [
26925                 {
26926                     tag : 'input',
26927                     cls : 'roo-upload-cropbox-selector',
26928                     type : 'file'
26929                 },
26930                 {
26931                     tag : 'div',
26932                     cls : 'roo-upload-cropbox-body',
26933                     style : 'cursor:pointer',
26934                     cn : [
26935                         {
26936                             tag : 'div',
26937                             cls : 'roo-upload-cropbox-preview'
26938                         },
26939                         {
26940                             tag : 'div',
26941                             cls : 'roo-upload-cropbox-thumb'
26942                         },
26943                         {
26944                             tag : 'div',
26945                             cls : 'roo-upload-cropbox-empty-notify',
26946                             html : this.emptyText
26947                         },
26948                         {
26949                             tag : 'div',
26950                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26951                             html : this.rotateNotify
26952                         }
26953                     ]
26954                 },
26955                 {
26956                     tag : 'div',
26957                     cls : 'roo-upload-cropbox-footer',
26958                     cn : {
26959                         tag : 'div',
26960                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26961                         cn : []
26962                     }
26963                 }
26964             ]
26965         };
26966         
26967         return cfg;
26968     },
26969     
26970     onRender : function(ct, position)
26971     {
26972         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26973         
26974         if (this.buttons.length) {
26975             
26976             Roo.each(this.buttons, function(bb) {
26977                 
26978                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26979                 
26980                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26981                 
26982             }, this);
26983         }
26984         
26985         if(this.loadMask){
26986             this.maskEl = this.el;
26987         }
26988     },
26989     
26990     initEvents : function()
26991     {
26992         this.urlAPI = (window.createObjectURL && window) || 
26993                                 (window.URL && URL.revokeObjectURL && URL) || 
26994                                 (window.webkitURL && webkitURL);
26995                         
26996         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26997         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26998         
26999         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27000         this.selectorEl.hide();
27001         
27002         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27003         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27004         
27005         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27006         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27007         this.thumbEl.hide();
27008         
27009         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27010         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27011         
27012         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27013         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27014         this.errorEl.hide();
27015         
27016         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27017         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27018         this.footerEl.hide();
27019         
27020         this.setThumbBoxSize();
27021         
27022         this.bind();
27023         
27024         this.resize();
27025         
27026         this.fireEvent('initial', this);
27027     },
27028
27029     bind : function()
27030     {
27031         var _this = this;
27032         
27033         window.addEventListener("resize", function() { _this.resize(); } );
27034         
27035         this.bodyEl.on('click', this.beforeSelectFile, this);
27036         
27037         if(Roo.isTouch){
27038             this.bodyEl.on('touchstart', this.onTouchStart, this);
27039             this.bodyEl.on('touchmove', this.onTouchMove, this);
27040             this.bodyEl.on('touchend', this.onTouchEnd, this);
27041         }
27042         
27043         if(!Roo.isTouch){
27044             this.bodyEl.on('mousedown', this.onMouseDown, this);
27045             this.bodyEl.on('mousemove', this.onMouseMove, this);
27046             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27047             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27048             Roo.get(document).on('mouseup', this.onMouseUp, this);
27049         }
27050         
27051         this.selectorEl.on('change', this.onFileSelected, this);
27052     },
27053     
27054     reset : function()
27055     {    
27056         this.scale = 0;
27057         this.baseScale = 1;
27058         this.rotate = 0;
27059         this.baseRotate = 1;
27060         this.dragable = false;
27061         this.pinching = false;
27062         this.mouseX = 0;
27063         this.mouseY = 0;
27064         this.cropData = false;
27065         this.notifyEl.dom.innerHTML = this.emptyText;
27066         
27067         this.selectorEl.dom.value = '';
27068         
27069     },
27070     
27071     resize : function()
27072     {
27073         if(this.fireEvent('resize', this) != false){
27074             this.setThumbBoxPosition();
27075             this.setCanvasPosition();
27076         }
27077     },
27078     
27079     onFooterButtonClick : function(e, el, o, type)
27080     {
27081         switch (type) {
27082             case 'rotate-left' :
27083                 this.onRotateLeft(e);
27084                 break;
27085             case 'rotate-right' :
27086                 this.onRotateRight(e);
27087                 break;
27088             case 'picture' :
27089                 this.beforeSelectFile(e);
27090                 break;
27091             case 'trash' :
27092                 this.trash(e);
27093                 break;
27094             case 'crop' :
27095                 this.crop(e);
27096                 break;
27097             case 'download' :
27098                 this.download(e);
27099                 break;
27100             default :
27101                 break;
27102         }
27103         
27104         this.fireEvent('footerbuttonclick', this, type);
27105     },
27106     
27107     beforeSelectFile : function(e)
27108     {
27109         e.preventDefault();
27110         
27111         if(this.fireEvent('beforeselectfile', this) != false){
27112             this.selectorEl.dom.click();
27113         }
27114     },
27115     
27116     onFileSelected : function(e)
27117     {
27118         e.preventDefault();
27119         
27120         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27121             return;
27122         }
27123         
27124         var file = this.selectorEl.dom.files[0];
27125         
27126         if(this.fireEvent('inspect', this, file) != false){
27127             this.prepare(file);
27128         }
27129         
27130     },
27131     
27132     trash : function(e)
27133     {
27134         this.fireEvent('trash', this);
27135     },
27136     
27137     download : function(e)
27138     {
27139         this.fireEvent('download', this);
27140     },
27141     
27142     loadCanvas : function(src)
27143     {   
27144         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27145             
27146             this.reset();
27147             
27148             this.imageEl = document.createElement('img');
27149             
27150             var _this = this;
27151             
27152             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27153             
27154             this.imageEl.src = src;
27155         }
27156     },
27157     
27158     onLoadCanvas : function()
27159     {   
27160         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27161         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27162         
27163         this.bodyEl.un('click', this.beforeSelectFile, this);
27164         
27165         this.notifyEl.hide();
27166         this.thumbEl.show();
27167         this.footerEl.show();
27168         
27169         this.baseRotateLevel();
27170         
27171         if(this.isDocument){
27172             this.setThumbBoxSize();
27173         }
27174         
27175         this.setThumbBoxPosition();
27176         
27177         this.baseScaleLevel();
27178         
27179         this.draw();
27180         
27181         this.resize();
27182         
27183         this.canvasLoaded = true;
27184         
27185         if(this.loadMask){
27186             this.maskEl.unmask();
27187         }
27188         
27189     },
27190     
27191     setCanvasPosition : function()
27192     {   
27193         if(!this.canvasEl){
27194             return;
27195         }
27196         
27197         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27198         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27199         
27200         this.previewEl.setLeft(pw);
27201         this.previewEl.setTop(ph);
27202         
27203     },
27204     
27205     onMouseDown : function(e)
27206     {   
27207         e.stopEvent();
27208         
27209         this.dragable = true;
27210         this.pinching = false;
27211         
27212         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27213             this.dragable = false;
27214             return;
27215         }
27216         
27217         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27218         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27219         
27220     },
27221     
27222     onMouseMove : function(e)
27223     {   
27224         e.stopEvent();
27225         
27226         if(!this.canvasLoaded){
27227             return;
27228         }
27229         
27230         if (!this.dragable){
27231             return;
27232         }
27233         
27234         var minX = Math.ceil(this.thumbEl.getLeft(true));
27235         var minY = Math.ceil(this.thumbEl.getTop(true));
27236         
27237         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27238         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27239         
27240         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27241         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27242         
27243         x = x - this.mouseX;
27244         y = y - this.mouseY;
27245         
27246         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27247         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27248         
27249         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27250         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27251         
27252         this.previewEl.setLeft(bgX);
27253         this.previewEl.setTop(bgY);
27254         
27255         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27256         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27257     },
27258     
27259     onMouseUp : function(e)
27260     {   
27261         e.stopEvent();
27262         
27263         this.dragable = false;
27264     },
27265     
27266     onMouseWheel : function(e)
27267     {   
27268         e.stopEvent();
27269         
27270         this.startScale = this.scale;
27271         
27272         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27273         
27274         if(!this.zoomable()){
27275             this.scale = this.startScale;
27276             return;
27277         }
27278         
27279         this.draw();
27280         
27281         return;
27282     },
27283     
27284     zoomable : function()
27285     {
27286         var minScale = this.thumbEl.getWidth() / this.minWidth;
27287         
27288         if(this.minWidth < this.minHeight){
27289             minScale = this.thumbEl.getHeight() / this.minHeight;
27290         }
27291         
27292         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27293         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27294         
27295         if(
27296                 this.isDocument &&
27297                 (this.rotate == 0 || this.rotate == 180) && 
27298                 (
27299                     width > this.imageEl.OriginWidth || 
27300                     height > this.imageEl.OriginHeight ||
27301                     (width < this.minWidth && height < this.minHeight)
27302                 )
27303         ){
27304             return false;
27305         }
27306         
27307         if(
27308                 this.isDocument &&
27309                 (this.rotate == 90 || this.rotate == 270) && 
27310                 (
27311                     width > this.imageEl.OriginWidth || 
27312                     height > this.imageEl.OriginHeight ||
27313                     (width < this.minHeight && height < this.minWidth)
27314                 )
27315         ){
27316             return false;
27317         }
27318         
27319         if(
27320                 !this.isDocument &&
27321                 (this.rotate == 0 || this.rotate == 180) && 
27322                 (
27323                     width < this.minWidth || 
27324                     width > this.imageEl.OriginWidth || 
27325                     height < this.minHeight || 
27326                     height > this.imageEl.OriginHeight
27327                 )
27328         ){
27329             return false;
27330         }
27331         
27332         if(
27333                 !this.isDocument &&
27334                 (this.rotate == 90 || this.rotate == 270) && 
27335                 (
27336                     width < this.minHeight || 
27337                     width > this.imageEl.OriginWidth || 
27338                     height < this.minWidth || 
27339                     height > this.imageEl.OriginHeight
27340                 )
27341         ){
27342             return false;
27343         }
27344         
27345         return true;
27346         
27347     },
27348     
27349     onRotateLeft : function(e)
27350     {   
27351         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27352             
27353             var minScale = this.thumbEl.getWidth() / this.minWidth;
27354             
27355             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27356             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27357             
27358             this.startScale = this.scale;
27359             
27360             while (this.getScaleLevel() < minScale){
27361             
27362                 this.scale = this.scale + 1;
27363                 
27364                 if(!this.zoomable()){
27365                     break;
27366                 }
27367                 
27368                 if(
27369                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27370                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27371                 ){
27372                     continue;
27373                 }
27374                 
27375                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27376
27377                 this.draw();
27378                 
27379                 return;
27380             }
27381             
27382             this.scale = this.startScale;
27383             
27384             this.onRotateFail();
27385             
27386             return false;
27387         }
27388         
27389         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27390
27391         if(this.isDocument){
27392             this.setThumbBoxSize();
27393             this.setThumbBoxPosition();
27394             this.setCanvasPosition();
27395         }
27396         
27397         this.draw();
27398         
27399         this.fireEvent('rotate', this, 'left');
27400         
27401     },
27402     
27403     onRotateRight : function(e)
27404     {
27405         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27406             
27407             var minScale = this.thumbEl.getWidth() / this.minWidth;
27408         
27409             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27410             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27411             
27412             this.startScale = this.scale;
27413             
27414             while (this.getScaleLevel() < minScale){
27415             
27416                 this.scale = this.scale + 1;
27417                 
27418                 if(!this.zoomable()){
27419                     break;
27420                 }
27421                 
27422                 if(
27423                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27424                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27425                 ){
27426                     continue;
27427                 }
27428                 
27429                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27430
27431                 this.draw();
27432                 
27433                 return;
27434             }
27435             
27436             this.scale = this.startScale;
27437             
27438             this.onRotateFail();
27439             
27440             return false;
27441         }
27442         
27443         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27444
27445         if(this.isDocument){
27446             this.setThumbBoxSize();
27447             this.setThumbBoxPosition();
27448             this.setCanvasPosition();
27449         }
27450         
27451         this.draw();
27452         
27453         this.fireEvent('rotate', this, 'right');
27454     },
27455     
27456     onRotateFail : function()
27457     {
27458         this.errorEl.show(true);
27459         
27460         var _this = this;
27461         
27462         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27463     },
27464     
27465     draw : function()
27466     {
27467         this.previewEl.dom.innerHTML = '';
27468         
27469         var canvasEl = document.createElement("canvas");
27470         
27471         var contextEl = canvasEl.getContext("2d");
27472         
27473         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27474         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27475         var center = this.imageEl.OriginWidth / 2;
27476         
27477         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27478             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27479             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27480             center = this.imageEl.OriginHeight / 2;
27481         }
27482         
27483         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27484         
27485         contextEl.translate(center, center);
27486         contextEl.rotate(this.rotate * Math.PI / 180);
27487
27488         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27489         
27490         this.canvasEl = document.createElement("canvas");
27491         
27492         this.contextEl = this.canvasEl.getContext("2d");
27493         
27494         switch (this.rotate) {
27495             case 0 :
27496                 
27497                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27498                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27499                 
27500                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27501                 
27502                 break;
27503             case 90 : 
27504                 
27505                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27506                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27507                 
27508                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27509                     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);
27510                     break;
27511                 }
27512                 
27513                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27514                 
27515                 break;
27516             case 180 :
27517                 
27518                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27519                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27520                 
27521                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27522                     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);
27523                     break;
27524                 }
27525                 
27526                 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);
27527                 
27528                 break;
27529             case 270 :
27530                 
27531                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27532                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27533         
27534                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27535                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27536                     break;
27537                 }
27538                 
27539                 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);
27540                 
27541                 break;
27542             default : 
27543                 break;
27544         }
27545         
27546         this.previewEl.appendChild(this.canvasEl);
27547         
27548         this.setCanvasPosition();
27549     },
27550     
27551     crop : function()
27552     {
27553         if(!this.canvasLoaded){
27554             return;
27555         }
27556         
27557         var imageCanvas = document.createElement("canvas");
27558         
27559         var imageContext = imageCanvas.getContext("2d");
27560         
27561         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27562         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27563         
27564         var center = imageCanvas.width / 2;
27565         
27566         imageContext.translate(center, center);
27567         
27568         imageContext.rotate(this.rotate * Math.PI / 180);
27569         
27570         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27571         
27572         var canvas = document.createElement("canvas");
27573         
27574         var context = canvas.getContext("2d");
27575                 
27576         canvas.width = this.minWidth;
27577         canvas.height = this.minHeight;
27578
27579         switch (this.rotate) {
27580             case 0 :
27581                 
27582                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27583                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27584                 
27585                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27586                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27587                 
27588                 var targetWidth = this.minWidth - 2 * x;
27589                 var targetHeight = this.minHeight - 2 * y;
27590                 
27591                 var scale = 1;
27592                 
27593                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27594                     scale = targetWidth / width;
27595                 }
27596                 
27597                 if(x > 0 && y == 0){
27598                     scale = targetHeight / height;
27599                 }
27600                 
27601                 if(x > 0 && y > 0){
27602                     scale = targetWidth / width;
27603                     
27604                     if(width < height){
27605                         scale = targetHeight / height;
27606                     }
27607                 }
27608                 
27609                 context.scale(scale, scale);
27610                 
27611                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27612                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27613
27614                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27615                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27616
27617                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27618                 
27619                 break;
27620             case 90 : 
27621                 
27622                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27623                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27624                 
27625                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27626                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27627                 
27628                 var targetWidth = this.minWidth - 2 * x;
27629                 var targetHeight = this.minHeight - 2 * y;
27630                 
27631                 var scale = 1;
27632                 
27633                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27634                     scale = targetWidth / width;
27635                 }
27636                 
27637                 if(x > 0 && y == 0){
27638                     scale = targetHeight / height;
27639                 }
27640                 
27641                 if(x > 0 && y > 0){
27642                     scale = targetWidth / width;
27643                     
27644                     if(width < height){
27645                         scale = targetHeight / height;
27646                     }
27647                 }
27648                 
27649                 context.scale(scale, scale);
27650                 
27651                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27652                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27653
27654                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27655                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27656                 
27657                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27658                 
27659                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27660                 
27661                 break;
27662             case 180 :
27663                 
27664                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27665                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27666                 
27667                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27668                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27669                 
27670                 var targetWidth = this.minWidth - 2 * x;
27671                 var targetHeight = this.minHeight - 2 * y;
27672                 
27673                 var scale = 1;
27674                 
27675                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27676                     scale = targetWidth / width;
27677                 }
27678                 
27679                 if(x > 0 && y == 0){
27680                     scale = targetHeight / height;
27681                 }
27682                 
27683                 if(x > 0 && y > 0){
27684                     scale = targetWidth / width;
27685                     
27686                     if(width < height){
27687                         scale = targetHeight / height;
27688                     }
27689                 }
27690                 
27691                 context.scale(scale, scale);
27692                 
27693                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27694                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27695
27696                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27697                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27698
27699                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27700                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27701                 
27702                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27703                 
27704                 break;
27705             case 270 :
27706                 
27707                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27708                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27709                 
27710                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27711                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27712                 
27713                 var targetWidth = this.minWidth - 2 * x;
27714                 var targetHeight = this.minHeight - 2 * y;
27715                 
27716                 var scale = 1;
27717                 
27718                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27719                     scale = targetWidth / width;
27720                 }
27721                 
27722                 if(x > 0 && y == 0){
27723                     scale = targetHeight / height;
27724                 }
27725                 
27726                 if(x > 0 && y > 0){
27727                     scale = targetWidth / width;
27728                     
27729                     if(width < height){
27730                         scale = targetHeight / height;
27731                     }
27732                 }
27733                 
27734                 context.scale(scale, scale);
27735                 
27736                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27737                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27738
27739                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27740                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27741                 
27742                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27743                 
27744                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27745                 
27746                 break;
27747             default : 
27748                 break;
27749         }
27750         
27751         this.cropData = canvas.toDataURL(this.cropType);
27752         
27753         if(this.fireEvent('crop', this, this.cropData) !== false){
27754             this.process(this.file, this.cropData);
27755         }
27756         
27757         return;
27758         
27759     },
27760     
27761     setThumbBoxSize : function()
27762     {
27763         var width, height;
27764         
27765         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27766             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27767             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27768             
27769             this.minWidth = width;
27770             this.minHeight = height;
27771             
27772             if(this.rotate == 90 || this.rotate == 270){
27773                 this.minWidth = height;
27774                 this.minHeight = width;
27775             }
27776         }
27777         
27778         height = 300;
27779         width = Math.ceil(this.minWidth * height / this.minHeight);
27780         
27781         if(this.minWidth > this.minHeight){
27782             width = 300;
27783             height = Math.ceil(this.minHeight * width / this.minWidth);
27784         }
27785         
27786         this.thumbEl.setStyle({
27787             width : width + 'px',
27788             height : height + 'px'
27789         });
27790
27791         return;
27792             
27793     },
27794     
27795     setThumbBoxPosition : function()
27796     {
27797         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27798         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27799         
27800         this.thumbEl.setLeft(x);
27801         this.thumbEl.setTop(y);
27802         
27803     },
27804     
27805     baseRotateLevel : function()
27806     {
27807         this.baseRotate = 1;
27808         
27809         if(
27810                 typeof(this.exif) != 'undefined' &&
27811                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27812                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27813         ){
27814             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27815         }
27816         
27817         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27818         
27819     },
27820     
27821     baseScaleLevel : function()
27822     {
27823         var width, height;
27824         
27825         if(this.isDocument){
27826             
27827             if(this.baseRotate == 6 || this.baseRotate == 8){
27828             
27829                 height = this.thumbEl.getHeight();
27830                 this.baseScale = height / this.imageEl.OriginWidth;
27831
27832                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27833                     width = this.thumbEl.getWidth();
27834                     this.baseScale = width / this.imageEl.OriginHeight;
27835                 }
27836
27837                 return;
27838             }
27839
27840             height = this.thumbEl.getHeight();
27841             this.baseScale = height / this.imageEl.OriginHeight;
27842
27843             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27844                 width = this.thumbEl.getWidth();
27845                 this.baseScale = width / this.imageEl.OriginWidth;
27846             }
27847
27848             return;
27849         }
27850         
27851         if(this.baseRotate == 6 || this.baseRotate == 8){
27852             
27853             width = this.thumbEl.getHeight();
27854             this.baseScale = width / this.imageEl.OriginHeight;
27855             
27856             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27857                 height = this.thumbEl.getWidth();
27858                 this.baseScale = height / this.imageEl.OriginHeight;
27859             }
27860             
27861             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27862                 height = this.thumbEl.getWidth();
27863                 this.baseScale = height / this.imageEl.OriginHeight;
27864                 
27865                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27866                     width = this.thumbEl.getHeight();
27867                     this.baseScale = width / this.imageEl.OriginWidth;
27868                 }
27869             }
27870             
27871             return;
27872         }
27873         
27874         width = this.thumbEl.getWidth();
27875         this.baseScale = width / this.imageEl.OriginWidth;
27876         
27877         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27878             height = this.thumbEl.getHeight();
27879             this.baseScale = height / this.imageEl.OriginHeight;
27880         }
27881         
27882         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27883             
27884             height = this.thumbEl.getHeight();
27885             this.baseScale = height / this.imageEl.OriginHeight;
27886             
27887             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27888                 width = this.thumbEl.getWidth();
27889                 this.baseScale = width / this.imageEl.OriginWidth;
27890             }
27891             
27892         }
27893         
27894         return;
27895     },
27896     
27897     getScaleLevel : function()
27898     {
27899         return this.baseScale * Math.pow(1.1, this.scale);
27900     },
27901     
27902     onTouchStart : function(e)
27903     {
27904         if(!this.canvasLoaded){
27905             this.beforeSelectFile(e);
27906             return;
27907         }
27908         
27909         var touches = e.browserEvent.touches;
27910         
27911         if(!touches){
27912             return;
27913         }
27914         
27915         if(touches.length == 1){
27916             this.onMouseDown(e);
27917             return;
27918         }
27919         
27920         if(touches.length != 2){
27921             return;
27922         }
27923         
27924         var coords = [];
27925         
27926         for(var i = 0, finger; finger = touches[i]; i++){
27927             coords.push(finger.pageX, finger.pageY);
27928         }
27929         
27930         var x = Math.pow(coords[0] - coords[2], 2);
27931         var y = Math.pow(coords[1] - coords[3], 2);
27932         
27933         this.startDistance = Math.sqrt(x + y);
27934         
27935         this.startScale = this.scale;
27936         
27937         this.pinching = true;
27938         this.dragable = false;
27939         
27940     },
27941     
27942     onTouchMove : function(e)
27943     {
27944         if(!this.pinching && !this.dragable){
27945             return;
27946         }
27947         
27948         var touches = e.browserEvent.touches;
27949         
27950         if(!touches){
27951             return;
27952         }
27953         
27954         if(this.dragable){
27955             this.onMouseMove(e);
27956             return;
27957         }
27958         
27959         var coords = [];
27960         
27961         for(var i = 0, finger; finger = touches[i]; i++){
27962             coords.push(finger.pageX, finger.pageY);
27963         }
27964         
27965         var x = Math.pow(coords[0] - coords[2], 2);
27966         var y = Math.pow(coords[1] - coords[3], 2);
27967         
27968         this.endDistance = Math.sqrt(x + y);
27969         
27970         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27971         
27972         if(!this.zoomable()){
27973             this.scale = this.startScale;
27974             return;
27975         }
27976         
27977         this.draw();
27978         
27979     },
27980     
27981     onTouchEnd : function(e)
27982     {
27983         this.pinching = false;
27984         this.dragable = false;
27985         
27986     },
27987     
27988     process : function(file, crop)
27989     {
27990         if(this.loadMask){
27991             this.maskEl.mask(this.loadingText);
27992         }
27993         
27994         this.xhr = new XMLHttpRequest();
27995         
27996         file.xhr = this.xhr;
27997
27998         this.xhr.open(this.method, this.url, true);
27999         
28000         var headers = {
28001             "Accept": "application/json",
28002             "Cache-Control": "no-cache",
28003             "X-Requested-With": "XMLHttpRequest"
28004         };
28005         
28006         for (var headerName in headers) {
28007             var headerValue = headers[headerName];
28008             if (headerValue) {
28009                 this.xhr.setRequestHeader(headerName, headerValue);
28010             }
28011         }
28012         
28013         var _this = this;
28014         
28015         this.xhr.onload = function()
28016         {
28017             _this.xhrOnLoad(_this.xhr);
28018         }
28019         
28020         this.xhr.onerror = function()
28021         {
28022             _this.xhrOnError(_this.xhr);
28023         }
28024         
28025         var formData = new FormData();
28026
28027         formData.append('returnHTML', 'NO');
28028         
28029         if(crop){
28030             formData.append('crop', crop);
28031         }
28032         
28033         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28034             formData.append(this.paramName, file, file.name);
28035         }
28036         
28037         if(typeof(file.filename) != 'undefined'){
28038             formData.append('filename', file.filename);
28039         }
28040         
28041         if(typeof(file.mimetype) != 'undefined'){
28042             formData.append('mimetype', file.mimetype);
28043         }
28044         
28045         if(this.fireEvent('arrange', this, formData) != false){
28046             this.xhr.send(formData);
28047         };
28048     },
28049     
28050     xhrOnLoad : function(xhr)
28051     {
28052         if(this.loadMask){
28053             this.maskEl.unmask();
28054         }
28055         
28056         if (xhr.readyState !== 4) {
28057             this.fireEvent('exception', this, xhr);
28058             return;
28059         }
28060
28061         var response = Roo.decode(xhr.responseText);
28062         
28063         if(!response.success){
28064             this.fireEvent('exception', this, xhr);
28065             return;
28066         }
28067         
28068         var response = Roo.decode(xhr.responseText);
28069         
28070         this.fireEvent('upload', this, response);
28071         
28072     },
28073     
28074     xhrOnError : function()
28075     {
28076         if(this.loadMask){
28077             this.maskEl.unmask();
28078         }
28079         
28080         Roo.log('xhr on error');
28081         
28082         var response = Roo.decode(xhr.responseText);
28083           
28084         Roo.log(response);
28085         
28086     },
28087     
28088     prepare : function(file)
28089     {   
28090         if(this.loadMask){
28091             this.maskEl.mask(this.loadingText);
28092         }
28093         
28094         this.file = false;
28095         this.exif = {};
28096         
28097         if(typeof(file) === 'string'){
28098             this.loadCanvas(file);
28099             return;
28100         }
28101         
28102         if(!file || !this.urlAPI){
28103             return;
28104         }
28105         
28106         this.file = file;
28107         this.cropType = file.type;
28108         
28109         var _this = this;
28110         
28111         if(this.fireEvent('prepare', this, this.file) != false){
28112             
28113             var reader = new FileReader();
28114             
28115             reader.onload = function (e) {
28116                 if (e.target.error) {
28117                     Roo.log(e.target.error);
28118                     return;
28119                 }
28120                 
28121                 var buffer = e.target.result,
28122                     dataView = new DataView(buffer),
28123                     offset = 2,
28124                     maxOffset = dataView.byteLength - 4,
28125                     markerBytes,
28126                     markerLength;
28127                 
28128                 if (dataView.getUint16(0) === 0xffd8) {
28129                     while (offset < maxOffset) {
28130                         markerBytes = dataView.getUint16(offset);
28131                         
28132                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28133                             markerLength = dataView.getUint16(offset + 2) + 2;
28134                             if (offset + markerLength > dataView.byteLength) {
28135                                 Roo.log('Invalid meta data: Invalid segment size.');
28136                                 break;
28137                             }
28138                             
28139                             if(markerBytes == 0xffe1){
28140                                 _this.parseExifData(
28141                                     dataView,
28142                                     offset,
28143                                     markerLength
28144                                 );
28145                             }
28146                             
28147                             offset += markerLength;
28148                             
28149                             continue;
28150                         }
28151                         
28152                         break;
28153                     }
28154                     
28155                 }
28156                 
28157                 var url = _this.urlAPI.createObjectURL(_this.file);
28158                 
28159                 _this.loadCanvas(url);
28160                 
28161                 return;
28162             }
28163             
28164             reader.readAsArrayBuffer(this.file);
28165             
28166         }
28167         
28168     },
28169     
28170     parseExifData : function(dataView, offset, length)
28171     {
28172         var tiffOffset = offset + 10,
28173             littleEndian,
28174             dirOffset;
28175     
28176         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28177             // No Exif data, might be XMP data instead
28178             return;
28179         }
28180         
28181         // Check for the ASCII code for "Exif" (0x45786966):
28182         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28183             // No Exif data, might be XMP data instead
28184             return;
28185         }
28186         if (tiffOffset + 8 > dataView.byteLength) {
28187             Roo.log('Invalid Exif data: Invalid segment size.');
28188             return;
28189         }
28190         // Check for the two null bytes:
28191         if (dataView.getUint16(offset + 8) !== 0x0000) {
28192             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28193             return;
28194         }
28195         // Check the byte alignment:
28196         switch (dataView.getUint16(tiffOffset)) {
28197         case 0x4949:
28198             littleEndian = true;
28199             break;
28200         case 0x4D4D:
28201             littleEndian = false;
28202             break;
28203         default:
28204             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28205             return;
28206         }
28207         // Check for the TIFF tag marker (0x002A):
28208         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28209             Roo.log('Invalid Exif data: Missing TIFF marker.');
28210             return;
28211         }
28212         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28213         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28214         
28215         this.parseExifTags(
28216             dataView,
28217             tiffOffset,
28218             tiffOffset + dirOffset,
28219             littleEndian
28220         );
28221     },
28222     
28223     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28224     {
28225         var tagsNumber,
28226             dirEndOffset,
28227             i;
28228         if (dirOffset + 6 > dataView.byteLength) {
28229             Roo.log('Invalid Exif data: Invalid directory offset.');
28230             return;
28231         }
28232         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28233         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28234         if (dirEndOffset + 4 > dataView.byteLength) {
28235             Roo.log('Invalid Exif data: Invalid directory size.');
28236             return;
28237         }
28238         for (i = 0; i < tagsNumber; i += 1) {
28239             this.parseExifTag(
28240                 dataView,
28241                 tiffOffset,
28242                 dirOffset + 2 + 12 * i, // tag offset
28243                 littleEndian
28244             );
28245         }
28246         // Return the offset to the next directory:
28247         return dataView.getUint32(dirEndOffset, littleEndian);
28248     },
28249     
28250     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28251     {
28252         var tag = dataView.getUint16(offset, littleEndian);
28253         
28254         this.exif[tag] = this.getExifValue(
28255             dataView,
28256             tiffOffset,
28257             offset,
28258             dataView.getUint16(offset + 2, littleEndian), // tag type
28259             dataView.getUint32(offset + 4, littleEndian), // tag length
28260             littleEndian
28261         );
28262     },
28263     
28264     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28265     {
28266         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28267             tagSize,
28268             dataOffset,
28269             values,
28270             i,
28271             str,
28272             c;
28273     
28274         if (!tagType) {
28275             Roo.log('Invalid Exif data: Invalid tag type.');
28276             return;
28277         }
28278         
28279         tagSize = tagType.size * length;
28280         // Determine if the value is contained in the dataOffset bytes,
28281         // or if the value at the dataOffset is a pointer to the actual data:
28282         dataOffset = tagSize > 4 ?
28283                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28284         if (dataOffset + tagSize > dataView.byteLength) {
28285             Roo.log('Invalid Exif data: Invalid data offset.');
28286             return;
28287         }
28288         if (length === 1) {
28289             return tagType.getValue(dataView, dataOffset, littleEndian);
28290         }
28291         values = [];
28292         for (i = 0; i < length; i += 1) {
28293             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28294         }
28295         
28296         if (tagType.ascii) {
28297             str = '';
28298             // Concatenate the chars:
28299             for (i = 0; i < values.length; i += 1) {
28300                 c = values[i];
28301                 // Ignore the terminating NULL byte(s):
28302                 if (c === '\u0000') {
28303                     break;
28304                 }
28305                 str += c;
28306             }
28307             return str;
28308         }
28309         return values;
28310     }
28311     
28312 });
28313
28314 Roo.apply(Roo.bootstrap.UploadCropbox, {
28315     tags : {
28316         'Orientation': 0x0112
28317     },
28318     
28319     Orientation: {
28320             1: 0, //'top-left',
28321 //            2: 'top-right',
28322             3: 180, //'bottom-right',
28323 //            4: 'bottom-left',
28324 //            5: 'left-top',
28325             6: 90, //'right-top',
28326 //            7: 'right-bottom',
28327             8: 270 //'left-bottom'
28328     },
28329     
28330     exifTagTypes : {
28331         // byte, 8-bit unsigned int:
28332         1: {
28333             getValue: function (dataView, dataOffset) {
28334                 return dataView.getUint8(dataOffset);
28335             },
28336             size: 1
28337         },
28338         // ascii, 8-bit byte:
28339         2: {
28340             getValue: function (dataView, dataOffset) {
28341                 return String.fromCharCode(dataView.getUint8(dataOffset));
28342             },
28343             size: 1,
28344             ascii: true
28345         },
28346         // short, 16 bit int:
28347         3: {
28348             getValue: function (dataView, dataOffset, littleEndian) {
28349                 return dataView.getUint16(dataOffset, littleEndian);
28350             },
28351             size: 2
28352         },
28353         // long, 32 bit int:
28354         4: {
28355             getValue: function (dataView, dataOffset, littleEndian) {
28356                 return dataView.getUint32(dataOffset, littleEndian);
28357             },
28358             size: 4
28359         },
28360         // rational = two long values, first is numerator, second is denominator:
28361         5: {
28362             getValue: function (dataView, dataOffset, littleEndian) {
28363                 return dataView.getUint32(dataOffset, littleEndian) /
28364                     dataView.getUint32(dataOffset + 4, littleEndian);
28365             },
28366             size: 8
28367         },
28368         // slong, 32 bit signed int:
28369         9: {
28370             getValue: function (dataView, dataOffset, littleEndian) {
28371                 return dataView.getInt32(dataOffset, littleEndian);
28372             },
28373             size: 4
28374         },
28375         // srational, two slongs, first is numerator, second is denominator:
28376         10: {
28377             getValue: function (dataView, dataOffset, littleEndian) {
28378                 return dataView.getInt32(dataOffset, littleEndian) /
28379                     dataView.getInt32(dataOffset + 4, littleEndian);
28380             },
28381             size: 8
28382         }
28383     },
28384     
28385     footer : {
28386         STANDARD : [
28387             {
28388                 tag : 'div',
28389                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28390                 action : 'rotate-left',
28391                 cn : [
28392                     {
28393                         tag : 'button',
28394                         cls : 'btn btn-default',
28395                         html : '<i class="fa fa-undo"></i>'
28396                     }
28397                 ]
28398             },
28399             {
28400                 tag : 'div',
28401                 cls : 'btn-group roo-upload-cropbox-picture',
28402                 action : 'picture',
28403                 cn : [
28404                     {
28405                         tag : 'button',
28406                         cls : 'btn btn-default',
28407                         html : '<i class="fa fa-picture-o"></i>'
28408                     }
28409                 ]
28410             },
28411             {
28412                 tag : 'div',
28413                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28414                 action : 'rotate-right',
28415                 cn : [
28416                     {
28417                         tag : 'button',
28418                         cls : 'btn btn-default',
28419                         html : '<i class="fa fa-repeat"></i>'
28420                     }
28421                 ]
28422             }
28423         ],
28424         DOCUMENT : [
28425             {
28426                 tag : 'div',
28427                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28428                 action : 'rotate-left',
28429                 cn : [
28430                     {
28431                         tag : 'button',
28432                         cls : 'btn btn-default',
28433                         html : '<i class="fa fa-undo"></i>'
28434                     }
28435                 ]
28436             },
28437             {
28438                 tag : 'div',
28439                 cls : 'btn-group roo-upload-cropbox-download',
28440                 action : 'download',
28441                 cn : [
28442                     {
28443                         tag : 'button',
28444                         cls : 'btn btn-default',
28445                         html : '<i class="fa fa-download"></i>'
28446                     }
28447                 ]
28448             },
28449             {
28450                 tag : 'div',
28451                 cls : 'btn-group roo-upload-cropbox-crop',
28452                 action : 'crop',
28453                 cn : [
28454                     {
28455                         tag : 'button',
28456                         cls : 'btn btn-default',
28457                         html : '<i class="fa fa-crop"></i>'
28458                     }
28459                 ]
28460             },
28461             {
28462                 tag : 'div',
28463                 cls : 'btn-group roo-upload-cropbox-trash',
28464                 action : 'trash',
28465                 cn : [
28466                     {
28467                         tag : 'button',
28468                         cls : 'btn btn-default',
28469                         html : '<i class="fa fa-trash"></i>'
28470                     }
28471                 ]
28472             },
28473             {
28474                 tag : 'div',
28475                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28476                 action : 'rotate-right',
28477                 cn : [
28478                     {
28479                         tag : 'button',
28480                         cls : 'btn btn-default',
28481                         html : '<i class="fa fa-repeat"></i>'
28482                     }
28483                 ]
28484             }
28485         ],
28486         ROTATOR : [
28487             {
28488                 tag : 'div',
28489                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28490                 action : 'rotate-left',
28491                 cn : [
28492                     {
28493                         tag : 'button',
28494                         cls : 'btn btn-default',
28495                         html : '<i class="fa fa-undo"></i>'
28496                     }
28497                 ]
28498             },
28499             {
28500                 tag : 'div',
28501                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28502                 action : 'rotate-right',
28503                 cn : [
28504                     {
28505                         tag : 'button',
28506                         cls : 'btn btn-default',
28507                         html : '<i class="fa fa-repeat"></i>'
28508                     }
28509                 ]
28510             }
28511         ]
28512     }
28513 });
28514
28515 /*
28516 * Licence: LGPL
28517 */
28518
28519 /**
28520  * @class Roo.bootstrap.DocumentManager
28521  * @extends Roo.bootstrap.Component
28522  * Bootstrap DocumentManager class
28523  * @cfg {String} paramName default 'imageUpload'
28524  * @cfg {String} toolTipName default 'filename'
28525  * @cfg {String} method default POST
28526  * @cfg {String} url action url
28527  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28528  * @cfg {Boolean} multiple multiple upload default true
28529  * @cfg {Number} thumbSize default 300
28530  * @cfg {String} fieldLabel
28531  * @cfg {Number} labelWidth default 4
28532  * @cfg {String} labelAlign (left|top) default left
28533  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28534 * @cfg {Number} labellg set the width of label (1-12)
28535  * @cfg {Number} labelmd set the width of label (1-12)
28536  * @cfg {Number} labelsm set the width of label (1-12)
28537  * @cfg {Number} labelxs set the width of label (1-12)
28538  * 
28539  * @constructor
28540  * Create a new DocumentManager
28541  * @param {Object} config The config object
28542  */
28543
28544 Roo.bootstrap.DocumentManager = function(config){
28545     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28546     
28547     this.files = [];
28548     this.delegates = [];
28549     
28550     this.addEvents({
28551         /**
28552          * @event initial
28553          * Fire when initial the DocumentManager
28554          * @param {Roo.bootstrap.DocumentManager} this
28555          */
28556         "initial" : true,
28557         /**
28558          * @event inspect
28559          * inspect selected file
28560          * @param {Roo.bootstrap.DocumentManager} this
28561          * @param {File} file
28562          */
28563         "inspect" : true,
28564         /**
28565          * @event exception
28566          * Fire when xhr load exception
28567          * @param {Roo.bootstrap.DocumentManager} this
28568          * @param {XMLHttpRequest} xhr
28569          */
28570         "exception" : true,
28571         /**
28572          * @event afterupload
28573          * Fire when xhr load exception
28574          * @param {Roo.bootstrap.DocumentManager} this
28575          * @param {XMLHttpRequest} xhr
28576          */
28577         "afterupload" : true,
28578         /**
28579          * @event prepare
28580          * prepare the form data
28581          * @param {Roo.bootstrap.DocumentManager} this
28582          * @param {Object} formData
28583          */
28584         "prepare" : true,
28585         /**
28586          * @event remove
28587          * Fire when remove the file
28588          * @param {Roo.bootstrap.DocumentManager} this
28589          * @param {Object} file
28590          */
28591         "remove" : true,
28592         /**
28593          * @event refresh
28594          * Fire after refresh the file
28595          * @param {Roo.bootstrap.DocumentManager} this
28596          */
28597         "refresh" : true,
28598         /**
28599          * @event click
28600          * Fire after click the image
28601          * @param {Roo.bootstrap.DocumentManager} this
28602          * @param {Object} file
28603          */
28604         "click" : true,
28605         /**
28606          * @event edit
28607          * Fire when upload a image and editable set to true
28608          * @param {Roo.bootstrap.DocumentManager} this
28609          * @param {Object} file
28610          */
28611         "edit" : true,
28612         /**
28613          * @event beforeselectfile
28614          * Fire before select file
28615          * @param {Roo.bootstrap.DocumentManager} this
28616          */
28617         "beforeselectfile" : true,
28618         /**
28619          * @event process
28620          * Fire before process file
28621          * @param {Roo.bootstrap.DocumentManager} this
28622          * @param {Object} file
28623          */
28624         "process" : true,
28625         /**
28626          * @event previewrendered
28627          * Fire when preview rendered
28628          * @param {Roo.bootstrap.DocumentManager} this
28629          * @param {Object} file
28630          */
28631         "previewrendered" : true
28632         
28633     });
28634 };
28635
28636 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28637     
28638     boxes : 0,
28639     inputName : '',
28640     thumbSize : 300,
28641     multiple : true,
28642     files : false,
28643     method : 'POST',
28644     url : '',
28645     paramName : 'imageUpload',
28646     toolTipName : 'filename',
28647     fieldLabel : '',
28648     labelWidth : 4,
28649     labelAlign : 'left',
28650     editable : true,
28651     delegates : false,
28652     xhr : false, 
28653     
28654     labellg : 0,
28655     labelmd : 0,
28656     labelsm : 0,
28657     labelxs : 0,
28658     
28659     getAutoCreate : function()
28660     {   
28661         var managerWidget = {
28662             tag : 'div',
28663             cls : 'roo-document-manager',
28664             cn : [
28665                 {
28666                     tag : 'input',
28667                     cls : 'roo-document-manager-selector',
28668                     type : 'file'
28669                 },
28670                 {
28671                     tag : 'div',
28672                     cls : 'roo-document-manager-uploader',
28673                     cn : [
28674                         {
28675                             tag : 'div',
28676                             cls : 'roo-document-manager-upload-btn',
28677                             html : '<i class="fa fa-plus"></i>'
28678                         }
28679                     ]
28680                     
28681                 }
28682             ]
28683         };
28684         
28685         var content = [
28686             {
28687                 tag : 'div',
28688                 cls : 'column col-md-12',
28689                 cn : managerWidget
28690             }
28691         ];
28692         
28693         if(this.fieldLabel.length){
28694             
28695             content = [
28696                 {
28697                     tag : 'div',
28698                     cls : 'column col-md-12',
28699                     html : this.fieldLabel
28700                 },
28701                 {
28702                     tag : 'div',
28703                     cls : 'column col-md-12',
28704                     cn : managerWidget
28705                 }
28706             ];
28707
28708             if(this.labelAlign == 'left'){
28709                 content = [
28710                     {
28711                         tag : 'div',
28712                         cls : 'column',
28713                         html : this.fieldLabel
28714                     },
28715                     {
28716                         tag : 'div',
28717                         cls : 'column',
28718                         cn : managerWidget
28719                     }
28720                 ];
28721                 
28722                 if(this.labelWidth > 12){
28723                     content[0].style = "width: " + this.labelWidth + 'px';
28724                 }
28725
28726                 if(this.labelWidth < 13 && this.labelmd == 0){
28727                     this.labelmd = this.labelWidth;
28728                 }
28729
28730                 if(this.labellg > 0){
28731                     content[0].cls += ' col-lg-' + this.labellg;
28732                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28733                 }
28734
28735                 if(this.labelmd > 0){
28736                     content[0].cls += ' col-md-' + this.labelmd;
28737                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28738                 }
28739
28740                 if(this.labelsm > 0){
28741                     content[0].cls += ' col-sm-' + this.labelsm;
28742                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28743                 }
28744
28745                 if(this.labelxs > 0){
28746                     content[0].cls += ' col-xs-' + this.labelxs;
28747                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28748                 }
28749                 
28750             }
28751         }
28752         
28753         var cfg = {
28754             tag : 'div',
28755             cls : 'row clearfix',
28756             cn : content
28757         };
28758         
28759         return cfg;
28760         
28761     },
28762     
28763     initEvents : function()
28764     {
28765         this.managerEl = this.el.select('.roo-document-manager', true).first();
28766         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28767         
28768         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28769         this.selectorEl.hide();
28770         
28771         if(this.multiple){
28772             this.selectorEl.attr('multiple', 'multiple');
28773         }
28774         
28775         this.selectorEl.on('change', this.onFileSelected, this);
28776         
28777         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28778         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28779         
28780         this.uploader.on('click', this.onUploaderClick, this);
28781         
28782         this.renderProgressDialog();
28783         
28784         var _this = this;
28785         
28786         window.addEventListener("resize", function() { _this.refresh(); } );
28787         
28788         this.fireEvent('initial', this);
28789     },
28790     
28791     renderProgressDialog : function()
28792     {
28793         var _this = this;
28794         
28795         this.progressDialog = new Roo.bootstrap.Modal({
28796             cls : 'roo-document-manager-progress-dialog',
28797             allow_close : false,
28798             title : '',
28799             buttons : [
28800                 {
28801                     name  :'cancel',
28802                     weight : 'danger',
28803                     html : 'Cancel'
28804                 }
28805             ], 
28806             listeners : { 
28807                 btnclick : function() {
28808                     _this.uploadCancel();
28809                     this.hide();
28810                 }
28811             }
28812         });
28813          
28814         this.progressDialog.render(Roo.get(document.body));
28815          
28816         this.progress = new Roo.bootstrap.Progress({
28817             cls : 'roo-document-manager-progress',
28818             active : true,
28819             striped : true
28820         });
28821         
28822         this.progress.render(this.progressDialog.getChildContainer());
28823         
28824         this.progressBar = new Roo.bootstrap.ProgressBar({
28825             cls : 'roo-document-manager-progress-bar',
28826             aria_valuenow : 0,
28827             aria_valuemin : 0,
28828             aria_valuemax : 12,
28829             panel : 'success'
28830         });
28831         
28832         this.progressBar.render(this.progress.getChildContainer());
28833     },
28834     
28835     onUploaderClick : function(e)
28836     {
28837         e.preventDefault();
28838      
28839         if(this.fireEvent('beforeselectfile', this) != false){
28840             this.selectorEl.dom.click();
28841         }
28842         
28843     },
28844     
28845     onFileSelected : function(e)
28846     {
28847         e.preventDefault();
28848         
28849         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28850             return;
28851         }
28852         
28853         Roo.each(this.selectorEl.dom.files, function(file){
28854             if(this.fireEvent('inspect', this, file) != false){
28855                 this.files.push(file);
28856             }
28857         }, this);
28858         
28859         this.queue();
28860         
28861     },
28862     
28863     queue : function()
28864     {
28865         this.selectorEl.dom.value = '';
28866         
28867         if(!this.files || !this.files.length){
28868             return;
28869         }
28870         
28871         if(this.boxes > 0 && this.files.length > this.boxes){
28872             this.files = this.files.slice(0, this.boxes);
28873         }
28874         
28875         this.uploader.show();
28876         
28877         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28878             this.uploader.hide();
28879         }
28880         
28881         var _this = this;
28882         
28883         var files = [];
28884         
28885         var docs = [];
28886         
28887         Roo.each(this.files, function(file){
28888             
28889             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28890                 var f = this.renderPreview(file);
28891                 files.push(f);
28892                 return;
28893             }
28894             
28895             if(file.type.indexOf('image') != -1){
28896                 this.delegates.push(
28897                     (function(){
28898                         _this.process(file);
28899                     }).createDelegate(this)
28900                 );
28901         
28902                 return;
28903             }
28904             
28905             docs.push(
28906                 (function(){
28907                     _this.process(file);
28908                 }).createDelegate(this)
28909             );
28910             
28911         }, this);
28912         
28913         this.files = files;
28914         
28915         this.delegates = this.delegates.concat(docs);
28916         
28917         if(!this.delegates.length){
28918             this.refresh();
28919             return;
28920         }
28921         
28922         this.progressBar.aria_valuemax = this.delegates.length;
28923         
28924         this.arrange();
28925         
28926         return;
28927     },
28928     
28929     arrange : function()
28930     {
28931         if(!this.delegates.length){
28932             this.progressDialog.hide();
28933             this.refresh();
28934             return;
28935         }
28936         
28937         var delegate = this.delegates.shift();
28938         
28939         this.progressDialog.show();
28940         
28941         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28942         
28943         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28944         
28945         delegate();
28946     },
28947     
28948     refresh : function()
28949     {
28950         this.uploader.show();
28951         
28952         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28953             this.uploader.hide();
28954         }
28955         
28956         Roo.isTouch ? this.closable(false) : this.closable(true);
28957         
28958         this.fireEvent('refresh', this);
28959     },
28960     
28961     onRemove : function(e, el, o)
28962     {
28963         e.preventDefault();
28964         
28965         this.fireEvent('remove', this, o);
28966         
28967     },
28968     
28969     remove : function(o)
28970     {
28971         var files = [];
28972         
28973         Roo.each(this.files, function(file){
28974             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28975                 files.push(file);
28976                 return;
28977             }
28978
28979             o.target.remove();
28980
28981         }, this);
28982         
28983         this.files = files;
28984         
28985         this.refresh();
28986     },
28987     
28988     clear : function()
28989     {
28990         Roo.each(this.files, function(file){
28991             if(!file.target){
28992                 return;
28993             }
28994             
28995             file.target.remove();
28996
28997         }, this);
28998         
28999         this.files = [];
29000         
29001         this.refresh();
29002     },
29003     
29004     onClick : function(e, el, o)
29005     {
29006         e.preventDefault();
29007         
29008         this.fireEvent('click', this, o);
29009         
29010     },
29011     
29012     closable : function(closable)
29013     {
29014         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29015             
29016             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29017             
29018             if(closable){
29019                 el.show();
29020                 return;
29021             }
29022             
29023             el.hide();
29024             
29025         }, this);
29026     },
29027     
29028     xhrOnLoad : function(xhr)
29029     {
29030         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29031             el.remove();
29032         }, this);
29033         
29034         if (xhr.readyState !== 4) {
29035             this.arrange();
29036             this.fireEvent('exception', this, xhr);
29037             return;
29038         }
29039
29040         var response = Roo.decode(xhr.responseText);
29041         
29042         if(!response.success){
29043             this.arrange();
29044             this.fireEvent('exception', this, xhr);
29045             return;
29046         }
29047         
29048         var file = this.renderPreview(response.data);
29049         
29050         this.files.push(file);
29051         
29052         this.arrange();
29053         
29054         this.fireEvent('afterupload', this, xhr);
29055         
29056     },
29057     
29058     xhrOnError : function(xhr)
29059     {
29060         Roo.log('xhr on error');
29061         
29062         var response = Roo.decode(xhr.responseText);
29063           
29064         Roo.log(response);
29065         
29066         this.arrange();
29067     },
29068     
29069     process : function(file)
29070     {
29071         if(this.fireEvent('process', this, file) !== false){
29072             if(this.editable && file.type.indexOf('image') != -1){
29073                 this.fireEvent('edit', this, file);
29074                 return;
29075             }
29076
29077             this.uploadStart(file, false);
29078
29079             return;
29080         }
29081         
29082     },
29083     
29084     uploadStart : function(file, crop)
29085     {
29086         this.xhr = new XMLHttpRequest();
29087         
29088         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29089             this.arrange();
29090             return;
29091         }
29092         
29093         file.xhr = this.xhr;
29094             
29095         this.managerEl.createChild({
29096             tag : 'div',
29097             cls : 'roo-document-manager-loading',
29098             cn : [
29099                 {
29100                     tag : 'div',
29101                     tooltip : file.name,
29102                     cls : 'roo-document-manager-thumb',
29103                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29104                 }
29105             ]
29106
29107         });
29108
29109         this.xhr.open(this.method, this.url, true);
29110         
29111         var headers = {
29112             "Accept": "application/json",
29113             "Cache-Control": "no-cache",
29114             "X-Requested-With": "XMLHttpRequest"
29115         };
29116         
29117         for (var headerName in headers) {
29118             var headerValue = headers[headerName];
29119             if (headerValue) {
29120                 this.xhr.setRequestHeader(headerName, headerValue);
29121             }
29122         }
29123         
29124         var _this = this;
29125         
29126         this.xhr.onload = function()
29127         {
29128             _this.xhrOnLoad(_this.xhr);
29129         }
29130         
29131         this.xhr.onerror = function()
29132         {
29133             _this.xhrOnError(_this.xhr);
29134         }
29135         
29136         var formData = new FormData();
29137
29138         formData.append('returnHTML', 'NO');
29139         
29140         if(crop){
29141             formData.append('crop', crop);
29142         }
29143         
29144         formData.append(this.paramName, file, file.name);
29145         
29146         var options = {
29147             file : file, 
29148             manually : false
29149         };
29150         
29151         if(this.fireEvent('prepare', this, formData, options) != false){
29152             
29153             if(options.manually){
29154                 return;
29155             }
29156             
29157             this.xhr.send(formData);
29158             return;
29159         };
29160         
29161         this.uploadCancel();
29162     },
29163     
29164     uploadCancel : function()
29165     {
29166         if (this.xhr) {
29167             this.xhr.abort();
29168         }
29169         
29170         this.delegates = [];
29171         
29172         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29173             el.remove();
29174         }, this);
29175         
29176         this.arrange();
29177     },
29178     
29179     renderPreview : function(file)
29180     {
29181         if(typeof(file.target) != 'undefined' && file.target){
29182             return file;
29183         }
29184         
29185         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29186         
29187         var previewEl = this.managerEl.createChild({
29188             tag : 'div',
29189             cls : 'roo-document-manager-preview',
29190             cn : [
29191                 {
29192                     tag : 'div',
29193                     tooltip : file[this.toolTipName],
29194                     cls : 'roo-document-manager-thumb',
29195                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29196                 },
29197                 {
29198                     tag : 'button',
29199                     cls : 'close',
29200                     html : '<i class="fa fa-times-circle"></i>'
29201                 }
29202             ]
29203         });
29204
29205         var close = previewEl.select('button.close', true).first();
29206
29207         close.on('click', this.onRemove, this, file);
29208
29209         file.target = previewEl;
29210
29211         var image = previewEl.select('img', true).first();
29212         
29213         var _this = this;
29214         
29215         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29216         
29217         image.on('click', this.onClick, this, file);
29218         
29219         this.fireEvent('previewrendered', this, file);
29220         
29221         return file;
29222         
29223     },
29224     
29225     onPreviewLoad : function(file, image)
29226     {
29227         if(typeof(file.target) == 'undefined' || !file.target){
29228             return;
29229         }
29230         
29231         var width = image.dom.naturalWidth || image.dom.width;
29232         var height = image.dom.naturalHeight || image.dom.height;
29233         
29234         if(width > height){
29235             file.target.addClass('wide');
29236             return;
29237         }
29238         
29239         file.target.addClass('tall');
29240         return;
29241         
29242     },
29243     
29244     uploadFromSource : function(file, crop)
29245     {
29246         this.xhr = new XMLHttpRequest();
29247         
29248         this.managerEl.createChild({
29249             tag : 'div',
29250             cls : 'roo-document-manager-loading',
29251             cn : [
29252                 {
29253                     tag : 'div',
29254                     tooltip : file.name,
29255                     cls : 'roo-document-manager-thumb',
29256                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29257                 }
29258             ]
29259
29260         });
29261
29262         this.xhr.open(this.method, this.url, true);
29263         
29264         var headers = {
29265             "Accept": "application/json",
29266             "Cache-Control": "no-cache",
29267             "X-Requested-With": "XMLHttpRequest"
29268         };
29269         
29270         for (var headerName in headers) {
29271             var headerValue = headers[headerName];
29272             if (headerValue) {
29273                 this.xhr.setRequestHeader(headerName, headerValue);
29274             }
29275         }
29276         
29277         var _this = this;
29278         
29279         this.xhr.onload = function()
29280         {
29281             _this.xhrOnLoad(_this.xhr);
29282         }
29283         
29284         this.xhr.onerror = function()
29285         {
29286             _this.xhrOnError(_this.xhr);
29287         }
29288         
29289         var formData = new FormData();
29290
29291         formData.append('returnHTML', 'NO');
29292         
29293         formData.append('crop', crop);
29294         
29295         if(typeof(file.filename) != 'undefined'){
29296             formData.append('filename', file.filename);
29297         }
29298         
29299         if(typeof(file.mimetype) != 'undefined'){
29300             formData.append('mimetype', file.mimetype);
29301         }
29302         
29303         Roo.log(formData);
29304         
29305         if(this.fireEvent('prepare', this, formData) != false){
29306             this.xhr.send(formData);
29307         };
29308     }
29309 });
29310
29311 /*
29312 * Licence: LGPL
29313 */
29314
29315 /**
29316  * @class Roo.bootstrap.DocumentViewer
29317  * @extends Roo.bootstrap.Component
29318  * Bootstrap DocumentViewer class
29319  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29320  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29321  * 
29322  * @constructor
29323  * Create a new DocumentViewer
29324  * @param {Object} config The config object
29325  */
29326
29327 Roo.bootstrap.DocumentViewer = function(config){
29328     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29329     
29330     this.addEvents({
29331         /**
29332          * @event initial
29333          * Fire after initEvent
29334          * @param {Roo.bootstrap.DocumentViewer} this
29335          */
29336         "initial" : true,
29337         /**
29338          * @event click
29339          * Fire after click
29340          * @param {Roo.bootstrap.DocumentViewer} this
29341          */
29342         "click" : true,
29343         /**
29344          * @event download
29345          * Fire after download button
29346          * @param {Roo.bootstrap.DocumentViewer} this
29347          */
29348         "download" : true,
29349         /**
29350          * @event trash
29351          * Fire after trash button
29352          * @param {Roo.bootstrap.DocumentViewer} this
29353          */
29354         "trash" : true
29355         
29356     });
29357 };
29358
29359 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29360     
29361     showDownload : true,
29362     
29363     showTrash : true,
29364     
29365     getAutoCreate : function()
29366     {
29367         var cfg = {
29368             tag : 'div',
29369             cls : 'roo-document-viewer',
29370             cn : [
29371                 {
29372                     tag : 'div',
29373                     cls : 'roo-document-viewer-body',
29374                     cn : [
29375                         {
29376                             tag : 'div',
29377                             cls : 'roo-document-viewer-thumb',
29378                             cn : [
29379                                 {
29380                                     tag : 'img',
29381                                     cls : 'roo-document-viewer-image'
29382                                 }
29383                             ]
29384                         }
29385                     ]
29386                 },
29387                 {
29388                     tag : 'div',
29389                     cls : 'roo-document-viewer-footer',
29390                     cn : {
29391                         tag : 'div',
29392                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29393                         cn : [
29394                             {
29395                                 tag : 'div',
29396                                 cls : 'btn-group roo-document-viewer-download',
29397                                 cn : [
29398                                     {
29399                                         tag : 'button',
29400                                         cls : 'btn btn-default',
29401                                         html : '<i class="fa fa-download"></i>'
29402                                     }
29403                                 ]
29404                             },
29405                             {
29406                                 tag : 'div',
29407                                 cls : 'btn-group roo-document-viewer-trash',
29408                                 cn : [
29409                                     {
29410                                         tag : 'button',
29411                                         cls : 'btn btn-default',
29412                                         html : '<i class="fa fa-trash"></i>'
29413                                     }
29414                                 ]
29415                             }
29416                         ]
29417                     }
29418                 }
29419             ]
29420         };
29421         
29422         return cfg;
29423     },
29424     
29425     initEvents : function()
29426     {
29427         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29428         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29429         
29430         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29431         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29432         
29433         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29434         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29435         
29436         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29437         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29438         
29439         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29440         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29441         
29442         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29443         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29444         
29445         this.bodyEl.on('click', this.onClick, this);
29446         this.downloadBtn.on('click', this.onDownload, this);
29447         this.trashBtn.on('click', this.onTrash, this);
29448         
29449         this.downloadBtn.hide();
29450         this.trashBtn.hide();
29451         
29452         if(this.showDownload){
29453             this.downloadBtn.show();
29454         }
29455         
29456         if(this.showTrash){
29457             this.trashBtn.show();
29458         }
29459         
29460         if(!this.showDownload && !this.showTrash) {
29461             this.footerEl.hide();
29462         }
29463         
29464     },
29465     
29466     initial : function()
29467     {
29468         this.fireEvent('initial', this);
29469         
29470     },
29471     
29472     onClick : function(e)
29473     {
29474         e.preventDefault();
29475         
29476         this.fireEvent('click', this);
29477     },
29478     
29479     onDownload : function(e)
29480     {
29481         e.preventDefault();
29482         
29483         this.fireEvent('download', this);
29484     },
29485     
29486     onTrash : function(e)
29487     {
29488         e.preventDefault();
29489         
29490         this.fireEvent('trash', this);
29491     }
29492     
29493 });
29494 /*
29495  * - LGPL
29496  *
29497  * nav progress bar
29498  * 
29499  */
29500
29501 /**
29502  * @class Roo.bootstrap.NavProgressBar
29503  * @extends Roo.bootstrap.Component
29504  * Bootstrap NavProgressBar class
29505  * 
29506  * @constructor
29507  * Create a new nav progress bar
29508  * @param {Object} config The config object
29509  */
29510
29511 Roo.bootstrap.NavProgressBar = function(config){
29512     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29513
29514     this.bullets = this.bullets || [];
29515    
29516 //    Roo.bootstrap.NavProgressBar.register(this);
29517      this.addEvents({
29518         /**
29519              * @event changed
29520              * Fires when the active item changes
29521              * @param {Roo.bootstrap.NavProgressBar} this
29522              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29523              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29524          */
29525         'changed': true
29526      });
29527     
29528 };
29529
29530 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29531     
29532     bullets : [],
29533     barItems : [],
29534     
29535     getAutoCreate : function()
29536     {
29537         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29538         
29539         cfg = {
29540             tag : 'div',
29541             cls : 'roo-navigation-bar-group',
29542             cn : [
29543                 {
29544                     tag : 'div',
29545                     cls : 'roo-navigation-top-bar'
29546                 },
29547                 {
29548                     tag : 'div',
29549                     cls : 'roo-navigation-bullets-bar',
29550                     cn : [
29551                         {
29552                             tag : 'ul',
29553                             cls : 'roo-navigation-bar'
29554                         }
29555                     ]
29556                 },
29557                 
29558                 {
29559                     tag : 'div',
29560                     cls : 'roo-navigation-bottom-bar'
29561                 }
29562             ]
29563             
29564         };
29565         
29566         return cfg;
29567         
29568     },
29569     
29570     initEvents: function() 
29571     {
29572         
29573     },
29574     
29575     onRender : function(ct, position) 
29576     {
29577         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29578         
29579         if(this.bullets.length){
29580             Roo.each(this.bullets, function(b){
29581                this.addItem(b);
29582             }, this);
29583         }
29584         
29585         this.format();
29586         
29587     },
29588     
29589     addItem : function(cfg)
29590     {
29591         var item = new Roo.bootstrap.NavProgressItem(cfg);
29592         
29593         item.parentId = this.id;
29594         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29595         
29596         if(cfg.html){
29597             var top = new Roo.bootstrap.Element({
29598                 tag : 'div',
29599                 cls : 'roo-navigation-bar-text'
29600             });
29601             
29602             var bottom = new Roo.bootstrap.Element({
29603                 tag : 'div',
29604                 cls : 'roo-navigation-bar-text'
29605             });
29606             
29607             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29608             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29609             
29610             var topText = new Roo.bootstrap.Element({
29611                 tag : 'span',
29612                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29613             });
29614             
29615             var bottomText = new Roo.bootstrap.Element({
29616                 tag : 'span',
29617                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29618             });
29619             
29620             topText.onRender(top.el, null);
29621             bottomText.onRender(bottom.el, null);
29622             
29623             item.topEl = top;
29624             item.bottomEl = bottom;
29625         }
29626         
29627         this.barItems.push(item);
29628         
29629         return item;
29630     },
29631     
29632     getActive : function()
29633     {
29634         var active = false;
29635         
29636         Roo.each(this.barItems, function(v){
29637             
29638             if (!v.isActive()) {
29639                 return;
29640             }
29641             
29642             active = v;
29643             return false;
29644             
29645         });
29646         
29647         return active;
29648     },
29649     
29650     setActiveItem : function(item)
29651     {
29652         var prev = false;
29653         
29654         Roo.each(this.barItems, function(v){
29655             if (v.rid == item.rid) {
29656                 return ;
29657             }
29658             
29659             if (v.isActive()) {
29660                 v.setActive(false);
29661                 prev = v;
29662             }
29663         });
29664
29665         item.setActive(true);
29666         
29667         this.fireEvent('changed', this, item, prev);
29668     },
29669     
29670     getBarItem: function(rid)
29671     {
29672         var ret = false;
29673         
29674         Roo.each(this.barItems, function(e) {
29675             if (e.rid != rid) {
29676                 return;
29677             }
29678             
29679             ret =  e;
29680             return false;
29681         });
29682         
29683         return ret;
29684     },
29685     
29686     indexOfItem : function(item)
29687     {
29688         var index = false;
29689         
29690         Roo.each(this.barItems, function(v, i){
29691             
29692             if (v.rid != item.rid) {
29693                 return;
29694             }
29695             
29696             index = i;
29697             return false
29698         });
29699         
29700         return index;
29701     },
29702     
29703     setActiveNext : function()
29704     {
29705         var i = this.indexOfItem(this.getActive());
29706         
29707         if (i > this.barItems.length) {
29708             return;
29709         }
29710         
29711         this.setActiveItem(this.barItems[i+1]);
29712     },
29713     
29714     setActivePrev : function()
29715     {
29716         var i = this.indexOfItem(this.getActive());
29717         
29718         if (i  < 1) {
29719             return;
29720         }
29721         
29722         this.setActiveItem(this.barItems[i-1]);
29723     },
29724     
29725     format : function()
29726     {
29727         if(!this.barItems.length){
29728             return;
29729         }
29730      
29731         var width = 100 / this.barItems.length;
29732         
29733         Roo.each(this.barItems, function(i){
29734             i.el.setStyle('width', width + '%');
29735             i.topEl.el.setStyle('width', width + '%');
29736             i.bottomEl.el.setStyle('width', width + '%');
29737         }, this);
29738         
29739     }
29740     
29741 });
29742 /*
29743  * - LGPL
29744  *
29745  * Nav Progress Item
29746  * 
29747  */
29748
29749 /**
29750  * @class Roo.bootstrap.NavProgressItem
29751  * @extends Roo.bootstrap.Component
29752  * Bootstrap NavProgressItem class
29753  * @cfg {String} rid the reference id
29754  * @cfg {Boolean} active (true|false) Is item active default false
29755  * @cfg {Boolean} disabled (true|false) Is item active default false
29756  * @cfg {String} html
29757  * @cfg {String} position (top|bottom) text position default bottom
29758  * @cfg {String} icon show icon instead of number
29759  * 
29760  * @constructor
29761  * Create a new NavProgressItem
29762  * @param {Object} config The config object
29763  */
29764 Roo.bootstrap.NavProgressItem = function(config){
29765     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29766     this.addEvents({
29767         // raw events
29768         /**
29769          * @event click
29770          * The raw click event for the entire grid.
29771          * @param {Roo.bootstrap.NavProgressItem} this
29772          * @param {Roo.EventObject} e
29773          */
29774         "click" : true
29775     });
29776    
29777 };
29778
29779 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29780     
29781     rid : '',
29782     active : false,
29783     disabled : false,
29784     html : '',
29785     position : 'bottom',
29786     icon : false,
29787     
29788     getAutoCreate : function()
29789     {
29790         var iconCls = 'roo-navigation-bar-item-icon';
29791         
29792         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29793         
29794         var cfg = {
29795             tag: 'li',
29796             cls: 'roo-navigation-bar-item',
29797             cn : [
29798                 {
29799                     tag : 'i',
29800                     cls : iconCls
29801                 }
29802             ]
29803         };
29804         
29805         if(this.active){
29806             cfg.cls += ' active';
29807         }
29808         if(this.disabled){
29809             cfg.cls += ' disabled';
29810         }
29811         
29812         return cfg;
29813     },
29814     
29815     disable : function()
29816     {
29817         this.setDisabled(true);
29818     },
29819     
29820     enable : function()
29821     {
29822         this.setDisabled(false);
29823     },
29824     
29825     initEvents: function() 
29826     {
29827         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29828         
29829         this.iconEl.on('click', this.onClick, this);
29830     },
29831     
29832     onClick : function(e)
29833     {
29834         e.preventDefault();
29835         
29836         if(this.disabled){
29837             return;
29838         }
29839         
29840         if(this.fireEvent('click', this, e) === false){
29841             return;
29842         };
29843         
29844         this.parent().setActiveItem(this);
29845     },
29846     
29847     isActive: function () 
29848     {
29849         return this.active;
29850     },
29851     
29852     setActive : function(state)
29853     {
29854         if(this.active == state){
29855             return;
29856         }
29857         
29858         this.active = state;
29859         
29860         if (state) {
29861             this.el.addClass('active');
29862             return;
29863         }
29864         
29865         this.el.removeClass('active');
29866         
29867         return;
29868     },
29869     
29870     setDisabled : function(state)
29871     {
29872         if(this.disabled == state){
29873             return;
29874         }
29875         
29876         this.disabled = state;
29877         
29878         if (state) {
29879             this.el.addClass('disabled');
29880             return;
29881         }
29882         
29883         this.el.removeClass('disabled');
29884     },
29885     
29886     tooltipEl : function()
29887     {
29888         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29889     }
29890 });
29891  
29892
29893  /*
29894  * - LGPL
29895  *
29896  * FieldLabel
29897  * 
29898  */
29899
29900 /**
29901  * @class Roo.bootstrap.FieldLabel
29902  * @extends Roo.bootstrap.Component
29903  * Bootstrap FieldLabel class
29904  * @cfg {String} html contents of the element
29905  * @cfg {String} tag tag of the element default label
29906  * @cfg {String} cls class of the element
29907  * @cfg {String} target label target 
29908  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29909  * @cfg {String} invalidClass default "text-warning"
29910  * @cfg {String} validClass default "text-success"
29911  * @cfg {String} iconTooltip default "This field is required"
29912  * @cfg {String} indicatorpos (left|right) default left
29913  * 
29914  * @constructor
29915  * Create a new FieldLabel
29916  * @param {Object} config The config object
29917  */
29918
29919 Roo.bootstrap.FieldLabel = function(config){
29920     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29921     
29922     this.addEvents({
29923             /**
29924              * @event invalid
29925              * Fires after the field has been marked as invalid.
29926              * @param {Roo.form.FieldLabel} this
29927              * @param {String} msg The validation message
29928              */
29929             invalid : true,
29930             /**
29931              * @event valid
29932              * Fires after the field has been validated with no errors.
29933              * @param {Roo.form.FieldLabel} this
29934              */
29935             valid : true
29936         });
29937 };
29938
29939 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29940     
29941     tag: 'label',
29942     cls: '',
29943     html: '',
29944     target: '',
29945     allowBlank : true,
29946     invalidClass : 'has-warning',
29947     validClass : 'has-success',
29948     iconTooltip : 'This field is required',
29949     indicatorpos : 'left',
29950     
29951     getAutoCreate : function(){
29952         
29953         var cfg = {
29954             tag : this.tag,
29955             cls : 'roo-bootstrap-field-label ' + this.cls,
29956             for : this.target,
29957             cn : [
29958                 {
29959                     tag : 'i',
29960                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29961                     tooltip : this.iconTooltip
29962                 },
29963                 {
29964                     tag : 'span',
29965                     html : this.html
29966                 }
29967             ] 
29968         };
29969         
29970         if(this.indicatorpos == 'right'){
29971             var cfg = {
29972                 tag : this.tag,
29973                 cls : 'roo-bootstrap-field-label ' + this.cls,
29974                 for : this.target,
29975                 cn : [
29976                     {
29977                         tag : 'span',
29978                         html : this.html
29979                     },
29980                     {
29981                         tag : 'i',
29982                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29983                         tooltip : this.iconTooltip
29984                     }
29985                 ] 
29986             };
29987         }
29988         
29989         return cfg;
29990     },
29991     
29992     initEvents: function() 
29993     {
29994         Roo.bootstrap.Element.superclass.initEvents.call(this);
29995         
29996         this.indicator = this.indicatorEl();
29997         
29998         if(this.indicator){
29999             this.indicator.removeClass('visible');
30000             this.indicator.addClass('invisible');
30001         }
30002         
30003         Roo.bootstrap.FieldLabel.register(this);
30004     },
30005     
30006     indicatorEl : function()
30007     {
30008         var indicator = this.el.select('i.roo-required-indicator',true).first();
30009         
30010         if(!indicator){
30011             return false;
30012         }
30013         
30014         return indicator;
30015         
30016     },
30017     
30018     /**
30019      * Mark this field as valid
30020      */
30021     markValid : function()
30022     {
30023         if(this.indicator){
30024             this.indicator.removeClass('visible');
30025             this.indicator.addClass('invisible');
30026         }
30027         
30028         this.el.removeClass(this.invalidClass);
30029         
30030         this.el.addClass(this.validClass);
30031         
30032         this.fireEvent('valid', this);
30033     },
30034     
30035     /**
30036      * Mark this field as invalid
30037      * @param {String} msg The validation message
30038      */
30039     markInvalid : function(msg)
30040     {
30041         if(this.indicator){
30042             this.indicator.removeClass('invisible');
30043             this.indicator.addClass('visible');
30044         }
30045         
30046         this.el.removeClass(this.validClass);
30047         
30048         this.el.addClass(this.invalidClass);
30049         
30050         this.fireEvent('invalid', this, msg);
30051     }
30052     
30053    
30054 });
30055
30056 Roo.apply(Roo.bootstrap.FieldLabel, {
30057     
30058     groups: {},
30059     
30060      /**
30061     * register a FieldLabel Group
30062     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30063     */
30064     register : function(label)
30065     {
30066         if(this.groups.hasOwnProperty(label.target)){
30067             return;
30068         }
30069      
30070         this.groups[label.target] = label;
30071         
30072     },
30073     /**
30074     * fetch a FieldLabel Group based on the target
30075     * @param {string} target
30076     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30077     */
30078     get: function(target) {
30079         if (typeof(this.groups[target]) == 'undefined') {
30080             return false;
30081         }
30082         
30083         return this.groups[target] ;
30084     }
30085 });
30086
30087  
30088
30089  /*
30090  * - LGPL
30091  *
30092  * page DateSplitField.
30093  * 
30094  */
30095
30096
30097 /**
30098  * @class Roo.bootstrap.DateSplitField
30099  * @extends Roo.bootstrap.Component
30100  * Bootstrap DateSplitField class
30101  * @cfg {string} fieldLabel - the label associated
30102  * @cfg {Number} labelWidth set the width of label (0-12)
30103  * @cfg {String} labelAlign (top|left)
30104  * @cfg {Boolean} dayAllowBlank (true|false) default false
30105  * @cfg {Boolean} monthAllowBlank (true|false) default false
30106  * @cfg {Boolean} yearAllowBlank (true|false) default false
30107  * @cfg {string} dayPlaceholder 
30108  * @cfg {string} monthPlaceholder
30109  * @cfg {string} yearPlaceholder
30110  * @cfg {string} dayFormat default 'd'
30111  * @cfg {string} monthFormat default 'm'
30112  * @cfg {string} yearFormat default 'Y'
30113  * @cfg {Number} labellg set the width of label (1-12)
30114  * @cfg {Number} labelmd set the width of label (1-12)
30115  * @cfg {Number} labelsm set the width of label (1-12)
30116  * @cfg {Number} labelxs set the width of label (1-12)
30117
30118  *     
30119  * @constructor
30120  * Create a new DateSplitField
30121  * @param {Object} config The config object
30122  */
30123
30124 Roo.bootstrap.DateSplitField = function(config){
30125     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30126     
30127     this.addEvents({
30128         // raw events
30129          /**
30130          * @event years
30131          * getting the data of years
30132          * @param {Roo.bootstrap.DateSplitField} this
30133          * @param {Object} years
30134          */
30135         "years" : true,
30136         /**
30137          * @event days
30138          * getting the data of days
30139          * @param {Roo.bootstrap.DateSplitField} this
30140          * @param {Object} days
30141          */
30142         "days" : true,
30143         /**
30144          * @event invalid
30145          * Fires after the field has been marked as invalid.
30146          * @param {Roo.form.Field} this
30147          * @param {String} msg The validation message
30148          */
30149         invalid : true,
30150        /**
30151          * @event valid
30152          * Fires after the field has been validated with no errors.
30153          * @param {Roo.form.Field} this
30154          */
30155         valid : true
30156     });
30157 };
30158
30159 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30160     
30161     fieldLabel : '',
30162     labelAlign : 'top',
30163     labelWidth : 3,
30164     dayAllowBlank : false,
30165     monthAllowBlank : false,
30166     yearAllowBlank : false,
30167     dayPlaceholder : '',
30168     monthPlaceholder : '',
30169     yearPlaceholder : '',
30170     dayFormat : 'd',
30171     monthFormat : 'm',
30172     yearFormat : 'Y',
30173     isFormField : true,
30174     labellg : 0,
30175     labelmd : 0,
30176     labelsm : 0,
30177     labelxs : 0,
30178     
30179     getAutoCreate : function()
30180     {
30181         var cfg = {
30182             tag : 'div',
30183             cls : 'row roo-date-split-field-group',
30184             cn : [
30185                 {
30186                     tag : 'input',
30187                     type : 'hidden',
30188                     cls : 'form-hidden-field roo-date-split-field-group-value',
30189                     name : this.name
30190                 }
30191             ]
30192         };
30193         
30194         var labelCls = 'col-md-12';
30195         var contentCls = 'col-md-4';
30196         
30197         if(this.fieldLabel){
30198             
30199             var label = {
30200                 tag : 'div',
30201                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30202                 cn : [
30203                     {
30204                         tag : 'label',
30205                         html : this.fieldLabel
30206                     }
30207                 ]
30208             };
30209             
30210             if(this.labelAlign == 'left'){
30211             
30212                 if(this.labelWidth > 12){
30213                     label.style = "width: " + this.labelWidth + 'px';
30214                 }
30215
30216                 if(this.labelWidth < 13 && this.labelmd == 0){
30217                     this.labelmd = this.labelWidth;
30218                 }
30219
30220                 if(this.labellg > 0){
30221                     labelCls = ' col-lg-' + this.labellg;
30222                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30223                 }
30224
30225                 if(this.labelmd > 0){
30226                     labelCls = ' col-md-' + this.labelmd;
30227                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30228                 }
30229
30230                 if(this.labelsm > 0){
30231                     labelCls = ' col-sm-' + this.labelsm;
30232                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30233                 }
30234
30235                 if(this.labelxs > 0){
30236                     labelCls = ' col-xs-' + this.labelxs;
30237                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30238                 }
30239             }
30240             
30241             label.cls += ' ' + labelCls;
30242             
30243             cfg.cn.push(label);
30244         }
30245         
30246         Roo.each(['day', 'month', 'year'], function(t){
30247             cfg.cn.push({
30248                 tag : 'div',
30249                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30250             });
30251         }, this);
30252         
30253         return cfg;
30254     },
30255     
30256     inputEl: function ()
30257     {
30258         return this.el.select('.roo-date-split-field-group-value', true).first();
30259     },
30260     
30261     onRender : function(ct, position) 
30262     {
30263         var _this = this;
30264         
30265         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30266         
30267         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30268         
30269         this.dayField = new Roo.bootstrap.ComboBox({
30270             allowBlank : this.dayAllowBlank,
30271             alwaysQuery : true,
30272             displayField : 'value',
30273             editable : false,
30274             fieldLabel : '',
30275             forceSelection : true,
30276             mode : 'local',
30277             placeholder : this.dayPlaceholder,
30278             selectOnFocus : true,
30279             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30280             triggerAction : 'all',
30281             typeAhead : true,
30282             valueField : 'value',
30283             store : new Roo.data.SimpleStore({
30284                 data : (function() {    
30285                     var days = [];
30286                     _this.fireEvent('days', _this, days);
30287                     return days;
30288                 })(),
30289                 fields : [ 'value' ]
30290             }),
30291             listeners : {
30292                 select : function (_self, record, index)
30293                 {
30294                     _this.setValue(_this.getValue());
30295                 }
30296             }
30297         });
30298
30299         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30300         
30301         this.monthField = new Roo.bootstrap.MonthField({
30302             after : '<i class=\"fa fa-calendar\"></i>',
30303             allowBlank : this.monthAllowBlank,
30304             placeholder : this.monthPlaceholder,
30305             readOnly : true,
30306             listeners : {
30307                 render : function (_self)
30308                 {
30309                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30310                         e.preventDefault();
30311                         _self.focus();
30312                     });
30313                 },
30314                 select : function (_self, oldvalue, newvalue)
30315                 {
30316                     _this.setValue(_this.getValue());
30317                 }
30318             }
30319         });
30320         
30321         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30322         
30323         this.yearField = new Roo.bootstrap.ComboBox({
30324             allowBlank : this.yearAllowBlank,
30325             alwaysQuery : true,
30326             displayField : 'value',
30327             editable : false,
30328             fieldLabel : '',
30329             forceSelection : true,
30330             mode : 'local',
30331             placeholder : this.yearPlaceholder,
30332             selectOnFocus : true,
30333             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30334             triggerAction : 'all',
30335             typeAhead : true,
30336             valueField : 'value',
30337             store : new Roo.data.SimpleStore({
30338                 data : (function() {
30339                     var years = [];
30340                     _this.fireEvent('years', _this, years);
30341                     return years;
30342                 })(),
30343                 fields : [ 'value' ]
30344             }),
30345             listeners : {
30346                 select : function (_self, record, index)
30347                 {
30348                     _this.setValue(_this.getValue());
30349                 }
30350             }
30351         });
30352
30353         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30354     },
30355     
30356     setValue : function(v, format)
30357     {
30358         this.inputEl.dom.value = v;
30359         
30360         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30361         
30362         var d = Date.parseDate(v, f);
30363         
30364         if(!d){
30365             this.validate();
30366             return;
30367         }
30368         
30369         this.setDay(d.format(this.dayFormat));
30370         this.setMonth(d.format(this.monthFormat));
30371         this.setYear(d.format(this.yearFormat));
30372         
30373         this.validate();
30374         
30375         return;
30376     },
30377     
30378     setDay : function(v)
30379     {
30380         this.dayField.setValue(v);
30381         this.inputEl.dom.value = this.getValue();
30382         this.validate();
30383         return;
30384     },
30385     
30386     setMonth : function(v)
30387     {
30388         this.monthField.setValue(v, true);
30389         this.inputEl.dom.value = this.getValue();
30390         this.validate();
30391         return;
30392     },
30393     
30394     setYear : function(v)
30395     {
30396         this.yearField.setValue(v);
30397         this.inputEl.dom.value = this.getValue();
30398         this.validate();
30399         return;
30400     },
30401     
30402     getDay : function()
30403     {
30404         return this.dayField.getValue();
30405     },
30406     
30407     getMonth : function()
30408     {
30409         return this.monthField.getValue();
30410     },
30411     
30412     getYear : function()
30413     {
30414         return this.yearField.getValue();
30415     },
30416     
30417     getValue : function()
30418     {
30419         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30420         
30421         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30422         
30423         return date;
30424     },
30425     
30426     reset : function()
30427     {
30428         this.setDay('');
30429         this.setMonth('');
30430         this.setYear('');
30431         this.inputEl.dom.value = '';
30432         this.validate();
30433         return;
30434     },
30435     
30436     validate : function()
30437     {
30438         var d = this.dayField.validate();
30439         var m = this.monthField.validate();
30440         var y = this.yearField.validate();
30441         
30442         var valid = true;
30443         
30444         if(
30445                 (!this.dayAllowBlank && !d) ||
30446                 (!this.monthAllowBlank && !m) ||
30447                 (!this.yearAllowBlank && !y)
30448         ){
30449             valid = false;
30450         }
30451         
30452         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30453             return valid;
30454         }
30455         
30456         if(valid){
30457             this.markValid();
30458             return valid;
30459         }
30460         
30461         this.markInvalid();
30462         
30463         return valid;
30464     },
30465     
30466     markValid : function()
30467     {
30468         
30469         var label = this.el.select('label', true).first();
30470         var icon = this.el.select('i.fa-star', true).first();
30471
30472         if(label && icon){
30473             icon.remove();
30474         }
30475         
30476         this.fireEvent('valid', this);
30477     },
30478     
30479      /**
30480      * Mark this field as invalid
30481      * @param {String} msg The validation message
30482      */
30483     markInvalid : function(msg)
30484     {
30485         
30486         var label = this.el.select('label', true).first();
30487         var icon = this.el.select('i.fa-star', true).first();
30488
30489         if(label && !icon){
30490             this.el.select('.roo-date-split-field-label', true).createChild({
30491                 tag : 'i',
30492                 cls : 'text-danger fa fa-lg fa-star',
30493                 tooltip : 'This field is required',
30494                 style : 'margin-right:5px;'
30495             }, label, true);
30496         }
30497         
30498         this.fireEvent('invalid', this, msg);
30499     },
30500     
30501     clearInvalid : function()
30502     {
30503         var label = this.el.select('label', true).first();
30504         var icon = this.el.select('i.fa-star', true).first();
30505
30506         if(label && icon){
30507             icon.remove();
30508         }
30509         
30510         this.fireEvent('valid', this);
30511     },
30512     
30513     getName: function()
30514     {
30515         return this.name;
30516     }
30517     
30518 });
30519
30520  /**
30521  *
30522  * This is based on 
30523  * http://masonry.desandro.com
30524  *
30525  * The idea is to render all the bricks based on vertical width...
30526  *
30527  * The original code extends 'outlayer' - we might need to use that....
30528  * 
30529  */
30530
30531
30532 /**
30533  * @class Roo.bootstrap.LayoutMasonry
30534  * @extends Roo.bootstrap.Component
30535  * Bootstrap Layout Masonry class
30536  * 
30537  * @constructor
30538  * Create a new Element
30539  * @param {Object} config The config object
30540  */
30541
30542 Roo.bootstrap.LayoutMasonry = function(config){
30543     
30544     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30545     
30546     this.bricks = [];
30547     
30548     Roo.bootstrap.LayoutMasonry.register(this);
30549     
30550     this.addEvents({
30551         // raw events
30552         /**
30553          * @event layout
30554          * Fire after layout the items
30555          * @param {Roo.bootstrap.LayoutMasonry} this
30556          * @param {Roo.EventObject} e
30557          */
30558         "layout" : true
30559     });
30560     
30561 };
30562
30563 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30564     
30565     /**
30566      * @cfg {Boolean} isLayoutInstant = no animation?
30567      */   
30568     isLayoutInstant : false, // needed?
30569    
30570     /**
30571      * @cfg {Number} boxWidth  width of the columns
30572      */   
30573     boxWidth : 450,
30574     
30575       /**
30576      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30577      */   
30578     boxHeight : 0,
30579     
30580     /**
30581      * @cfg {Number} padWidth padding below box..
30582      */   
30583     padWidth : 10, 
30584     
30585     /**
30586      * @cfg {Number} gutter gutter width..
30587      */   
30588     gutter : 10,
30589     
30590      /**
30591      * @cfg {Number} maxCols maximum number of columns
30592      */   
30593     
30594     maxCols: 0,
30595     
30596     /**
30597      * @cfg {Boolean} isAutoInitial defalut true
30598      */   
30599     isAutoInitial : true, 
30600     
30601     containerWidth: 0,
30602     
30603     /**
30604      * @cfg {Boolean} isHorizontal defalut false
30605      */   
30606     isHorizontal : false, 
30607
30608     currentSize : null,
30609     
30610     tag: 'div',
30611     
30612     cls: '',
30613     
30614     bricks: null, //CompositeElement
30615     
30616     cols : 1,
30617     
30618     _isLayoutInited : false,
30619     
30620 //    isAlternative : false, // only use for vertical layout...
30621     
30622     /**
30623      * @cfg {Number} alternativePadWidth padding below box..
30624      */   
30625     alternativePadWidth : 50,
30626     
30627     selectedBrick : [],
30628     
30629     getAutoCreate : function(){
30630         
30631         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30632         
30633         var cfg = {
30634             tag: this.tag,
30635             cls: 'blog-masonary-wrapper ' + this.cls,
30636             cn : {
30637                 cls : 'mas-boxes masonary'
30638             }
30639         };
30640         
30641         return cfg;
30642     },
30643     
30644     getChildContainer: function( )
30645     {
30646         if (this.boxesEl) {
30647             return this.boxesEl;
30648         }
30649         
30650         this.boxesEl = this.el.select('.mas-boxes').first();
30651         
30652         return this.boxesEl;
30653     },
30654     
30655     
30656     initEvents : function()
30657     {
30658         var _this = this;
30659         
30660         if(this.isAutoInitial){
30661             Roo.log('hook children rendered');
30662             this.on('childrenrendered', function() {
30663                 Roo.log('children rendered');
30664                 _this.initial();
30665             } ,this);
30666         }
30667     },
30668     
30669     initial : function()
30670     {
30671         this.selectedBrick = [];
30672         
30673         this.currentSize = this.el.getBox(true);
30674         
30675         Roo.EventManager.onWindowResize(this.resize, this); 
30676
30677         if(!this.isAutoInitial){
30678             this.layout();
30679             return;
30680         }
30681         
30682         this.layout();
30683         
30684         return;
30685         //this.layout.defer(500,this);
30686         
30687     },
30688     
30689     resize : function()
30690     {
30691         var cs = this.el.getBox(true);
30692         
30693         if (
30694                 this.currentSize.width == cs.width && 
30695                 this.currentSize.x == cs.x && 
30696                 this.currentSize.height == cs.height && 
30697                 this.currentSize.y == cs.y 
30698         ) {
30699             Roo.log("no change in with or X or Y");
30700             return;
30701         }
30702         
30703         this.currentSize = cs;
30704         
30705         this.layout();
30706         
30707     },
30708     
30709     layout : function()
30710     {   
30711         this._resetLayout();
30712         
30713         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30714         
30715         this.layoutItems( isInstant );
30716       
30717         this._isLayoutInited = true;
30718         
30719         this.fireEvent('layout', this);
30720         
30721     },
30722     
30723     _resetLayout : function()
30724     {
30725         if(this.isHorizontal){
30726             this.horizontalMeasureColumns();
30727             return;
30728         }
30729         
30730         this.verticalMeasureColumns();
30731         
30732     },
30733     
30734     verticalMeasureColumns : function()
30735     {
30736         this.getContainerWidth();
30737         
30738 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30739 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30740 //            return;
30741 //        }
30742         
30743         var boxWidth = this.boxWidth + this.padWidth;
30744         
30745         if(this.containerWidth < this.boxWidth){
30746             boxWidth = this.containerWidth
30747         }
30748         
30749         var containerWidth = this.containerWidth;
30750         
30751         var cols = Math.floor(containerWidth / boxWidth);
30752         
30753         this.cols = Math.max( cols, 1 );
30754         
30755         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30756         
30757         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30758         
30759         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30760         
30761         this.colWidth = boxWidth + avail - this.padWidth;
30762         
30763         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30764         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30765     },
30766     
30767     horizontalMeasureColumns : function()
30768     {
30769         this.getContainerWidth();
30770         
30771         var boxWidth = this.boxWidth;
30772         
30773         if(this.containerWidth < boxWidth){
30774             boxWidth = this.containerWidth;
30775         }
30776         
30777         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30778         
30779         this.el.setHeight(boxWidth);
30780         
30781     },
30782     
30783     getContainerWidth : function()
30784     {
30785         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30786     },
30787     
30788     layoutItems : function( isInstant )
30789     {
30790         Roo.log(this.bricks);
30791         
30792         var items = Roo.apply([], this.bricks);
30793         
30794         if(this.isHorizontal){
30795             this._horizontalLayoutItems( items , isInstant );
30796             return;
30797         }
30798         
30799 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30800 //            this._verticalAlternativeLayoutItems( items , isInstant );
30801 //            return;
30802 //        }
30803         
30804         this._verticalLayoutItems( items , isInstant );
30805         
30806     },
30807     
30808     _verticalLayoutItems : function ( items , isInstant)
30809     {
30810         if ( !items || !items.length ) {
30811             return;
30812         }
30813         
30814         var standard = [
30815             ['xs', 'xs', 'xs', 'tall'],
30816             ['xs', 'xs', 'tall'],
30817             ['xs', 'xs', 'sm'],
30818             ['xs', 'xs', 'xs'],
30819             ['xs', 'tall'],
30820             ['xs', 'sm'],
30821             ['xs', 'xs'],
30822             ['xs'],
30823             
30824             ['sm', 'xs', 'xs'],
30825             ['sm', 'xs'],
30826             ['sm'],
30827             
30828             ['tall', 'xs', 'xs', 'xs'],
30829             ['tall', 'xs', 'xs'],
30830             ['tall', 'xs'],
30831             ['tall']
30832             
30833         ];
30834         
30835         var queue = [];
30836         
30837         var boxes = [];
30838         
30839         var box = [];
30840         
30841         Roo.each(items, function(item, k){
30842             
30843             switch (item.size) {
30844                 // these layouts take up a full box,
30845                 case 'md' :
30846                 case 'md-left' :
30847                 case 'md-right' :
30848                 case 'wide' :
30849                     
30850                     if(box.length){
30851                         boxes.push(box);
30852                         box = [];
30853                     }
30854                     
30855                     boxes.push([item]);
30856                     
30857                     break;
30858                     
30859                 case 'xs' :
30860                 case 'sm' :
30861                 case 'tall' :
30862                     
30863                     box.push(item);
30864                     
30865                     break;
30866                 default :
30867                     break;
30868                     
30869             }
30870             
30871         }, this);
30872         
30873         if(box.length){
30874             boxes.push(box);
30875             box = [];
30876         }
30877         
30878         var filterPattern = function(box, length)
30879         {
30880             if(!box.length){
30881                 return;
30882             }
30883             
30884             var match = false;
30885             
30886             var pattern = box.slice(0, length);
30887             
30888             var format = [];
30889             
30890             Roo.each(pattern, function(i){
30891                 format.push(i.size);
30892             }, this);
30893             
30894             Roo.each(standard, function(s){
30895                 
30896                 if(String(s) != String(format)){
30897                     return;
30898                 }
30899                 
30900                 match = true;
30901                 return false;
30902                 
30903             }, this);
30904             
30905             if(!match && length == 1){
30906                 return;
30907             }
30908             
30909             if(!match){
30910                 filterPattern(box, length - 1);
30911                 return;
30912             }
30913                 
30914             queue.push(pattern);
30915
30916             box = box.slice(length, box.length);
30917
30918             filterPattern(box, 4);
30919
30920             return;
30921             
30922         }
30923         
30924         Roo.each(boxes, function(box, k){
30925             
30926             if(!box.length){
30927                 return;
30928             }
30929             
30930             if(box.length == 1){
30931                 queue.push(box);
30932                 return;
30933             }
30934             
30935             filterPattern(box, 4);
30936             
30937         }, this);
30938         
30939         this._processVerticalLayoutQueue( queue, isInstant );
30940         
30941     },
30942     
30943 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30944 //    {
30945 //        if ( !items || !items.length ) {
30946 //            return;
30947 //        }
30948 //
30949 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30950 //        
30951 //    },
30952     
30953     _horizontalLayoutItems : function ( items , isInstant)
30954     {
30955         if ( !items || !items.length || items.length < 3) {
30956             return;
30957         }
30958         
30959         items.reverse();
30960         
30961         var eItems = items.slice(0, 3);
30962         
30963         items = items.slice(3, items.length);
30964         
30965         var standard = [
30966             ['xs', 'xs', 'xs', 'wide'],
30967             ['xs', 'xs', 'wide'],
30968             ['xs', 'xs', 'sm'],
30969             ['xs', 'xs', 'xs'],
30970             ['xs', 'wide'],
30971             ['xs', 'sm'],
30972             ['xs', 'xs'],
30973             ['xs'],
30974             
30975             ['sm', 'xs', 'xs'],
30976             ['sm', 'xs'],
30977             ['sm'],
30978             
30979             ['wide', 'xs', 'xs', 'xs'],
30980             ['wide', 'xs', 'xs'],
30981             ['wide', 'xs'],
30982             ['wide'],
30983             
30984             ['wide-thin']
30985         ];
30986         
30987         var queue = [];
30988         
30989         var boxes = [];
30990         
30991         var box = [];
30992         
30993         Roo.each(items, function(item, k){
30994             
30995             switch (item.size) {
30996                 case 'md' :
30997                 case 'md-left' :
30998                 case 'md-right' :
30999                 case 'tall' :
31000                     
31001                     if(box.length){
31002                         boxes.push(box);
31003                         box = [];
31004                     }
31005                     
31006                     boxes.push([item]);
31007                     
31008                     break;
31009                     
31010                 case 'xs' :
31011                 case 'sm' :
31012                 case 'wide' :
31013                 case 'wide-thin' :
31014                     
31015                     box.push(item);
31016                     
31017                     break;
31018                 default :
31019                     break;
31020                     
31021             }
31022             
31023         }, this);
31024         
31025         if(box.length){
31026             boxes.push(box);
31027             box = [];
31028         }
31029         
31030         var filterPattern = function(box, length)
31031         {
31032             if(!box.length){
31033                 return;
31034             }
31035             
31036             var match = false;
31037             
31038             var pattern = box.slice(0, length);
31039             
31040             var format = [];
31041             
31042             Roo.each(pattern, function(i){
31043                 format.push(i.size);
31044             }, this);
31045             
31046             Roo.each(standard, function(s){
31047                 
31048                 if(String(s) != String(format)){
31049                     return;
31050                 }
31051                 
31052                 match = true;
31053                 return false;
31054                 
31055             }, this);
31056             
31057             if(!match && length == 1){
31058                 return;
31059             }
31060             
31061             if(!match){
31062                 filterPattern(box, length - 1);
31063                 return;
31064             }
31065                 
31066             queue.push(pattern);
31067
31068             box = box.slice(length, box.length);
31069
31070             filterPattern(box, 4);
31071
31072             return;
31073             
31074         }
31075         
31076         Roo.each(boxes, function(box, k){
31077             
31078             if(!box.length){
31079                 return;
31080             }
31081             
31082             if(box.length == 1){
31083                 queue.push(box);
31084                 return;
31085             }
31086             
31087             filterPattern(box, 4);
31088             
31089         }, this);
31090         
31091         
31092         var prune = [];
31093         
31094         var pos = this.el.getBox(true);
31095         
31096         var minX = pos.x;
31097         
31098         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31099         
31100         var hit_end = false;
31101         
31102         Roo.each(queue, function(box){
31103             
31104             if(hit_end){
31105                 
31106                 Roo.each(box, function(b){
31107                 
31108                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31109                     b.el.hide();
31110
31111                 }, this);
31112
31113                 return;
31114             }
31115             
31116             var mx = 0;
31117             
31118             Roo.each(box, function(b){
31119                 
31120                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31121                 b.el.show();
31122
31123                 mx = Math.max(mx, b.x);
31124                 
31125             }, this);
31126             
31127             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31128             
31129             if(maxX < minX){
31130                 
31131                 Roo.each(box, function(b){
31132                 
31133                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31134                     b.el.hide();
31135                     
31136                 }, this);
31137                 
31138                 hit_end = true;
31139                 
31140                 return;
31141             }
31142             
31143             prune.push(box);
31144             
31145         }, this);
31146         
31147         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31148     },
31149     
31150     /** Sets position of item in DOM
31151     * @param {Element} item
31152     * @param {Number} x - horizontal position
31153     * @param {Number} y - vertical position
31154     * @param {Boolean} isInstant - disables transitions
31155     */
31156     _processVerticalLayoutQueue : function( queue, isInstant )
31157     {
31158         var pos = this.el.getBox(true);
31159         var x = pos.x;
31160         var y = pos.y;
31161         var maxY = [];
31162         
31163         for (var i = 0; i < this.cols; i++){
31164             maxY[i] = pos.y;
31165         }
31166         
31167         Roo.each(queue, function(box, k){
31168             
31169             var col = k % this.cols;
31170             
31171             Roo.each(box, function(b,kk){
31172                 
31173                 b.el.position('absolute');
31174                 
31175                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31176                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31177                 
31178                 if(b.size == 'md-left' || b.size == 'md-right'){
31179                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31180                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31181                 }
31182                 
31183                 b.el.setWidth(width);
31184                 b.el.setHeight(height);
31185                 // iframe?
31186                 b.el.select('iframe',true).setSize(width,height);
31187                 
31188             }, this);
31189             
31190             for (var i = 0; i < this.cols; i++){
31191                 
31192                 if(maxY[i] < maxY[col]){
31193                     col = i;
31194                     continue;
31195                 }
31196                 
31197                 col = Math.min(col, i);
31198                 
31199             }
31200             
31201             x = pos.x + col * (this.colWidth + this.padWidth);
31202             
31203             y = maxY[col];
31204             
31205             var positions = [];
31206             
31207             switch (box.length){
31208                 case 1 :
31209                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31210                     break;
31211                 case 2 :
31212                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31213                     break;
31214                 case 3 :
31215                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31216                     break;
31217                 case 4 :
31218                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31219                     break;
31220                 default :
31221                     break;
31222             }
31223             
31224             Roo.each(box, function(b,kk){
31225                 
31226                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31227                 
31228                 var sz = b.el.getSize();
31229                 
31230                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31231                 
31232             }, this);
31233             
31234         }, this);
31235         
31236         var mY = 0;
31237         
31238         for (var i = 0; i < this.cols; i++){
31239             mY = Math.max(mY, maxY[i]);
31240         }
31241         
31242         this.el.setHeight(mY - pos.y);
31243         
31244     },
31245     
31246 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31247 //    {
31248 //        var pos = this.el.getBox(true);
31249 //        var x = pos.x;
31250 //        var y = pos.y;
31251 //        var maxX = pos.right;
31252 //        
31253 //        var maxHeight = 0;
31254 //        
31255 //        Roo.each(items, function(item, k){
31256 //            
31257 //            var c = k % 2;
31258 //            
31259 //            item.el.position('absolute');
31260 //                
31261 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31262 //
31263 //            item.el.setWidth(width);
31264 //
31265 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31266 //
31267 //            item.el.setHeight(height);
31268 //            
31269 //            if(c == 0){
31270 //                item.el.setXY([x, y], isInstant ? false : true);
31271 //            } else {
31272 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31273 //            }
31274 //            
31275 //            y = y + height + this.alternativePadWidth;
31276 //            
31277 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31278 //            
31279 //        }, this);
31280 //        
31281 //        this.el.setHeight(maxHeight);
31282 //        
31283 //    },
31284     
31285     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31286     {
31287         var pos = this.el.getBox(true);
31288         
31289         var minX = pos.x;
31290         var minY = pos.y;
31291         
31292         var maxX = pos.right;
31293         
31294         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31295         
31296         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31297         
31298         Roo.each(queue, function(box, k){
31299             
31300             Roo.each(box, function(b, kk){
31301                 
31302                 b.el.position('absolute');
31303                 
31304                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31305                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31306                 
31307                 if(b.size == 'md-left' || b.size == 'md-right'){
31308                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31309                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31310                 }
31311                 
31312                 b.el.setWidth(width);
31313                 b.el.setHeight(height);
31314                 
31315             }, this);
31316             
31317             if(!box.length){
31318                 return;
31319             }
31320             
31321             var positions = [];
31322             
31323             switch (box.length){
31324                 case 1 :
31325                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31326                     break;
31327                 case 2 :
31328                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31329                     break;
31330                 case 3 :
31331                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31332                     break;
31333                 case 4 :
31334                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31335                     break;
31336                 default :
31337                     break;
31338             }
31339             
31340             Roo.each(box, function(b,kk){
31341                 
31342                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31343                 
31344                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31345                 
31346             }, this);
31347             
31348         }, this);
31349         
31350     },
31351     
31352     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31353     {
31354         Roo.each(eItems, function(b,k){
31355             
31356             b.size = (k == 0) ? 'sm' : 'xs';
31357             b.x = (k == 0) ? 2 : 1;
31358             b.y = (k == 0) ? 2 : 1;
31359             
31360             b.el.position('absolute');
31361             
31362             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31363                 
31364             b.el.setWidth(width);
31365             
31366             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31367             
31368             b.el.setHeight(height);
31369             
31370         }, this);
31371
31372         var positions = [];
31373         
31374         positions.push({
31375             x : maxX - this.unitWidth * 2 - this.gutter,
31376             y : minY
31377         });
31378         
31379         positions.push({
31380             x : maxX - this.unitWidth,
31381             y : minY + (this.unitWidth + this.gutter) * 2
31382         });
31383         
31384         positions.push({
31385             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31386             y : minY
31387         });
31388         
31389         Roo.each(eItems, function(b,k){
31390             
31391             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31392
31393         }, this);
31394         
31395     },
31396     
31397     getVerticalOneBoxColPositions : function(x, y, box)
31398     {
31399         var pos = [];
31400         
31401         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31402         
31403         if(box[0].size == 'md-left'){
31404             rand = 0;
31405         }
31406         
31407         if(box[0].size == 'md-right'){
31408             rand = 1;
31409         }
31410         
31411         pos.push({
31412             x : x + (this.unitWidth + this.gutter) * rand,
31413             y : y
31414         });
31415         
31416         return pos;
31417     },
31418     
31419     getVerticalTwoBoxColPositions : function(x, y, box)
31420     {
31421         var pos = [];
31422         
31423         if(box[0].size == 'xs'){
31424             
31425             pos.push({
31426                 x : x,
31427                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31428             });
31429
31430             pos.push({
31431                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31432                 y : y
31433             });
31434             
31435             return pos;
31436             
31437         }
31438         
31439         pos.push({
31440             x : x,
31441             y : y
31442         });
31443
31444         pos.push({
31445             x : x + (this.unitWidth + this.gutter) * 2,
31446             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31447         });
31448         
31449         return pos;
31450         
31451     },
31452     
31453     getVerticalThreeBoxColPositions : function(x, y, box)
31454     {
31455         var pos = [];
31456         
31457         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31458             
31459             pos.push({
31460                 x : x,
31461                 y : y
31462             });
31463
31464             pos.push({
31465                 x : x + (this.unitWidth + this.gutter) * 1,
31466                 y : y
31467             });
31468             
31469             pos.push({
31470                 x : x + (this.unitWidth + this.gutter) * 2,
31471                 y : y
31472             });
31473             
31474             return pos;
31475             
31476         }
31477         
31478         if(box[0].size == 'xs' && box[1].size == 'xs'){
31479             
31480             pos.push({
31481                 x : x,
31482                 y : y
31483             });
31484
31485             pos.push({
31486                 x : x,
31487                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31488             });
31489             
31490             pos.push({
31491                 x : x + (this.unitWidth + this.gutter) * 1,
31492                 y : y
31493             });
31494             
31495             return pos;
31496             
31497         }
31498         
31499         pos.push({
31500             x : x,
31501             y : y
31502         });
31503
31504         pos.push({
31505             x : x + (this.unitWidth + this.gutter) * 2,
31506             y : y
31507         });
31508
31509         pos.push({
31510             x : x + (this.unitWidth + this.gutter) * 2,
31511             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31512         });
31513             
31514         return pos;
31515         
31516     },
31517     
31518     getVerticalFourBoxColPositions : function(x, y, box)
31519     {
31520         var pos = [];
31521         
31522         if(box[0].size == 'xs'){
31523             
31524             pos.push({
31525                 x : x,
31526                 y : y
31527             });
31528
31529             pos.push({
31530                 x : x,
31531                 y : y + (this.unitHeight + this.gutter) * 1
31532             });
31533             
31534             pos.push({
31535                 x : x,
31536                 y : y + (this.unitHeight + this.gutter) * 2
31537             });
31538             
31539             pos.push({
31540                 x : x + (this.unitWidth + this.gutter) * 1,
31541                 y : y
31542             });
31543             
31544             return pos;
31545             
31546         }
31547         
31548         pos.push({
31549             x : x,
31550             y : y
31551         });
31552
31553         pos.push({
31554             x : x + (this.unitWidth + this.gutter) * 2,
31555             y : y
31556         });
31557
31558         pos.push({
31559             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31560             y : y + (this.unitHeight + this.gutter) * 1
31561         });
31562
31563         pos.push({
31564             x : x + (this.unitWidth + this.gutter) * 2,
31565             y : y + (this.unitWidth + this.gutter) * 2
31566         });
31567
31568         return pos;
31569         
31570     },
31571     
31572     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31573     {
31574         var pos = [];
31575         
31576         if(box[0].size == 'md-left'){
31577             pos.push({
31578                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31579                 y : minY
31580             });
31581             
31582             return pos;
31583         }
31584         
31585         if(box[0].size == 'md-right'){
31586             pos.push({
31587                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31588                 y : minY + (this.unitWidth + this.gutter) * 1
31589             });
31590             
31591             return pos;
31592         }
31593         
31594         var rand = Math.floor(Math.random() * (4 - box[0].y));
31595         
31596         pos.push({
31597             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31598             y : minY + (this.unitWidth + this.gutter) * rand
31599         });
31600         
31601         return pos;
31602         
31603     },
31604     
31605     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31606     {
31607         var pos = [];
31608         
31609         if(box[0].size == 'xs'){
31610             
31611             pos.push({
31612                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31613                 y : minY
31614             });
31615
31616             pos.push({
31617                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31618                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31619             });
31620             
31621             return pos;
31622             
31623         }
31624         
31625         pos.push({
31626             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31627             y : minY
31628         });
31629
31630         pos.push({
31631             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31632             y : minY + (this.unitWidth + this.gutter) * 2
31633         });
31634         
31635         return pos;
31636         
31637     },
31638     
31639     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31640     {
31641         var pos = [];
31642         
31643         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31644             
31645             pos.push({
31646                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31647                 y : minY
31648             });
31649
31650             pos.push({
31651                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31652                 y : minY + (this.unitWidth + this.gutter) * 1
31653             });
31654             
31655             pos.push({
31656                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31657                 y : minY + (this.unitWidth + this.gutter) * 2
31658             });
31659             
31660             return pos;
31661             
31662         }
31663         
31664         if(box[0].size == 'xs' && box[1].size == 'xs'){
31665             
31666             pos.push({
31667                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31668                 y : minY
31669             });
31670
31671             pos.push({
31672                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31673                 y : minY
31674             });
31675             
31676             pos.push({
31677                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31678                 y : minY + (this.unitWidth + this.gutter) * 1
31679             });
31680             
31681             return pos;
31682             
31683         }
31684         
31685         pos.push({
31686             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31687             y : minY
31688         });
31689
31690         pos.push({
31691             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31692             y : minY + (this.unitWidth + this.gutter) * 2
31693         });
31694
31695         pos.push({
31696             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31697             y : minY + (this.unitWidth + this.gutter) * 2
31698         });
31699             
31700         return pos;
31701         
31702     },
31703     
31704     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31705     {
31706         var pos = [];
31707         
31708         if(box[0].size == 'xs'){
31709             
31710             pos.push({
31711                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31712                 y : minY
31713             });
31714
31715             pos.push({
31716                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31717                 y : minY
31718             });
31719             
31720             pos.push({
31721                 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),
31722                 y : minY
31723             });
31724             
31725             pos.push({
31726                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31727                 y : minY + (this.unitWidth + this.gutter) * 1
31728             });
31729             
31730             return pos;
31731             
31732         }
31733         
31734         pos.push({
31735             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31736             y : minY
31737         });
31738         
31739         pos.push({
31740             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31741             y : minY + (this.unitWidth + this.gutter) * 2
31742         });
31743         
31744         pos.push({
31745             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31746             y : minY + (this.unitWidth + this.gutter) * 2
31747         });
31748         
31749         pos.push({
31750             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),
31751             y : minY + (this.unitWidth + this.gutter) * 2
31752         });
31753
31754         return pos;
31755         
31756     },
31757     
31758     /**
31759     * remove a Masonry Brick
31760     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31761     */
31762     removeBrick : function(brick_id)
31763     {
31764         if (!brick_id) {
31765             return;
31766         }
31767         
31768         for (var i = 0; i<this.bricks.length; i++) {
31769             if (this.bricks[i].id == brick_id) {
31770                 this.bricks.splice(i,1);
31771                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31772                 this.initial();
31773             }
31774         }
31775     },
31776     
31777     /**
31778     * adds a Masonry Brick
31779     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31780     */
31781     addBrick : function(cfg)
31782     {
31783         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31784         //this.register(cn);
31785         cn.parentId = this.id;
31786         cn.onRender(this.el, null);
31787         return cn;
31788     },
31789     
31790     /**
31791     * register a Masonry Brick
31792     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31793     */
31794     
31795     register : function(brick)
31796     {
31797         this.bricks.push(brick);
31798         brick.masonryId = this.id;
31799     },
31800     
31801     /**
31802     * clear all the Masonry Brick
31803     */
31804     clearAll : function()
31805     {
31806         this.bricks = [];
31807         //this.getChildContainer().dom.innerHTML = "";
31808         this.el.dom.innerHTML = '';
31809     },
31810     
31811     getSelected : function()
31812     {
31813         if (!this.selectedBrick) {
31814             return false;
31815         }
31816         
31817         return this.selectedBrick;
31818     }
31819 });
31820
31821 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31822     
31823     groups: {},
31824      /**
31825     * register a Masonry Layout
31826     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31827     */
31828     
31829     register : function(layout)
31830     {
31831         this.groups[layout.id] = layout;
31832     },
31833     /**
31834     * fetch a  Masonry Layout based on the masonry layout ID
31835     * @param {string} the masonry layout to add
31836     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31837     */
31838     
31839     get: function(layout_id) {
31840         if (typeof(this.groups[layout_id]) == 'undefined') {
31841             return false;
31842         }
31843         return this.groups[layout_id] ;
31844     }
31845     
31846     
31847     
31848 });
31849
31850  
31851
31852  /**
31853  *
31854  * This is based on 
31855  * http://masonry.desandro.com
31856  *
31857  * The idea is to render all the bricks based on vertical width...
31858  *
31859  * The original code extends 'outlayer' - we might need to use that....
31860  * 
31861  */
31862
31863
31864 /**
31865  * @class Roo.bootstrap.LayoutMasonryAuto
31866  * @extends Roo.bootstrap.Component
31867  * Bootstrap Layout Masonry class
31868  * 
31869  * @constructor
31870  * Create a new Element
31871  * @param {Object} config The config object
31872  */
31873
31874 Roo.bootstrap.LayoutMasonryAuto = function(config){
31875     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31876 };
31877
31878 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31879     
31880       /**
31881      * @cfg {Boolean} isFitWidth  - resize the width..
31882      */   
31883     isFitWidth : false,  // options..
31884     /**
31885      * @cfg {Boolean} isOriginLeft = left align?
31886      */   
31887     isOriginLeft : true,
31888     /**
31889      * @cfg {Boolean} isOriginTop = top align?
31890      */   
31891     isOriginTop : false,
31892     /**
31893      * @cfg {Boolean} isLayoutInstant = no animation?
31894      */   
31895     isLayoutInstant : false, // needed?
31896     /**
31897      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31898      */   
31899     isResizingContainer : true,
31900     /**
31901      * @cfg {Number} columnWidth  width of the columns 
31902      */   
31903     
31904     columnWidth : 0,
31905     
31906     /**
31907      * @cfg {Number} maxCols maximum number of columns
31908      */   
31909     
31910     maxCols: 0,
31911     /**
31912      * @cfg {Number} padHeight padding below box..
31913      */   
31914     
31915     padHeight : 10, 
31916     
31917     /**
31918      * @cfg {Boolean} isAutoInitial defalut true
31919      */   
31920     
31921     isAutoInitial : true, 
31922     
31923     // private?
31924     gutter : 0,
31925     
31926     containerWidth: 0,
31927     initialColumnWidth : 0,
31928     currentSize : null,
31929     
31930     colYs : null, // array.
31931     maxY : 0,
31932     padWidth: 10,
31933     
31934     
31935     tag: 'div',
31936     cls: '',
31937     bricks: null, //CompositeElement
31938     cols : 0, // array?
31939     // element : null, // wrapped now this.el
31940     _isLayoutInited : null, 
31941     
31942     
31943     getAutoCreate : function(){
31944         
31945         var cfg = {
31946             tag: this.tag,
31947             cls: 'blog-masonary-wrapper ' + this.cls,
31948             cn : {
31949                 cls : 'mas-boxes masonary'
31950             }
31951         };
31952         
31953         return cfg;
31954     },
31955     
31956     getChildContainer: function( )
31957     {
31958         if (this.boxesEl) {
31959             return this.boxesEl;
31960         }
31961         
31962         this.boxesEl = this.el.select('.mas-boxes').first();
31963         
31964         return this.boxesEl;
31965     },
31966     
31967     
31968     initEvents : function()
31969     {
31970         var _this = this;
31971         
31972         if(this.isAutoInitial){
31973             Roo.log('hook children rendered');
31974             this.on('childrenrendered', function() {
31975                 Roo.log('children rendered');
31976                 _this.initial();
31977             } ,this);
31978         }
31979         
31980     },
31981     
31982     initial : function()
31983     {
31984         this.reloadItems();
31985
31986         this.currentSize = this.el.getBox(true);
31987
31988         /// was window resize... - let's see if this works..
31989         Roo.EventManager.onWindowResize(this.resize, this); 
31990
31991         if(!this.isAutoInitial){
31992             this.layout();
31993             return;
31994         }
31995         
31996         this.layout.defer(500,this);
31997     },
31998     
31999     reloadItems: function()
32000     {
32001         this.bricks = this.el.select('.masonry-brick', true);
32002         
32003         this.bricks.each(function(b) {
32004             //Roo.log(b.getSize());
32005             if (!b.attr('originalwidth')) {
32006                 b.attr('originalwidth',  b.getSize().width);
32007             }
32008             
32009         });
32010         
32011         Roo.log(this.bricks.elements.length);
32012     },
32013     
32014     resize : function()
32015     {
32016         Roo.log('resize');
32017         var cs = this.el.getBox(true);
32018         
32019         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32020             Roo.log("no change in with or X");
32021             return;
32022         }
32023         this.currentSize = cs;
32024         this.layout();
32025     },
32026     
32027     layout : function()
32028     {
32029          Roo.log('layout');
32030         this._resetLayout();
32031         //this._manageStamps();
32032       
32033         // don't animate first layout
32034         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32035         this.layoutItems( isInstant );
32036       
32037         // flag for initalized
32038         this._isLayoutInited = true;
32039     },
32040     
32041     layoutItems : function( isInstant )
32042     {
32043         //var items = this._getItemsForLayout( this.items );
32044         // original code supports filtering layout items.. we just ignore it..
32045         
32046         this._layoutItems( this.bricks , isInstant );
32047       
32048         this._postLayout();
32049     },
32050     _layoutItems : function ( items , isInstant)
32051     {
32052        //this.fireEvent( 'layout', this, items );
32053     
32054
32055         if ( !items || !items.elements.length ) {
32056           // no items, emit event with empty array
32057             return;
32058         }
32059
32060         var queue = [];
32061         items.each(function(item) {
32062             Roo.log("layout item");
32063             Roo.log(item);
32064             // get x/y object from method
32065             var position = this._getItemLayoutPosition( item );
32066             // enqueue
32067             position.item = item;
32068             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32069             queue.push( position );
32070         }, this);
32071       
32072         this._processLayoutQueue( queue );
32073     },
32074     /** Sets position of item in DOM
32075     * @param {Element} item
32076     * @param {Number} x - horizontal position
32077     * @param {Number} y - vertical position
32078     * @param {Boolean} isInstant - disables transitions
32079     */
32080     _processLayoutQueue : function( queue )
32081     {
32082         for ( var i=0, len = queue.length; i < len; i++ ) {
32083             var obj = queue[i];
32084             obj.item.position('absolute');
32085             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32086         }
32087     },
32088       
32089     
32090     /**
32091     * Any logic you want to do after each layout,
32092     * i.e. size the container
32093     */
32094     _postLayout : function()
32095     {
32096         this.resizeContainer();
32097     },
32098     
32099     resizeContainer : function()
32100     {
32101         if ( !this.isResizingContainer ) {
32102             return;
32103         }
32104         var size = this._getContainerSize();
32105         if ( size ) {
32106             this.el.setSize(size.width,size.height);
32107             this.boxesEl.setSize(size.width,size.height);
32108         }
32109     },
32110     
32111     
32112     
32113     _resetLayout : function()
32114     {
32115         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32116         this.colWidth = this.el.getWidth();
32117         //this.gutter = this.el.getWidth(); 
32118         
32119         this.measureColumns();
32120
32121         // reset column Y
32122         var i = this.cols;
32123         this.colYs = [];
32124         while (i--) {
32125             this.colYs.push( 0 );
32126         }
32127     
32128         this.maxY = 0;
32129     },
32130
32131     measureColumns : function()
32132     {
32133         this.getContainerWidth();
32134       // if columnWidth is 0, default to outerWidth of first item
32135         if ( !this.columnWidth ) {
32136             var firstItem = this.bricks.first();
32137             Roo.log(firstItem);
32138             this.columnWidth  = this.containerWidth;
32139             if (firstItem && firstItem.attr('originalwidth') ) {
32140                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32141             }
32142             // columnWidth fall back to item of first element
32143             Roo.log("set column width?");
32144                         this.initialColumnWidth = this.columnWidth  ;
32145
32146             // if first elem has no width, default to size of container
32147             
32148         }
32149         
32150         
32151         if (this.initialColumnWidth) {
32152             this.columnWidth = this.initialColumnWidth;
32153         }
32154         
32155         
32156             
32157         // column width is fixed at the top - however if container width get's smaller we should
32158         // reduce it...
32159         
32160         // this bit calcs how man columns..
32161             
32162         var columnWidth = this.columnWidth += this.gutter;
32163       
32164         // calculate columns
32165         var containerWidth = this.containerWidth + this.gutter;
32166         
32167         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32168         // fix rounding errors, typically with gutters
32169         var excess = columnWidth - containerWidth % columnWidth;
32170         
32171         
32172         // if overshoot is less than a pixel, round up, otherwise floor it
32173         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32174         cols = Math[ mathMethod ]( cols );
32175         this.cols = Math.max( cols, 1 );
32176         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32177         
32178          // padding positioning..
32179         var totalColWidth = this.cols * this.columnWidth;
32180         var padavail = this.containerWidth - totalColWidth;
32181         // so for 2 columns - we need 3 'pads'
32182         
32183         var padNeeded = (1+this.cols) * this.padWidth;
32184         
32185         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32186         
32187         this.columnWidth += padExtra
32188         //this.padWidth = Math.floor(padavail /  ( this.cols));
32189         
32190         // adjust colum width so that padding is fixed??
32191         
32192         // we have 3 columns ... total = width * 3
32193         // we have X left over... that should be used by 
32194         
32195         //if (this.expandC) {
32196             
32197         //}
32198         
32199         
32200         
32201     },
32202     
32203     getContainerWidth : function()
32204     {
32205        /* // container is parent if fit width
32206         var container = this.isFitWidth ? this.element.parentNode : this.element;
32207         // check that this.size and size are there
32208         // IE8 triggers resize on body size change, so they might not be
32209         
32210         var size = getSize( container );  //FIXME
32211         this.containerWidth = size && size.innerWidth; //FIXME
32212         */
32213          
32214         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32215         
32216     },
32217     
32218     _getItemLayoutPosition : function( item )  // what is item?
32219     {
32220         // we resize the item to our columnWidth..
32221       
32222         item.setWidth(this.columnWidth);
32223         item.autoBoxAdjust  = false;
32224         
32225         var sz = item.getSize();
32226  
32227         // how many columns does this brick span
32228         var remainder = this.containerWidth % this.columnWidth;
32229         
32230         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32231         // round if off by 1 pixel, otherwise use ceil
32232         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32233         colSpan = Math.min( colSpan, this.cols );
32234         
32235         // normally this should be '1' as we dont' currently allow multi width columns..
32236         
32237         var colGroup = this._getColGroup( colSpan );
32238         // get the minimum Y value from the columns
32239         var minimumY = Math.min.apply( Math, colGroup );
32240         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32241         
32242         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32243          
32244         // position the brick
32245         var position = {
32246             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32247             y: this.currentSize.y + minimumY + this.padHeight
32248         };
32249         
32250         Roo.log(position);
32251         // apply setHeight to necessary columns
32252         var setHeight = minimumY + sz.height + this.padHeight;
32253         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32254         
32255         var setSpan = this.cols + 1 - colGroup.length;
32256         for ( var i = 0; i < setSpan; i++ ) {
32257           this.colYs[ shortColIndex + i ] = setHeight ;
32258         }
32259       
32260         return position;
32261     },
32262     
32263     /**
32264      * @param {Number} colSpan - number of columns the element spans
32265      * @returns {Array} colGroup
32266      */
32267     _getColGroup : function( colSpan )
32268     {
32269         if ( colSpan < 2 ) {
32270           // if brick spans only one column, use all the column Ys
32271           return this.colYs;
32272         }
32273       
32274         var colGroup = [];
32275         // how many different places could this brick fit horizontally
32276         var groupCount = this.cols + 1 - colSpan;
32277         // for each group potential horizontal position
32278         for ( var i = 0; i < groupCount; i++ ) {
32279           // make an array of colY values for that one group
32280           var groupColYs = this.colYs.slice( i, i + colSpan );
32281           // and get the max value of the array
32282           colGroup[i] = Math.max.apply( Math, groupColYs );
32283         }
32284         return colGroup;
32285     },
32286     /*
32287     _manageStamp : function( stamp )
32288     {
32289         var stampSize =  stamp.getSize();
32290         var offset = stamp.getBox();
32291         // get the columns that this stamp affects
32292         var firstX = this.isOriginLeft ? offset.x : offset.right;
32293         var lastX = firstX + stampSize.width;
32294         var firstCol = Math.floor( firstX / this.columnWidth );
32295         firstCol = Math.max( 0, firstCol );
32296         
32297         var lastCol = Math.floor( lastX / this.columnWidth );
32298         // lastCol should not go over if multiple of columnWidth #425
32299         lastCol -= lastX % this.columnWidth ? 0 : 1;
32300         lastCol = Math.min( this.cols - 1, lastCol );
32301         
32302         // set colYs to bottom of the stamp
32303         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32304             stampSize.height;
32305             
32306         for ( var i = firstCol; i <= lastCol; i++ ) {
32307           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32308         }
32309     },
32310     */
32311     
32312     _getContainerSize : function()
32313     {
32314         this.maxY = Math.max.apply( Math, this.colYs );
32315         var size = {
32316             height: this.maxY
32317         };
32318       
32319         if ( this.isFitWidth ) {
32320             size.width = this._getContainerFitWidth();
32321         }
32322       
32323         return size;
32324     },
32325     
32326     _getContainerFitWidth : function()
32327     {
32328         var unusedCols = 0;
32329         // count unused columns
32330         var i = this.cols;
32331         while ( --i ) {
32332           if ( this.colYs[i] !== 0 ) {
32333             break;
32334           }
32335           unusedCols++;
32336         }
32337         // fit container to columns that have been used
32338         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32339     },
32340     
32341     needsResizeLayout : function()
32342     {
32343         var previousWidth = this.containerWidth;
32344         this.getContainerWidth();
32345         return previousWidth !== this.containerWidth;
32346     }
32347  
32348 });
32349
32350  
32351
32352  /*
32353  * - LGPL
32354  *
32355  * element
32356  * 
32357  */
32358
32359 /**
32360  * @class Roo.bootstrap.MasonryBrick
32361  * @extends Roo.bootstrap.Component
32362  * Bootstrap MasonryBrick class
32363  * 
32364  * @constructor
32365  * Create a new MasonryBrick
32366  * @param {Object} config The config object
32367  */
32368
32369 Roo.bootstrap.MasonryBrick = function(config){
32370     
32371     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32372     
32373     Roo.bootstrap.MasonryBrick.register(this);
32374     
32375     this.addEvents({
32376         // raw events
32377         /**
32378          * @event click
32379          * When a MasonryBrick is clcik
32380          * @param {Roo.bootstrap.MasonryBrick} this
32381          * @param {Roo.EventObject} e
32382          */
32383         "click" : true
32384     });
32385 };
32386
32387 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32388     
32389     /**
32390      * @cfg {String} title
32391      */   
32392     title : '',
32393     /**
32394      * @cfg {String} html
32395      */   
32396     html : '',
32397     /**
32398      * @cfg {String} bgimage
32399      */   
32400     bgimage : '',
32401     /**
32402      * @cfg {String} videourl
32403      */   
32404     videourl : '',
32405     /**
32406      * @cfg {String} cls
32407      */   
32408     cls : '',
32409     /**
32410      * @cfg {String} href
32411      */   
32412     href : '',
32413     /**
32414      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32415      */   
32416     size : 'xs',
32417     
32418     /**
32419      * @cfg {String} placetitle (center|bottom)
32420      */   
32421     placetitle : '',
32422     
32423     /**
32424      * @cfg {Boolean} isFitContainer defalut true
32425      */   
32426     isFitContainer : true, 
32427     
32428     /**
32429      * @cfg {Boolean} preventDefault defalut false
32430      */   
32431     preventDefault : false, 
32432     
32433     /**
32434      * @cfg {Boolean} inverse defalut false
32435      */   
32436     maskInverse : false, 
32437     
32438     getAutoCreate : function()
32439     {
32440         if(!this.isFitContainer){
32441             return this.getSplitAutoCreate();
32442         }
32443         
32444         var cls = 'masonry-brick masonry-brick-full';
32445         
32446         if(this.href.length){
32447             cls += ' masonry-brick-link';
32448         }
32449         
32450         if(this.bgimage.length){
32451             cls += ' masonry-brick-image';
32452         }
32453         
32454         if(this.maskInverse){
32455             cls += ' mask-inverse';
32456         }
32457         
32458         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32459             cls += ' enable-mask';
32460         }
32461         
32462         if(this.size){
32463             cls += ' masonry-' + this.size + '-brick';
32464         }
32465         
32466         if(this.placetitle.length){
32467             
32468             switch (this.placetitle) {
32469                 case 'center' :
32470                     cls += ' masonry-center-title';
32471                     break;
32472                 case 'bottom' :
32473                     cls += ' masonry-bottom-title';
32474                     break;
32475                 default:
32476                     break;
32477             }
32478             
32479         } else {
32480             if(!this.html.length && !this.bgimage.length){
32481                 cls += ' masonry-center-title';
32482             }
32483
32484             if(!this.html.length && this.bgimage.length){
32485                 cls += ' masonry-bottom-title';
32486             }
32487         }
32488         
32489         if(this.cls){
32490             cls += ' ' + this.cls;
32491         }
32492         
32493         var cfg = {
32494             tag: (this.href.length) ? 'a' : 'div',
32495             cls: cls,
32496             cn: [
32497                 {
32498                     tag: 'div',
32499                     cls: 'masonry-brick-mask'
32500                 },
32501                 {
32502                     tag: 'div',
32503                     cls: 'masonry-brick-paragraph',
32504                     cn: []
32505                 }
32506             ]
32507         };
32508         
32509         if(this.href.length){
32510             cfg.href = this.href;
32511         }
32512         
32513         var cn = cfg.cn[1].cn;
32514         
32515         if(this.title.length){
32516             cn.push({
32517                 tag: 'h4',
32518                 cls: 'masonry-brick-title',
32519                 html: this.title
32520             });
32521         }
32522         
32523         if(this.html.length){
32524             cn.push({
32525                 tag: 'p',
32526                 cls: 'masonry-brick-text',
32527                 html: this.html
32528             });
32529         }
32530         
32531         if (!this.title.length && !this.html.length) {
32532             cfg.cn[1].cls += ' hide';
32533         }
32534         
32535         if(this.bgimage.length){
32536             cfg.cn.push({
32537                 tag: 'img',
32538                 cls: 'masonry-brick-image-view',
32539                 src: this.bgimage
32540             });
32541         }
32542         
32543         if(this.videourl.length){
32544             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32545             // youtube support only?
32546             cfg.cn.push({
32547                 tag: 'iframe',
32548                 cls: 'masonry-brick-image-view',
32549                 src: vurl,
32550                 frameborder : 0,
32551                 allowfullscreen : true
32552             });
32553         }
32554         
32555         return cfg;
32556         
32557     },
32558     
32559     getSplitAutoCreate : function()
32560     {
32561         var cls = 'masonry-brick masonry-brick-split';
32562         
32563         if(this.href.length){
32564             cls += ' masonry-brick-link';
32565         }
32566         
32567         if(this.bgimage.length){
32568             cls += ' masonry-brick-image';
32569         }
32570         
32571         if(this.size){
32572             cls += ' masonry-' + this.size + '-brick';
32573         }
32574         
32575         switch (this.placetitle) {
32576             case 'center' :
32577                 cls += ' masonry-center-title';
32578                 break;
32579             case 'bottom' :
32580                 cls += ' masonry-bottom-title';
32581                 break;
32582             default:
32583                 if(!this.bgimage.length){
32584                     cls += ' masonry-center-title';
32585                 }
32586
32587                 if(this.bgimage.length){
32588                     cls += ' masonry-bottom-title';
32589                 }
32590                 break;
32591         }
32592         
32593         if(this.cls){
32594             cls += ' ' + this.cls;
32595         }
32596         
32597         var cfg = {
32598             tag: (this.href.length) ? 'a' : 'div',
32599             cls: cls,
32600             cn: [
32601                 {
32602                     tag: 'div',
32603                     cls: 'masonry-brick-split-head',
32604                     cn: [
32605                         {
32606                             tag: 'div',
32607                             cls: 'masonry-brick-paragraph',
32608                             cn: []
32609                         }
32610                     ]
32611                 },
32612                 {
32613                     tag: 'div',
32614                     cls: 'masonry-brick-split-body',
32615                     cn: []
32616                 }
32617             ]
32618         };
32619         
32620         if(this.href.length){
32621             cfg.href = this.href;
32622         }
32623         
32624         if(this.title.length){
32625             cfg.cn[0].cn[0].cn.push({
32626                 tag: 'h4',
32627                 cls: 'masonry-brick-title',
32628                 html: this.title
32629             });
32630         }
32631         
32632         if(this.html.length){
32633             cfg.cn[1].cn.push({
32634                 tag: 'p',
32635                 cls: 'masonry-brick-text',
32636                 html: this.html
32637             });
32638         }
32639
32640         if(this.bgimage.length){
32641             cfg.cn[0].cn.push({
32642                 tag: 'img',
32643                 cls: 'masonry-brick-image-view',
32644                 src: this.bgimage
32645             });
32646         }
32647         
32648         if(this.videourl.length){
32649             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32650             // youtube support only?
32651             cfg.cn[0].cn.cn.push({
32652                 tag: 'iframe',
32653                 cls: 'masonry-brick-image-view',
32654                 src: vurl,
32655                 frameborder : 0,
32656                 allowfullscreen : true
32657             });
32658         }
32659         
32660         return cfg;
32661     },
32662     
32663     initEvents: function() 
32664     {
32665         switch (this.size) {
32666             case 'xs' :
32667                 this.x = 1;
32668                 this.y = 1;
32669                 break;
32670             case 'sm' :
32671                 this.x = 2;
32672                 this.y = 2;
32673                 break;
32674             case 'md' :
32675             case 'md-left' :
32676             case 'md-right' :
32677                 this.x = 3;
32678                 this.y = 3;
32679                 break;
32680             case 'tall' :
32681                 this.x = 2;
32682                 this.y = 3;
32683                 break;
32684             case 'wide' :
32685                 this.x = 3;
32686                 this.y = 2;
32687                 break;
32688             case 'wide-thin' :
32689                 this.x = 3;
32690                 this.y = 1;
32691                 break;
32692                         
32693             default :
32694                 break;
32695         }
32696         
32697         if(Roo.isTouch){
32698             this.el.on('touchstart', this.onTouchStart, this);
32699             this.el.on('touchmove', this.onTouchMove, this);
32700             this.el.on('touchend', this.onTouchEnd, this);
32701             this.el.on('contextmenu', this.onContextMenu, this);
32702         } else {
32703             this.el.on('mouseenter'  ,this.enter, this);
32704             this.el.on('mouseleave', this.leave, this);
32705             this.el.on('click', this.onClick, this);
32706         }
32707         
32708         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32709             this.parent().bricks.push(this);   
32710         }
32711         
32712     },
32713     
32714     onClick: function(e, el)
32715     {
32716         var time = this.endTimer - this.startTimer;
32717         // Roo.log(e.preventDefault());
32718         if(Roo.isTouch){
32719             if(time > 1000){
32720                 e.preventDefault();
32721                 return;
32722             }
32723         }
32724         
32725         if(!this.preventDefault){
32726             return;
32727         }
32728         
32729         e.preventDefault();
32730         
32731         if (this.activcClass != '') {
32732             this.selectBrick();
32733         }
32734         
32735         this.fireEvent('click', this);
32736     },
32737     
32738     enter: function(e, el)
32739     {
32740         e.preventDefault();
32741         
32742         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32743             return;
32744         }
32745         
32746         if(this.bgimage.length && this.html.length){
32747             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32748         }
32749     },
32750     
32751     leave: function(e, el)
32752     {
32753         e.preventDefault();
32754         
32755         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32756             return;
32757         }
32758         
32759         if(this.bgimage.length && this.html.length){
32760             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32761         }
32762     },
32763     
32764     onTouchStart: function(e, el)
32765     {
32766 //        e.preventDefault();
32767         
32768         this.touchmoved = false;
32769         
32770         if(!this.isFitContainer){
32771             return;
32772         }
32773         
32774         if(!this.bgimage.length || !this.html.length){
32775             return;
32776         }
32777         
32778         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32779         
32780         this.timer = new Date().getTime();
32781         
32782     },
32783     
32784     onTouchMove: function(e, el)
32785     {
32786         this.touchmoved = true;
32787     },
32788     
32789     onContextMenu : function(e,el)
32790     {
32791         e.preventDefault();
32792         e.stopPropagation();
32793         return false;
32794     },
32795     
32796     onTouchEnd: function(e, el)
32797     {
32798 //        e.preventDefault();
32799         
32800         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32801         
32802             this.leave(e,el);
32803             
32804             return;
32805         }
32806         
32807         if(!this.bgimage.length || !this.html.length){
32808             
32809             if(this.href.length){
32810                 window.location.href = this.href;
32811             }
32812             
32813             return;
32814         }
32815         
32816         if(!this.isFitContainer){
32817             return;
32818         }
32819         
32820         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32821         
32822         window.location.href = this.href;
32823     },
32824     
32825     //selection on single brick only
32826     selectBrick : function() {
32827         
32828         if (!this.parentId) {
32829             return;
32830         }
32831         
32832         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32833         var index = m.selectedBrick.indexOf(this.id);
32834         
32835         if ( index > -1) {
32836             m.selectedBrick.splice(index,1);
32837             this.el.removeClass(this.activeClass);
32838             return;
32839         }
32840         
32841         for(var i = 0; i < m.selectedBrick.length; i++) {
32842             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32843             b.el.removeClass(b.activeClass);
32844         }
32845         
32846         m.selectedBrick = [];
32847         
32848         m.selectedBrick.push(this.id);
32849         this.el.addClass(this.activeClass);
32850         return;
32851     }
32852     
32853 });
32854
32855 Roo.apply(Roo.bootstrap.MasonryBrick, {
32856     
32857     //groups: {},
32858     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32859      /**
32860     * register a Masonry Brick
32861     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32862     */
32863     
32864     register : function(brick)
32865     {
32866         //this.groups[brick.id] = brick;
32867         this.groups.add(brick.id, brick);
32868     },
32869     /**
32870     * fetch a  masonry brick based on the masonry brick ID
32871     * @param {string} the masonry brick to add
32872     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32873     */
32874     
32875     get: function(brick_id) 
32876     {
32877         // if (typeof(this.groups[brick_id]) == 'undefined') {
32878         //     return false;
32879         // }
32880         // return this.groups[brick_id] ;
32881         
32882         if(this.groups.key(brick_id)) {
32883             return this.groups.key(brick_id);
32884         }
32885         
32886         return false;
32887     }
32888     
32889     
32890     
32891 });
32892
32893  /*
32894  * - LGPL
32895  *
32896  * element
32897  * 
32898  */
32899
32900 /**
32901  * @class Roo.bootstrap.Brick
32902  * @extends Roo.bootstrap.Component
32903  * Bootstrap Brick class
32904  * 
32905  * @constructor
32906  * Create a new Brick
32907  * @param {Object} config The config object
32908  */
32909
32910 Roo.bootstrap.Brick = function(config){
32911     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32912     
32913     this.addEvents({
32914         // raw events
32915         /**
32916          * @event click
32917          * When a Brick is click
32918          * @param {Roo.bootstrap.Brick} this
32919          * @param {Roo.EventObject} e
32920          */
32921         "click" : true
32922     });
32923 };
32924
32925 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32926     
32927     /**
32928      * @cfg {String} title
32929      */   
32930     title : '',
32931     /**
32932      * @cfg {String} html
32933      */   
32934     html : '',
32935     /**
32936      * @cfg {String} bgimage
32937      */   
32938     bgimage : '',
32939     /**
32940      * @cfg {String} cls
32941      */   
32942     cls : '',
32943     /**
32944      * @cfg {String} href
32945      */   
32946     href : '',
32947     /**
32948      * @cfg {String} video
32949      */   
32950     video : '',
32951     /**
32952      * @cfg {Boolean} square
32953      */   
32954     square : true,
32955     
32956     getAutoCreate : function()
32957     {
32958         var cls = 'roo-brick';
32959         
32960         if(this.href.length){
32961             cls += ' roo-brick-link';
32962         }
32963         
32964         if(this.bgimage.length){
32965             cls += ' roo-brick-image';
32966         }
32967         
32968         if(!this.html.length && !this.bgimage.length){
32969             cls += ' roo-brick-center-title';
32970         }
32971         
32972         if(!this.html.length && this.bgimage.length){
32973             cls += ' roo-brick-bottom-title';
32974         }
32975         
32976         if(this.cls){
32977             cls += ' ' + this.cls;
32978         }
32979         
32980         var cfg = {
32981             tag: (this.href.length) ? 'a' : 'div',
32982             cls: cls,
32983             cn: [
32984                 {
32985                     tag: 'div',
32986                     cls: 'roo-brick-paragraph',
32987                     cn: []
32988                 }
32989             ]
32990         };
32991         
32992         if(this.href.length){
32993             cfg.href = this.href;
32994         }
32995         
32996         var cn = cfg.cn[0].cn;
32997         
32998         if(this.title.length){
32999             cn.push({
33000                 tag: 'h4',
33001                 cls: 'roo-brick-title',
33002                 html: this.title
33003             });
33004         }
33005         
33006         if(this.html.length){
33007             cn.push({
33008                 tag: 'p',
33009                 cls: 'roo-brick-text',
33010                 html: this.html
33011             });
33012         } else {
33013             cn.cls += ' hide';
33014         }
33015         
33016         if(this.bgimage.length){
33017             cfg.cn.push({
33018                 tag: 'img',
33019                 cls: 'roo-brick-image-view',
33020                 src: this.bgimage
33021             });
33022         }
33023         
33024         return cfg;
33025     },
33026     
33027     initEvents: function() 
33028     {
33029         if(this.title.length || this.html.length){
33030             this.el.on('mouseenter'  ,this.enter, this);
33031             this.el.on('mouseleave', this.leave, this);
33032         }
33033         
33034         Roo.EventManager.onWindowResize(this.resize, this); 
33035         
33036         if(this.bgimage.length){
33037             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33038             this.imageEl.on('load', this.onImageLoad, this);
33039             return;
33040         }
33041         
33042         this.resize();
33043     },
33044     
33045     onImageLoad : function()
33046     {
33047         this.resize();
33048     },
33049     
33050     resize : function()
33051     {
33052         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33053         
33054         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33055         
33056         if(this.bgimage.length){
33057             var image = this.el.select('.roo-brick-image-view', true).first();
33058             
33059             image.setWidth(paragraph.getWidth());
33060             
33061             if(this.square){
33062                 image.setHeight(paragraph.getWidth());
33063             }
33064             
33065             this.el.setHeight(image.getHeight());
33066             paragraph.setHeight(image.getHeight());
33067             
33068         }
33069         
33070     },
33071     
33072     enter: function(e, el)
33073     {
33074         e.preventDefault();
33075         
33076         if(this.bgimage.length){
33077             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33078             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33079         }
33080     },
33081     
33082     leave: function(e, el)
33083     {
33084         e.preventDefault();
33085         
33086         if(this.bgimage.length){
33087             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33088             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33089         }
33090     }
33091     
33092 });
33093
33094  
33095
33096  /*
33097  * - LGPL
33098  *
33099  * Number field 
33100  */
33101
33102 /**
33103  * @class Roo.bootstrap.NumberField
33104  * @extends Roo.bootstrap.Input
33105  * Bootstrap NumberField class
33106  * 
33107  * 
33108  * 
33109  * 
33110  * @constructor
33111  * Create a new NumberField
33112  * @param {Object} config The config object
33113  */
33114
33115 Roo.bootstrap.NumberField = function(config){
33116     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33117 };
33118
33119 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33120     
33121     /**
33122      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33123      */
33124     allowDecimals : true,
33125     /**
33126      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33127      */
33128     decimalSeparator : ".",
33129     /**
33130      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33131      */
33132     decimalPrecision : 2,
33133     /**
33134      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33135      */
33136     allowNegative : true,
33137     
33138     /**
33139      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33140      */
33141     allowZero: true,
33142     /**
33143      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33144      */
33145     minValue : Number.NEGATIVE_INFINITY,
33146     /**
33147      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33148      */
33149     maxValue : Number.MAX_VALUE,
33150     /**
33151      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33152      */
33153     minText : "The minimum value for this field is {0}",
33154     /**
33155      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33156      */
33157     maxText : "The maximum value for this field is {0}",
33158     /**
33159      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33160      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33161      */
33162     nanText : "{0} is not a valid number",
33163     /**
33164      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33165      */
33166     castInt : true,
33167     /**
33168      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33169      */
33170     thousandsDelimiter : false,
33171     /**
33172      * @cfg {String} valueAlign alignment of value
33173      */
33174     valueAlign : "left",
33175
33176     getAutoCreate : function()
33177     {
33178         var hiddenInput = {
33179             tag: 'input',
33180             type: 'hidden',
33181             id: Roo.id(),
33182             cls: 'hidden-number-input'
33183         };
33184         
33185         if (this.name) {
33186             hiddenInput.name = this.name;
33187         }
33188         
33189         this.name = '';
33190         
33191         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33192         
33193         this.name = hiddenInput.name;
33194         
33195         if(cfg.cn.length > 0) {
33196             cfg.cn.push(hiddenInput);
33197         }
33198         
33199         return cfg;
33200     },
33201
33202     // private
33203     initEvents : function()
33204     {   
33205         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33206         
33207         var allowed = "0123456789";
33208         
33209         if(this.allowDecimals){
33210             allowed += this.decimalSeparator;
33211         }
33212         
33213         if(this.allowNegative){
33214             allowed += "-";
33215         }
33216         
33217         if(this.thousandsDelimiter) {
33218             allowed += ",";
33219         }
33220         
33221         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33222         
33223         var keyPress = function(e){
33224             
33225             var k = e.getKey();
33226             
33227             var c = e.getCharCode();
33228             
33229             if(
33230                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33231                     allowed.indexOf(String.fromCharCode(c)) === -1
33232             ){
33233                 e.stopEvent();
33234                 return;
33235             }
33236             
33237             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33238                 return;
33239             }
33240             
33241             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33242                 e.stopEvent();
33243             }
33244         };
33245         
33246         this.el.on("keypress", keyPress, this);
33247     },
33248     
33249     validateValue : function(value)
33250     {
33251         
33252         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33253             return false;
33254         }
33255         
33256         var num = this.parseValue(value);
33257         
33258         if(isNaN(num)){
33259             this.markInvalid(String.format(this.nanText, value));
33260             return false;
33261         }
33262         
33263         if(num < this.minValue){
33264             this.markInvalid(String.format(this.minText, this.minValue));
33265             return false;
33266         }
33267         
33268         if(num > this.maxValue){
33269             this.markInvalid(String.format(this.maxText, this.maxValue));
33270             return false;
33271         }
33272         
33273         return true;
33274     },
33275
33276     getValue : function()
33277     {
33278         var v = this.hiddenEl().getValue();
33279         
33280         return this.fixPrecision(this.parseValue(v));
33281     },
33282
33283     parseValue : function(value)
33284     {
33285         if(this.thousandsDelimiter) {
33286             value += "";
33287             r = new RegExp(",", "g");
33288             value = value.replace(r, "");
33289         }
33290         
33291         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33292         return isNaN(value) ? '' : value;
33293     },
33294
33295     fixPrecision : function(value)
33296     {
33297         if(this.thousandsDelimiter) {
33298             value += "";
33299             r = new RegExp(",", "g");
33300             value = value.replace(r, "");
33301         }
33302         
33303         var nan = isNaN(value);
33304         
33305         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33306             return nan ? '' : value;
33307         }
33308         return parseFloat(value).toFixed(this.decimalPrecision);
33309     },
33310
33311     setValue : function(v)
33312     {
33313         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33314         
33315         this.value = v;
33316         
33317         if(this.rendered){
33318             
33319             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33320             
33321             this.inputEl().dom.value = (v == '') ? '' :
33322                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33323             
33324             if(!this.allowZero && v === '0') {
33325                 this.hiddenEl().dom.value = '';
33326                 this.inputEl().dom.value = '';
33327             }
33328             
33329             this.validate();
33330         }
33331     },
33332
33333     decimalPrecisionFcn : function(v)
33334     {
33335         return Math.floor(v);
33336     },
33337
33338     beforeBlur : function()
33339     {
33340         if(!this.castInt){
33341             return;
33342         }
33343         
33344         var v = this.parseValue(this.getRawValue());
33345         
33346         if(v || v === 0){
33347             this.setValue(v);
33348         }
33349     },
33350     
33351     hiddenEl : function()
33352     {
33353         return this.el.select('input.hidden-number-input',true).first();
33354     }
33355     
33356 });
33357
33358  
33359
33360 /*
33361 * Licence: LGPL
33362 */
33363
33364 /**
33365  * @class Roo.bootstrap.DocumentSlider
33366  * @extends Roo.bootstrap.Component
33367  * Bootstrap DocumentSlider class
33368  * 
33369  * @constructor
33370  * Create a new DocumentViewer
33371  * @param {Object} config The config object
33372  */
33373
33374 Roo.bootstrap.DocumentSlider = function(config){
33375     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33376     
33377     this.files = [];
33378     
33379     this.addEvents({
33380         /**
33381          * @event initial
33382          * Fire after initEvent
33383          * @param {Roo.bootstrap.DocumentSlider} this
33384          */
33385         "initial" : true,
33386         /**
33387          * @event update
33388          * Fire after update
33389          * @param {Roo.bootstrap.DocumentSlider} this
33390          */
33391         "update" : true,
33392         /**
33393          * @event click
33394          * Fire after click
33395          * @param {Roo.bootstrap.DocumentSlider} this
33396          */
33397         "click" : true
33398     });
33399 };
33400
33401 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33402     
33403     files : false,
33404     
33405     indicator : 0,
33406     
33407     getAutoCreate : function()
33408     {
33409         var cfg = {
33410             tag : 'div',
33411             cls : 'roo-document-slider',
33412             cn : [
33413                 {
33414                     tag : 'div',
33415                     cls : 'roo-document-slider-header',
33416                     cn : [
33417                         {
33418                             tag : 'div',
33419                             cls : 'roo-document-slider-header-title'
33420                         }
33421                     ]
33422                 },
33423                 {
33424                     tag : 'div',
33425                     cls : 'roo-document-slider-body',
33426                     cn : [
33427                         {
33428                             tag : 'div',
33429                             cls : 'roo-document-slider-prev',
33430                             cn : [
33431                                 {
33432                                     tag : 'i',
33433                                     cls : 'fa fa-chevron-left'
33434                                 }
33435                             ]
33436                         },
33437                         {
33438                             tag : 'div',
33439                             cls : 'roo-document-slider-thumb',
33440                             cn : [
33441                                 {
33442                                     tag : 'img',
33443                                     cls : 'roo-document-slider-image'
33444                                 }
33445                             ]
33446                         },
33447                         {
33448                             tag : 'div',
33449                             cls : 'roo-document-slider-next',
33450                             cn : [
33451                                 {
33452                                     tag : 'i',
33453                                     cls : 'fa fa-chevron-right'
33454                                 }
33455                             ]
33456                         }
33457                     ]
33458                 }
33459             ]
33460         };
33461         
33462         return cfg;
33463     },
33464     
33465     initEvents : function()
33466     {
33467         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33468         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33469         
33470         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33471         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33472         
33473         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33474         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33475         
33476         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33477         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33478         
33479         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33480         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33481         
33482         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33483         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33484         
33485         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33486         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33487         
33488         this.thumbEl.on('click', this.onClick, this);
33489         
33490         this.prevIndicator.on('click', this.prev, this);
33491         
33492         this.nextIndicator.on('click', this.next, this);
33493         
33494     },
33495     
33496     initial : function()
33497     {
33498         if(this.files.length){
33499             this.indicator = 1;
33500             this.update()
33501         }
33502         
33503         this.fireEvent('initial', this);
33504     },
33505     
33506     update : function()
33507     {
33508         this.imageEl.attr('src', this.files[this.indicator - 1]);
33509         
33510         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33511         
33512         this.prevIndicator.show();
33513         
33514         if(this.indicator == 1){
33515             this.prevIndicator.hide();
33516         }
33517         
33518         this.nextIndicator.show();
33519         
33520         if(this.indicator == this.files.length){
33521             this.nextIndicator.hide();
33522         }
33523         
33524         this.thumbEl.scrollTo('top');
33525         
33526         this.fireEvent('update', this);
33527     },
33528     
33529     onClick : function(e)
33530     {
33531         e.preventDefault();
33532         
33533         this.fireEvent('click', this);
33534     },
33535     
33536     prev : function(e)
33537     {
33538         e.preventDefault();
33539         
33540         this.indicator = Math.max(1, this.indicator - 1);
33541         
33542         this.update();
33543     },
33544     
33545     next : function(e)
33546     {
33547         e.preventDefault();
33548         
33549         this.indicator = Math.min(this.files.length, this.indicator + 1);
33550         
33551         this.update();
33552     }
33553 });
33554 /*
33555  * - LGPL
33556  *
33557  * RadioSet
33558  *
33559  *
33560  */
33561
33562 /**
33563  * @class Roo.bootstrap.RadioSet
33564  * @extends Roo.bootstrap.Input
33565  * Bootstrap RadioSet class
33566  * @cfg {String} indicatorpos (left|right) default left
33567  * @cfg {Boolean} inline (true|false) inline the element (default true)
33568  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33569  * @constructor
33570  * Create a new RadioSet
33571  * @param {Object} config The config object
33572  */
33573
33574 Roo.bootstrap.RadioSet = function(config){
33575     
33576     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33577     
33578     this.radioes = [];
33579     
33580     Roo.bootstrap.RadioSet.register(this);
33581     
33582     this.addEvents({
33583         /**
33584         * @event check
33585         * Fires when the element is checked or unchecked.
33586         * @param {Roo.bootstrap.RadioSet} this This radio
33587         * @param {Roo.bootstrap.Radio} item The checked item
33588         */
33589        check : true,
33590        /**
33591         * @event click
33592         * Fires when the element is click.
33593         * @param {Roo.bootstrap.RadioSet} this This radio set
33594         * @param {Roo.bootstrap.Radio} item The checked item
33595         * @param {Roo.EventObject} e The event object
33596         */
33597        click : true
33598     });
33599     
33600 };
33601
33602 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33603
33604     radioes : false,
33605     
33606     inline : true,
33607     
33608     weight : '',
33609     
33610     indicatorpos : 'left',
33611     
33612     getAutoCreate : function()
33613     {
33614         var label = {
33615             tag : 'label',
33616             cls : 'roo-radio-set-label',
33617             cn : [
33618                 {
33619                     tag : 'span',
33620                     html : this.fieldLabel
33621                 }
33622             ]
33623         };
33624         
33625         if(this.indicatorpos == 'left'){
33626             label.cn.unshift({
33627                 tag : 'i',
33628                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33629                 tooltip : 'This field is required'
33630             });
33631         } else {
33632             label.cn.push({
33633                 tag : 'i',
33634                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33635                 tooltip : 'This field is required'
33636             });
33637         }
33638         
33639         var items = {
33640             tag : 'div',
33641             cls : 'roo-radio-set-items'
33642         };
33643         
33644         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33645         
33646         if (align === 'left' && this.fieldLabel.length) {
33647             
33648             items = {
33649                 cls : "roo-radio-set-right", 
33650                 cn: [
33651                     items
33652                 ]
33653             };
33654             
33655             if(this.labelWidth > 12){
33656                 label.style = "width: " + this.labelWidth + 'px';
33657             }
33658             
33659             if(this.labelWidth < 13 && this.labelmd == 0){
33660                 this.labelmd = this.labelWidth;
33661             }
33662             
33663             if(this.labellg > 0){
33664                 label.cls += ' col-lg-' + this.labellg;
33665                 items.cls += ' col-lg-' + (12 - this.labellg);
33666             }
33667             
33668             if(this.labelmd > 0){
33669                 label.cls += ' col-md-' + this.labelmd;
33670                 items.cls += ' col-md-' + (12 - this.labelmd);
33671             }
33672             
33673             if(this.labelsm > 0){
33674                 label.cls += ' col-sm-' + this.labelsm;
33675                 items.cls += ' col-sm-' + (12 - this.labelsm);
33676             }
33677             
33678             if(this.labelxs > 0){
33679                 label.cls += ' col-xs-' + this.labelxs;
33680                 items.cls += ' col-xs-' + (12 - this.labelxs);
33681             }
33682         }
33683         
33684         var cfg = {
33685             tag : 'div',
33686             cls : 'roo-radio-set',
33687             cn : [
33688                 {
33689                     tag : 'input',
33690                     cls : 'roo-radio-set-input',
33691                     type : 'hidden',
33692                     name : this.name,
33693                     value : this.value ? this.value :  ''
33694                 },
33695                 label,
33696                 items
33697             ]
33698         };
33699         
33700         if(this.weight.length){
33701             cfg.cls += ' roo-radio-' + this.weight;
33702         }
33703         
33704         if(this.inline) {
33705             cfg.cls += ' roo-radio-set-inline';
33706         }
33707         
33708         var settings=this;
33709         ['xs','sm','md','lg'].map(function(size){
33710             if (settings[size]) {
33711                 cfg.cls += ' col-' + size + '-' + settings[size];
33712             }
33713         });
33714         
33715         return cfg;
33716         
33717     },
33718
33719     initEvents : function()
33720     {
33721         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33722         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33723         
33724         if(!this.fieldLabel.length){
33725             this.labelEl.hide();
33726         }
33727         
33728         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33729         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33730         
33731         this.indicator = this.indicatorEl();
33732         
33733         if(this.indicator){
33734             this.indicator.addClass('invisible');
33735         }
33736         
33737         this.originalValue = this.getValue();
33738         
33739     },
33740     
33741     inputEl: function ()
33742     {
33743         return this.el.select('.roo-radio-set-input', true).first();
33744     },
33745     
33746     getChildContainer : function()
33747     {
33748         return this.itemsEl;
33749     },
33750     
33751     register : function(item)
33752     {
33753         this.radioes.push(item);
33754         
33755     },
33756     
33757     validate : function()
33758     {   
33759         if(this.getVisibilityEl().hasClass('hidden')){
33760             return true;
33761         }
33762         
33763         var valid = false;
33764         
33765         Roo.each(this.radioes, function(i){
33766             if(!i.checked){
33767                 return;
33768             }
33769             
33770             valid = true;
33771             return false;
33772         });
33773         
33774         if(this.allowBlank) {
33775             return true;
33776         }
33777         
33778         if(this.disabled || valid){
33779             this.markValid();
33780             return true;
33781         }
33782         
33783         this.markInvalid();
33784         return false;
33785         
33786     },
33787     
33788     markValid : function()
33789     {
33790         if(this.labelEl.isVisible(true)){
33791             this.indicatorEl().removeClass('visible');
33792             this.indicatorEl().addClass('invisible');
33793         }
33794         
33795         this.el.removeClass([this.invalidClass, this.validClass]);
33796         this.el.addClass(this.validClass);
33797         
33798         this.fireEvent('valid', this);
33799     },
33800     
33801     markInvalid : function(msg)
33802     {
33803         if(this.allowBlank || this.disabled){
33804             return;
33805         }
33806         
33807         if(this.labelEl.isVisible(true)){
33808             this.indicatorEl().removeClass('invisible');
33809             this.indicatorEl().addClass('visible');
33810         }
33811         
33812         this.el.removeClass([this.invalidClass, this.validClass]);
33813         this.el.addClass(this.invalidClass);
33814         
33815         this.fireEvent('invalid', this, msg);
33816         
33817     },
33818     
33819     setValue : function(v, suppressEvent)
33820     {   
33821         if(this.value === v){
33822             return;
33823         }
33824         
33825         this.value = v;
33826         
33827         if(this.rendered){
33828             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33829         }
33830         
33831         Roo.each(this.radioes, function(i){
33832             i.checked = false;
33833             i.el.removeClass('checked');
33834         });
33835         
33836         Roo.each(this.radioes, function(i){
33837             
33838             if(i.value === v || i.value.toString() === v.toString()){
33839                 i.checked = true;
33840                 i.el.addClass('checked');
33841                 
33842                 if(suppressEvent !== true){
33843                     this.fireEvent('check', this, i);
33844                 }
33845                 
33846                 return false;
33847             }
33848             
33849         }, this);
33850         
33851         this.validate();
33852     },
33853     
33854     clearInvalid : function(){
33855         
33856         if(!this.el || this.preventMark){
33857             return;
33858         }
33859         
33860         this.el.removeClass([this.invalidClass]);
33861         
33862         this.fireEvent('valid', this);
33863     }
33864     
33865 });
33866
33867 Roo.apply(Roo.bootstrap.RadioSet, {
33868     
33869     groups: {},
33870     
33871     register : function(set)
33872     {
33873         this.groups[set.name] = set;
33874     },
33875     
33876     get: function(name) 
33877     {
33878         if (typeof(this.groups[name]) == 'undefined') {
33879             return false;
33880         }
33881         
33882         return this.groups[name] ;
33883     }
33884     
33885 });
33886 /*
33887  * Based on:
33888  * Ext JS Library 1.1.1
33889  * Copyright(c) 2006-2007, Ext JS, LLC.
33890  *
33891  * Originally Released Under LGPL - original licence link has changed is not relivant.
33892  *
33893  * Fork - LGPL
33894  * <script type="text/javascript">
33895  */
33896
33897
33898 /**
33899  * @class Roo.bootstrap.SplitBar
33900  * @extends Roo.util.Observable
33901  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33902  * <br><br>
33903  * Usage:
33904  * <pre><code>
33905 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33906                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33907 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33908 split.minSize = 100;
33909 split.maxSize = 600;
33910 split.animate = true;
33911 split.on('moved', splitterMoved);
33912 </code></pre>
33913  * @constructor
33914  * Create a new SplitBar
33915  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33916  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33917  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33918  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33919                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33920                         position of the SplitBar).
33921  */
33922 Roo.bootstrap.SplitBar = function(cfg){
33923     
33924     /** @private */
33925     
33926     //{
33927     //  dragElement : elm
33928     //  resizingElement: el,
33929         // optional..
33930     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33931     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33932         // existingProxy ???
33933     //}
33934     
33935     this.el = Roo.get(cfg.dragElement, true);
33936     this.el.dom.unselectable = "on";
33937     /** @private */
33938     this.resizingEl = Roo.get(cfg.resizingElement, true);
33939
33940     /**
33941      * @private
33942      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33943      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33944      * @type Number
33945      */
33946     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33947     
33948     /**
33949      * The minimum size of the resizing element. (Defaults to 0)
33950      * @type Number
33951      */
33952     this.minSize = 0;
33953     
33954     /**
33955      * The maximum size of the resizing element. (Defaults to 2000)
33956      * @type Number
33957      */
33958     this.maxSize = 2000;
33959     
33960     /**
33961      * Whether to animate the transition to the new size
33962      * @type Boolean
33963      */
33964     this.animate = false;
33965     
33966     /**
33967      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33968      * @type Boolean
33969      */
33970     this.useShim = false;
33971     
33972     /** @private */
33973     this.shim = null;
33974     
33975     if(!cfg.existingProxy){
33976         /** @private */
33977         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33978     }else{
33979         this.proxy = Roo.get(cfg.existingProxy).dom;
33980     }
33981     /** @private */
33982     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33983     
33984     /** @private */
33985     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33986     
33987     /** @private */
33988     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33989     
33990     /** @private */
33991     this.dragSpecs = {};
33992     
33993     /**
33994      * @private The adapter to use to positon and resize elements
33995      */
33996     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33997     this.adapter.init(this);
33998     
33999     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34000         /** @private */
34001         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34002         this.el.addClass("roo-splitbar-h");
34003     }else{
34004         /** @private */
34005         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34006         this.el.addClass("roo-splitbar-v");
34007     }
34008     
34009     this.addEvents({
34010         /**
34011          * @event resize
34012          * Fires when the splitter is moved (alias for {@link #event-moved})
34013          * @param {Roo.bootstrap.SplitBar} this
34014          * @param {Number} newSize the new width or height
34015          */
34016         "resize" : true,
34017         /**
34018          * @event moved
34019          * Fires when the splitter is moved
34020          * @param {Roo.bootstrap.SplitBar} this
34021          * @param {Number} newSize the new width or height
34022          */
34023         "moved" : true,
34024         /**
34025          * @event beforeresize
34026          * Fires before the splitter is dragged
34027          * @param {Roo.bootstrap.SplitBar} this
34028          */
34029         "beforeresize" : true,
34030
34031         "beforeapply" : true
34032     });
34033
34034     Roo.util.Observable.call(this);
34035 };
34036
34037 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34038     onStartProxyDrag : function(x, y){
34039         this.fireEvent("beforeresize", this);
34040         if(!this.overlay){
34041             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34042             o.unselectable();
34043             o.enableDisplayMode("block");
34044             // all splitbars share the same overlay
34045             Roo.bootstrap.SplitBar.prototype.overlay = o;
34046         }
34047         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34048         this.overlay.show();
34049         Roo.get(this.proxy).setDisplayed("block");
34050         var size = this.adapter.getElementSize(this);
34051         this.activeMinSize = this.getMinimumSize();;
34052         this.activeMaxSize = this.getMaximumSize();;
34053         var c1 = size - this.activeMinSize;
34054         var c2 = Math.max(this.activeMaxSize - size, 0);
34055         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34056             this.dd.resetConstraints();
34057             this.dd.setXConstraint(
34058                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34059                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34060             );
34061             this.dd.setYConstraint(0, 0);
34062         }else{
34063             this.dd.resetConstraints();
34064             this.dd.setXConstraint(0, 0);
34065             this.dd.setYConstraint(
34066                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34067                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34068             );
34069          }
34070         this.dragSpecs.startSize = size;
34071         this.dragSpecs.startPoint = [x, y];
34072         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34073     },
34074     
34075     /** 
34076      * @private Called after the drag operation by the DDProxy
34077      */
34078     onEndProxyDrag : function(e){
34079         Roo.get(this.proxy).setDisplayed(false);
34080         var endPoint = Roo.lib.Event.getXY(e);
34081         if(this.overlay){
34082             this.overlay.hide();
34083         }
34084         var newSize;
34085         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34086             newSize = this.dragSpecs.startSize + 
34087                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34088                     endPoint[0] - this.dragSpecs.startPoint[0] :
34089                     this.dragSpecs.startPoint[0] - endPoint[0]
34090                 );
34091         }else{
34092             newSize = this.dragSpecs.startSize + 
34093                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34094                     endPoint[1] - this.dragSpecs.startPoint[1] :
34095                     this.dragSpecs.startPoint[1] - endPoint[1]
34096                 );
34097         }
34098         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34099         if(newSize != this.dragSpecs.startSize){
34100             if(this.fireEvent('beforeapply', this, newSize) !== false){
34101                 this.adapter.setElementSize(this, newSize);
34102                 this.fireEvent("moved", this, newSize);
34103                 this.fireEvent("resize", this, newSize);
34104             }
34105         }
34106     },
34107     
34108     /**
34109      * Get the adapter this SplitBar uses
34110      * @return The adapter object
34111      */
34112     getAdapter : function(){
34113         return this.adapter;
34114     },
34115     
34116     /**
34117      * Set the adapter this SplitBar uses
34118      * @param {Object} adapter A SplitBar adapter object
34119      */
34120     setAdapter : function(adapter){
34121         this.adapter = adapter;
34122         this.adapter.init(this);
34123     },
34124     
34125     /**
34126      * Gets the minimum size for the resizing element
34127      * @return {Number} The minimum size
34128      */
34129     getMinimumSize : function(){
34130         return this.minSize;
34131     },
34132     
34133     /**
34134      * Sets the minimum size for the resizing element
34135      * @param {Number} minSize The minimum size
34136      */
34137     setMinimumSize : function(minSize){
34138         this.minSize = minSize;
34139     },
34140     
34141     /**
34142      * Gets the maximum size for the resizing element
34143      * @return {Number} The maximum size
34144      */
34145     getMaximumSize : function(){
34146         return this.maxSize;
34147     },
34148     
34149     /**
34150      * Sets the maximum size for the resizing element
34151      * @param {Number} maxSize The maximum size
34152      */
34153     setMaximumSize : function(maxSize){
34154         this.maxSize = maxSize;
34155     },
34156     
34157     /**
34158      * Sets the initialize size for the resizing element
34159      * @param {Number} size The initial size
34160      */
34161     setCurrentSize : function(size){
34162         var oldAnimate = this.animate;
34163         this.animate = false;
34164         this.adapter.setElementSize(this, size);
34165         this.animate = oldAnimate;
34166     },
34167     
34168     /**
34169      * Destroy this splitbar. 
34170      * @param {Boolean} removeEl True to remove the element
34171      */
34172     destroy : function(removeEl){
34173         if(this.shim){
34174             this.shim.remove();
34175         }
34176         this.dd.unreg();
34177         this.proxy.parentNode.removeChild(this.proxy);
34178         if(removeEl){
34179             this.el.remove();
34180         }
34181     }
34182 });
34183
34184 /**
34185  * @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.
34186  */
34187 Roo.bootstrap.SplitBar.createProxy = function(dir){
34188     var proxy = new Roo.Element(document.createElement("div"));
34189     proxy.unselectable();
34190     var cls = 'roo-splitbar-proxy';
34191     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34192     document.body.appendChild(proxy.dom);
34193     return proxy.dom;
34194 };
34195
34196 /** 
34197  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34198  * Default Adapter. It assumes the splitter and resizing element are not positioned
34199  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34200  */
34201 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34202 };
34203
34204 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34205     // do nothing for now
34206     init : function(s){
34207     
34208     },
34209     /**
34210      * Called before drag operations to get the current size of the resizing element. 
34211      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34212      */
34213      getElementSize : function(s){
34214         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34215             return s.resizingEl.getWidth();
34216         }else{
34217             return s.resizingEl.getHeight();
34218         }
34219     },
34220     
34221     /**
34222      * Called after drag operations to set the size of the resizing element.
34223      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34224      * @param {Number} newSize The new size to set
34225      * @param {Function} onComplete A function to be invoked when resizing is complete
34226      */
34227     setElementSize : function(s, newSize, onComplete){
34228         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34229             if(!s.animate){
34230                 s.resizingEl.setWidth(newSize);
34231                 if(onComplete){
34232                     onComplete(s, newSize);
34233                 }
34234             }else{
34235                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34236             }
34237         }else{
34238             
34239             if(!s.animate){
34240                 s.resizingEl.setHeight(newSize);
34241                 if(onComplete){
34242                     onComplete(s, newSize);
34243                 }
34244             }else{
34245                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34246             }
34247         }
34248     }
34249 };
34250
34251 /** 
34252  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34253  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34254  * Adapter that  moves the splitter element to align with the resized sizing element. 
34255  * Used with an absolute positioned SplitBar.
34256  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34257  * document.body, make sure you assign an id to the body element.
34258  */
34259 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34260     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34261     this.container = Roo.get(container);
34262 };
34263
34264 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34265     init : function(s){
34266         this.basic.init(s);
34267     },
34268     
34269     getElementSize : function(s){
34270         return this.basic.getElementSize(s);
34271     },
34272     
34273     setElementSize : function(s, newSize, onComplete){
34274         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34275     },
34276     
34277     moveSplitter : function(s){
34278         var yes = Roo.bootstrap.SplitBar;
34279         switch(s.placement){
34280             case yes.LEFT:
34281                 s.el.setX(s.resizingEl.getRight());
34282                 break;
34283             case yes.RIGHT:
34284                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34285                 break;
34286             case yes.TOP:
34287                 s.el.setY(s.resizingEl.getBottom());
34288                 break;
34289             case yes.BOTTOM:
34290                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34291                 break;
34292         }
34293     }
34294 };
34295
34296 /**
34297  * Orientation constant - Create a vertical SplitBar
34298  * @static
34299  * @type Number
34300  */
34301 Roo.bootstrap.SplitBar.VERTICAL = 1;
34302
34303 /**
34304  * Orientation constant - Create a horizontal SplitBar
34305  * @static
34306  * @type Number
34307  */
34308 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34309
34310 /**
34311  * Placement constant - The resizing element is to the left of the splitter element
34312  * @static
34313  * @type Number
34314  */
34315 Roo.bootstrap.SplitBar.LEFT = 1;
34316
34317 /**
34318  * Placement constant - The resizing element is to the right of the splitter element
34319  * @static
34320  * @type Number
34321  */
34322 Roo.bootstrap.SplitBar.RIGHT = 2;
34323
34324 /**
34325  * Placement constant - The resizing element is positioned above the splitter element
34326  * @static
34327  * @type Number
34328  */
34329 Roo.bootstrap.SplitBar.TOP = 3;
34330
34331 /**
34332  * Placement constant - The resizing element is positioned under splitter element
34333  * @static
34334  * @type Number
34335  */
34336 Roo.bootstrap.SplitBar.BOTTOM = 4;
34337 Roo.namespace("Roo.bootstrap.layout");/*
34338  * Based on:
34339  * Ext JS Library 1.1.1
34340  * Copyright(c) 2006-2007, Ext JS, LLC.
34341  *
34342  * Originally Released Under LGPL - original licence link has changed is not relivant.
34343  *
34344  * Fork - LGPL
34345  * <script type="text/javascript">
34346  */
34347
34348 /**
34349  * @class Roo.bootstrap.layout.Manager
34350  * @extends Roo.bootstrap.Component
34351  * Base class for layout managers.
34352  */
34353 Roo.bootstrap.layout.Manager = function(config)
34354 {
34355     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34356
34357
34358
34359
34360
34361     /** false to disable window resize monitoring @type Boolean */
34362     this.monitorWindowResize = true;
34363     this.regions = {};
34364     this.addEvents({
34365         /**
34366          * @event layout
34367          * Fires when a layout is performed.
34368          * @param {Roo.LayoutManager} this
34369          */
34370         "layout" : true,
34371         /**
34372          * @event regionresized
34373          * Fires when the user resizes a region.
34374          * @param {Roo.LayoutRegion} region The resized region
34375          * @param {Number} newSize The new size (width for east/west, height for north/south)
34376          */
34377         "regionresized" : true,
34378         /**
34379          * @event regioncollapsed
34380          * Fires when a region is collapsed.
34381          * @param {Roo.LayoutRegion} region The collapsed region
34382          */
34383         "regioncollapsed" : true,
34384         /**
34385          * @event regionexpanded
34386          * Fires when a region is expanded.
34387          * @param {Roo.LayoutRegion} region The expanded region
34388          */
34389         "regionexpanded" : true
34390     });
34391     this.updating = false;
34392
34393     if (config.el) {
34394         this.el = Roo.get(config.el);
34395         this.initEvents();
34396     }
34397
34398 };
34399
34400 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34401
34402
34403     regions : null,
34404
34405     monitorWindowResize : true,
34406
34407
34408     updating : false,
34409
34410
34411     onRender : function(ct, position)
34412     {
34413         if(!this.el){
34414             this.el = Roo.get(ct);
34415             this.initEvents();
34416         }
34417         //this.fireEvent('render',this);
34418     },
34419
34420
34421     initEvents: function()
34422     {
34423
34424
34425         // ie scrollbar fix
34426         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34427             document.body.scroll = "no";
34428         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34429             this.el.position('relative');
34430         }
34431         this.id = this.el.id;
34432         this.el.addClass("roo-layout-container");
34433         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34434         if(this.el.dom != document.body ) {
34435             this.el.on('resize', this.layout,this);
34436             this.el.on('show', this.layout,this);
34437         }
34438
34439     },
34440
34441     /**
34442      * Returns true if this layout is currently being updated
34443      * @return {Boolean}
34444      */
34445     isUpdating : function(){
34446         return this.updating;
34447     },
34448
34449     /**
34450      * Suspend the LayoutManager from doing auto-layouts while
34451      * making multiple add or remove calls
34452      */
34453     beginUpdate : function(){
34454         this.updating = true;
34455     },
34456
34457     /**
34458      * Restore auto-layouts and optionally disable the manager from performing a layout
34459      * @param {Boolean} noLayout true to disable a layout update
34460      */
34461     endUpdate : function(noLayout){
34462         this.updating = false;
34463         if(!noLayout){
34464             this.layout();
34465         }
34466     },
34467
34468     layout: function(){
34469         // abstract...
34470     },
34471
34472     onRegionResized : function(region, newSize){
34473         this.fireEvent("regionresized", region, newSize);
34474         this.layout();
34475     },
34476
34477     onRegionCollapsed : function(region){
34478         this.fireEvent("regioncollapsed", region);
34479     },
34480
34481     onRegionExpanded : function(region){
34482         this.fireEvent("regionexpanded", region);
34483     },
34484
34485     /**
34486      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34487      * performs box-model adjustments.
34488      * @return {Object} The size as an object {width: (the width), height: (the height)}
34489      */
34490     getViewSize : function()
34491     {
34492         var size;
34493         if(this.el.dom != document.body){
34494             size = this.el.getSize();
34495         }else{
34496             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34497         }
34498         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34499         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34500         return size;
34501     },
34502
34503     /**
34504      * Returns the Element this layout is bound to.
34505      * @return {Roo.Element}
34506      */
34507     getEl : function(){
34508         return this.el;
34509     },
34510
34511     /**
34512      * Returns the specified region.
34513      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34514      * @return {Roo.LayoutRegion}
34515      */
34516     getRegion : function(target){
34517         return this.regions[target.toLowerCase()];
34518     },
34519
34520     onWindowResize : function(){
34521         if(this.monitorWindowResize){
34522             this.layout();
34523         }
34524     }
34525 });
34526 /*
34527  * Based on:
34528  * Ext JS Library 1.1.1
34529  * Copyright(c) 2006-2007, Ext JS, LLC.
34530  *
34531  * Originally Released Under LGPL - original licence link has changed is not relivant.
34532  *
34533  * Fork - LGPL
34534  * <script type="text/javascript">
34535  */
34536 /**
34537  * @class Roo.bootstrap.layout.Border
34538  * @extends Roo.bootstrap.layout.Manager
34539  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34540  * please see: examples/bootstrap/nested.html<br><br>
34541  
34542 <b>The container the layout is rendered into can be either the body element or any other element.
34543 If it is not the body element, the container needs to either be an absolute positioned element,
34544 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34545 the container size if it is not the body element.</b>
34546
34547 * @constructor
34548 * Create a new Border
34549 * @param {Object} config Configuration options
34550  */
34551 Roo.bootstrap.layout.Border = function(config){
34552     config = config || {};
34553     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34554     
34555     
34556     
34557     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34558         if(config[region]){
34559             config[region].region = region;
34560             this.addRegion(config[region]);
34561         }
34562     },this);
34563     
34564 };
34565
34566 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34567
34568 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34569     /**
34570      * Creates and adds a new region if it doesn't already exist.
34571      * @param {String} target The target region key (north, south, east, west or center).
34572      * @param {Object} config The regions config object
34573      * @return {BorderLayoutRegion} The new region
34574      */
34575     addRegion : function(config)
34576     {
34577         if(!this.regions[config.region]){
34578             var r = this.factory(config);
34579             this.bindRegion(r);
34580         }
34581         return this.regions[config.region];
34582     },
34583
34584     // private (kinda)
34585     bindRegion : function(r){
34586         this.regions[r.config.region] = r;
34587         
34588         r.on("visibilitychange",    this.layout, this);
34589         r.on("paneladded",          this.layout, this);
34590         r.on("panelremoved",        this.layout, this);
34591         r.on("invalidated",         this.layout, this);
34592         r.on("resized",             this.onRegionResized, this);
34593         r.on("collapsed",           this.onRegionCollapsed, this);
34594         r.on("expanded",            this.onRegionExpanded, this);
34595     },
34596
34597     /**
34598      * Performs a layout update.
34599      */
34600     layout : function()
34601     {
34602         if(this.updating) {
34603             return;
34604         }
34605         
34606         // render all the rebions if they have not been done alreayd?
34607         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34608             if(this.regions[region] && !this.regions[region].bodyEl){
34609                 this.regions[region].onRender(this.el)
34610             }
34611         },this);
34612         
34613         var size = this.getViewSize();
34614         var w = size.width;
34615         var h = size.height;
34616         var centerW = w;
34617         var centerH = h;
34618         var centerY = 0;
34619         var centerX = 0;
34620         //var x = 0, y = 0;
34621
34622         var rs = this.regions;
34623         var north = rs["north"];
34624         var south = rs["south"]; 
34625         var west = rs["west"];
34626         var east = rs["east"];
34627         var center = rs["center"];
34628         //if(this.hideOnLayout){ // not supported anymore
34629             //c.el.setStyle("display", "none");
34630         //}
34631         if(north && north.isVisible()){
34632             var b = north.getBox();
34633             var m = north.getMargins();
34634             b.width = w - (m.left+m.right);
34635             b.x = m.left;
34636             b.y = m.top;
34637             centerY = b.height + b.y + m.bottom;
34638             centerH -= centerY;
34639             north.updateBox(this.safeBox(b));
34640         }
34641         if(south && south.isVisible()){
34642             var b = south.getBox();
34643             var m = south.getMargins();
34644             b.width = w - (m.left+m.right);
34645             b.x = m.left;
34646             var totalHeight = (b.height + m.top + m.bottom);
34647             b.y = h - totalHeight + m.top;
34648             centerH -= totalHeight;
34649             south.updateBox(this.safeBox(b));
34650         }
34651         if(west && west.isVisible()){
34652             var b = west.getBox();
34653             var m = west.getMargins();
34654             b.height = centerH - (m.top+m.bottom);
34655             b.x = m.left;
34656             b.y = centerY + m.top;
34657             var totalWidth = (b.width + m.left + m.right);
34658             centerX += totalWidth;
34659             centerW -= totalWidth;
34660             west.updateBox(this.safeBox(b));
34661         }
34662         if(east && east.isVisible()){
34663             var b = east.getBox();
34664             var m = east.getMargins();
34665             b.height = centerH - (m.top+m.bottom);
34666             var totalWidth = (b.width + m.left + m.right);
34667             b.x = w - totalWidth + m.left;
34668             b.y = centerY + m.top;
34669             centerW -= totalWidth;
34670             east.updateBox(this.safeBox(b));
34671         }
34672         if(center){
34673             var m = center.getMargins();
34674             var centerBox = {
34675                 x: centerX + m.left,
34676                 y: centerY + m.top,
34677                 width: centerW - (m.left+m.right),
34678                 height: centerH - (m.top+m.bottom)
34679             };
34680             //if(this.hideOnLayout){
34681                 //center.el.setStyle("display", "block");
34682             //}
34683             center.updateBox(this.safeBox(centerBox));
34684         }
34685         this.el.repaint();
34686         this.fireEvent("layout", this);
34687     },
34688
34689     // private
34690     safeBox : function(box){
34691         box.width = Math.max(0, box.width);
34692         box.height = Math.max(0, box.height);
34693         return box;
34694     },
34695
34696     /**
34697      * Adds a ContentPanel (or subclass) to this layout.
34698      * @param {String} target The target region key (north, south, east, west or center).
34699      * @param {Roo.ContentPanel} panel The panel to add
34700      * @return {Roo.ContentPanel} The added panel
34701      */
34702     add : function(target, panel){
34703          
34704         target = target.toLowerCase();
34705         return this.regions[target].add(panel);
34706     },
34707
34708     /**
34709      * Remove a ContentPanel (or subclass) to this layout.
34710      * @param {String} target The target region key (north, south, east, west or center).
34711      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34712      * @return {Roo.ContentPanel} The removed panel
34713      */
34714     remove : function(target, panel){
34715         target = target.toLowerCase();
34716         return this.regions[target].remove(panel);
34717     },
34718
34719     /**
34720      * Searches all regions for a panel with the specified id
34721      * @param {String} panelId
34722      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34723      */
34724     findPanel : function(panelId){
34725         var rs = this.regions;
34726         for(var target in rs){
34727             if(typeof rs[target] != "function"){
34728                 var p = rs[target].getPanel(panelId);
34729                 if(p){
34730                     return p;
34731                 }
34732             }
34733         }
34734         return null;
34735     },
34736
34737     /**
34738      * Searches all regions for a panel with the specified id and activates (shows) it.
34739      * @param {String/ContentPanel} panelId The panels id or the panel itself
34740      * @return {Roo.ContentPanel} The shown panel or null
34741      */
34742     showPanel : function(panelId) {
34743       var rs = this.regions;
34744       for(var target in rs){
34745          var r = rs[target];
34746          if(typeof r != "function"){
34747             if(r.hasPanel(panelId)){
34748                return r.showPanel(panelId);
34749             }
34750          }
34751       }
34752       return null;
34753    },
34754
34755    /**
34756      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34757      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34758      */
34759    /*
34760     restoreState : function(provider){
34761         if(!provider){
34762             provider = Roo.state.Manager;
34763         }
34764         var sm = new Roo.LayoutStateManager();
34765         sm.init(this, provider);
34766     },
34767 */
34768  
34769  
34770     /**
34771      * Adds a xtype elements to the layout.
34772      * <pre><code>
34773
34774 layout.addxtype({
34775        xtype : 'ContentPanel',
34776        region: 'west',
34777        items: [ .... ]
34778    }
34779 );
34780
34781 layout.addxtype({
34782         xtype : 'NestedLayoutPanel',
34783         region: 'west',
34784         layout: {
34785            center: { },
34786            west: { }   
34787         },
34788         items : [ ... list of content panels or nested layout panels.. ]
34789    }
34790 );
34791 </code></pre>
34792      * @param {Object} cfg Xtype definition of item to add.
34793      */
34794     addxtype : function(cfg)
34795     {
34796         // basically accepts a pannel...
34797         // can accept a layout region..!?!?
34798         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34799         
34800         
34801         // theory?  children can only be panels??
34802         
34803         //if (!cfg.xtype.match(/Panel$/)) {
34804         //    return false;
34805         //}
34806         var ret = false;
34807         
34808         if (typeof(cfg.region) == 'undefined') {
34809             Roo.log("Failed to add Panel, region was not set");
34810             Roo.log(cfg);
34811             return false;
34812         }
34813         var region = cfg.region;
34814         delete cfg.region;
34815         
34816           
34817         var xitems = [];
34818         if (cfg.items) {
34819             xitems = cfg.items;
34820             delete cfg.items;
34821         }
34822         var nb = false;
34823         
34824         switch(cfg.xtype) 
34825         {
34826             case 'Content':  // ContentPanel (el, cfg)
34827             case 'Scroll':  // ContentPanel (el, cfg)
34828             case 'View': 
34829                 cfg.autoCreate = true;
34830                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34831                 //} else {
34832                 //    var el = this.el.createChild();
34833                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34834                 //}
34835                 
34836                 this.add(region, ret);
34837                 break;
34838             
34839             /*
34840             case 'TreePanel': // our new panel!
34841                 cfg.el = this.el.createChild();
34842                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34843                 this.add(region, ret);
34844                 break;
34845             */
34846             
34847             case 'Nest': 
34848                 // create a new Layout (which is  a Border Layout...
34849                 
34850                 var clayout = cfg.layout;
34851                 clayout.el  = this.el.createChild();
34852                 clayout.items   = clayout.items  || [];
34853                 
34854                 delete cfg.layout;
34855                 
34856                 // replace this exitems with the clayout ones..
34857                 xitems = clayout.items;
34858                  
34859                 // force background off if it's in center...
34860                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34861                     cfg.background = false;
34862                 }
34863                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34864                 
34865                 
34866                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34867                 //console.log('adding nested layout panel '  + cfg.toSource());
34868                 this.add(region, ret);
34869                 nb = {}; /// find first...
34870                 break;
34871             
34872             case 'Grid':
34873                 
34874                 // needs grid and region
34875                 
34876                 //var el = this.getRegion(region).el.createChild();
34877                 /*
34878                  *var el = this.el.createChild();
34879                 // create the grid first...
34880                 cfg.grid.container = el;
34881                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34882                 */
34883                 
34884                 if (region == 'center' && this.active ) {
34885                     cfg.background = false;
34886                 }
34887                 
34888                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34889                 
34890                 this.add(region, ret);
34891                 /*
34892                 if (cfg.background) {
34893                     // render grid on panel activation (if panel background)
34894                     ret.on('activate', function(gp) {
34895                         if (!gp.grid.rendered) {
34896                     //        gp.grid.render(el);
34897                         }
34898                     });
34899                 } else {
34900                   //  cfg.grid.render(el);
34901                 }
34902                 */
34903                 break;
34904            
34905            
34906             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34907                 // it was the old xcomponent building that caused this before.
34908                 // espeically if border is the top element in the tree.
34909                 ret = this;
34910                 break; 
34911                 
34912                     
34913                 
34914                 
34915                 
34916             default:
34917                 /*
34918                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34919                     
34920                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34921                     this.add(region, ret);
34922                 } else {
34923                 */
34924                     Roo.log(cfg);
34925                     throw "Can not add '" + cfg.xtype + "' to Border";
34926                     return null;
34927              
34928                                 
34929              
34930         }
34931         this.beginUpdate();
34932         // add children..
34933         var region = '';
34934         var abn = {};
34935         Roo.each(xitems, function(i)  {
34936             region = nb && i.region ? i.region : false;
34937             
34938             var add = ret.addxtype(i);
34939            
34940             if (region) {
34941                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34942                 if (!i.background) {
34943                     abn[region] = nb[region] ;
34944                 }
34945             }
34946             
34947         });
34948         this.endUpdate();
34949
34950         // make the last non-background panel active..
34951         //if (nb) { Roo.log(abn); }
34952         if (nb) {
34953             
34954             for(var r in abn) {
34955                 region = this.getRegion(r);
34956                 if (region) {
34957                     // tried using nb[r], but it does not work..
34958                      
34959                     region.showPanel(abn[r]);
34960                    
34961                 }
34962             }
34963         }
34964         return ret;
34965         
34966     },
34967     
34968     
34969 // private
34970     factory : function(cfg)
34971     {
34972         
34973         var validRegions = Roo.bootstrap.layout.Border.regions;
34974
34975         var target = cfg.region;
34976         cfg.mgr = this;
34977         
34978         var r = Roo.bootstrap.layout;
34979         Roo.log(target);
34980         switch(target){
34981             case "north":
34982                 return new r.North(cfg);
34983             case "south":
34984                 return new r.South(cfg);
34985             case "east":
34986                 return new r.East(cfg);
34987             case "west":
34988                 return new r.West(cfg);
34989             case "center":
34990                 return new r.Center(cfg);
34991         }
34992         throw 'Layout region "'+target+'" not supported.';
34993     }
34994     
34995     
34996 });
34997  /*
34998  * Based on:
34999  * Ext JS Library 1.1.1
35000  * Copyright(c) 2006-2007, Ext JS, LLC.
35001  *
35002  * Originally Released Under LGPL - original licence link has changed is not relivant.
35003  *
35004  * Fork - LGPL
35005  * <script type="text/javascript">
35006  */
35007  
35008 /**
35009  * @class Roo.bootstrap.layout.Basic
35010  * @extends Roo.util.Observable
35011  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35012  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35013  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35014  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35015  * @cfg {string}   region  the region that it inhabits..
35016  * @cfg {bool}   skipConfig skip config?
35017  * 
35018
35019  */
35020 Roo.bootstrap.layout.Basic = function(config){
35021     
35022     this.mgr = config.mgr;
35023     
35024     this.position = config.region;
35025     
35026     var skipConfig = config.skipConfig;
35027     
35028     this.events = {
35029         /**
35030          * @scope Roo.BasicLayoutRegion
35031          */
35032         
35033         /**
35034          * @event beforeremove
35035          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35036          * @param {Roo.LayoutRegion} this
35037          * @param {Roo.ContentPanel} panel The panel
35038          * @param {Object} e The cancel event object
35039          */
35040         "beforeremove" : true,
35041         /**
35042          * @event invalidated
35043          * Fires when the layout for this region is changed.
35044          * @param {Roo.LayoutRegion} this
35045          */
35046         "invalidated" : true,
35047         /**
35048          * @event visibilitychange
35049          * Fires when this region is shown or hidden 
35050          * @param {Roo.LayoutRegion} this
35051          * @param {Boolean} visibility true or false
35052          */
35053         "visibilitychange" : true,
35054         /**
35055          * @event paneladded
35056          * Fires when a panel is added. 
35057          * @param {Roo.LayoutRegion} this
35058          * @param {Roo.ContentPanel} panel The panel
35059          */
35060         "paneladded" : true,
35061         /**
35062          * @event panelremoved
35063          * Fires when a panel is removed. 
35064          * @param {Roo.LayoutRegion} this
35065          * @param {Roo.ContentPanel} panel The panel
35066          */
35067         "panelremoved" : true,
35068         /**
35069          * @event beforecollapse
35070          * Fires when this region before collapse.
35071          * @param {Roo.LayoutRegion} this
35072          */
35073         "beforecollapse" : true,
35074         /**
35075          * @event collapsed
35076          * Fires when this region is collapsed.
35077          * @param {Roo.LayoutRegion} this
35078          */
35079         "collapsed" : true,
35080         /**
35081          * @event expanded
35082          * Fires when this region is expanded.
35083          * @param {Roo.LayoutRegion} this
35084          */
35085         "expanded" : true,
35086         /**
35087          * @event slideshow
35088          * Fires when this region is slid into view.
35089          * @param {Roo.LayoutRegion} this
35090          */
35091         "slideshow" : true,
35092         /**
35093          * @event slidehide
35094          * Fires when this region slides out of view. 
35095          * @param {Roo.LayoutRegion} this
35096          */
35097         "slidehide" : true,
35098         /**
35099          * @event panelactivated
35100          * Fires when a panel is activated. 
35101          * @param {Roo.LayoutRegion} this
35102          * @param {Roo.ContentPanel} panel The activated panel
35103          */
35104         "panelactivated" : true,
35105         /**
35106          * @event resized
35107          * Fires when the user resizes this region. 
35108          * @param {Roo.LayoutRegion} this
35109          * @param {Number} newSize The new size (width for east/west, height for north/south)
35110          */
35111         "resized" : true
35112     };
35113     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35114     this.panels = new Roo.util.MixedCollection();
35115     this.panels.getKey = this.getPanelId.createDelegate(this);
35116     this.box = null;
35117     this.activePanel = null;
35118     // ensure listeners are added...
35119     
35120     if (config.listeners || config.events) {
35121         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35122             listeners : config.listeners || {},
35123             events : config.events || {}
35124         });
35125     }
35126     
35127     if(skipConfig !== true){
35128         this.applyConfig(config);
35129     }
35130 };
35131
35132 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35133 {
35134     getPanelId : function(p){
35135         return p.getId();
35136     },
35137     
35138     applyConfig : function(config){
35139         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35140         this.config = config;
35141         
35142     },
35143     
35144     /**
35145      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35146      * the width, for horizontal (north, south) the height.
35147      * @param {Number} newSize The new width or height
35148      */
35149     resizeTo : function(newSize){
35150         var el = this.el ? this.el :
35151                  (this.activePanel ? this.activePanel.getEl() : null);
35152         if(el){
35153             switch(this.position){
35154                 case "east":
35155                 case "west":
35156                     el.setWidth(newSize);
35157                     this.fireEvent("resized", this, newSize);
35158                 break;
35159                 case "north":
35160                 case "south":
35161                     el.setHeight(newSize);
35162                     this.fireEvent("resized", this, newSize);
35163                 break;                
35164             }
35165         }
35166     },
35167     
35168     getBox : function(){
35169         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35170     },
35171     
35172     getMargins : function(){
35173         return this.margins;
35174     },
35175     
35176     updateBox : function(box){
35177         this.box = box;
35178         var el = this.activePanel.getEl();
35179         el.dom.style.left = box.x + "px";
35180         el.dom.style.top = box.y + "px";
35181         this.activePanel.setSize(box.width, box.height);
35182     },
35183     
35184     /**
35185      * Returns the container element for this region.
35186      * @return {Roo.Element}
35187      */
35188     getEl : function(){
35189         return this.activePanel;
35190     },
35191     
35192     /**
35193      * Returns true if this region is currently visible.
35194      * @return {Boolean}
35195      */
35196     isVisible : function(){
35197         return this.activePanel ? true : false;
35198     },
35199     
35200     setActivePanel : function(panel){
35201         panel = this.getPanel(panel);
35202         if(this.activePanel && this.activePanel != panel){
35203             this.activePanel.setActiveState(false);
35204             this.activePanel.getEl().setLeftTop(-10000,-10000);
35205         }
35206         this.activePanel = panel;
35207         panel.setActiveState(true);
35208         if(this.box){
35209             panel.setSize(this.box.width, this.box.height);
35210         }
35211         this.fireEvent("panelactivated", this, panel);
35212         this.fireEvent("invalidated");
35213     },
35214     
35215     /**
35216      * Show the specified panel.
35217      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35218      * @return {Roo.ContentPanel} The shown panel or null
35219      */
35220     showPanel : function(panel){
35221         panel = this.getPanel(panel);
35222         if(panel){
35223             this.setActivePanel(panel);
35224         }
35225         return panel;
35226     },
35227     
35228     /**
35229      * Get the active panel for this region.
35230      * @return {Roo.ContentPanel} The active panel or null
35231      */
35232     getActivePanel : function(){
35233         return this.activePanel;
35234     },
35235     
35236     /**
35237      * Add the passed ContentPanel(s)
35238      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35239      * @return {Roo.ContentPanel} The panel added (if only one was added)
35240      */
35241     add : function(panel){
35242         if(arguments.length > 1){
35243             for(var i = 0, len = arguments.length; i < len; i++) {
35244                 this.add(arguments[i]);
35245             }
35246             return null;
35247         }
35248         if(this.hasPanel(panel)){
35249             this.showPanel(panel);
35250             return panel;
35251         }
35252         var el = panel.getEl();
35253         if(el.dom.parentNode != this.mgr.el.dom){
35254             this.mgr.el.dom.appendChild(el.dom);
35255         }
35256         if(panel.setRegion){
35257             panel.setRegion(this);
35258         }
35259         this.panels.add(panel);
35260         el.setStyle("position", "absolute");
35261         if(!panel.background){
35262             this.setActivePanel(panel);
35263             if(this.config.initialSize && this.panels.getCount()==1){
35264                 this.resizeTo(this.config.initialSize);
35265             }
35266         }
35267         this.fireEvent("paneladded", this, panel);
35268         return panel;
35269     },
35270     
35271     /**
35272      * Returns true if the panel is in this region.
35273      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35274      * @return {Boolean}
35275      */
35276     hasPanel : function(panel){
35277         if(typeof panel == "object"){ // must be panel obj
35278             panel = panel.getId();
35279         }
35280         return this.getPanel(panel) ? true : false;
35281     },
35282     
35283     /**
35284      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35285      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35286      * @param {Boolean} preservePanel Overrides the config preservePanel option
35287      * @return {Roo.ContentPanel} The panel that was removed
35288      */
35289     remove : function(panel, preservePanel){
35290         panel = this.getPanel(panel);
35291         if(!panel){
35292             return null;
35293         }
35294         var e = {};
35295         this.fireEvent("beforeremove", this, panel, e);
35296         if(e.cancel === true){
35297             return null;
35298         }
35299         var panelId = panel.getId();
35300         this.panels.removeKey(panelId);
35301         return panel;
35302     },
35303     
35304     /**
35305      * Returns the panel specified or null if it's not in this region.
35306      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35307      * @return {Roo.ContentPanel}
35308      */
35309     getPanel : function(id){
35310         if(typeof id == "object"){ // must be panel obj
35311             return id;
35312         }
35313         return this.panels.get(id);
35314     },
35315     
35316     /**
35317      * Returns this regions position (north/south/east/west/center).
35318      * @return {String} 
35319      */
35320     getPosition: function(){
35321         return this.position;    
35322     }
35323 });/*
35324  * Based on:
35325  * Ext JS Library 1.1.1
35326  * Copyright(c) 2006-2007, Ext JS, LLC.
35327  *
35328  * Originally Released Under LGPL - original licence link has changed is not relivant.
35329  *
35330  * Fork - LGPL
35331  * <script type="text/javascript">
35332  */
35333  
35334 /**
35335  * @class Roo.bootstrap.layout.Region
35336  * @extends Roo.bootstrap.layout.Basic
35337  * This class represents a region in a layout manager.
35338  
35339  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35340  * @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})
35341  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35342  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35343  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35344  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35345  * @cfg {String}    title           The title for the region (overrides panel titles)
35346  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35347  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35348  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35349  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35350  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35351  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35352  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35353  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35354  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35355  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35356
35357  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35358  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35359  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35360  * @cfg {Number}    width           For East/West panels
35361  * @cfg {Number}    height          For North/South panels
35362  * @cfg {Boolean}   split           To show the splitter
35363  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35364  * 
35365  * @cfg {string}   cls             Extra CSS classes to add to region
35366  * 
35367  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35368  * @cfg {string}   region  the region that it inhabits..
35369  *
35370
35371  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35372  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35373
35374  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35375  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35376  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35377  */
35378 Roo.bootstrap.layout.Region = function(config)
35379 {
35380     this.applyConfig(config);
35381
35382     var mgr = config.mgr;
35383     var pos = config.region;
35384     config.skipConfig = true;
35385     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35386     
35387     if (mgr.el) {
35388         this.onRender(mgr.el);   
35389     }
35390      
35391     this.visible = true;
35392     this.collapsed = false;
35393     this.unrendered_panels = [];
35394 };
35395
35396 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35397
35398     position: '', // set by wrapper (eg. north/south etc..)
35399     unrendered_panels : null,  // unrendered panels.
35400     createBody : function(){
35401         /** This region's body element 
35402         * @type Roo.Element */
35403         this.bodyEl = this.el.createChild({
35404                 tag: "div",
35405                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35406         });
35407     },
35408
35409     onRender: function(ctr, pos)
35410     {
35411         var dh = Roo.DomHelper;
35412         /** This region's container element 
35413         * @type Roo.Element */
35414         this.el = dh.append(ctr.dom, {
35415                 tag: "div",
35416                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35417             }, true);
35418         /** This region's title element 
35419         * @type Roo.Element */
35420     
35421         this.titleEl = dh.append(this.el.dom,
35422             {
35423                     tag: "div",
35424                     unselectable: "on",
35425                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35426                     children:[
35427                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35428                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35429                     ]}, true);
35430         
35431         this.titleEl.enableDisplayMode();
35432         /** This region's title text element 
35433         * @type HTMLElement */
35434         this.titleTextEl = this.titleEl.dom.firstChild;
35435         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35436         /*
35437         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35438         this.closeBtn.enableDisplayMode();
35439         this.closeBtn.on("click", this.closeClicked, this);
35440         this.closeBtn.hide();
35441     */
35442         this.createBody(this.config);
35443         if(this.config.hideWhenEmpty){
35444             this.hide();
35445             this.on("paneladded", this.validateVisibility, this);
35446             this.on("panelremoved", this.validateVisibility, this);
35447         }
35448         if(this.autoScroll){
35449             this.bodyEl.setStyle("overflow", "auto");
35450         }else{
35451             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35452         }
35453         //if(c.titlebar !== false){
35454             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35455                 this.titleEl.hide();
35456             }else{
35457                 this.titleEl.show();
35458                 if(this.config.title){
35459                     this.titleTextEl.innerHTML = this.config.title;
35460                 }
35461             }
35462         //}
35463         if(this.config.collapsed){
35464             this.collapse(true);
35465         }
35466         if(this.config.hidden){
35467             this.hide();
35468         }
35469         
35470         if (this.unrendered_panels && this.unrendered_panels.length) {
35471             for (var i =0;i< this.unrendered_panels.length; i++) {
35472                 this.add(this.unrendered_panels[i]);
35473             }
35474             this.unrendered_panels = null;
35475             
35476         }
35477         
35478     },
35479     
35480     applyConfig : function(c)
35481     {
35482         /*
35483          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35484             var dh = Roo.DomHelper;
35485             if(c.titlebar !== false){
35486                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35487                 this.collapseBtn.on("click", this.collapse, this);
35488                 this.collapseBtn.enableDisplayMode();
35489                 /*
35490                 if(c.showPin === true || this.showPin){
35491                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35492                     this.stickBtn.enableDisplayMode();
35493                     this.stickBtn.on("click", this.expand, this);
35494                     this.stickBtn.hide();
35495                 }
35496                 
35497             }
35498             */
35499             /** This region's collapsed element
35500             * @type Roo.Element */
35501             /*
35502              *
35503             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35504                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35505             ]}, true);
35506             
35507             if(c.floatable !== false){
35508                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35509                this.collapsedEl.on("click", this.collapseClick, this);
35510             }
35511
35512             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35513                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35514                    id: "message", unselectable: "on", style:{"float":"left"}});
35515                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35516              }
35517             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35518             this.expandBtn.on("click", this.expand, this);
35519             
35520         }
35521         
35522         if(this.collapseBtn){
35523             this.collapseBtn.setVisible(c.collapsible == true);
35524         }
35525         
35526         this.cmargins = c.cmargins || this.cmargins ||
35527                          (this.position == "west" || this.position == "east" ?
35528                              {top: 0, left: 2, right:2, bottom: 0} :
35529                              {top: 2, left: 0, right:0, bottom: 2});
35530         */
35531         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35532         
35533         
35534         this.bottomTabs = c.tabPosition != "top";
35535         
35536         this.autoScroll = c.autoScroll || false;
35537         
35538         
35539        
35540         
35541         this.duration = c.duration || .30;
35542         this.slideDuration = c.slideDuration || .45;
35543         this.config = c;
35544        
35545     },
35546     /**
35547      * Returns true if this region is currently visible.
35548      * @return {Boolean}
35549      */
35550     isVisible : function(){
35551         return this.visible;
35552     },
35553
35554     /**
35555      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35556      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35557      */
35558     //setCollapsedTitle : function(title){
35559     //    title = title || "&#160;";
35560      //   if(this.collapsedTitleTextEl){
35561       //      this.collapsedTitleTextEl.innerHTML = title;
35562        // }
35563     //},
35564
35565     getBox : function(){
35566         var b;
35567       //  if(!this.collapsed){
35568             b = this.el.getBox(false, true);
35569        // }else{
35570           //  b = this.collapsedEl.getBox(false, true);
35571         //}
35572         return b;
35573     },
35574
35575     getMargins : function(){
35576         return this.margins;
35577         //return this.collapsed ? this.cmargins : this.margins;
35578     },
35579 /*
35580     highlight : function(){
35581         this.el.addClass("x-layout-panel-dragover");
35582     },
35583
35584     unhighlight : function(){
35585         this.el.removeClass("x-layout-panel-dragover");
35586     },
35587 */
35588     updateBox : function(box)
35589     {
35590         if (!this.bodyEl) {
35591             return; // not rendered yet..
35592         }
35593         
35594         this.box = box;
35595         if(!this.collapsed){
35596             this.el.dom.style.left = box.x + "px";
35597             this.el.dom.style.top = box.y + "px";
35598             this.updateBody(box.width, box.height);
35599         }else{
35600             this.collapsedEl.dom.style.left = box.x + "px";
35601             this.collapsedEl.dom.style.top = box.y + "px";
35602             this.collapsedEl.setSize(box.width, box.height);
35603         }
35604         if(this.tabs){
35605             this.tabs.autoSizeTabs();
35606         }
35607     },
35608
35609     updateBody : function(w, h)
35610     {
35611         if(w !== null){
35612             this.el.setWidth(w);
35613             w -= this.el.getBorderWidth("rl");
35614             if(this.config.adjustments){
35615                 w += this.config.adjustments[0];
35616             }
35617         }
35618         if(h !== null && h > 0){
35619             this.el.setHeight(h);
35620             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35621             h -= this.el.getBorderWidth("tb");
35622             if(this.config.adjustments){
35623                 h += this.config.adjustments[1];
35624             }
35625             this.bodyEl.setHeight(h);
35626             if(this.tabs){
35627                 h = this.tabs.syncHeight(h);
35628             }
35629         }
35630         if(this.panelSize){
35631             w = w !== null ? w : this.panelSize.width;
35632             h = h !== null ? h : this.panelSize.height;
35633         }
35634         if(this.activePanel){
35635             var el = this.activePanel.getEl();
35636             w = w !== null ? w : el.getWidth();
35637             h = h !== null ? h : el.getHeight();
35638             this.panelSize = {width: w, height: h};
35639             this.activePanel.setSize(w, h);
35640         }
35641         if(Roo.isIE && this.tabs){
35642             this.tabs.el.repaint();
35643         }
35644     },
35645
35646     /**
35647      * Returns the container element for this region.
35648      * @return {Roo.Element}
35649      */
35650     getEl : function(){
35651         return this.el;
35652     },
35653
35654     /**
35655      * Hides this region.
35656      */
35657     hide : function(){
35658         //if(!this.collapsed){
35659             this.el.dom.style.left = "-2000px";
35660             this.el.hide();
35661         //}else{
35662          //   this.collapsedEl.dom.style.left = "-2000px";
35663          //   this.collapsedEl.hide();
35664        // }
35665         this.visible = false;
35666         this.fireEvent("visibilitychange", this, false);
35667     },
35668
35669     /**
35670      * Shows this region if it was previously hidden.
35671      */
35672     show : function(){
35673         //if(!this.collapsed){
35674             this.el.show();
35675         //}else{
35676         //    this.collapsedEl.show();
35677        // }
35678         this.visible = true;
35679         this.fireEvent("visibilitychange", this, true);
35680     },
35681 /*
35682     closeClicked : function(){
35683         if(this.activePanel){
35684             this.remove(this.activePanel);
35685         }
35686     },
35687
35688     collapseClick : function(e){
35689         if(this.isSlid){
35690            e.stopPropagation();
35691            this.slideIn();
35692         }else{
35693            e.stopPropagation();
35694            this.slideOut();
35695         }
35696     },
35697 */
35698     /**
35699      * Collapses this region.
35700      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35701      */
35702     /*
35703     collapse : function(skipAnim, skipCheck = false){
35704         if(this.collapsed) {
35705             return;
35706         }
35707         
35708         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35709             
35710             this.collapsed = true;
35711             if(this.split){
35712                 this.split.el.hide();
35713             }
35714             if(this.config.animate && skipAnim !== true){
35715                 this.fireEvent("invalidated", this);
35716                 this.animateCollapse();
35717             }else{
35718                 this.el.setLocation(-20000,-20000);
35719                 this.el.hide();
35720                 this.collapsedEl.show();
35721                 this.fireEvent("collapsed", this);
35722                 this.fireEvent("invalidated", this);
35723             }
35724         }
35725         
35726     },
35727 */
35728     animateCollapse : function(){
35729         // overridden
35730     },
35731
35732     /**
35733      * Expands this region if it was previously collapsed.
35734      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35735      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35736      */
35737     /*
35738     expand : function(e, skipAnim){
35739         if(e) {
35740             e.stopPropagation();
35741         }
35742         if(!this.collapsed || this.el.hasActiveFx()) {
35743             return;
35744         }
35745         if(this.isSlid){
35746             this.afterSlideIn();
35747             skipAnim = true;
35748         }
35749         this.collapsed = false;
35750         if(this.config.animate && skipAnim !== true){
35751             this.animateExpand();
35752         }else{
35753             this.el.show();
35754             if(this.split){
35755                 this.split.el.show();
35756             }
35757             this.collapsedEl.setLocation(-2000,-2000);
35758             this.collapsedEl.hide();
35759             this.fireEvent("invalidated", this);
35760             this.fireEvent("expanded", this);
35761         }
35762     },
35763 */
35764     animateExpand : function(){
35765         // overridden
35766     },
35767
35768     initTabs : function()
35769     {
35770         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35771         
35772         var ts = new Roo.bootstrap.panel.Tabs({
35773                 el: this.bodyEl.dom,
35774                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35775                 disableTooltips: this.config.disableTabTips,
35776                 toolbar : this.config.toolbar
35777             });
35778         
35779         if(this.config.hideTabs){
35780             ts.stripWrap.setDisplayed(false);
35781         }
35782         this.tabs = ts;
35783         ts.resizeTabs = this.config.resizeTabs === true;
35784         ts.minTabWidth = this.config.minTabWidth || 40;
35785         ts.maxTabWidth = this.config.maxTabWidth || 250;
35786         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35787         ts.monitorResize = false;
35788         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35789         ts.bodyEl.addClass('roo-layout-tabs-body');
35790         this.panels.each(this.initPanelAsTab, this);
35791     },
35792
35793     initPanelAsTab : function(panel){
35794         var ti = this.tabs.addTab(
35795             panel.getEl().id,
35796             panel.getTitle(),
35797             null,
35798             this.config.closeOnTab && panel.isClosable(),
35799             panel.tpl
35800         );
35801         if(panel.tabTip !== undefined){
35802             ti.setTooltip(panel.tabTip);
35803         }
35804         ti.on("activate", function(){
35805               this.setActivePanel(panel);
35806         }, this);
35807         
35808         if(this.config.closeOnTab){
35809             ti.on("beforeclose", function(t, e){
35810                 e.cancel = true;
35811                 this.remove(panel);
35812             }, this);
35813         }
35814         
35815         panel.tabItem = ti;
35816         
35817         return ti;
35818     },
35819
35820     updatePanelTitle : function(panel, title)
35821     {
35822         if(this.activePanel == panel){
35823             this.updateTitle(title);
35824         }
35825         if(this.tabs){
35826             var ti = this.tabs.getTab(panel.getEl().id);
35827             ti.setText(title);
35828             if(panel.tabTip !== undefined){
35829                 ti.setTooltip(panel.tabTip);
35830             }
35831         }
35832     },
35833
35834     updateTitle : function(title){
35835         if(this.titleTextEl && !this.config.title){
35836             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35837         }
35838     },
35839
35840     setActivePanel : function(panel)
35841     {
35842         panel = this.getPanel(panel);
35843         if(this.activePanel && this.activePanel != panel){
35844             if(this.activePanel.setActiveState(false) === false){
35845                 return;
35846             }
35847         }
35848         this.activePanel = panel;
35849         panel.setActiveState(true);
35850         if(this.panelSize){
35851             panel.setSize(this.panelSize.width, this.panelSize.height);
35852         }
35853         if(this.closeBtn){
35854             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35855         }
35856         this.updateTitle(panel.getTitle());
35857         if(this.tabs){
35858             this.fireEvent("invalidated", this);
35859         }
35860         this.fireEvent("panelactivated", this, panel);
35861     },
35862
35863     /**
35864      * Shows the specified panel.
35865      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35866      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35867      */
35868     showPanel : function(panel)
35869     {
35870         panel = this.getPanel(panel);
35871         if(panel){
35872             if(this.tabs){
35873                 var tab = this.tabs.getTab(panel.getEl().id);
35874                 if(tab.isHidden()){
35875                     this.tabs.unhideTab(tab.id);
35876                 }
35877                 tab.activate();
35878             }else{
35879                 this.setActivePanel(panel);
35880             }
35881         }
35882         return panel;
35883     },
35884
35885     /**
35886      * Get the active panel for this region.
35887      * @return {Roo.ContentPanel} The active panel or null
35888      */
35889     getActivePanel : function(){
35890         return this.activePanel;
35891     },
35892
35893     validateVisibility : function(){
35894         if(this.panels.getCount() < 1){
35895             this.updateTitle("&#160;");
35896             this.closeBtn.hide();
35897             this.hide();
35898         }else{
35899             if(!this.isVisible()){
35900                 this.show();
35901             }
35902         }
35903     },
35904
35905     /**
35906      * Adds the passed ContentPanel(s) to this region.
35907      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35908      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35909      */
35910     add : function(panel)
35911     {
35912         if(arguments.length > 1){
35913             for(var i = 0, len = arguments.length; i < len; i++) {
35914                 this.add(arguments[i]);
35915             }
35916             return null;
35917         }
35918         
35919         // if we have not been rendered yet, then we can not really do much of this..
35920         if (!this.bodyEl) {
35921             this.unrendered_panels.push(panel);
35922             return panel;
35923         }
35924         
35925         
35926         
35927         
35928         if(this.hasPanel(panel)){
35929             this.showPanel(panel);
35930             return panel;
35931         }
35932         panel.setRegion(this);
35933         this.panels.add(panel);
35934        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35935             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35936             // and hide them... ???
35937             this.bodyEl.dom.appendChild(panel.getEl().dom);
35938             if(panel.background !== true){
35939                 this.setActivePanel(panel);
35940             }
35941             this.fireEvent("paneladded", this, panel);
35942             return panel;
35943         }
35944         */
35945         if(!this.tabs){
35946             this.initTabs();
35947         }else{
35948             this.initPanelAsTab(panel);
35949         }
35950         
35951         
35952         if(panel.background !== true){
35953             this.tabs.activate(panel.getEl().id);
35954         }
35955         this.fireEvent("paneladded", this, panel);
35956         return panel;
35957     },
35958
35959     /**
35960      * Hides the tab for the specified panel.
35961      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35962      */
35963     hidePanel : function(panel){
35964         if(this.tabs && (panel = this.getPanel(panel))){
35965             this.tabs.hideTab(panel.getEl().id);
35966         }
35967     },
35968
35969     /**
35970      * Unhides the tab for a previously hidden panel.
35971      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35972      */
35973     unhidePanel : function(panel){
35974         if(this.tabs && (panel = this.getPanel(panel))){
35975             this.tabs.unhideTab(panel.getEl().id);
35976         }
35977     },
35978
35979     clearPanels : function(){
35980         while(this.panels.getCount() > 0){
35981              this.remove(this.panels.first());
35982         }
35983     },
35984
35985     /**
35986      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35987      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35988      * @param {Boolean} preservePanel Overrides the config preservePanel option
35989      * @return {Roo.ContentPanel} The panel that was removed
35990      */
35991     remove : function(panel, preservePanel)
35992     {
35993         panel = this.getPanel(panel);
35994         if(!panel){
35995             return null;
35996         }
35997         var e = {};
35998         this.fireEvent("beforeremove", this, panel, e);
35999         if(e.cancel === true){
36000             return null;
36001         }
36002         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36003         var panelId = panel.getId();
36004         this.panels.removeKey(panelId);
36005         if(preservePanel){
36006             document.body.appendChild(panel.getEl().dom);
36007         }
36008         if(this.tabs){
36009             this.tabs.removeTab(panel.getEl().id);
36010         }else if (!preservePanel){
36011             this.bodyEl.dom.removeChild(panel.getEl().dom);
36012         }
36013         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36014             var p = this.panels.first();
36015             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36016             tempEl.appendChild(p.getEl().dom);
36017             this.bodyEl.update("");
36018             this.bodyEl.dom.appendChild(p.getEl().dom);
36019             tempEl = null;
36020             this.updateTitle(p.getTitle());
36021             this.tabs = null;
36022             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36023             this.setActivePanel(p);
36024         }
36025         panel.setRegion(null);
36026         if(this.activePanel == panel){
36027             this.activePanel = null;
36028         }
36029         if(this.config.autoDestroy !== false && preservePanel !== true){
36030             try{panel.destroy();}catch(e){}
36031         }
36032         this.fireEvent("panelremoved", this, panel);
36033         return panel;
36034     },
36035
36036     /**
36037      * Returns the TabPanel component used by this region
36038      * @return {Roo.TabPanel}
36039      */
36040     getTabs : function(){
36041         return this.tabs;
36042     },
36043
36044     createTool : function(parentEl, className){
36045         var btn = Roo.DomHelper.append(parentEl, {
36046             tag: "div",
36047             cls: "x-layout-tools-button",
36048             children: [ {
36049                 tag: "div",
36050                 cls: "roo-layout-tools-button-inner " + className,
36051                 html: "&#160;"
36052             }]
36053         }, true);
36054         btn.addClassOnOver("roo-layout-tools-button-over");
36055         return btn;
36056     }
36057 });/*
36058  * Based on:
36059  * Ext JS Library 1.1.1
36060  * Copyright(c) 2006-2007, Ext JS, LLC.
36061  *
36062  * Originally Released Under LGPL - original licence link has changed is not relivant.
36063  *
36064  * Fork - LGPL
36065  * <script type="text/javascript">
36066  */
36067  
36068
36069
36070 /**
36071  * @class Roo.SplitLayoutRegion
36072  * @extends Roo.LayoutRegion
36073  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36074  */
36075 Roo.bootstrap.layout.Split = function(config){
36076     this.cursor = config.cursor;
36077     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36078 };
36079
36080 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36081 {
36082     splitTip : "Drag to resize.",
36083     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36084     useSplitTips : false,
36085
36086     applyConfig : function(config){
36087         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36088     },
36089     
36090     onRender : function(ctr,pos) {
36091         
36092         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36093         if(!this.config.split){
36094             return;
36095         }
36096         if(!this.split){
36097             
36098             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36099                             tag: "div",
36100                             id: this.el.id + "-split",
36101                             cls: "roo-layout-split roo-layout-split-"+this.position,
36102                             html: "&#160;"
36103             });
36104             /** The SplitBar for this region 
36105             * @type Roo.SplitBar */
36106             // does not exist yet...
36107             Roo.log([this.position, this.orientation]);
36108             
36109             this.split = new Roo.bootstrap.SplitBar({
36110                 dragElement : splitEl,
36111                 resizingElement: this.el,
36112                 orientation : this.orientation
36113             });
36114             
36115             this.split.on("moved", this.onSplitMove, this);
36116             this.split.useShim = this.config.useShim === true;
36117             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36118             if(this.useSplitTips){
36119                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36120             }
36121             //if(config.collapsible){
36122             //    this.split.el.on("dblclick", this.collapse,  this);
36123             //}
36124         }
36125         if(typeof this.config.minSize != "undefined"){
36126             this.split.minSize = this.config.minSize;
36127         }
36128         if(typeof this.config.maxSize != "undefined"){
36129             this.split.maxSize = this.config.maxSize;
36130         }
36131         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36132             this.hideSplitter();
36133         }
36134         
36135     },
36136
36137     getHMaxSize : function(){
36138          var cmax = this.config.maxSize || 10000;
36139          var center = this.mgr.getRegion("center");
36140          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36141     },
36142
36143     getVMaxSize : function(){
36144          var cmax = this.config.maxSize || 10000;
36145          var center = this.mgr.getRegion("center");
36146          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36147     },
36148
36149     onSplitMove : function(split, newSize){
36150         this.fireEvent("resized", this, newSize);
36151     },
36152     
36153     /** 
36154      * Returns the {@link Roo.SplitBar} for this region.
36155      * @return {Roo.SplitBar}
36156      */
36157     getSplitBar : function(){
36158         return this.split;
36159     },
36160     
36161     hide : function(){
36162         this.hideSplitter();
36163         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36164     },
36165
36166     hideSplitter : function(){
36167         if(this.split){
36168             this.split.el.setLocation(-2000,-2000);
36169             this.split.el.hide();
36170         }
36171     },
36172
36173     show : function(){
36174         if(this.split){
36175             this.split.el.show();
36176         }
36177         Roo.bootstrap.layout.Split.superclass.show.call(this);
36178     },
36179     
36180     beforeSlide: function(){
36181         if(Roo.isGecko){// firefox overflow auto bug workaround
36182             this.bodyEl.clip();
36183             if(this.tabs) {
36184                 this.tabs.bodyEl.clip();
36185             }
36186             if(this.activePanel){
36187                 this.activePanel.getEl().clip();
36188                 
36189                 if(this.activePanel.beforeSlide){
36190                     this.activePanel.beforeSlide();
36191                 }
36192             }
36193         }
36194     },
36195     
36196     afterSlide : function(){
36197         if(Roo.isGecko){// firefox overflow auto bug workaround
36198             this.bodyEl.unclip();
36199             if(this.tabs) {
36200                 this.tabs.bodyEl.unclip();
36201             }
36202             if(this.activePanel){
36203                 this.activePanel.getEl().unclip();
36204                 if(this.activePanel.afterSlide){
36205                     this.activePanel.afterSlide();
36206                 }
36207             }
36208         }
36209     },
36210
36211     initAutoHide : function(){
36212         if(this.autoHide !== false){
36213             if(!this.autoHideHd){
36214                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36215                 this.autoHideHd = {
36216                     "mouseout": function(e){
36217                         if(!e.within(this.el, true)){
36218                             st.delay(500);
36219                         }
36220                     },
36221                     "mouseover" : function(e){
36222                         st.cancel();
36223                     },
36224                     scope : this
36225                 };
36226             }
36227             this.el.on(this.autoHideHd);
36228         }
36229     },
36230
36231     clearAutoHide : function(){
36232         if(this.autoHide !== false){
36233             this.el.un("mouseout", this.autoHideHd.mouseout);
36234             this.el.un("mouseover", this.autoHideHd.mouseover);
36235         }
36236     },
36237
36238     clearMonitor : function(){
36239         Roo.get(document).un("click", this.slideInIf, this);
36240     },
36241
36242     // these names are backwards but not changed for compat
36243     slideOut : function(){
36244         if(this.isSlid || this.el.hasActiveFx()){
36245             return;
36246         }
36247         this.isSlid = true;
36248         if(this.collapseBtn){
36249             this.collapseBtn.hide();
36250         }
36251         this.closeBtnState = this.closeBtn.getStyle('display');
36252         this.closeBtn.hide();
36253         if(this.stickBtn){
36254             this.stickBtn.show();
36255         }
36256         this.el.show();
36257         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36258         this.beforeSlide();
36259         this.el.setStyle("z-index", 10001);
36260         this.el.slideIn(this.getSlideAnchor(), {
36261             callback: function(){
36262                 this.afterSlide();
36263                 this.initAutoHide();
36264                 Roo.get(document).on("click", this.slideInIf, this);
36265                 this.fireEvent("slideshow", this);
36266             },
36267             scope: this,
36268             block: true
36269         });
36270     },
36271
36272     afterSlideIn : function(){
36273         this.clearAutoHide();
36274         this.isSlid = false;
36275         this.clearMonitor();
36276         this.el.setStyle("z-index", "");
36277         if(this.collapseBtn){
36278             this.collapseBtn.show();
36279         }
36280         this.closeBtn.setStyle('display', this.closeBtnState);
36281         if(this.stickBtn){
36282             this.stickBtn.hide();
36283         }
36284         this.fireEvent("slidehide", this);
36285     },
36286
36287     slideIn : function(cb){
36288         if(!this.isSlid || this.el.hasActiveFx()){
36289             Roo.callback(cb);
36290             return;
36291         }
36292         this.isSlid = false;
36293         this.beforeSlide();
36294         this.el.slideOut(this.getSlideAnchor(), {
36295             callback: function(){
36296                 this.el.setLeftTop(-10000, -10000);
36297                 this.afterSlide();
36298                 this.afterSlideIn();
36299                 Roo.callback(cb);
36300             },
36301             scope: this,
36302             block: true
36303         });
36304     },
36305     
36306     slideInIf : function(e){
36307         if(!e.within(this.el)){
36308             this.slideIn();
36309         }
36310     },
36311
36312     animateCollapse : function(){
36313         this.beforeSlide();
36314         this.el.setStyle("z-index", 20000);
36315         var anchor = this.getSlideAnchor();
36316         this.el.slideOut(anchor, {
36317             callback : function(){
36318                 this.el.setStyle("z-index", "");
36319                 this.collapsedEl.slideIn(anchor, {duration:.3});
36320                 this.afterSlide();
36321                 this.el.setLocation(-10000,-10000);
36322                 this.el.hide();
36323                 this.fireEvent("collapsed", this);
36324             },
36325             scope: this,
36326             block: true
36327         });
36328     },
36329
36330     animateExpand : function(){
36331         this.beforeSlide();
36332         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36333         this.el.setStyle("z-index", 20000);
36334         this.collapsedEl.hide({
36335             duration:.1
36336         });
36337         this.el.slideIn(this.getSlideAnchor(), {
36338             callback : function(){
36339                 this.el.setStyle("z-index", "");
36340                 this.afterSlide();
36341                 if(this.split){
36342                     this.split.el.show();
36343                 }
36344                 this.fireEvent("invalidated", this);
36345                 this.fireEvent("expanded", this);
36346             },
36347             scope: this,
36348             block: true
36349         });
36350     },
36351
36352     anchors : {
36353         "west" : "left",
36354         "east" : "right",
36355         "north" : "top",
36356         "south" : "bottom"
36357     },
36358
36359     sanchors : {
36360         "west" : "l",
36361         "east" : "r",
36362         "north" : "t",
36363         "south" : "b"
36364     },
36365
36366     canchors : {
36367         "west" : "tl-tr",
36368         "east" : "tr-tl",
36369         "north" : "tl-bl",
36370         "south" : "bl-tl"
36371     },
36372
36373     getAnchor : function(){
36374         return this.anchors[this.position];
36375     },
36376
36377     getCollapseAnchor : function(){
36378         return this.canchors[this.position];
36379     },
36380
36381     getSlideAnchor : function(){
36382         return this.sanchors[this.position];
36383     },
36384
36385     getAlignAdj : function(){
36386         var cm = this.cmargins;
36387         switch(this.position){
36388             case "west":
36389                 return [0, 0];
36390             break;
36391             case "east":
36392                 return [0, 0];
36393             break;
36394             case "north":
36395                 return [0, 0];
36396             break;
36397             case "south":
36398                 return [0, 0];
36399             break;
36400         }
36401     },
36402
36403     getExpandAdj : function(){
36404         var c = this.collapsedEl, cm = this.cmargins;
36405         switch(this.position){
36406             case "west":
36407                 return [-(cm.right+c.getWidth()+cm.left), 0];
36408             break;
36409             case "east":
36410                 return [cm.right+c.getWidth()+cm.left, 0];
36411             break;
36412             case "north":
36413                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36414             break;
36415             case "south":
36416                 return [0, cm.top+cm.bottom+c.getHeight()];
36417             break;
36418         }
36419     }
36420 });/*
36421  * Based on:
36422  * Ext JS Library 1.1.1
36423  * Copyright(c) 2006-2007, Ext JS, LLC.
36424  *
36425  * Originally Released Under LGPL - original licence link has changed is not relivant.
36426  *
36427  * Fork - LGPL
36428  * <script type="text/javascript">
36429  */
36430 /*
36431  * These classes are private internal classes
36432  */
36433 Roo.bootstrap.layout.Center = function(config){
36434     config.region = "center";
36435     Roo.bootstrap.layout.Region.call(this, config);
36436     this.visible = true;
36437     this.minWidth = config.minWidth || 20;
36438     this.minHeight = config.minHeight || 20;
36439 };
36440
36441 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36442     hide : function(){
36443         // center panel can't be hidden
36444     },
36445     
36446     show : function(){
36447         // center panel can't be hidden
36448     },
36449     
36450     getMinWidth: function(){
36451         return this.minWidth;
36452     },
36453     
36454     getMinHeight: function(){
36455         return this.minHeight;
36456     }
36457 });
36458
36459
36460
36461
36462  
36463
36464
36465
36466
36467
36468 Roo.bootstrap.layout.North = function(config)
36469 {
36470     config.region = 'north';
36471     config.cursor = 'n-resize';
36472     
36473     Roo.bootstrap.layout.Split.call(this, config);
36474     
36475     
36476     if(this.split){
36477         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36478         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36479         this.split.el.addClass("roo-layout-split-v");
36480     }
36481     var size = config.initialSize || config.height;
36482     if(typeof size != "undefined"){
36483         this.el.setHeight(size);
36484     }
36485 };
36486 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36487 {
36488     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36489     
36490     
36491     
36492     getBox : function(){
36493         if(this.collapsed){
36494             return this.collapsedEl.getBox();
36495         }
36496         var box = this.el.getBox();
36497         if(this.split){
36498             box.height += this.split.el.getHeight();
36499         }
36500         return box;
36501     },
36502     
36503     updateBox : function(box){
36504         if(this.split && !this.collapsed){
36505             box.height -= this.split.el.getHeight();
36506             this.split.el.setLeft(box.x);
36507             this.split.el.setTop(box.y+box.height);
36508             this.split.el.setWidth(box.width);
36509         }
36510         if(this.collapsed){
36511             this.updateBody(box.width, null);
36512         }
36513         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36514     }
36515 });
36516
36517
36518
36519
36520
36521 Roo.bootstrap.layout.South = function(config){
36522     config.region = 'south';
36523     config.cursor = 's-resize';
36524     Roo.bootstrap.layout.Split.call(this, config);
36525     if(this.split){
36526         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36527         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36528         this.split.el.addClass("roo-layout-split-v");
36529     }
36530     var size = config.initialSize || config.height;
36531     if(typeof size != "undefined"){
36532         this.el.setHeight(size);
36533     }
36534 };
36535
36536 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36537     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36538     getBox : function(){
36539         if(this.collapsed){
36540             return this.collapsedEl.getBox();
36541         }
36542         var box = this.el.getBox();
36543         if(this.split){
36544             var sh = this.split.el.getHeight();
36545             box.height += sh;
36546             box.y -= sh;
36547         }
36548         return box;
36549     },
36550     
36551     updateBox : function(box){
36552         if(this.split && !this.collapsed){
36553             var sh = this.split.el.getHeight();
36554             box.height -= sh;
36555             box.y += sh;
36556             this.split.el.setLeft(box.x);
36557             this.split.el.setTop(box.y-sh);
36558             this.split.el.setWidth(box.width);
36559         }
36560         if(this.collapsed){
36561             this.updateBody(box.width, null);
36562         }
36563         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36564     }
36565 });
36566
36567 Roo.bootstrap.layout.East = function(config){
36568     config.region = "east";
36569     config.cursor = "e-resize";
36570     Roo.bootstrap.layout.Split.call(this, config);
36571     if(this.split){
36572         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36573         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36574         this.split.el.addClass("roo-layout-split-h");
36575     }
36576     var size = config.initialSize || config.width;
36577     if(typeof size != "undefined"){
36578         this.el.setWidth(size);
36579     }
36580 };
36581 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36582     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36583     getBox : function(){
36584         if(this.collapsed){
36585             return this.collapsedEl.getBox();
36586         }
36587         var box = this.el.getBox();
36588         if(this.split){
36589             var sw = this.split.el.getWidth();
36590             box.width += sw;
36591             box.x -= sw;
36592         }
36593         return box;
36594     },
36595
36596     updateBox : function(box){
36597         if(this.split && !this.collapsed){
36598             var sw = this.split.el.getWidth();
36599             box.width -= sw;
36600             this.split.el.setLeft(box.x);
36601             this.split.el.setTop(box.y);
36602             this.split.el.setHeight(box.height);
36603             box.x += sw;
36604         }
36605         if(this.collapsed){
36606             this.updateBody(null, box.height);
36607         }
36608         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36609     }
36610 });
36611
36612 Roo.bootstrap.layout.West = function(config){
36613     config.region = "west";
36614     config.cursor = "w-resize";
36615     
36616     Roo.bootstrap.layout.Split.call(this, config);
36617     if(this.split){
36618         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36619         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36620         this.split.el.addClass("roo-layout-split-h");
36621     }
36622     
36623 };
36624 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36625     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36626     
36627     onRender: function(ctr, pos)
36628     {
36629         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36630         var size = this.config.initialSize || this.config.width;
36631         if(typeof size != "undefined"){
36632             this.el.setWidth(size);
36633         }
36634     },
36635     
36636     getBox : function(){
36637         if(this.collapsed){
36638             return this.collapsedEl.getBox();
36639         }
36640         var box = this.el.getBox();
36641         if(this.split){
36642             box.width += this.split.el.getWidth();
36643         }
36644         return box;
36645     },
36646     
36647     updateBox : function(box){
36648         if(this.split && !this.collapsed){
36649             var sw = this.split.el.getWidth();
36650             box.width -= sw;
36651             this.split.el.setLeft(box.x+box.width);
36652             this.split.el.setTop(box.y);
36653             this.split.el.setHeight(box.height);
36654         }
36655         if(this.collapsed){
36656             this.updateBody(null, box.height);
36657         }
36658         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36659     }
36660 });
36661 Roo.namespace("Roo.bootstrap.panel");/*
36662  * Based on:
36663  * Ext JS Library 1.1.1
36664  * Copyright(c) 2006-2007, Ext JS, LLC.
36665  *
36666  * Originally Released Under LGPL - original licence link has changed is not relivant.
36667  *
36668  * Fork - LGPL
36669  * <script type="text/javascript">
36670  */
36671 /**
36672  * @class Roo.ContentPanel
36673  * @extends Roo.util.Observable
36674  * A basic ContentPanel element.
36675  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36676  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36677  * @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
36678  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36679  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36680  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36681  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36682  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36683  * @cfg {String} title          The title for this panel
36684  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36685  * @cfg {String} url            Calls {@link #setUrl} with this value
36686  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36687  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36688  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36689  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36690  * @cfg {Boolean} badges render the badges
36691
36692  * @constructor
36693  * Create a new ContentPanel.
36694  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36695  * @param {String/Object} config A string to set only the title or a config object
36696  * @param {String} content (optional) Set the HTML content for this panel
36697  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36698  */
36699 Roo.bootstrap.panel.Content = function( config){
36700     
36701     this.tpl = config.tpl || false;
36702     
36703     var el = config.el;
36704     var content = config.content;
36705
36706     if(config.autoCreate){ // xtype is available if this is called from factory
36707         el = Roo.id();
36708     }
36709     this.el = Roo.get(el);
36710     if(!this.el && config && config.autoCreate){
36711         if(typeof config.autoCreate == "object"){
36712             if(!config.autoCreate.id){
36713                 config.autoCreate.id = config.id||el;
36714             }
36715             this.el = Roo.DomHelper.append(document.body,
36716                         config.autoCreate, true);
36717         }else{
36718             var elcfg =  {   tag: "div",
36719                             cls: "roo-layout-inactive-content",
36720                             id: config.id||el
36721                             };
36722             if (config.html) {
36723                 elcfg.html = config.html;
36724                 
36725             }
36726                         
36727             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36728         }
36729     } 
36730     this.closable = false;
36731     this.loaded = false;
36732     this.active = false;
36733    
36734       
36735     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36736         
36737         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36738         
36739         this.wrapEl = this.el; //this.el.wrap();
36740         var ti = [];
36741         if (config.toolbar.items) {
36742             ti = config.toolbar.items ;
36743             delete config.toolbar.items ;
36744         }
36745         
36746         var nitems = [];
36747         this.toolbar.render(this.wrapEl, 'before');
36748         for(var i =0;i < ti.length;i++) {
36749           //  Roo.log(['add child', items[i]]);
36750             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36751         }
36752         this.toolbar.items = nitems;
36753         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36754         delete config.toolbar;
36755         
36756     }
36757     /*
36758     // xtype created footer. - not sure if will work as we normally have to render first..
36759     if (this.footer && !this.footer.el && this.footer.xtype) {
36760         if (!this.wrapEl) {
36761             this.wrapEl = this.el.wrap();
36762         }
36763     
36764         this.footer.container = this.wrapEl.createChild();
36765          
36766         this.footer = Roo.factory(this.footer, Roo);
36767         
36768     }
36769     */
36770     
36771      if(typeof config == "string"){
36772         this.title = config;
36773     }else{
36774         Roo.apply(this, config);
36775     }
36776     
36777     if(this.resizeEl){
36778         this.resizeEl = Roo.get(this.resizeEl, true);
36779     }else{
36780         this.resizeEl = this.el;
36781     }
36782     // handle view.xtype
36783     
36784  
36785     
36786     
36787     this.addEvents({
36788         /**
36789          * @event activate
36790          * Fires when this panel is activated. 
36791          * @param {Roo.ContentPanel} this
36792          */
36793         "activate" : true,
36794         /**
36795          * @event deactivate
36796          * Fires when this panel is activated. 
36797          * @param {Roo.ContentPanel} this
36798          */
36799         "deactivate" : true,
36800
36801         /**
36802          * @event resize
36803          * Fires when this panel is resized if fitToFrame is true.
36804          * @param {Roo.ContentPanel} this
36805          * @param {Number} width The width after any component adjustments
36806          * @param {Number} height The height after any component adjustments
36807          */
36808         "resize" : true,
36809         
36810          /**
36811          * @event render
36812          * Fires when this tab is created
36813          * @param {Roo.ContentPanel} this
36814          */
36815         "render" : true
36816         
36817         
36818         
36819     });
36820     
36821
36822     
36823     
36824     if(this.autoScroll){
36825         this.resizeEl.setStyle("overflow", "auto");
36826     } else {
36827         // fix randome scrolling
36828         //this.el.on('scroll', function() {
36829         //    Roo.log('fix random scolling');
36830         //    this.scrollTo('top',0); 
36831         //});
36832     }
36833     content = content || this.content;
36834     if(content){
36835         this.setContent(content);
36836     }
36837     if(config && config.url){
36838         this.setUrl(this.url, this.params, this.loadOnce);
36839     }
36840     
36841     
36842     
36843     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36844     
36845     if (this.view && typeof(this.view.xtype) != 'undefined') {
36846         this.view.el = this.el.appendChild(document.createElement("div"));
36847         this.view = Roo.factory(this.view); 
36848         this.view.render  &&  this.view.render(false, '');  
36849     }
36850     
36851     
36852     this.fireEvent('render', this);
36853 };
36854
36855 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36856     
36857     tabTip : '',
36858     
36859     setRegion : function(region){
36860         this.region = region;
36861         this.setActiveClass(region && !this.background);
36862     },
36863     
36864     
36865     setActiveClass: function(state)
36866     {
36867         if(state){
36868            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36869            this.el.setStyle('position','relative');
36870         }else{
36871            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36872            this.el.setStyle('position', 'absolute');
36873         } 
36874     },
36875     
36876     /**
36877      * Returns the toolbar for this Panel if one was configured. 
36878      * @return {Roo.Toolbar} 
36879      */
36880     getToolbar : function(){
36881         return this.toolbar;
36882     },
36883     
36884     setActiveState : function(active)
36885     {
36886         this.active = active;
36887         this.setActiveClass(active);
36888         if(!active){
36889             if(this.fireEvent("deactivate", this) === false){
36890                 return false;
36891             }
36892             return true;
36893         }
36894         this.fireEvent("activate", this);
36895         return true;
36896     },
36897     /**
36898      * Updates this panel's element
36899      * @param {String} content The new content
36900      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36901     */
36902     setContent : function(content, loadScripts){
36903         this.el.update(content, loadScripts);
36904     },
36905
36906     ignoreResize : function(w, h){
36907         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36908             return true;
36909         }else{
36910             this.lastSize = {width: w, height: h};
36911             return false;
36912         }
36913     },
36914     /**
36915      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36916      * @return {Roo.UpdateManager} The UpdateManager
36917      */
36918     getUpdateManager : function(){
36919         return this.el.getUpdateManager();
36920     },
36921      /**
36922      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36923      * @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:
36924 <pre><code>
36925 panel.load({
36926     url: "your-url.php",
36927     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36928     callback: yourFunction,
36929     scope: yourObject, //(optional scope)
36930     discardUrl: false,
36931     nocache: false,
36932     text: "Loading...",
36933     timeout: 30,
36934     scripts: false
36935 });
36936 </code></pre>
36937      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36938      * 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.
36939      * @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}
36940      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36941      * @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.
36942      * @return {Roo.ContentPanel} this
36943      */
36944     load : function(){
36945         var um = this.el.getUpdateManager();
36946         um.update.apply(um, arguments);
36947         return this;
36948     },
36949
36950
36951     /**
36952      * 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.
36953      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36954      * @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)
36955      * @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)
36956      * @return {Roo.UpdateManager} The UpdateManager
36957      */
36958     setUrl : function(url, params, loadOnce){
36959         if(this.refreshDelegate){
36960             this.removeListener("activate", this.refreshDelegate);
36961         }
36962         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36963         this.on("activate", this.refreshDelegate);
36964         return this.el.getUpdateManager();
36965     },
36966     
36967     _handleRefresh : function(url, params, loadOnce){
36968         if(!loadOnce || !this.loaded){
36969             var updater = this.el.getUpdateManager();
36970             updater.update(url, params, this._setLoaded.createDelegate(this));
36971         }
36972     },
36973     
36974     _setLoaded : function(){
36975         this.loaded = true;
36976     }, 
36977     
36978     /**
36979      * Returns this panel's id
36980      * @return {String} 
36981      */
36982     getId : function(){
36983         return this.el.id;
36984     },
36985     
36986     /** 
36987      * Returns this panel's element - used by regiosn to add.
36988      * @return {Roo.Element} 
36989      */
36990     getEl : function(){
36991         return this.wrapEl || this.el;
36992     },
36993     
36994    
36995     
36996     adjustForComponents : function(width, height)
36997     {
36998         //Roo.log('adjustForComponents ');
36999         if(this.resizeEl != this.el){
37000             width -= this.el.getFrameWidth('lr');
37001             height -= this.el.getFrameWidth('tb');
37002         }
37003         if(this.toolbar){
37004             var te = this.toolbar.getEl();
37005             te.setWidth(width);
37006             height -= te.getHeight();
37007         }
37008         if(this.footer){
37009             var te = this.footer.getEl();
37010             te.setWidth(width);
37011             height -= te.getHeight();
37012         }
37013         
37014         
37015         if(this.adjustments){
37016             width += this.adjustments[0];
37017             height += this.adjustments[1];
37018         }
37019         return {"width": width, "height": height};
37020     },
37021     
37022     setSize : function(width, height){
37023         if(this.fitToFrame && !this.ignoreResize(width, height)){
37024             if(this.fitContainer && this.resizeEl != this.el){
37025                 this.el.setSize(width, height);
37026             }
37027             var size = this.adjustForComponents(width, height);
37028             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37029             this.fireEvent('resize', this, size.width, size.height);
37030         }
37031     },
37032     
37033     /**
37034      * Returns this panel's title
37035      * @return {String} 
37036      */
37037     getTitle : function(){
37038         
37039         if (typeof(this.title) != 'object') {
37040             return this.title;
37041         }
37042         
37043         var t = '';
37044         for (var k in this.title) {
37045             if (!this.title.hasOwnProperty(k)) {
37046                 continue;
37047             }
37048             
37049             if (k.indexOf('-') >= 0) {
37050                 var s = k.split('-');
37051                 for (var i = 0; i<s.length; i++) {
37052                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37053                 }
37054             } else {
37055                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37056             }
37057         }
37058         return t;
37059     },
37060     
37061     /**
37062      * Set this panel's title
37063      * @param {String} title
37064      */
37065     setTitle : function(title){
37066         this.title = title;
37067         if(this.region){
37068             this.region.updatePanelTitle(this, title);
37069         }
37070     },
37071     
37072     /**
37073      * Returns true is this panel was configured to be closable
37074      * @return {Boolean} 
37075      */
37076     isClosable : function(){
37077         return this.closable;
37078     },
37079     
37080     beforeSlide : function(){
37081         this.el.clip();
37082         this.resizeEl.clip();
37083     },
37084     
37085     afterSlide : function(){
37086         this.el.unclip();
37087         this.resizeEl.unclip();
37088     },
37089     
37090     /**
37091      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37092      *   Will fail silently if the {@link #setUrl} method has not been called.
37093      *   This does not activate the panel, just updates its content.
37094      */
37095     refresh : function(){
37096         if(this.refreshDelegate){
37097            this.loaded = false;
37098            this.refreshDelegate();
37099         }
37100     },
37101     
37102     /**
37103      * Destroys this panel
37104      */
37105     destroy : function(){
37106         this.el.removeAllListeners();
37107         var tempEl = document.createElement("span");
37108         tempEl.appendChild(this.el.dom);
37109         tempEl.innerHTML = "";
37110         this.el.remove();
37111         this.el = null;
37112     },
37113     
37114     /**
37115      * form - if the content panel contains a form - this is a reference to it.
37116      * @type {Roo.form.Form}
37117      */
37118     form : false,
37119     /**
37120      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37121      *    This contains a reference to it.
37122      * @type {Roo.View}
37123      */
37124     view : false,
37125     
37126       /**
37127      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37128      * <pre><code>
37129
37130 layout.addxtype({
37131        xtype : 'Form',
37132        items: [ .... ]
37133    }
37134 );
37135
37136 </code></pre>
37137      * @param {Object} cfg Xtype definition of item to add.
37138      */
37139     
37140     
37141     getChildContainer: function () {
37142         return this.getEl();
37143     }
37144     
37145     
37146     /*
37147         var  ret = new Roo.factory(cfg);
37148         return ret;
37149         
37150         
37151         // add form..
37152         if (cfg.xtype.match(/^Form$/)) {
37153             
37154             var el;
37155             //if (this.footer) {
37156             //    el = this.footer.container.insertSibling(false, 'before');
37157             //} else {
37158                 el = this.el.createChild();
37159             //}
37160
37161             this.form = new  Roo.form.Form(cfg);
37162             
37163             
37164             if ( this.form.allItems.length) {
37165                 this.form.render(el.dom);
37166             }
37167             return this.form;
37168         }
37169         // should only have one of theses..
37170         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37171             // views.. should not be just added - used named prop 'view''
37172             
37173             cfg.el = this.el.appendChild(document.createElement("div"));
37174             // factory?
37175             
37176             var ret = new Roo.factory(cfg);
37177              
37178              ret.render && ret.render(false, ''); // render blank..
37179             this.view = ret;
37180             return ret;
37181         }
37182         return false;
37183     }
37184     \*/
37185 });
37186  
37187 /**
37188  * @class Roo.bootstrap.panel.Grid
37189  * @extends Roo.bootstrap.panel.Content
37190  * @constructor
37191  * Create a new GridPanel.
37192  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37193  * @param {Object} config A the config object
37194   
37195  */
37196
37197
37198
37199 Roo.bootstrap.panel.Grid = function(config)
37200 {
37201     
37202       
37203     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37204         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37205
37206     config.el = this.wrapper;
37207     //this.el = this.wrapper;
37208     
37209       if (config.container) {
37210         // ctor'ed from a Border/panel.grid
37211         
37212         
37213         this.wrapper.setStyle("overflow", "hidden");
37214         this.wrapper.addClass('roo-grid-container');
37215
37216     }
37217     
37218     
37219     if(config.toolbar){
37220         var tool_el = this.wrapper.createChild();    
37221         this.toolbar = Roo.factory(config.toolbar);
37222         var ti = [];
37223         if (config.toolbar.items) {
37224             ti = config.toolbar.items ;
37225             delete config.toolbar.items ;
37226         }
37227         
37228         var nitems = [];
37229         this.toolbar.render(tool_el);
37230         for(var i =0;i < ti.length;i++) {
37231           //  Roo.log(['add child', items[i]]);
37232             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37233         }
37234         this.toolbar.items = nitems;
37235         
37236         delete config.toolbar;
37237     }
37238     
37239     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37240     config.grid.scrollBody = true;;
37241     config.grid.monitorWindowResize = false; // turn off autosizing
37242     config.grid.autoHeight = false;
37243     config.grid.autoWidth = false;
37244     
37245     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37246     
37247     if (config.background) {
37248         // render grid on panel activation (if panel background)
37249         this.on('activate', function(gp) {
37250             if (!gp.grid.rendered) {
37251                 gp.grid.render(this.wrapper);
37252                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37253             }
37254         });
37255             
37256     } else {
37257         this.grid.render(this.wrapper);
37258         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37259
37260     }
37261     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37262     // ??? needed ??? config.el = this.wrapper;
37263     
37264     
37265     
37266   
37267     // xtype created footer. - not sure if will work as we normally have to render first..
37268     if (this.footer && !this.footer.el && this.footer.xtype) {
37269         
37270         var ctr = this.grid.getView().getFooterPanel(true);
37271         this.footer.dataSource = this.grid.dataSource;
37272         this.footer = Roo.factory(this.footer, Roo);
37273         this.footer.render(ctr);
37274         
37275     }
37276     
37277     
37278     
37279     
37280      
37281 };
37282
37283 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37284     getId : function(){
37285         return this.grid.id;
37286     },
37287     
37288     /**
37289      * Returns the grid for this panel
37290      * @return {Roo.bootstrap.Table} 
37291      */
37292     getGrid : function(){
37293         return this.grid;    
37294     },
37295     
37296     setSize : function(width, height){
37297         if(!this.ignoreResize(width, height)){
37298             var grid = this.grid;
37299             var size = this.adjustForComponents(width, height);
37300             var gridel = grid.getGridEl();
37301             gridel.setSize(size.width, size.height);
37302             /*
37303             var thd = grid.getGridEl().select('thead',true).first();
37304             var tbd = grid.getGridEl().select('tbody', true).first();
37305             if (tbd) {
37306                 tbd.setSize(width, height - thd.getHeight());
37307             }
37308             */
37309             grid.autoSize();
37310         }
37311     },
37312      
37313     
37314     
37315     beforeSlide : function(){
37316         this.grid.getView().scroller.clip();
37317     },
37318     
37319     afterSlide : function(){
37320         this.grid.getView().scroller.unclip();
37321     },
37322     
37323     destroy : function(){
37324         this.grid.destroy();
37325         delete this.grid;
37326         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37327     }
37328 });
37329
37330 /**
37331  * @class Roo.bootstrap.panel.Nest
37332  * @extends Roo.bootstrap.panel.Content
37333  * @constructor
37334  * Create a new Panel, that can contain a layout.Border.
37335  * 
37336  * 
37337  * @param {Roo.BorderLayout} layout The layout for this panel
37338  * @param {String/Object} config A string to set only the title or a config object
37339  */
37340 Roo.bootstrap.panel.Nest = function(config)
37341 {
37342     // construct with only one argument..
37343     /* FIXME - implement nicer consturctors
37344     if (layout.layout) {
37345         config = layout;
37346         layout = config.layout;
37347         delete config.layout;
37348     }
37349     if (layout.xtype && !layout.getEl) {
37350         // then layout needs constructing..
37351         layout = Roo.factory(layout, Roo);
37352     }
37353     */
37354     
37355     config.el =  config.layout.getEl();
37356     
37357     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37358     
37359     config.layout.monitorWindowResize = false; // turn off autosizing
37360     this.layout = config.layout;
37361     this.layout.getEl().addClass("roo-layout-nested-layout");
37362     
37363     
37364     
37365     
37366 };
37367
37368 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37369
37370     setSize : function(width, height){
37371         if(!this.ignoreResize(width, height)){
37372             var size = this.adjustForComponents(width, height);
37373             var el = this.layout.getEl();
37374             if (size.height < 1) {
37375                 el.setWidth(size.width);   
37376             } else {
37377                 el.setSize(size.width, size.height);
37378             }
37379             var touch = el.dom.offsetWidth;
37380             this.layout.layout();
37381             // ie requires a double layout on the first pass
37382             if(Roo.isIE && !this.initialized){
37383                 this.initialized = true;
37384                 this.layout.layout();
37385             }
37386         }
37387     },
37388     
37389     // activate all subpanels if not currently active..
37390     
37391     setActiveState : function(active){
37392         this.active = active;
37393         this.setActiveClass(active);
37394         
37395         if(!active){
37396             this.fireEvent("deactivate", this);
37397             return;
37398         }
37399         
37400         this.fireEvent("activate", this);
37401         // not sure if this should happen before or after..
37402         if (!this.layout) {
37403             return; // should not happen..
37404         }
37405         var reg = false;
37406         for (var r in this.layout.regions) {
37407             reg = this.layout.getRegion(r);
37408             if (reg.getActivePanel()) {
37409                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37410                 reg.setActivePanel(reg.getActivePanel());
37411                 continue;
37412             }
37413             if (!reg.panels.length) {
37414                 continue;
37415             }
37416             reg.showPanel(reg.getPanel(0));
37417         }
37418         
37419         
37420         
37421         
37422     },
37423     
37424     /**
37425      * Returns the nested BorderLayout for this panel
37426      * @return {Roo.BorderLayout} 
37427      */
37428     getLayout : function(){
37429         return this.layout;
37430     },
37431     
37432      /**
37433      * Adds a xtype elements to the layout of the nested panel
37434      * <pre><code>
37435
37436 panel.addxtype({
37437        xtype : 'ContentPanel',
37438        region: 'west',
37439        items: [ .... ]
37440    }
37441 );
37442
37443 panel.addxtype({
37444         xtype : 'NestedLayoutPanel',
37445         region: 'west',
37446         layout: {
37447            center: { },
37448            west: { }   
37449         },
37450         items : [ ... list of content panels or nested layout panels.. ]
37451    }
37452 );
37453 </code></pre>
37454      * @param {Object} cfg Xtype definition of item to add.
37455      */
37456     addxtype : function(cfg) {
37457         return this.layout.addxtype(cfg);
37458     
37459     }
37460 });        /*
37461  * Based on:
37462  * Ext JS Library 1.1.1
37463  * Copyright(c) 2006-2007, Ext JS, LLC.
37464  *
37465  * Originally Released Under LGPL - original licence link has changed is not relivant.
37466  *
37467  * Fork - LGPL
37468  * <script type="text/javascript">
37469  */
37470 /**
37471  * @class Roo.TabPanel
37472  * @extends Roo.util.Observable
37473  * A lightweight tab container.
37474  * <br><br>
37475  * Usage:
37476  * <pre><code>
37477 // basic tabs 1, built from existing content
37478 var tabs = new Roo.TabPanel("tabs1");
37479 tabs.addTab("script", "View Script");
37480 tabs.addTab("markup", "View Markup");
37481 tabs.activate("script");
37482
37483 // more advanced tabs, built from javascript
37484 var jtabs = new Roo.TabPanel("jtabs");
37485 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37486
37487 // set up the UpdateManager
37488 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37489 var updater = tab2.getUpdateManager();
37490 updater.setDefaultUrl("ajax1.htm");
37491 tab2.on('activate', updater.refresh, updater, true);
37492
37493 // Use setUrl for Ajax loading
37494 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37495 tab3.setUrl("ajax2.htm", null, true);
37496
37497 // Disabled tab
37498 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37499 tab4.disable();
37500
37501 jtabs.activate("jtabs-1");
37502  * </code></pre>
37503  * @constructor
37504  * Create a new TabPanel.
37505  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37506  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37507  */
37508 Roo.bootstrap.panel.Tabs = function(config){
37509     /**
37510     * The container element for this TabPanel.
37511     * @type Roo.Element
37512     */
37513     this.el = Roo.get(config.el);
37514     delete config.el;
37515     if(config){
37516         if(typeof config == "boolean"){
37517             this.tabPosition = config ? "bottom" : "top";
37518         }else{
37519             Roo.apply(this, config);
37520         }
37521     }
37522     
37523     if(this.tabPosition == "bottom"){
37524         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37525         this.el.addClass("roo-tabs-bottom");
37526     }
37527     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37528     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37529     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37530     if(Roo.isIE){
37531         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37532     }
37533     if(this.tabPosition != "bottom"){
37534         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37535          * @type Roo.Element
37536          */
37537         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37538         this.el.addClass("roo-tabs-top");
37539     }
37540     this.items = [];
37541
37542     this.bodyEl.setStyle("position", "relative");
37543
37544     this.active = null;
37545     this.activateDelegate = this.activate.createDelegate(this);
37546
37547     this.addEvents({
37548         /**
37549          * @event tabchange
37550          * Fires when the active tab changes
37551          * @param {Roo.TabPanel} this
37552          * @param {Roo.TabPanelItem} activePanel The new active tab
37553          */
37554         "tabchange": true,
37555         /**
37556          * @event beforetabchange
37557          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37558          * @param {Roo.TabPanel} this
37559          * @param {Object} e Set cancel to true on this object to cancel the tab change
37560          * @param {Roo.TabPanelItem} tab The tab being changed to
37561          */
37562         "beforetabchange" : true
37563     });
37564
37565     Roo.EventManager.onWindowResize(this.onResize, this);
37566     this.cpad = this.el.getPadding("lr");
37567     this.hiddenCount = 0;
37568
37569
37570     // toolbar on the tabbar support...
37571     if (this.toolbar) {
37572         alert("no toolbar support yet");
37573         this.toolbar  = false;
37574         /*
37575         var tcfg = this.toolbar;
37576         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37577         this.toolbar = new Roo.Toolbar(tcfg);
37578         if (Roo.isSafari) {
37579             var tbl = tcfg.container.child('table', true);
37580             tbl.setAttribute('width', '100%');
37581         }
37582         */
37583         
37584     }
37585    
37586
37587
37588     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37589 };
37590
37591 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37592     /*
37593      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37594      */
37595     tabPosition : "top",
37596     /*
37597      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37598      */
37599     currentTabWidth : 0,
37600     /*
37601      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37602      */
37603     minTabWidth : 40,
37604     /*
37605      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37606      */
37607     maxTabWidth : 250,
37608     /*
37609      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37610      */
37611     preferredTabWidth : 175,
37612     /*
37613      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37614      */
37615     resizeTabs : false,
37616     /*
37617      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37618      */
37619     monitorResize : true,
37620     /*
37621      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37622      */
37623     toolbar : false,
37624
37625     /**
37626      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37627      * @param {String} id The id of the div to use <b>or create</b>
37628      * @param {String} text The text for the tab
37629      * @param {String} content (optional) Content to put in the TabPanelItem body
37630      * @param {Boolean} closable (optional) True to create a close icon on the tab
37631      * @return {Roo.TabPanelItem} The created TabPanelItem
37632      */
37633     addTab : function(id, text, content, closable, tpl)
37634     {
37635         var item = new Roo.bootstrap.panel.TabItem({
37636             panel: this,
37637             id : id,
37638             text : text,
37639             closable : closable,
37640             tpl : tpl
37641         });
37642         this.addTabItem(item);
37643         if(content){
37644             item.setContent(content);
37645         }
37646         return item;
37647     },
37648
37649     /**
37650      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37651      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37652      * @return {Roo.TabPanelItem}
37653      */
37654     getTab : function(id){
37655         return this.items[id];
37656     },
37657
37658     /**
37659      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37660      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37661      */
37662     hideTab : function(id){
37663         var t = this.items[id];
37664         if(!t.isHidden()){
37665            t.setHidden(true);
37666            this.hiddenCount++;
37667            this.autoSizeTabs();
37668         }
37669     },
37670
37671     /**
37672      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37673      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37674      */
37675     unhideTab : function(id){
37676         var t = this.items[id];
37677         if(t.isHidden()){
37678            t.setHidden(false);
37679            this.hiddenCount--;
37680            this.autoSizeTabs();
37681         }
37682     },
37683
37684     /**
37685      * Adds an existing {@link Roo.TabPanelItem}.
37686      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37687      */
37688     addTabItem : function(item){
37689         this.items[item.id] = item;
37690         this.items.push(item);
37691       //  if(this.resizeTabs){
37692     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37693   //         this.autoSizeTabs();
37694 //        }else{
37695 //            item.autoSize();
37696        // }
37697     },
37698
37699     /**
37700      * Removes a {@link Roo.TabPanelItem}.
37701      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37702      */
37703     removeTab : function(id){
37704         var items = this.items;
37705         var tab = items[id];
37706         if(!tab) { return; }
37707         var index = items.indexOf(tab);
37708         if(this.active == tab && items.length > 1){
37709             var newTab = this.getNextAvailable(index);
37710             if(newTab) {
37711                 newTab.activate();
37712             }
37713         }
37714         this.stripEl.dom.removeChild(tab.pnode.dom);
37715         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37716             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37717         }
37718         items.splice(index, 1);
37719         delete this.items[tab.id];
37720         tab.fireEvent("close", tab);
37721         tab.purgeListeners();
37722         this.autoSizeTabs();
37723     },
37724
37725     getNextAvailable : function(start){
37726         var items = this.items;
37727         var index = start;
37728         // look for a next tab that will slide over to
37729         // replace the one being removed
37730         while(index < items.length){
37731             var item = items[++index];
37732             if(item && !item.isHidden()){
37733                 return item;
37734             }
37735         }
37736         // if one isn't found select the previous tab (on the left)
37737         index = start;
37738         while(index >= 0){
37739             var item = items[--index];
37740             if(item && !item.isHidden()){
37741                 return item;
37742             }
37743         }
37744         return null;
37745     },
37746
37747     /**
37748      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37749      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37750      */
37751     disableTab : function(id){
37752         var tab = this.items[id];
37753         if(tab && this.active != tab){
37754             tab.disable();
37755         }
37756     },
37757
37758     /**
37759      * Enables a {@link Roo.TabPanelItem} that is disabled.
37760      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37761      */
37762     enableTab : function(id){
37763         var tab = this.items[id];
37764         tab.enable();
37765     },
37766
37767     /**
37768      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37769      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37770      * @return {Roo.TabPanelItem} The TabPanelItem.
37771      */
37772     activate : function(id){
37773         var tab = this.items[id];
37774         if(!tab){
37775             return null;
37776         }
37777         if(tab == this.active || tab.disabled){
37778             return tab;
37779         }
37780         var e = {};
37781         this.fireEvent("beforetabchange", this, e, tab);
37782         if(e.cancel !== true && !tab.disabled){
37783             if(this.active){
37784                 this.active.hide();
37785             }
37786             this.active = this.items[id];
37787             this.active.show();
37788             this.fireEvent("tabchange", this, this.active);
37789         }
37790         return tab;
37791     },
37792
37793     /**
37794      * Gets the active {@link Roo.TabPanelItem}.
37795      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37796      */
37797     getActiveTab : function(){
37798         return this.active;
37799     },
37800
37801     /**
37802      * Updates the tab body element to fit the height of the container element
37803      * for overflow scrolling
37804      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37805      */
37806     syncHeight : function(targetHeight){
37807         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37808         var bm = this.bodyEl.getMargins();
37809         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37810         this.bodyEl.setHeight(newHeight);
37811         return newHeight;
37812     },
37813
37814     onResize : function(){
37815         if(this.monitorResize){
37816             this.autoSizeTabs();
37817         }
37818     },
37819
37820     /**
37821      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37822      */
37823     beginUpdate : function(){
37824         this.updating = true;
37825     },
37826
37827     /**
37828      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37829      */
37830     endUpdate : function(){
37831         this.updating = false;
37832         this.autoSizeTabs();
37833     },
37834
37835     /**
37836      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37837      */
37838     autoSizeTabs : function(){
37839         var count = this.items.length;
37840         var vcount = count - this.hiddenCount;
37841         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37842             return;
37843         }
37844         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37845         var availWidth = Math.floor(w / vcount);
37846         var b = this.stripBody;
37847         if(b.getWidth() > w){
37848             var tabs = this.items;
37849             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37850             if(availWidth < this.minTabWidth){
37851                 /*if(!this.sleft){    // incomplete scrolling code
37852                     this.createScrollButtons();
37853                 }
37854                 this.showScroll();
37855                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37856             }
37857         }else{
37858             if(this.currentTabWidth < this.preferredTabWidth){
37859                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37860             }
37861         }
37862     },
37863
37864     /**
37865      * Returns the number of tabs in this TabPanel.
37866      * @return {Number}
37867      */
37868      getCount : function(){
37869          return this.items.length;
37870      },
37871
37872     /**
37873      * Resizes all the tabs to the passed width
37874      * @param {Number} The new width
37875      */
37876     setTabWidth : function(width){
37877         this.currentTabWidth = width;
37878         for(var i = 0, len = this.items.length; i < len; i++) {
37879                 if(!this.items[i].isHidden()) {
37880                 this.items[i].setWidth(width);
37881             }
37882         }
37883     },
37884
37885     /**
37886      * Destroys this TabPanel
37887      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37888      */
37889     destroy : function(removeEl){
37890         Roo.EventManager.removeResizeListener(this.onResize, this);
37891         for(var i = 0, len = this.items.length; i < len; i++){
37892             this.items[i].purgeListeners();
37893         }
37894         if(removeEl === true){
37895             this.el.update("");
37896             this.el.remove();
37897         }
37898     },
37899     
37900     createStrip : function(container)
37901     {
37902         var strip = document.createElement("nav");
37903         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37904         container.appendChild(strip);
37905         return strip;
37906     },
37907     
37908     createStripList : function(strip)
37909     {
37910         // div wrapper for retard IE
37911         // returns the "tr" element.
37912         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37913         //'<div class="x-tabs-strip-wrap">'+
37914           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37915           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37916         return strip.firstChild; //.firstChild.firstChild.firstChild;
37917     },
37918     createBody : function(container)
37919     {
37920         var body = document.createElement("div");
37921         Roo.id(body, "tab-body");
37922         //Roo.fly(body).addClass("x-tabs-body");
37923         Roo.fly(body).addClass("tab-content");
37924         container.appendChild(body);
37925         return body;
37926     },
37927     createItemBody :function(bodyEl, id){
37928         var body = Roo.getDom(id);
37929         if(!body){
37930             body = document.createElement("div");
37931             body.id = id;
37932         }
37933         //Roo.fly(body).addClass("x-tabs-item-body");
37934         Roo.fly(body).addClass("tab-pane");
37935          bodyEl.insertBefore(body, bodyEl.firstChild);
37936         return body;
37937     },
37938     /** @private */
37939     createStripElements :  function(stripEl, text, closable, tpl)
37940     {
37941         var td = document.createElement("li"); // was td..
37942         
37943         
37944         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37945         
37946         
37947         stripEl.appendChild(td);
37948         /*if(closable){
37949             td.className = "x-tabs-closable";
37950             if(!this.closeTpl){
37951                 this.closeTpl = new Roo.Template(
37952                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37953                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37954                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37955                 );
37956             }
37957             var el = this.closeTpl.overwrite(td, {"text": text});
37958             var close = el.getElementsByTagName("div")[0];
37959             var inner = el.getElementsByTagName("em")[0];
37960             return {"el": el, "close": close, "inner": inner};
37961         } else {
37962         */
37963         // not sure what this is..
37964 //            if(!this.tabTpl){
37965                 //this.tabTpl = new Roo.Template(
37966                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37967                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37968                 //);
37969 //                this.tabTpl = new Roo.Template(
37970 //                   '<a href="#">' +
37971 //                   '<span unselectable="on"' +
37972 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37973 //                            ' >{text}</span></a>'
37974 //                );
37975 //                
37976 //            }
37977
37978
37979             var template = tpl || this.tabTpl || false;
37980             
37981             if(!template){
37982                 
37983                 template = new Roo.Template(
37984                    '<a href="#">' +
37985                    '<span unselectable="on"' +
37986                             (this.disableTooltips ? '' : ' title="{text}"') +
37987                             ' >{text}</span></a>'
37988                 );
37989             }
37990             
37991             switch (typeof(template)) {
37992                 case 'object' :
37993                     break;
37994                 case 'string' :
37995                     template = new Roo.Template(template);
37996                     break;
37997                 default :
37998                     break;
37999             }
38000             
38001             var el = template.overwrite(td, {"text": text});
38002             
38003             var inner = el.getElementsByTagName("span")[0];
38004             
38005             return {"el": el, "inner": inner};
38006             
38007     }
38008         
38009     
38010 });
38011
38012 /**
38013  * @class Roo.TabPanelItem
38014  * @extends Roo.util.Observable
38015  * Represents an individual item (tab plus body) in a TabPanel.
38016  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38017  * @param {String} id The id of this TabPanelItem
38018  * @param {String} text The text for the tab of this TabPanelItem
38019  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38020  */
38021 Roo.bootstrap.panel.TabItem = function(config){
38022     /**
38023      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38024      * @type Roo.TabPanel
38025      */
38026     this.tabPanel = config.panel;
38027     /**
38028      * The id for this TabPanelItem
38029      * @type String
38030      */
38031     this.id = config.id;
38032     /** @private */
38033     this.disabled = false;
38034     /** @private */
38035     this.text = config.text;
38036     /** @private */
38037     this.loaded = false;
38038     this.closable = config.closable;
38039
38040     /**
38041      * The body element for this TabPanelItem.
38042      * @type Roo.Element
38043      */
38044     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38045     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38046     this.bodyEl.setStyle("display", "block");
38047     this.bodyEl.setStyle("zoom", "1");
38048     //this.hideAction();
38049
38050     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38051     /** @private */
38052     this.el = Roo.get(els.el);
38053     this.inner = Roo.get(els.inner, true);
38054     this.textEl = Roo.get(this.el.dom.firstChild, true);
38055     this.pnode = Roo.get(els.el.parentNode, true);
38056 //    this.el.on("mousedown", this.onTabMouseDown, this);
38057     this.el.on("click", this.onTabClick, this);
38058     /** @private */
38059     if(config.closable){
38060         var c = Roo.get(els.close, true);
38061         c.dom.title = this.closeText;
38062         c.addClassOnOver("close-over");
38063         c.on("click", this.closeClick, this);
38064      }
38065
38066     this.addEvents({
38067          /**
38068          * @event activate
38069          * Fires when this tab becomes the active tab.
38070          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38071          * @param {Roo.TabPanelItem} this
38072          */
38073         "activate": true,
38074         /**
38075          * @event beforeclose
38076          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38077          * @param {Roo.TabPanelItem} this
38078          * @param {Object} e Set cancel to true on this object to cancel the close.
38079          */
38080         "beforeclose": true,
38081         /**
38082          * @event close
38083          * Fires when this tab is closed.
38084          * @param {Roo.TabPanelItem} this
38085          */
38086          "close": true,
38087         /**
38088          * @event deactivate
38089          * Fires when this tab is no longer the active tab.
38090          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38091          * @param {Roo.TabPanelItem} this
38092          */
38093          "deactivate" : true
38094     });
38095     this.hidden = false;
38096
38097     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38098 };
38099
38100 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38101            {
38102     purgeListeners : function(){
38103        Roo.util.Observable.prototype.purgeListeners.call(this);
38104        this.el.removeAllListeners();
38105     },
38106     /**
38107      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38108      */
38109     show : function(){
38110         this.pnode.addClass("active");
38111         this.showAction();
38112         if(Roo.isOpera){
38113             this.tabPanel.stripWrap.repaint();
38114         }
38115         this.fireEvent("activate", this.tabPanel, this);
38116     },
38117
38118     /**
38119      * Returns true if this tab is the active tab.
38120      * @return {Boolean}
38121      */
38122     isActive : function(){
38123         return this.tabPanel.getActiveTab() == this;
38124     },
38125
38126     /**
38127      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38128      */
38129     hide : function(){
38130         this.pnode.removeClass("active");
38131         this.hideAction();
38132         this.fireEvent("deactivate", this.tabPanel, this);
38133     },
38134
38135     hideAction : function(){
38136         this.bodyEl.hide();
38137         this.bodyEl.setStyle("position", "absolute");
38138         this.bodyEl.setLeft("-20000px");
38139         this.bodyEl.setTop("-20000px");
38140     },
38141
38142     showAction : function(){
38143         this.bodyEl.setStyle("position", "relative");
38144         this.bodyEl.setTop("");
38145         this.bodyEl.setLeft("");
38146         this.bodyEl.show();
38147     },
38148
38149     /**
38150      * Set the tooltip for the tab.
38151      * @param {String} tooltip The tab's tooltip
38152      */
38153     setTooltip : function(text){
38154         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38155             this.textEl.dom.qtip = text;
38156             this.textEl.dom.removeAttribute('title');
38157         }else{
38158             this.textEl.dom.title = text;
38159         }
38160     },
38161
38162     onTabClick : function(e){
38163         e.preventDefault();
38164         this.tabPanel.activate(this.id);
38165     },
38166
38167     onTabMouseDown : function(e){
38168         e.preventDefault();
38169         this.tabPanel.activate(this.id);
38170     },
38171 /*
38172     getWidth : function(){
38173         return this.inner.getWidth();
38174     },
38175
38176     setWidth : function(width){
38177         var iwidth = width - this.pnode.getPadding("lr");
38178         this.inner.setWidth(iwidth);
38179         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38180         this.pnode.setWidth(width);
38181     },
38182 */
38183     /**
38184      * Show or hide the tab
38185      * @param {Boolean} hidden True to hide or false to show.
38186      */
38187     setHidden : function(hidden){
38188         this.hidden = hidden;
38189         this.pnode.setStyle("display", hidden ? "none" : "");
38190     },
38191
38192     /**
38193      * Returns true if this tab is "hidden"
38194      * @return {Boolean}
38195      */
38196     isHidden : function(){
38197         return this.hidden;
38198     },
38199
38200     /**
38201      * Returns the text for this tab
38202      * @return {String}
38203      */
38204     getText : function(){
38205         return this.text;
38206     },
38207     /*
38208     autoSize : function(){
38209         //this.el.beginMeasure();
38210         this.textEl.setWidth(1);
38211         /*
38212          *  #2804 [new] Tabs in Roojs
38213          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38214          */
38215         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38216         //this.el.endMeasure();
38217     //},
38218
38219     /**
38220      * Sets the text for the tab (Note: this also sets the tooltip text)
38221      * @param {String} text The tab's text and tooltip
38222      */
38223     setText : function(text){
38224         this.text = text;
38225         this.textEl.update(text);
38226         this.setTooltip(text);
38227         //if(!this.tabPanel.resizeTabs){
38228         //    this.autoSize();
38229         //}
38230     },
38231     /**
38232      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38233      */
38234     activate : function(){
38235         this.tabPanel.activate(this.id);
38236     },
38237
38238     /**
38239      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38240      */
38241     disable : function(){
38242         if(this.tabPanel.active != this){
38243             this.disabled = true;
38244             this.pnode.addClass("disabled");
38245         }
38246     },
38247
38248     /**
38249      * Enables this TabPanelItem if it was previously disabled.
38250      */
38251     enable : function(){
38252         this.disabled = false;
38253         this.pnode.removeClass("disabled");
38254     },
38255
38256     /**
38257      * Sets the content for this TabPanelItem.
38258      * @param {String} content The content
38259      * @param {Boolean} loadScripts true to look for and load scripts
38260      */
38261     setContent : function(content, loadScripts){
38262         this.bodyEl.update(content, loadScripts);
38263     },
38264
38265     /**
38266      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38267      * @return {Roo.UpdateManager} The UpdateManager
38268      */
38269     getUpdateManager : function(){
38270         return this.bodyEl.getUpdateManager();
38271     },
38272
38273     /**
38274      * Set a URL to be used to load the content for this TabPanelItem.
38275      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38276      * @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)
38277      * @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)
38278      * @return {Roo.UpdateManager} The UpdateManager
38279      */
38280     setUrl : function(url, params, loadOnce){
38281         if(this.refreshDelegate){
38282             this.un('activate', this.refreshDelegate);
38283         }
38284         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38285         this.on("activate", this.refreshDelegate);
38286         return this.bodyEl.getUpdateManager();
38287     },
38288
38289     /** @private */
38290     _handleRefresh : function(url, params, loadOnce){
38291         if(!loadOnce || !this.loaded){
38292             var updater = this.bodyEl.getUpdateManager();
38293             updater.update(url, params, this._setLoaded.createDelegate(this));
38294         }
38295     },
38296
38297     /**
38298      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38299      *   Will fail silently if the setUrl method has not been called.
38300      *   This does not activate the panel, just updates its content.
38301      */
38302     refresh : function(){
38303         if(this.refreshDelegate){
38304            this.loaded = false;
38305            this.refreshDelegate();
38306         }
38307     },
38308
38309     /** @private */
38310     _setLoaded : function(){
38311         this.loaded = true;
38312     },
38313
38314     /** @private */
38315     closeClick : function(e){
38316         var o = {};
38317         e.stopEvent();
38318         this.fireEvent("beforeclose", this, o);
38319         if(o.cancel !== true){
38320             this.tabPanel.removeTab(this.id);
38321         }
38322     },
38323     /**
38324      * The text displayed in the tooltip for the close icon.
38325      * @type String
38326      */
38327     closeText : "Close this tab"
38328 });
38329 /**
38330 *    This script refer to:
38331 *    Title: International Telephone Input
38332 *    Author: Jack O'Connor
38333 *    Code version:  v12.1.12
38334 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38335 **/
38336
38337 Roo.bootstrap.PhoneInputData = function() {
38338     var d = [
38339       [
38340         "Afghanistan (‫افغانستان‬‎)",
38341         "af",
38342         "93"
38343       ],
38344       [
38345         "Albania (Shqipëri)",
38346         "al",
38347         "355"
38348       ],
38349       [
38350         "Algeria (‫الجزائر‬‎)",
38351         "dz",
38352         "213"
38353       ],
38354       [
38355         "American Samoa",
38356         "as",
38357         "1684"
38358       ],
38359       [
38360         "Andorra",
38361         "ad",
38362         "376"
38363       ],
38364       [
38365         "Angola",
38366         "ao",
38367         "244"
38368       ],
38369       [
38370         "Anguilla",
38371         "ai",
38372         "1264"
38373       ],
38374       [
38375         "Antigua and Barbuda",
38376         "ag",
38377         "1268"
38378       ],
38379       [
38380         "Argentina",
38381         "ar",
38382         "54"
38383       ],
38384       [
38385         "Armenia (Հայաստան)",
38386         "am",
38387         "374"
38388       ],
38389       [
38390         "Aruba",
38391         "aw",
38392         "297"
38393       ],
38394       [
38395         "Australia",
38396         "au",
38397         "61",
38398         0
38399       ],
38400       [
38401         "Austria (Österreich)",
38402         "at",
38403         "43"
38404       ],
38405       [
38406         "Azerbaijan (Azərbaycan)",
38407         "az",
38408         "994"
38409       ],
38410       [
38411         "Bahamas",
38412         "bs",
38413         "1242"
38414       ],
38415       [
38416         "Bahrain (‫البحرين‬‎)",
38417         "bh",
38418         "973"
38419       ],
38420       [
38421         "Bangladesh (বাংলাদেশ)",
38422         "bd",
38423         "880"
38424       ],
38425       [
38426         "Barbados",
38427         "bb",
38428         "1246"
38429       ],
38430       [
38431         "Belarus (Беларусь)",
38432         "by",
38433         "375"
38434       ],
38435       [
38436         "Belgium (België)",
38437         "be",
38438         "32"
38439       ],
38440       [
38441         "Belize",
38442         "bz",
38443         "501"
38444       ],
38445       [
38446         "Benin (Bénin)",
38447         "bj",
38448         "229"
38449       ],
38450       [
38451         "Bermuda",
38452         "bm",
38453         "1441"
38454       ],
38455       [
38456         "Bhutan (འབྲུག)",
38457         "bt",
38458         "975"
38459       ],
38460       [
38461         "Bolivia",
38462         "bo",
38463         "591"
38464       ],
38465       [
38466         "Bosnia and Herzegovina (Босна и Херцеговина)",
38467         "ba",
38468         "387"
38469       ],
38470       [
38471         "Botswana",
38472         "bw",
38473         "267"
38474       ],
38475       [
38476         "Brazil (Brasil)",
38477         "br",
38478         "55"
38479       ],
38480       [
38481         "British Indian Ocean Territory",
38482         "io",
38483         "246"
38484       ],
38485       [
38486         "British Virgin Islands",
38487         "vg",
38488         "1284"
38489       ],
38490       [
38491         "Brunei",
38492         "bn",
38493         "673"
38494       ],
38495       [
38496         "Bulgaria (България)",
38497         "bg",
38498         "359"
38499       ],
38500       [
38501         "Burkina Faso",
38502         "bf",
38503         "226"
38504       ],
38505       [
38506         "Burundi (Uburundi)",
38507         "bi",
38508         "257"
38509       ],
38510       [
38511         "Cambodia (កម្ពុជា)",
38512         "kh",
38513         "855"
38514       ],
38515       [
38516         "Cameroon (Cameroun)",
38517         "cm",
38518         "237"
38519       ],
38520       [
38521         "Canada",
38522         "ca",
38523         "1",
38524         1,
38525         ["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"]
38526       ],
38527       [
38528         "Cape Verde (Kabu Verdi)",
38529         "cv",
38530         "238"
38531       ],
38532       [
38533         "Caribbean Netherlands",
38534         "bq",
38535         "599",
38536         1
38537       ],
38538       [
38539         "Cayman Islands",
38540         "ky",
38541         "1345"
38542       ],
38543       [
38544         "Central African Republic (République centrafricaine)",
38545         "cf",
38546         "236"
38547       ],
38548       [
38549         "Chad (Tchad)",
38550         "td",
38551         "235"
38552       ],
38553       [
38554         "Chile",
38555         "cl",
38556         "56"
38557       ],
38558       [
38559         "China (中国)",
38560         "cn",
38561         "86"
38562       ],
38563       [
38564         "Christmas Island",
38565         "cx",
38566         "61",
38567         2
38568       ],
38569       [
38570         "Cocos (Keeling) Islands",
38571         "cc",
38572         "61",
38573         1
38574       ],
38575       [
38576         "Colombia",
38577         "co",
38578         "57"
38579       ],
38580       [
38581         "Comoros (‫جزر القمر‬‎)",
38582         "km",
38583         "269"
38584       ],
38585       [
38586         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38587         "cd",
38588         "243"
38589       ],
38590       [
38591         "Congo (Republic) (Congo-Brazzaville)",
38592         "cg",
38593         "242"
38594       ],
38595       [
38596         "Cook Islands",
38597         "ck",
38598         "682"
38599       ],
38600       [
38601         "Costa Rica",
38602         "cr",
38603         "506"
38604       ],
38605       [
38606         "Côte d’Ivoire",
38607         "ci",
38608         "225"
38609       ],
38610       [
38611         "Croatia (Hrvatska)",
38612         "hr",
38613         "385"
38614       ],
38615       [
38616         "Cuba",
38617         "cu",
38618         "53"
38619       ],
38620       [
38621         "Curaçao",
38622         "cw",
38623         "599",
38624         0
38625       ],
38626       [
38627         "Cyprus (Κύπρος)",
38628         "cy",
38629         "357"
38630       ],
38631       [
38632         "Czech Republic (Česká republika)",
38633         "cz",
38634         "420"
38635       ],
38636       [
38637         "Denmark (Danmark)",
38638         "dk",
38639         "45"
38640       ],
38641       [
38642         "Djibouti",
38643         "dj",
38644         "253"
38645       ],
38646       [
38647         "Dominica",
38648         "dm",
38649         "1767"
38650       ],
38651       [
38652         "Dominican Republic (República Dominicana)",
38653         "do",
38654         "1",
38655         2,
38656         ["809", "829", "849"]
38657       ],
38658       [
38659         "Ecuador",
38660         "ec",
38661         "593"
38662       ],
38663       [
38664         "Egypt (‫مصر‬‎)",
38665         "eg",
38666         "20"
38667       ],
38668       [
38669         "El Salvador",
38670         "sv",
38671         "503"
38672       ],
38673       [
38674         "Equatorial Guinea (Guinea Ecuatorial)",
38675         "gq",
38676         "240"
38677       ],
38678       [
38679         "Eritrea",
38680         "er",
38681         "291"
38682       ],
38683       [
38684         "Estonia (Eesti)",
38685         "ee",
38686         "372"
38687       ],
38688       [
38689         "Ethiopia",
38690         "et",
38691         "251"
38692       ],
38693       [
38694         "Falkland Islands (Islas Malvinas)",
38695         "fk",
38696         "500"
38697       ],
38698       [
38699         "Faroe Islands (Føroyar)",
38700         "fo",
38701         "298"
38702       ],
38703       [
38704         "Fiji",
38705         "fj",
38706         "679"
38707       ],
38708       [
38709         "Finland (Suomi)",
38710         "fi",
38711         "358",
38712         0
38713       ],
38714       [
38715         "France",
38716         "fr",
38717         "33"
38718       ],
38719       [
38720         "French Guiana (Guyane française)",
38721         "gf",
38722         "594"
38723       ],
38724       [
38725         "French Polynesia (Polynésie française)",
38726         "pf",
38727         "689"
38728       ],
38729       [
38730         "Gabon",
38731         "ga",
38732         "241"
38733       ],
38734       [
38735         "Gambia",
38736         "gm",
38737         "220"
38738       ],
38739       [
38740         "Georgia (საქართველო)",
38741         "ge",
38742         "995"
38743       ],
38744       [
38745         "Germany (Deutschland)",
38746         "de",
38747         "49"
38748       ],
38749       [
38750         "Ghana (Gaana)",
38751         "gh",
38752         "233"
38753       ],
38754       [
38755         "Gibraltar",
38756         "gi",
38757         "350"
38758       ],
38759       [
38760         "Greece (Ελλάδα)",
38761         "gr",
38762         "30"
38763       ],
38764       [
38765         "Greenland (Kalaallit Nunaat)",
38766         "gl",
38767         "299"
38768       ],
38769       [
38770         "Grenada",
38771         "gd",
38772         "1473"
38773       ],
38774       [
38775         "Guadeloupe",
38776         "gp",
38777         "590",
38778         0
38779       ],
38780       [
38781         "Guam",
38782         "gu",
38783         "1671"
38784       ],
38785       [
38786         "Guatemala",
38787         "gt",
38788         "502"
38789       ],
38790       [
38791         "Guernsey",
38792         "gg",
38793         "44",
38794         1
38795       ],
38796       [
38797         "Guinea (Guinée)",
38798         "gn",
38799         "224"
38800       ],
38801       [
38802         "Guinea-Bissau (Guiné Bissau)",
38803         "gw",
38804         "245"
38805       ],
38806       [
38807         "Guyana",
38808         "gy",
38809         "592"
38810       ],
38811       [
38812         "Haiti",
38813         "ht",
38814         "509"
38815       ],
38816       [
38817         "Honduras",
38818         "hn",
38819         "504"
38820       ],
38821       [
38822         "Hong Kong (香港)",
38823         "hk",
38824         "852"
38825       ],
38826       [
38827         "Hungary (Magyarország)",
38828         "hu",
38829         "36"
38830       ],
38831       [
38832         "Iceland (Ísland)",
38833         "is",
38834         "354"
38835       ],
38836       [
38837         "India (भारत)",
38838         "in",
38839         "91"
38840       ],
38841       [
38842         "Indonesia",
38843         "id",
38844         "62"
38845       ],
38846       [
38847         "Iran (‫ایران‬‎)",
38848         "ir",
38849         "98"
38850       ],
38851       [
38852         "Iraq (‫العراق‬‎)",
38853         "iq",
38854         "964"
38855       ],
38856       [
38857         "Ireland",
38858         "ie",
38859         "353"
38860       ],
38861       [
38862         "Isle of Man",
38863         "im",
38864         "44",
38865         2
38866       ],
38867       [
38868         "Israel (‫ישראל‬‎)",
38869         "il",
38870         "972"
38871       ],
38872       [
38873         "Italy (Italia)",
38874         "it",
38875         "39",
38876         0
38877       ],
38878       [
38879         "Jamaica",
38880         "jm",
38881         "1876"
38882       ],
38883       [
38884         "Japan (日本)",
38885         "jp",
38886         "81"
38887       ],
38888       [
38889         "Jersey",
38890         "je",
38891         "44",
38892         3
38893       ],
38894       [
38895         "Jordan (‫الأردن‬‎)",
38896         "jo",
38897         "962"
38898       ],
38899       [
38900         "Kazakhstan (Казахстан)",
38901         "kz",
38902         "7",
38903         1
38904       ],
38905       [
38906         "Kenya",
38907         "ke",
38908         "254"
38909       ],
38910       [
38911         "Kiribati",
38912         "ki",
38913         "686"
38914       ],
38915       [
38916         "Kosovo",
38917         "xk",
38918         "383"
38919       ],
38920       [
38921         "Kuwait (‫الكويت‬‎)",
38922         "kw",
38923         "965"
38924       ],
38925       [
38926         "Kyrgyzstan (Кыргызстан)",
38927         "kg",
38928         "996"
38929       ],
38930       [
38931         "Laos (ລາວ)",
38932         "la",
38933         "856"
38934       ],
38935       [
38936         "Latvia (Latvija)",
38937         "lv",
38938         "371"
38939       ],
38940       [
38941         "Lebanon (‫لبنان‬‎)",
38942         "lb",
38943         "961"
38944       ],
38945       [
38946         "Lesotho",
38947         "ls",
38948         "266"
38949       ],
38950       [
38951         "Liberia",
38952         "lr",
38953         "231"
38954       ],
38955       [
38956         "Libya (‫ليبيا‬‎)",
38957         "ly",
38958         "218"
38959       ],
38960       [
38961         "Liechtenstein",
38962         "li",
38963         "423"
38964       ],
38965       [
38966         "Lithuania (Lietuva)",
38967         "lt",
38968         "370"
38969       ],
38970       [
38971         "Luxembourg",
38972         "lu",
38973         "352"
38974       ],
38975       [
38976         "Macau (澳門)",
38977         "mo",
38978         "853"
38979       ],
38980       [
38981         "Macedonia (FYROM) (Македонија)",
38982         "mk",
38983         "389"
38984       ],
38985       [
38986         "Madagascar (Madagasikara)",
38987         "mg",
38988         "261"
38989       ],
38990       [
38991         "Malawi",
38992         "mw",
38993         "265"
38994       ],
38995       [
38996         "Malaysia",
38997         "my",
38998         "60"
38999       ],
39000       [
39001         "Maldives",
39002         "mv",
39003         "960"
39004       ],
39005       [
39006         "Mali",
39007         "ml",
39008         "223"
39009       ],
39010       [
39011         "Malta",
39012         "mt",
39013         "356"
39014       ],
39015       [
39016         "Marshall Islands",
39017         "mh",
39018         "692"
39019       ],
39020       [
39021         "Martinique",
39022         "mq",
39023         "596"
39024       ],
39025       [
39026         "Mauritania (‫موريتانيا‬‎)",
39027         "mr",
39028         "222"
39029       ],
39030       [
39031         "Mauritius (Moris)",
39032         "mu",
39033         "230"
39034       ],
39035       [
39036         "Mayotte",
39037         "yt",
39038         "262",
39039         1
39040       ],
39041       [
39042         "Mexico (México)",
39043         "mx",
39044         "52"
39045       ],
39046       [
39047         "Micronesia",
39048         "fm",
39049         "691"
39050       ],
39051       [
39052         "Moldova (Republica Moldova)",
39053         "md",
39054         "373"
39055       ],
39056       [
39057         "Monaco",
39058         "mc",
39059         "377"
39060       ],
39061       [
39062         "Mongolia (Монгол)",
39063         "mn",
39064         "976"
39065       ],
39066       [
39067         "Montenegro (Crna Gora)",
39068         "me",
39069         "382"
39070       ],
39071       [
39072         "Montserrat",
39073         "ms",
39074         "1664"
39075       ],
39076       [
39077         "Morocco (‫المغرب‬‎)",
39078         "ma",
39079         "212",
39080         0
39081       ],
39082       [
39083         "Mozambique (Moçambique)",
39084         "mz",
39085         "258"
39086       ],
39087       [
39088         "Myanmar (Burma) (မြန်မာ)",
39089         "mm",
39090         "95"
39091       ],
39092       [
39093         "Namibia (Namibië)",
39094         "na",
39095         "264"
39096       ],
39097       [
39098         "Nauru",
39099         "nr",
39100         "674"
39101       ],
39102       [
39103         "Nepal (नेपाल)",
39104         "np",
39105         "977"
39106       ],
39107       [
39108         "Netherlands (Nederland)",
39109         "nl",
39110         "31"
39111       ],
39112       [
39113         "New Caledonia (Nouvelle-Calédonie)",
39114         "nc",
39115         "687"
39116       ],
39117       [
39118         "New Zealand",
39119         "nz",
39120         "64"
39121       ],
39122       [
39123         "Nicaragua",
39124         "ni",
39125         "505"
39126       ],
39127       [
39128         "Niger (Nijar)",
39129         "ne",
39130         "227"
39131       ],
39132       [
39133         "Nigeria",
39134         "ng",
39135         "234"
39136       ],
39137       [
39138         "Niue",
39139         "nu",
39140         "683"
39141       ],
39142       [
39143         "Norfolk Island",
39144         "nf",
39145         "672"
39146       ],
39147       [
39148         "North Korea (조선 민주주의 인민 공화국)",
39149         "kp",
39150         "850"
39151       ],
39152       [
39153         "Northern Mariana Islands",
39154         "mp",
39155         "1670"
39156       ],
39157       [
39158         "Norway (Norge)",
39159         "no",
39160         "47",
39161         0
39162       ],
39163       [
39164         "Oman (‫عُمان‬‎)",
39165         "om",
39166         "968"
39167       ],
39168       [
39169         "Pakistan (‫پاکستان‬‎)",
39170         "pk",
39171         "92"
39172       ],
39173       [
39174         "Palau",
39175         "pw",
39176         "680"
39177       ],
39178       [
39179         "Palestine (‫فلسطين‬‎)",
39180         "ps",
39181         "970"
39182       ],
39183       [
39184         "Panama (Panamá)",
39185         "pa",
39186         "507"
39187       ],
39188       [
39189         "Papua New Guinea",
39190         "pg",
39191         "675"
39192       ],
39193       [
39194         "Paraguay",
39195         "py",
39196         "595"
39197       ],
39198       [
39199         "Peru (Perú)",
39200         "pe",
39201         "51"
39202       ],
39203       [
39204         "Philippines",
39205         "ph",
39206         "63"
39207       ],
39208       [
39209         "Poland (Polska)",
39210         "pl",
39211         "48"
39212       ],
39213       [
39214         "Portugal",
39215         "pt",
39216         "351"
39217       ],
39218       [
39219         "Puerto Rico",
39220         "pr",
39221         "1",
39222         3,
39223         ["787", "939"]
39224       ],
39225       [
39226         "Qatar (‫قطر‬‎)",
39227         "qa",
39228         "974"
39229       ],
39230       [
39231         "Réunion (La Réunion)",
39232         "re",
39233         "262",
39234         0
39235       ],
39236       [
39237         "Romania (România)",
39238         "ro",
39239         "40"
39240       ],
39241       [
39242         "Russia (Россия)",
39243         "ru",
39244         "7",
39245         0
39246       ],
39247       [
39248         "Rwanda",
39249         "rw",
39250         "250"
39251       ],
39252       [
39253         "Saint Barthélemy",
39254         "bl",
39255         "590",
39256         1
39257       ],
39258       [
39259         "Saint Helena",
39260         "sh",
39261         "290"
39262       ],
39263       [
39264         "Saint Kitts and Nevis",
39265         "kn",
39266         "1869"
39267       ],
39268       [
39269         "Saint Lucia",
39270         "lc",
39271         "1758"
39272       ],
39273       [
39274         "Saint Martin (Saint-Martin (partie française))",
39275         "mf",
39276         "590",
39277         2
39278       ],
39279       [
39280         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39281         "pm",
39282         "508"
39283       ],
39284       [
39285         "Saint Vincent and the Grenadines",
39286         "vc",
39287         "1784"
39288       ],
39289       [
39290         "Samoa",
39291         "ws",
39292         "685"
39293       ],
39294       [
39295         "San Marino",
39296         "sm",
39297         "378"
39298       ],
39299       [
39300         "São Tomé and Príncipe (São Tomé e Príncipe)",
39301         "st",
39302         "239"
39303       ],
39304       [
39305         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39306         "sa",
39307         "966"
39308       ],
39309       [
39310         "Senegal (Sénégal)",
39311         "sn",
39312         "221"
39313       ],
39314       [
39315         "Serbia (Србија)",
39316         "rs",
39317         "381"
39318       ],
39319       [
39320         "Seychelles",
39321         "sc",
39322         "248"
39323       ],
39324       [
39325         "Sierra Leone",
39326         "sl",
39327         "232"
39328       ],
39329       [
39330         "Singapore",
39331         "sg",
39332         "65"
39333       ],
39334       [
39335         "Sint Maarten",
39336         "sx",
39337         "1721"
39338       ],
39339       [
39340         "Slovakia (Slovensko)",
39341         "sk",
39342         "421"
39343       ],
39344       [
39345         "Slovenia (Slovenija)",
39346         "si",
39347         "386"
39348       ],
39349       [
39350         "Solomon Islands",
39351         "sb",
39352         "677"
39353       ],
39354       [
39355         "Somalia (Soomaaliya)",
39356         "so",
39357         "252"
39358       ],
39359       [
39360         "South Africa",
39361         "za",
39362         "27"
39363       ],
39364       [
39365         "South Korea (대한민국)",
39366         "kr",
39367         "82"
39368       ],
39369       [
39370         "South Sudan (‫جنوب السودان‬‎)",
39371         "ss",
39372         "211"
39373       ],
39374       [
39375         "Spain (España)",
39376         "es",
39377         "34"
39378       ],
39379       [
39380         "Sri Lanka (ශ්‍රී ලංකාව)",
39381         "lk",
39382         "94"
39383       ],
39384       [
39385         "Sudan (‫السودان‬‎)",
39386         "sd",
39387         "249"
39388       ],
39389       [
39390         "Suriname",
39391         "sr",
39392         "597"
39393       ],
39394       [
39395         "Svalbard and Jan Mayen",
39396         "sj",
39397         "47",
39398         1
39399       ],
39400       [
39401         "Swaziland",
39402         "sz",
39403         "268"
39404       ],
39405       [
39406         "Sweden (Sverige)",
39407         "se",
39408         "46"
39409       ],
39410       [
39411         "Switzerland (Schweiz)",
39412         "ch",
39413         "41"
39414       ],
39415       [
39416         "Syria (‫سوريا‬‎)",
39417         "sy",
39418         "963"
39419       ],
39420       [
39421         "Taiwan (台灣)",
39422         "tw",
39423         "886"
39424       ],
39425       [
39426         "Tajikistan",
39427         "tj",
39428         "992"
39429       ],
39430       [
39431         "Tanzania",
39432         "tz",
39433         "255"
39434       ],
39435       [
39436         "Thailand (ไทย)",
39437         "th",
39438         "66"
39439       ],
39440       [
39441         "Timor-Leste",
39442         "tl",
39443         "670"
39444       ],
39445       [
39446         "Togo",
39447         "tg",
39448         "228"
39449       ],
39450       [
39451         "Tokelau",
39452         "tk",
39453         "690"
39454       ],
39455       [
39456         "Tonga",
39457         "to",
39458         "676"
39459       ],
39460       [
39461         "Trinidad and Tobago",
39462         "tt",
39463         "1868"
39464       ],
39465       [
39466         "Tunisia (‫تونس‬‎)",
39467         "tn",
39468         "216"
39469       ],
39470       [
39471         "Turkey (Türkiye)",
39472         "tr",
39473         "90"
39474       ],
39475       [
39476         "Turkmenistan",
39477         "tm",
39478         "993"
39479       ],
39480       [
39481         "Turks and Caicos Islands",
39482         "tc",
39483         "1649"
39484       ],
39485       [
39486         "Tuvalu",
39487         "tv",
39488         "688"
39489       ],
39490       [
39491         "U.S. Virgin Islands",
39492         "vi",
39493         "1340"
39494       ],
39495       [
39496         "Uganda",
39497         "ug",
39498         "256"
39499       ],
39500       [
39501         "Ukraine (Україна)",
39502         "ua",
39503         "380"
39504       ],
39505       [
39506         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39507         "ae",
39508         "971"
39509       ],
39510       [
39511         "United Kingdom",
39512         "gb",
39513         "44",
39514         0
39515       ],
39516       [
39517         "United States",
39518         "us",
39519         "1",
39520         0
39521       ],
39522       [
39523         "Uruguay",
39524         "uy",
39525         "598"
39526       ],
39527       [
39528         "Uzbekistan (Oʻzbekiston)",
39529         "uz",
39530         "998"
39531       ],
39532       [
39533         "Vanuatu",
39534         "vu",
39535         "678"
39536       ],
39537       [
39538         "Vatican City (Città del Vaticano)",
39539         "va",
39540         "39",
39541         1
39542       ],
39543       [
39544         "Venezuela",
39545         "ve",
39546         "58"
39547       ],
39548       [
39549         "Vietnam (Việt Nam)",
39550         "vn",
39551         "84"
39552       ],
39553       [
39554         "Wallis and Futuna (Wallis-et-Futuna)",
39555         "wf",
39556         "681"
39557       ],
39558       [
39559         "Western Sahara (‫الصحراء الغربية‬‎)",
39560         "eh",
39561         "212",
39562         1
39563       ],
39564       [
39565         "Yemen (‫اليمن‬‎)",
39566         "ye",
39567         "967"
39568       ],
39569       [
39570         "Zambia",
39571         "zm",
39572         "260"
39573       ],
39574       [
39575         "Zimbabwe",
39576         "zw",
39577         "263"
39578       ],
39579       [
39580         "Åland Islands",
39581         "ax",
39582         "358",
39583         1
39584       ]
39585   ];
39586   
39587   return d;
39588 }/**
39589 *    This script refer to:
39590 *    Title: International Telephone Input
39591 *    Author: Jack O'Connor
39592 *    Code version:  v12.1.12
39593 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39594 **/
39595
39596 /**
39597  * @class Roo.bootstrap.PhoneInput
39598  * @extends Roo.bootstrap.TriggerField
39599  * An input with International dial-code selection
39600  
39601  * @cfg {String} defaultDialCode default '+852'
39602  * @cfg {Array} preferedCountries default []
39603   
39604  * @constructor
39605  * Create a new PhoneInput.
39606  * @param {Object} config Configuration options
39607  */
39608
39609 Roo.bootstrap.PhoneInput = function(config) {
39610     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39611 };
39612
39613 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39614         
39615         listWidth: undefined,
39616         
39617         selectedClass: 'active',
39618         
39619         invalidClass : "has-warning",
39620         
39621         validClass: 'has-success',
39622         
39623         allowed: '0123456789',
39624         
39625         /**
39626          * @cfg {String} defaultDialCode The default dial code when initializing the input
39627          */
39628         defaultDialCode: '+852',
39629         
39630         /**
39631          * @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
39632          */
39633         preferedCountries: false,
39634         
39635         getAutoCreate : function()
39636         {
39637             var data = Roo.bootstrap.PhoneInputData();
39638             var align = this.labelAlign || this.parentLabelAlign();
39639             var id = Roo.id();
39640             
39641             this.allCountries = [];
39642             this.dialCodeMapping = [];
39643             
39644             for (var i = 0; i < data.length; i++) {
39645               var c = data[i];
39646               this.allCountries[i] = {
39647                 name: c[0],
39648                 iso2: c[1],
39649                 dialCode: c[2],
39650                 priority: c[3] || 0,
39651                 areaCodes: c[4] || null
39652               };
39653               this.dialCodeMapping[c[2]] = {
39654                   name: c[0],
39655                   iso2: c[1],
39656                   priority: c[3] || 0,
39657                   areaCodes: c[4] || null
39658               };
39659             }
39660             
39661             var cfg = {
39662                 cls: 'form-group',
39663                 cn: []
39664             };
39665             
39666             var input =  {
39667                 tag: 'input',
39668                 id : id,
39669                 cls : 'form-control tel-input',
39670                 autocomplete: 'new-password'
39671             };
39672             
39673             var hiddenInput = {
39674                 tag: 'input',
39675                 type: 'hidden',
39676                 cls: 'hidden-tel-input'
39677             };
39678             
39679             if (this.name) {
39680                 hiddenInput.name = this.name;
39681             }
39682             
39683             if (this.disabled) {
39684                 input.disabled = true;
39685             }
39686             
39687             var flag_container = {
39688                 tag: 'div',
39689                 cls: 'flag-box',
39690                 cn: [
39691                     {
39692                         tag: 'div',
39693                         cls: 'flag'
39694                     },
39695                     {
39696                         tag: 'div',
39697                         cls: 'caret'
39698                     }
39699                 ]
39700             };
39701             
39702             var box = {
39703                 tag: 'div',
39704                 cls: this.hasFeedback ? 'has-feedback' : '',
39705                 cn: [
39706                     hiddenInput,
39707                     input,
39708                     {
39709                         tag: 'input',
39710                         cls: 'dial-code-holder',
39711                         disabled: true
39712                     }
39713                 ]
39714             };
39715             
39716             var container = {
39717                 cls: 'roo-select2-container input-group',
39718                 cn: [
39719                     flag_container,
39720                     box
39721                 ]
39722             };
39723             
39724             if (this.fieldLabel.length) {
39725                 var indicator = {
39726                     tag: 'i',
39727                     tooltip: 'This field is required'
39728                 };
39729                 
39730                 var label = {
39731                     tag: 'label',
39732                     'for':  id,
39733                     cls: 'control-label',
39734                     cn: []
39735                 };
39736                 
39737                 var label_text = {
39738                     tag: 'span',
39739                     html: this.fieldLabel
39740                 };
39741                 
39742                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39743                 label.cn = [
39744                     indicator,
39745                     label_text
39746                 ];
39747                 
39748                 if(this.indicatorpos == 'right') {
39749                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39750                     label.cn = [
39751                         label_text,
39752                         indicator
39753                     ];
39754                 }
39755                 
39756                 if(align == 'left') {
39757                     container = {
39758                         tag: 'div',
39759                         cn: [
39760                             container
39761                         ]
39762                     };
39763                     
39764                     if(this.labelWidth > 12){
39765                         label.style = "width: " + this.labelWidth + 'px';
39766                     }
39767                     if(this.labelWidth < 13 && this.labelmd == 0){
39768                         this.labelmd = this.labelWidth;
39769                     }
39770                     if(this.labellg > 0){
39771                         label.cls += ' col-lg-' + this.labellg;
39772                         input.cls += ' col-lg-' + (12 - this.labellg);
39773                     }
39774                     if(this.labelmd > 0){
39775                         label.cls += ' col-md-' + this.labelmd;
39776                         container.cls += ' col-md-' + (12 - this.labelmd);
39777                     }
39778                     if(this.labelsm > 0){
39779                         label.cls += ' col-sm-' + this.labelsm;
39780                         container.cls += ' col-sm-' + (12 - this.labelsm);
39781                     }
39782                     if(this.labelxs > 0){
39783                         label.cls += ' col-xs-' + this.labelxs;
39784                         container.cls += ' col-xs-' + (12 - this.labelxs);
39785                     }
39786                 }
39787             }
39788             
39789             cfg.cn = [
39790                 label,
39791                 container
39792             ];
39793             
39794             var settings = this;
39795             
39796             ['xs','sm','md','lg'].map(function(size){
39797                 if (settings[size]) {
39798                     cfg.cls += ' col-' + size + '-' + settings[size];
39799                 }
39800             });
39801             
39802             this.store = new Roo.data.Store({
39803                 proxy : new Roo.data.MemoryProxy({}),
39804                 reader : new Roo.data.JsonReader({
39805                     fields : [
39806                         {
39807                             'name' : 'name',
39808                             'type' : 'string'
39809                         },
39810                         {
39811                             'name' : 'iso2',
39812                             'type' : 'string'
39813                         },
39814                         {
39815                             'name' : 'dialCode',
39816                             'type' : 'string'
39817                         },
39818                         {
39819                             'name' : 'priority',
39820                             'type' : 'string'
39821                         },
39822                         {
39823                             'name' : 'areaCodes',
39824                             'type' : 'string'
39825                         }
39826                     ]
39827                 })
39828             });
39829             
39830             if(!this.preferedCountries) {
39831                 this.preferedCountries = [
39832                     'hk',
39833                     'gb',
39834                     'us'
39835                 ];
39836             }
39837             
39838             var p = this.preferedCountries.reverse();
39839             
39840             if(p) {
39841                 for (var i = 0; i < p.length; i++) {
39842                     for (var j = 0; j < this.allCountries.length; j++) {
39843                         if(this.allCountries[j].iso2 == p[i]) {
39844                             var t = this.allCountries[j];
39845                             this.allCountries.splice(j,1);
39846                             this.allCountries.unshift(t);
39847                         }
39848                     } 
39849                 }
39850             }
39851             
39852             this.store.proxy.data = {
39853                 success: true,
39854                 data: this.allCountries
39855             };
39856             
39857             return cfg;
39858         },
39859         
39860         initEvents : function()
39861         {
39862             this.createList();
39863             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39864             
39865             this.indicator = this.indicatorEl();
39866             this.flag = this.flagEl();
39867             this.dialCodeHolder = this.dialCodeHolderEl();
39868             
39869             this.trigger = this.el.select('div.flag-box',true).first();
39870             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39871             
39872             var _this = this;
39873             
39874             (function(){
39875                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39876                 _this.list.setWidth(lw);
39877             }).defer(100);
39878             
39879             this.list.on('mouseover', this.onViewOver, this);
39880             this.list.on('mousemove', this.onViewMove, this);
39881             this.inputEl().on("keyup", this.onKeyUp, this);
39882             
39883             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39884
39885             this.view = new Roo.View(this.list, this.tpl, {
39886                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39887             });
39888             
39889             this.view.on('click', this.onViewClick, this);
39890             this.setValue(this.defaultDialCode);
39891         },
39892         
39893         onTriggerClick : function(e)
39894         {
39895             Roo.log('trigger click');
39896             if(this.disabled){
39897                 return;
39898             }
39899             
39900             if(this.isExpanded()){
39901                 this.collapse();
39902                 this.hasFocus = false;
39903             }else {
39904                 this.store.load({});
39905                 this.hasFocus = true;
39906                 this.expand();
39907             }
39908         },
39909         
39910         isExpanded : function()
39911         {
39912             return this.list.isVisible();
39913         },
39914         
39915         collapse : function()
39916         {
39917             if(!this.isExpanded()){
39918                 return;
39919             }
39920             this.list.hide();
39921             Roo.get(document).un('mousedown', this.collapseIf, this);
39922             Roo.get(document).un('mousewheel', this.collapseIf, this);
39923             this.fireEvent('collapse', this);
39924             this.validate();
39925         },
39926         
39927         expand : function()
39928         {
39929             Roo.log('expand');
39930
39931             if(this.isExpanded() || !this.hasFocus){
39932                 return;
39933             }
39934             
39935             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39936             this.list.setWidth(lw);
39937             
39938             this.list.show();
39939             this.restrictHeight();
39940             
39941             Roo.get(document).on('mousedown', this.collapseIf, this);
39942             Roo.get(document).on('mousewheel', this.collapseIf, this);
39943             
39944             this.fireEvent('expand', this);
39945         },
39946         
39947         restrictHeight : function()
39948         {
39949             this.list.alignTo(this.inputEl(), this.listAlign);
39950             this.list.alignTo(this.inputEl(), this.listAlign);
39951         },
39952         
39953         onViewOver : function(e, t)
39954         {
39955             if(this.inKeyMode){
39956                 return;
39957             }
39958             var item = this.view.findItemFromChild(t);
39959             
39960             if(item){
39961                 var index = this.view.indexOf(item);
39962                 this.select(index, false);
39963             }
39964         },
39965
39966         // private
39967         onViewClick : function(view, doFocus, el, e)
39968         {
39969             var index = this.view.getSelectedIndexes()[0];
39970             
39971             var r = this.store.getAt(index);
39972             
39973             if(r){
39974                 this.onSelect(r, index);
39975             }
39976             if(doFocus !== false && !this.blockFocus){
39977                 this.inputEl().focus();
39978             }
39979         },
39980         
39981         onViewMove : function(e, t)
39982         {
39983             this.inKeyMode = false;
39984         },
39985         
39986         select : function(index, scrollIntoView)
39987         {
39988             this.selectedIndex = index;
39989             this.view.select(index);
39990             if(scrollIntoView !== false){
39991                 var el = this.view.getNode(index);
39992                 if(el){
39993                     this.list.scrollChildIntoView(el, false);
39994                 }
39995             }
39996         },
39997         
39998         createList : function()
39999         {
40000             this.list = Roo.get(document.body).createChild({
40001                 tag: 'ul',
40002                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40003                 style: 'display:none'
40004             });
40005             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
40006         },
40007         
40008         collapseIf : function(e)
40009         {
40010             var in_combo  = e.within(this.el);
40011             var in_list =  e.within(this.list);
40012             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40013             
40014             if (in_combo || in_list || is_list) {
40015                 return;
40016             }
40017             this.collapse();
40018         },
40019         
40020         onSelect : function(record, index)
40021         {
40022             if(this.fireEvent('beforeselect', this, record, index) !== false){
40023                 
40024                 this.setFlagClass(record.data.iso2);
40025                 this.setDialCode(record.data.dialCode);
40026                 this.hasFocus = false;
40027                 this.collapse();
40028                 this.fireEvent('select', this, record, index);
40029             }
40030         },
40031         
40032         flagEl : function()
40033         {
40034             var flag = this.el.select('div.flag',true).first();
40035             if(!flag){
40036                 return false;
40037             }
40038             return flag;
40039         },
40040         
40041         dialCodeHolderEl : function()
40042         {
40043             var d = this.el.select('input.dial-code-holder',true).first();
40044             if(!d){
40045                 return false;
40046             }
40047             return d;
40048         },
40049         
40050         setDialCode : function(v)
40051         {
40052             this.dialCodeHolder.dom.value = '+'+v;
40053         },
40054         
40055         setFlagClass : function(n)
40056         {
40057             this.flag.dom.className = 'flag '+n;
40058         },
40059         
40060         getValue : function()
40061         {
40062             var v = this.inputEl().getValue();
40063             if(this.dialCodeHolder) {
40064                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40065             }
40066             return v;
40067         },
40068         
40069         setValue : function(v)
40070         {
40071             var d = this.getDialCode(v);
40072             
40073             //invalid dial code
40074             if(v.length == 0 || !d || d.length == 0) {
40075                 if(this.rendered){
40076                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40077                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40078                 }
40079                 return;
40080             }
40081             
40082             //valid dial code
40083             this.setFlagClass(this.dialCodeMapping[d].iso2);
40084             this.setDialCode(d);
40085             this.inputEl().dom.value = v.replace('+'+d,'');
40086             this.hiddenEl().dom.value = this.getValue();
40087             
40088             this.validate();
40089         },
40090         
40091         getDialCode : function(v = '')
40092         {
40093             if (v.length == 0) {
40094                 return this.dialCodeHolder.dom.value;
40095             }
40096             
40097             var dialCode = "";
40098             if (v.charAt(0) != "+") {
40099                 return false;
40100             }
40101             var numericChars = "";
40102             for (var i = 1; i < v.length; i++) {
40103               var c = v.charAt(i);
40104               if (!isNaN(c)) {
40105                 numericChars += c;
40106                 if (this.dialCodeMapping[numericChars]) {
40107                   dialCode = v.substr(1, i);
40108                 }
40109                 if (numericChars.length == 4) {
40110                   break;
40111                 }
40112               }
40113             }
40114             return dialCode;
40115         },
40116         
40117         reset : function()
40118         {
40119             this.setValue(this.defaultDialCode);
40120             this.validate();
40121         },
40122         
40123         hiddenEl : function()
40124         {
40125             return this.el.select('input.hidden-tel-input',true).first();
40126         },
40127         
40128         onKeyUp : function(e){
40129             
40130             var k = e.getKey();
40131             var c = e.getCharCode();
40132             
40133             if(
40134                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40135                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40136             ){
40137                 e.stopEvent();
40138             }
40139             
40140             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40141             //     return;
40142             // }
40143             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40144                 e.stopEvent();
40145             }
40146             
40147             this.setValue(this.getValue());
40148         }
40149         
40150 });
40151 /**
40152  * @class Roo.bootstrap.MoneyField
40153  * @extends Roo.bootstrap.ComboBox
40154  * Bootstrap MoneyField class
40155  * 
40156  * @constructor
40157  * Create a new MoneyField.
40158  * @param {Object} config Configuration options
40159  */
40160
40161 Roo.bootstrap.MoneyField = function(config) {
40162     
40163     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40164     
40165 };
40166
40167 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40168     
40169     /**
40170      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40171      */
40172     allowDecimals : true,
40173     /**
40174      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40175      */
40176     decimalSeparator : ".",
40177     /**
40178      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40179      */
40180     decimalPrecision : 0,
40181     /**
40182      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40183      */
40184     allowNegative : true,
40185     /**
40186      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40187      */
40188     minValue : Number.NEGATIVE_INFINITY,
40189     /**
40190      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40191      */
40192     maxValue : Number.MAX_VALUE,
40193     /**
40194      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40195      */
40196     minText : "The minimum value for this field is {0}",
40197     /**
40198      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40199      */
40200     maxText : "The maximum value for this field is {0}",
40201     /**
40202      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40203      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40204      */
40205     nanText : "{0} is not a valid number",
40206     /**
40207      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40208      */
40209     castInt : true,
40210     /**
40211      * @cfg {String} defaults currency of the MoneyField
40212      * value should be in lkey
40213      */
40214     defaultCurrency : false,
40215     /**
40216      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40217      */
40218     thousandsDelimiter : false,
40219     
40220     
40221     inputlg : 9,
40222     inputmd : 9,
40223     inputsm : 9,
40224     inputxs : 6,
40225     
40226     store : false,
40227     
40228     getAutoCreate : function()
40229     {
40230         var align = this.labelAlign || this.parentLabelAlign();
40231         
40232         var id = Roo.id();
40233
40234         var cfg = {
40235             cls: 'form-group',
40236             cn: []
40237         };
40238
40239         var input =  {
40240             tag: 'input',
40241             id : id,
40242             cls : 'form-control roo-money-amount-input',
40243             autocomplete: 'new-password'
40244         };
40245         
40246         var hiddenInput = {
40247             tag: 'input',
40248             type: 'hidden',
40249             id: Roo.id(),
40250             cls: 'hidden-number-input'
40251         };
40252         
40253         if (this.name) {
40254             hiddenInput.name = this.name;
40255         }
40256
40257         if (this.disabled) {
40258             input.disabled = true;
40259         }
40260
40261         var clg = 12 - this.inputlg;
40262         var cmd = 12 - this.inputmd;
40263         var csm = 12 - this.inputsm;
40264         var cxs = 12 - this.inputxs;
40265         
40266         var container = {
40267             tag : 'div',
40268             cls : 'row roo-money-field',
40269             cn : [
40270                 {
40271                     tag : 'div',
40272                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40273                     cn : [
40274                         {
40275                             tag : 'div',
40276                             cls: 'roo-select2-container input-group',
40277                             cn: [
40278                                 {
40279                                     tag : 'input',
40280                                     cls : 'form-control roo-money-currency-input',
40281                                     autocomplete: 'new-password',
40282                                     readOnly : 1,
40283                                     name : this.currencyName
40284                                 },
40285                                 {
40286                                     tag :'span',
40287                                     cls : 'input-group-addon',
40288                                     cn : [
40289                                         {
40290                                             tag: 'span',
40291                                             cls: 'caret'
40292                                         }
40293                                     ]
40294                                 }
40295                             ]
40296                         }
40297                     ]
40298                 },
40299                 {
40300                     tag : 'div',
40301                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40302                     cn : [
40303                         {
40304                             tag: 'div',
40305                             cls: this.hasFeedback ? 'has-feedback' : '',
40306                             cn: [
40307                                 input
40308                             ]
40309                         }
40310                     ]
40311                 }
40312             ]
40313             
40314         };
40315         
40316         if (this.fieldLabel.length) {
40317             var indicator = {
40318                 tag: 'i',
40319                 tooltip: 'This field is required'
40320             };
40321
40322             var label = {
40323                 tag: 'label',
40324                 'for':  id,
40325                 cls: 'control-label',
40326                 cn: []
40327             };
40328
40329             var label_text = {
40330                 tag: 'span',
40331                 html: this.fieldLabel
40332             };
40333
40334             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40335             label.cn = [
40336                 indicator,
40337                 label_text
40338             ];
40339
40340             if(this.indicatorpos == 'right') {
40341                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40342                 label.cn = [
40343                     label_text,
40344                     indicator
40345                 ];
40346             }
40347
40348             if(align == 'left') {
40349                 container = {
40350                     tag: 'div',
40351                     cn: [
40352                         container
40353                     ]
40354                 };
40355
40356                 if(this.labelWidth > 12){
40357                     label.style = "width: " + this.labelWidth + 'px';
40358                 }
40359                 if(this.labelWidth < 13 && this.labelmd == 0){
40360                     this.labelmd = this.labelWidth;
40361                 }
40362                 if(this.labellg > 0){
40363                     label.cls += ' col-lg-' + this.labellg;
40364                     input.cls += ' col-lg-' + (12 - this.labellg);
40365                 }
40366                 if(this.labelmd > 0){
40367                     label.cls += ' col-md-' + this.labelmd;
40368                     container.cls += ' col-md-' + (12 - this.labelmd);
40369                 }
40370                 if(this.labelsm > 0){
40371                     label.cls += ' col-sm-' + this.labelsm;
40372                     container.cls += ' col-sm-' + (12 - this.labelsm);
40373                 }
40374                 if(this.labelxs > 0){
40375                     label.cls += ' col-xs-' + this.labelxs;
40376                     container.cls += ' col-xs-' + (12 - this.labelxs);
40377                 }
40378             }
40379         }
40380
40381         cfg.cn = [
40382             label,
40383             container,
40384             hiddenInput
40385         ];
40386         
40387         var settings = this;
40388
40389         ['xs','sm','md','lg'].map(function(size){
40390             if (settings[size]) {
40391                 cfg.cls += ' col-' + size + '-' + settings[size];
40392             }
40393         });
40394         
40395         return cfg;
40396     },
40397     
40398     initEvents : function()
40399     {
40400         this.indicator = this.indicatorEl();
40401         
40402         this.initCurrencyEvent();
40403         
40404         this.initNumberEvent();
40405     },
40406     
40407     initCurrencyEvent : function()
40408     {
40409         if (!this.store) {
40410             throw "can not find store for combo";
40411         }
40412         
40413         this.store = Roo.factory(this.store, Roo.data);
40414         this.store.parent = this;
40415         
40416         this.createList();
40417         
40418         this.triggerEl = this.el.select('.input-group-addon', true).first();
40419         
40420         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40421         
40422         var _this = this;
40423         
40424         (function(){
40425             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40426             _this.list.setWidth(lw);
40427         }).defer(100);
40428         
40429         this.list.on('mouseover', this.onViewOver, this);
40430         this.list.on('mousemove', this.onViewMove, this);
40431         this.list.on('scroll', this.onViewScroll, this);
40432         
40433         if(!this.tpl){
40434             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40435         }
40436         
40437         this.view = new Roo.View(this.list, this.tpl, {
40438             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40439         });
40440         
40441         this.view.on('click', this.onViewClick, this);
40442         
40443         this.store.on('beforeload', this.onBeforeLoad, this);
40444         this.store.on('load', this.onLoad, this);
40445         this.store.on('loadexception', this.onLoadException, this);
40446         
40447         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40448             "up" : function(e){
40449                 this.inKeyMode = true;
40450                 this.selectPrev();
40451             },
40452
40453             "down" : function(e){
40454                 if(!this.isExpanded()){
40455                     this.onTriggerClick();
40456                 }else{
40457                     this.inKeyMode = true;
40458                     this.selectNext();
40459                 }
40460             },
40461
40462             "enter" : function(e){
40463                 this.collapse();
40464                 
40465                 if(this.fireEvent("specialkey", this, e)){
40466                     this.onViewClick(false);
40467                 }
40468                 
40469                 return true;
40470             },
40471
40472             "esc" : function(e){
40473                 this.collapse();
40474             },
40475
40476             "tab" : function(e){
40477                 this.collapse();
40478                 
40479                 if(this.fireEvent("specialkey", this, e)){
40480                     this.onViewClick(false);
40481                 }
40482                 
40483                 return true;
40484             },
40485
40486             scope : this,
40487
40488             doRelay : function(foo, bar, hname){
40489                 if(hname == 'down' || this.scope.isExpanded()){
40490                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40491                 }
40492                 return true;
40493             },
40494
40495             forceKeyDown: true
40496         });
40497         
40498         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40499         
40500     },
40501     
40502     initNumberEvent : function(e)
40503     {
40504         this.inputEl().on("keydown" , this.fireKey,  this);
40505         this.inputEl().on("focus", this.onFocus,  this);
40506         this.inputEl().on("blur", this.onBlur,  this);
40507         
40508         this.inputEl().relayEvent('keyup', this);
40509         
40510         if(this.indicator){
40511             this.indicator.addClass('invisible');
40512         }
40513  
40514         this.originalValue = this.getValue();
40515         
40516         if(this.validationEvent == 'keyup'){
40517             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40518             this.inputEl().on('keyup', this.filterValidation, this);
40519         }
40520         else if(this.validationEvent !== false){
40521             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40522         }
40523         
40524         if(this.selectOnFocus){
40525             this.on("focus", this.preFocus, this);
40526             
40527         }
40528         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40529             this.inputEl().on("keypress", this.filterKeys, this);
40530         } else {
40531             this.inputEl().relayEvent('keypress', this);
40532         }
40533         
40534         var allowed = "0123456789";
40535         
40536         if(this.allowDecimals){
40537             allowed += this.decimalSeparator;
40538         }
40539         
40540         if(this.allowNegative){
40541             allowed += "-";
40542         }
40543         
40544         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40545         
40546         var keyPress = function(e){
40547             
40548             var k = e.getKey();
40549             
40550             var c = e.getCharCode();
40551             
40552             if(
40553                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40554                     allowed.indexOf(String.fromCharCode(c)) === -1
40555             ){
40556                 e.stopEvent();
40557                 return;
40558             }
40559             
40560             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40561                 return;
40562             }
40563             
40564             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40565                 e.stopEvent();
40566             }
40567         };
40568         
40569         this.inputEl().on("keypress", keyPress, this);
40570         
40571     },
40572     
40573     onTriggerClick : function(e)
40574     {   
40575         if(this.disabled){
40576             return;
40577         }
40578         
40579         this.page = 0;
40580         this.loadNext = false;
40581         
40582         if(this.isExpanded()){
40583             this.collapse();
40584             return;
40585         }
40586         
40587         this.hasFocus = true;
40588         
40589         if(this.triggerAction == 'all') {
40590             this.doQuery(this.allQuery, true);
40591             return;
40592         }
40593         
40594         this.doQuery(this.getRawValue());
40595     },
40596     
40597     getCurrency : function()
40598     {   
40599         var v = this.currencyEl().getValue();
40600         
40601         return v;
40602     },
40603     
40604     restrictHeight : function()
40605     {
40606         this.list.alignTo(this.currencyEl(), this.listAlign);
40607         this.list.alignTo(this.currencyEl(), this.listAlign);
40608     },
40609     
40610     onViewClick : function(view, doFocus, el, e)
40611     {
40612         var index = this.view.getSelectedIndexes()[0];
40613         
40614         var r = this.store.getAt(index);
40615         
40616         if(r){
40617             this.onSelect(r, index);
40618         }
40619     },
40620     
40621     onSelect : function(record, index){
40622         
40623         if(this.fireEvent('beforeselect', this, record, index) !== false){
40624         
40625             this.setFromCurrencyData(index > -1 ? record.data : false);
40626             
40627             this.collapse();
40628             
40629             this.fireEvent('select', this, record, index);
40630         }
40631     },
40632     
40633     setFromCurrencyData : function(o)
40634     {
40635         var currency = '';
40636         
40637         this.lastCurrency = o;
40638         
40639         if (this.currencyField) {
40640             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40641         } else {
40642             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40643         }
40644         
40645         this.lastSelectionText = currency;
40646         
40647         //setting default currency
40648         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40649             this.setCurrency(this.defaultCurrency);
40650             return;
40651         }
40652         
40653         this.setCurrency(currency);
40654     },
40655     
40656     setFromData : function(o)
40657     {
40658         var c = {};
40659         
40660         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40661         
40662         this.setFromCurrencyData(c);
40663         
40664         var value = '';
40665         
40666         if (this.name) {
40667             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40668         } else {
40669             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40670         }
40671         
40672         this.setValue(value);
40673         
40674     },
40675     
40676     setCurrency : function(v)
40677     {   
40678         this.currencyValue = v;
40679         
40680         if(this.rendered){
40681             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40682             this.validate();
40683         }
40684     },
40685     
40686     setValue : function(v)
40687     {
40688         v = this.fixPrecision(v);
40689         
40690         v = String(v).replace(".", this.decimalSeparator);
40691         
40692         this.value = v;
40693         
40694         if(this.rendered){
40695             
40696             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40697             
40698             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40699                 this.thousandsDelimiter || ','
40700             );
40701             
40702             if(this.allowBlank && !v) {
40703                 this.inputEl().dom.value = '';
40704             }
40705             
40706             this.validate();
40707         }
40708     },
40709     
40710     getRawValue : function()
40711     {
40712         var v = this.inputEl().getValue();
40713         
40714         return v;
40715     },
40716     
40717     getValue : function()
40718     {
40719         return this.fixPrecision(this.parseValue(this.getRawValue()));
40720     },
40721     
40722     parseValue : function(value)
40723     {
40724         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40725         return isNaN(value) ? '' : value;
40726     },
40727     
40728     fixPrecision : function(value)
40729     {
40730         var nan = isNaN(value);
40731         
40732         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40733             return nan ? '' : value;
40734         }
40735         
40736         return parseFloat(value).toFixed(this.decimalPrecision);
40737     },
40738     
40739     decimalPrecisionFcn : function(v)
40740     {
40741         return Math.floor(v);
40742     },
40743     
40744     validateValue : function(value)
40745     {
40746         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40747             return false;
40748         }
40749         
40750         var num = this.parseValue(value);
40751         
40752         if(isNaN(num)){
40753             this.markInvalid(String.format(this.nanText, value));
40754             return false;
40755         }
40756         
40757         if(num < this.minValue){
40758             this.markInvalid(String.format(this.minText, this.minValue));
40759             return false;
40760         }
40761         
40762         if(num > this.maxValue){
40763             this.markInvalid(String.format(this.maxText, this.maxValue));
40764             return false;
40765         }
40766         
40767         return true;
40768     },
40769     
40770     validate : function()
40771     {
40772         if(this.disabled || this.allowBlank){
40773             this.markValid();
40774             return true;
40775         }
40776         
40777         var currency = this.getCurrency();
40778         
40779         if(this.validateValue(this.getRawValue()) && currency.length){
40780             this.markValid();
40781             return true;
40782         }
40783         
40784         this.markInvalid();
40785         return false;
40786     },
40787     
40788     getName: function()
40789     {
40790         return this.name;
40791     },
40792     
40793     beforeBlur : function()
40794     {
40795         if(!this.castInt){
40796             return;
40797         }
40798         
40799         var v = this.parseValue(this.getRawValue());
40800         
40801         if(v || v == 0){
40802             this.setValue(v);
40803         }
40804     },
40805     
40806     onBlur : function()
40807     {
40808         this.beforeBlur();
40809         
40810         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40811             //this.el.removeClass(this.focusClass);
40812         }
40813         
40814         this.hasFocus = false;
40815         
40816         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40817             this.validate();
40818         }
40819         
40820         var v = this.getValue();
40821         
40822         if(String(v) !== String(this.startValue)){
40823             this.fireEvent('change', this, v, this.startValue);
40824         }
40825         
40826         this.fireEvent("blur", this);
40827     },
40828     
40829     inputEl : function()
40830     {
40831         return this.el.select('.roo-money-amount-input', true).first();
40832     },
40833     
40834     currencyEl : function()
40835     {
40836         return this.el.select('.roo-money-currency-input', true).first();
40837     },
40838     
40839     hiddenEl : function()
40840     {
40841         return this.el.select('input.hidden-number-input',true).first();
40842     }
40843     
40844 });