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     },
6175     
6176     onContextMenu : function(e, t)
6177     {
6178         this.processEvent("contextmenu", e);
6179     },
6180     
6181     processEvent : function(name, e)
6182     {
6183         if (name != 'touchstart' ) {
6184             this.fireEvent(name, e);    
6185         }
6186         
6187         var t = e.getTarget();
6188         
6189         var cell = Roo.get(t);
6190         
6191         if(!cell){
6192             return;
6193         }
6194         
6195         if(cell.findParent('tfoot', false, true)){
6196             return;
6197         }
6198         
6199         if(cell.findParent('thead', false, true)){
6200             
6201             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6202                 cell = Roo.get(t).findParent('th', false, true);
6203                 if (!cell) {
6204                     Roo.log("failed to find th in thead?");
6205                     Roo.log(e.getTarget());
6206                     return;
6207                 }
6208             }
6209             
6210             var cellIndex = cell.dom.cellIndex;
6211             
6212             var ename = name == 'touchstart' ? 'click' : name;
6213             this.fireEvent("header" + ename, this, cellIndex, e);
6214             
6215             return;
6216         }
6217         
6218         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219             cell = Roo.get(t).findParent('td', false, true);
6220             if (!cell) {
6221                 Roo.log("failed to find th in tbody?");
6222                 Roo.log(e.getTarget());
6223                 return;
6224             }
6225         }
6226         
6227         var row = cell.findParent('tr', false, true);
6228         var cellIndex = cell.dom.cellIndex;
6229         var rowIndex = row.dom.rowIndex - 1;
6230         
6231         if(row !== false){
6232             
6233             this.fireEvent("row" + name, this, rowIndex, e);
6234             
6235             if(cell !== false){
6236             
6237                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6238             }
6239         }
6240         
6241     },
6242     
6243     onMouseover : function(e, el)
6244     {
6245         var cell = Roo.get(el);
6246         
6247         if(!cell){
6248             return;
6249         }
6250         
6251         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6252             cell = cell.findParent('td', false, true);
6253         }
6254         
6255         var row = cell.findParent('tr', false, true);
6256         var cellIndex = cell.dom.cellIndex;
6257         var rowIndex = row.dom.rowIndex - 1; // start from 0
6258         
6259         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6260         
6261     },
6262     
6263     onMouseout : function(e, el)
6264     {
6265         var cell = Roo.get(el);
6266         
6267         if(!cell){
6268             return;
6269         }
6270         
6271         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6272             cell = cell.findParent('td', false, true);
6273         }
6274         
6275         var row = cell.findParent('tr', false, true);
6276         var cellIndex = cell.dom.cellIndex;
6277         var rowIndex = row.dom.rowIndex - 1; // start from 0
6278         
6279         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6280         
6281     },
6282     
6283     onClick : function(e, el)
6284     {
6285         var cell = Roo.get(el);
6286         
6287         if(!cell || (!this.cellSelection && !this.rowSelection)){
6288             return;
6289         }
6290         
6291         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6292             cell = cell.findParent('td', false, true);
6293         }
6294         
6295         if(!cell || typeof(cell) == 'undefined'){
6296             return;
6297         }
6298         
6299         var row = cell.findParent('tr', false, true);
6300         
6301         if(!row || typeof(row) == 'undefined'){
6302             return;
6303         }
6304         
6305         var cellIndex = cell.dom.cellIndex;
6306         var rowIndex = this.getRowIndex(row);
6307         
6308         // why??? - should these not be based on SelectionModel?
6309         if(this.cellSelection){
6310             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6311         }
6312         
6313         if(this.rowSelection){
6314             this.fireEvent('rowclick', this, row, rowIndex, e);
6315         }
6316         
6317         
6318     },
6319         
6320     onDblClick : function(e,el)
6321     {
6322         var cell = Roo.get(el);
6323         
6324         if(!cell || (!this.cellSelection && !this.rowSelection)){
6325             return;
6326         }
6327         
6328         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6329             cell = cell.findParent('td', false, true);
6330         }
6331         
6332         if(!cell || typeof(cell) == 'undefined'){
6333             return;
6334         }
6335         
6336         var row = cell.findParent('tr', false, true);
6337         
6338         if(!row || typeof(row) == 'undefined'){
6339             return;
6340         }
6341         
6342         var cellIndex = cell.dom.cellIndex;
6343         var rowIndex = this.getRowIndex(row);
6344         
6345         if(this.cellSelection){
6346             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6347         }
6348         
6349         if(this.rowSelection){
6350             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6351         }
6352     },
6353     
6354     sort : function(e,el)
6355     {
6356         var col = Roo.get(el);
6357         
6358         if(!col.hasClass('sortable')){
6359             return;
6360         }
6361         
6362         var sort = col.attr('sort');
6363         var dir = 'ASC';
6364         
6365         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6366             dir = 'DESC';
6367         }
6368         
6369         this.store.sortInfo = {field : sort, direction : dir};
6370         
6371         if (this.footer) {
6372             Roo.log("calling footer first");
6373             this.footer.onClick('first');
6374         } else {
6375         
6376             this.store.load({ params : { start : 0 } });
6377         }
6378     },
6379     
6380     renderHeader : function()
6381     {
6382         var header = {
6383             tag: 'thead',
6384             cn : []
6385         };
6386         
6387         var cm = this.cm;
6388         this.totalWidth = 0;
6389         
6390         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6391             
6392             var config = cm.config[i];
6393             
6394             var c = {
6395                 tag: 'th',
6396                 style : '',
6397                 html: cm.getColumnHeader(i)
6398             };
6399             
6400             var hh = '';
6401             
6402             if(typeof(config.sortable) != 'undefined' && config.sortable){
6403                 c.cls = 'sortable';
6404                 c.html = '<i class="glyphicon"></i>' + c.html;
6405             }
6406             
6407             if(typeof(config.lgHeader) != 'undefined'){
6408                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6409             }
6410             
6411             if(typeof(config.mdHeader) != 'undefined'){
6412                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6413             }
6414             
6415             if(typeof(config.smHeader) != 'undefined'){
6416                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6417             }
6418             
6419             if(typeof(config.xsHeader) != 'undefined'){
6420                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6421             }
6422             
6423             if(hh.length){
6424                 c.html = hh;
6425             }
6426             
6427             if(typeof(config.tooltip) != 'undefined'){
6428                 c.tooltip = config.tooltip;
6429             }
6430             
6431             if(typeof(config.colspan) != 'undefined'){
6432                 c.colspan = config.colspan;
6433             }
6434             
6435             if(typeof(config.hidden) != 'undefined' && config.hidden){
6436                 c.style += ' display:none;';
6437             }
6438             
6439             if(typeof(config.dataIndex) != 'undefined'){
6440                 c.sort = config.dataIndex;
6441             }
6442             
6443            
6444             
6445             if(typeof(config.align) != 'undefined' && config.align.length){
6446                 c.style += ' text-align:' + config.align + ';';
6447             }
6448             
6449             if(typeof(config.width) != 'undefined'){
6450                 c.style += ' width:' + config.width + 'px;';
6451                 this.totalWidth += config.width;
6452             } else {
6453                 this.totalWidth += 100; // assume minimum of 100 per column?
6454             }
6455             
6456             if(typeof(config.cls) != 'undefined'){
6457                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6458             }
6459             
6460             ['xs','sm','md','lg'].map(function(size){
6461                 
6462                 if(typeof(config[size]) == 'undefined'){
6463                     return;
6464                 }
6465                 
6466                 if (!config[size]) { // 0 = hidden
6467                     c.cls += ' hidden-' + size;
6468                     return;
6469                 }
6470                 
6471                 c.cls += ' col-' + size + '-' + config[size];
6472
6473             });
6474             
6475             header.cn.push(c)
6476         }
6477         
6478         return header;
6479     },
6480     
6481     renderBody : function()
6482     {
6483         var body = {
6484             tag: 'tbody',
6485             cn : [
6486                 {
6487                     tag: 'tr',
6488                     cn : [
6489                         {
6490                             tag : 'td',
6491                             colspan :  this.cm.getColumnCount()
6492                         }
6493                     ]
6494                 }
6495             ]
6496         };
6497         
6498         return body;
6499     },
6500     
6501     renderFooter : function()
6502     {
6503         var footer = {
6504             tag: 'tfoot',
6505             cn : [
6506                 {
6507                     tag: 'tr',
6508                     cn : [
6509                         {
6510                             tag : 'td',
6511                             colspan :  this.cm.getColumnCount()
6512                         }
6513                     ]
6514                 }
6515             ]
6516         };
6517         
6518         return footer;
6519     },
6520     
6521     
6522     
6523     onLoad : function()
6524     {
6525 //        Roo.log('ds onload');
6526         this.clear();
6527         
6528         var _this = this;
6529         var cm = this.cm;
6530         var ds = this.store;
6531         
6532         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6533             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6534             if (_this.store.sortInfo) {
6535                     
6536                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6537                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6538                 }
6539                 
6540                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6541                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6542                 }
6543             }
6544         });
6545         
6546         var tbody =  this.mainBody;
6547               
6548         if(ds.getCount() > 0){
6549             ds.data.each(function(d,rowIndex){
6550                 var row =  this.renderRow(cm, ds, rowIndex);
6551                 
6552                 tbody.createChild(row);
6553                 
6554                 var _this = this;
6555                 
6556                 if(row.cellObjects.length){
6557                     Roo.each(row.cellObjects, function(r){
6558                         _this.renderCellObject(r);
6559                     })
6560                 }
6561                 
6562             }, this);
6563         }
6564         
6565         Roo.each(this.el.select('tbody td', true).elements, function(e){
6566             e.on('mouseover', _this.onMouseover, _this);
6567         });
6568         
6569         Roo.each(this.el.select('tbody td', true).elements, function(e){
6570             e.on('mouseout', _this.onMouseout, _this);
6571         });
6572         this.fireEvent('rowsrendered', this);
6573         //if(this.loadMask){
6574         //    this.maskEl.hide();
6575         //}
6576         
6577         this.autoSize();
6578     },
6579     
6580     
6581     onUpdate : function(ds,record)
6582     {
6583         this.refreshRow(record);
6584         this.autoSize();
6585     },
6586     
6587     onRemove : function(ds, record, index, isUpdate){
6588         if(isUpdate !== true){
6589             this.fireEvent("beforerowremoved", this, index, record);
6590         }
6591         var bt = this.mainBody.dom;
6592         
6593         var rows = this.el.select('tbody > tr', true).elements;
6594         
6595         if(typeof(rows[index]) != 'undefined'){
6596             bt.removeChild(rows[index].dom);
6597         }
6598         
6599 //        if(bt.rows[index]){
6600 //            bt.removeChild(bt.rows[index]);
6601 //        }
6602         
6603         if(isUpdate !== true){
6604             //this.stripeRows(index);
6605             //this.syncRowHeights(index, index);
6606             //this.layout();
6607             this.fireEvent("rowremoved", this, index, record);
6608         }
6609     },
6610     
6611     onAdd : function(ds, records, rowIndex)
6612     {
6613         //Roo.log('on Add called');
6614         // - note this does not handle multiple adding very well..
6615         var bt = this.mainBody.dom;
6616         for (var i =0 ; i < records.length;i++) {
6617             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6618             //Roo.log(records[i]);
6619             //Roo.log(this.store.getAt(rowIndex+i));
6620             this.insertRow(this.store, rowIndex + i, false);
6621             return;
6622         }
6623         
6624     },
6625     
6626     
6627     refreshRow : function(record){
6628         var ds = this.store, index;
6629         if(typeof record == 'number'){
6630             index = record;
6631             record = ds.getAt(index);
6632         }else{
6633             index = ds.indexOf(record);
6634         }
6635         this.insertRow(ds, index, true);
6636         this.autoSize();
6637         this.onRemove(ds, record, index+1, true);
6638         this.autoSize();
6639         //this.syncRowHeights(index, index);
6640         //this.layout();
6641         this.fireEvent("rowupdated", this, index, record);
6642     },
6643     
6644     insertRow : function(dm, rowIndex, isUpdate){
6645         
6646         if(!isUpdate){
6647             this.fireEvent("beforerowsinserted", this, rowIndex);
6648         }
6649             //var s = this.getScrollState();
6650         var row = this.renderRow(this.cm, this.store, rowIndex);
6651         // insert before rowIndex..
6652         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6653         
6654         var _this = this;
6655                 
6656         if(row.cellObjects.length){
6657             Roo.each(row.cellObjects, function(r){
6658                 _this.renderCellObject(r);
6659             })
6660         }
6661             
6662         if(!isUpdate){
6663             this.fireEvent("rowsinserted", this, rowIndex);
6664             //this.syncRowHeights(firstRow, lastRow);
6665             //this.stripeRows(firstRow);
6666             //this.layout();
6667         }
6668         
6669     },
6670     
6671     
6672     getRowDom : function(rowIndex)
6673     {
6674         var rows = this.el.select('tbody > tr', true).elements;
6675         
6676         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6677         
6678     },
6679     // returns the object tree for a tr..
6680   
6681     
6682     renderRow : function(cm, ds, rowIndex) 
6683     {
6684         
6685         var d = ds.getAt(rowIndex);
6686         
6687         var row = {
6688             tag : 'tr',
6689             cn : []
6690         };
6691             
6692         var cellObjects = [];
6693         
6694         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6695             var config = cm.config[i];
6696             
6697             var renderer = cm.getRenderer(i);
6698             var value = '';
6699             var id = false;
6700             
6701             if(typeof(renderer) !== 'undefined'){
6702                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6703             }
6704             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6705             // and are rendered into the cells after the row is rendered - using the id for the element.
6706             
6707             if(typeof(value) === 'object'){
6708                 id = Roo.id();
6709                 cellObjects.push({
6710                     container : id,
6711                     cfg : value 
6712                 })
6713             }
6714             
6715             var rowcfg = {
6716                 record: d,
6717                 rowIndex : rowIndex,
6718                 colIndex : i,
6719                 rowClass : ''
6720             };
6721
6722             this.fireEvent('rowclass', this, rowcfg);
6723             
6724             var td = {
6725                 tag: 'td',
6726                 cls : rowcfg.rowClass,
6727                 style: '',
6728                 html: (typeof(value) === 'object') ? '' : value
6729             };
6730             
6731             if (id) {
6732                 td.id = id;
6733             }
6734             
6735             if(typeof(config.colspan) != 'undefined'){
6736                 td.colspan = config.colspan;
6737             }
6738             
6739             if(typeof(config.hidden) != 'undefined' && config.hidden){
6740                 td.style += ' display:none;';
6741             }
6742             
6743             if(typeof(config.align) != 'undefined' && config.align.length){
6744                 td.style += ' text-align:' + config.align + ';';
6745             }
6746             
6747             if(typeof(config.width) != 'undefined'){
6748                 td.style += ' width:' +  config.width + 'px;';
6749             }
6750             
6751             if(typeof(config.cursor) != 'undefined'){
6752                 td.style += ' cursor:' +  config.cursor + ';';
6753             }
6754             
6755             if(typeof(config.cls) != 'undefined'){
6756                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6757             }
6758             
6759             ['xs','sm','md','lg'].map(function(size){
6760                 
6761                 if(typeof(config[size]) == 'undefined'){
6762                     return;
6763                 }
6764                 
6765                 if (!config[size]) { // 0 = hidden
6766                     td.cls += ' hidden-' + size;
6767                     return;
6768                 }
6769                 
6770                 td.cls += ' col-' + size + '-' + config[size];
6771
6772             });
6773              
6774             row.cn.push(td);
6775            
6776         }
6777         
6778         row.cellObjects = cellObjects;
6779         
6780         return row;
6781           
6782     },
6783     
6784     
6785     
6786     onBeforeLoad : function()
6787     {
6788         //Roo.log('ds onBeforeLoad');
6789         
6790         //this.clear();
6791         
6792         //if(this.loadMask){
6793         //    this.maskEl.show();
6794         //}
6795     },
6796      /**
6797      * Remove all rows
6798      */
6799     clear : function()
6800     {
6801         this.el.select('tbody', true).first().dom.innerHTML = '';
6802     },
6803     /**
6804      * Show or hide a row.
6805      * @param {Number} rowIndex to show or hide
6806      * @param {Boolean} state hide
6807      */
6808     setRowVisibility : function(rowIndex, state)
6809     {
6810         var bt = this.mainBody.dom;
6811         
6812         var rows = this.el.select('tbody > tr', true).elements;
6813         
6814         if(typeof(rows[rowIndex]) == 'undefined'){
6815             return;
6816         }
6817         rows[rowIndex].dom.style.display = state ? '' : 'none';
6818     },
6819     
6820     
6821     getSelectionModel : function(){
6822         if(!this.selModel){
6823             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6824         }
6825         return this.selModel;
6826     },
6827     /*
6828      * Render the Roo.bootstrap object from renderder
6829      */
6830     renderCellObject : function(r)
6831     {
6832         var _this = this;
6833         
6834         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6835         
6836         var t = r.cfg.render(r.container);
6837         
6838         if(r.cfg.cn){
6839             Roo.each(r.cfg.cn, function(c){
6840                 var child = {
6841                     container: t.getChildContainer(),
6842                     cfg: c
6843                 };
6844                 _this.renderCellObject(child);
6845             })
6846         }
6847     },
6848     
6849     getRowIndex : function(row)
6850     {
6851         var rowIndex = -1;
6852         
6853         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6854             if(el != row){
6855                 return;
6856             }
6857             
6858             rowIndex = index;
6859         });
6860         
6861         return rowIndex;
6862     },
6863      /**
6864      * Returns the grid's underlying element = used by panel.Grid
6865      * @return {Element} The element
6866      */
6867     getGridEl : function(){
6868         return this.el;
6869     },
6870      /**
6871      * Forces a resize - used by panel.Grid
6872      * @return {Element} The element
6873      */
6874     autoSize : function()
6875     {
6876         //var ctr = Roo.get(this.container.dom.parentElement);
6877         var ctr = Roo.get(this.el.dom);
6878         
6879         var thd = this.getGridEl().select('thead',true).first();
6880         var tbd = this.getGridEl().select('tbody', true).first();
6881         var tfd = this.getGridEl().select('tfoot', true).first();
6882         
6883         var cw = ctr.getWidth();
6884         
6885         if (tbd) {
6886             
6887             tbd.setSize(ctr.getWidth(),
6888                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6889             );
6890             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6891             cw -= barsize;
6892         }
6893         cw = Math.max(cw, this.totalWidth);
6894         this.getGridEl().select('tr',true).setWidth(cw);
6895         // resize 'expandable coloumn?
6896         
6897         return; // we doe not have a view in this design..
6898         
6899     },
6900     onBodyScroll: function()
6901     {
6902         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6903         if(this.mainHead){
6904             this.mainHead.setStyle({
6905                 'position' : 'relative',
6906                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6907             });
6908         }
6909         
6910         if(this.lazyLoad){
6911             
6912             var scrollHeight = this.mainBody.dom.scrollHeight;
6913             
6914             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6915             
6916             var height = this.mainBody.getHeight();
6917             
6918             if(scrollHeight - height == scrollTop) {
6919                 
6920                 var total = this.ds.getTotalCount();
6921                 
6922                 if(this.footer.cursor + this.footer.pageSize < total){
6923                     
6924                     this.footer.ds.load({
6925                         params : {
6926                             start : this.footer.cursor + this.footer.pageSize,
6927                             limit : this.footer.pageSize
6928                         },
6929                         add : true
6930                     });
6931                 }
6932             }
6933             
6934         }
6935     },
6936     
6937     onHeaderChange : function()
6938     {
6939         
6940         var header = this.renderHeader();
6941         var table = this.el.select('table', true).first();
6942         
6943         this.mainHead.remove();
6944         this.mainHead = table.createChild(header, this.mainBody, false);
6945     }
6946     
6947 });
6948
6949  
6950
6951  /*
6952  * - LGPL
6953  *
6954  * table cell
6955  * 
6956  */
6957
6958 /**
6959  * @class Roo.bootstrap.TableCell
6960  * @extends Roo.bootstrap.Component
6961  * Bootstrap TableCell class
6962  * @cfg {String} html cell contain text
6963  * @cfg {String} cls cell class
6964  * @cfg {String} tag cell tag (td|th) default td
6965  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6966  * @cfg {String} align Aligns the content in a cell
6967  * @cfg {String} axis Categorizes cells
6968  * @cfg {String} bgcolor Specifies the background color of a cell
6969  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6970  * @cfg {Number} colspan Specifies the number of columns a cell should span
6971  * @cfg {String} headers Specifies one or more header cells a cell is related to
6972  * @cfg {Number} height Sets the height of a cell
6973  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6974  * @cfg {Number} rowspan Sets the number of rows a cell should span
6975  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6976  * @cfg {String} valign Vertical aligns the content in a cell
6977  * @cfg {Number} width Specifies the width of a cell
6978  * 
6979  * @constructor
6980  * Create a new TableCell
6981  * @param {Object} config The config object
6982  */
6983
6984 Roo.bootstrap.TableCell = function(config){
6985     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6986 };
6987
6988 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6989     
6990     html: false,
6991     cls: false,
6992     tag: false,
6993     abbr: false,
6994     align: false,
6995     axis: false,
6996     bgcolor: false,
6997     charoff: false,
6998     colspan: false,
6999     headers: false,
7000     height: false,
7001     nowrap: false,
7002     rowspan: false,
7003     scope: false,
7004     valign: false,
7005     width: false,
7006     
7007     
7008     getAutoCreate : function(){
7009         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7010         
7011         cfg = {
7012             tag: 'td'
7013         };
7014         
7015         if(this.tag){
7016             cfg.tag = this.tag;
7017         }
7018         
7019         if (this.html) {
7020             cfg.html=this.html
7021         }
7022         if (this.cls) {
7023             cfg.cls=this.cls
7024         }
7025         if (this.abbr) {
7026             cfg.abbr=this.abbr
7027         }
7028         if (this.align) {
7029             cfg.align=this.align
7030         }
7031         if (this.axis) {
7032             cfg.axis=this.axis
7033         }
7034         if (this.bgcolor) {
7035             cfg.bgcolor=this.bgcolor
7036         }
7037         if (this.charoff) {
7038             cfg.charoff=this.charoff
7039         }
7040         if (this.colspan) {
7041             cfg.colspan=this.colspan
7042         }
7043         if (this.headers) {
7044             cfg.headers=this.headers
7045         }
7046         if (this.height) {
7047             cfg.height=this.height
7048         }
7049         if (this.nowrap) {
7050             cfg.nowrap=this.nowrap
7051         }
7052         if (this.rowspan) {
7053             cfg.rowspan=this.rowspan
7054         }
7055         if (this.scope) {
7056             cfg.scope=this.scope
7057         }
7058         if (this.valign) {
7059             cfg.valign=this.valign
7060         }
7061         if (this.width) {
7062             cfg.width=this.width
7063         }
7064         
7065         
7066         return cfg;
7067     }
7068    
7069 });
7070
7071  
7072
7073  /*
7074  * - LGPL
7075  *
7076  * table row
7077  * 
7078  */
7079
7080 /**
7081  * @class Roo.bootstrap.TableRow
7082  * @extends Roo.bootstrap.Component
7083  * Bootstrap TableRow class
7084  * @cfg {String} cls row class
7085  * @cfg {String} align Aligns the content in a table row
7086  * @cfg {String} bgcolor Specifies a background color for a table row
7087  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7088  * @cfg {String} valign Vertical aligns the content in a table row
7089  * 
7090  * @constructor
7091  * Create a new TableRow
7092  * @param {Object} config The config object
7093  */
7094
7095 Roo.bootstrap.TableRow = function(config){
7096     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7097 };
7098
7099 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7100     
7101     cls: false,
7102     align: false,
7103     bgcolor: false,
7104     charoff: false,
7105     valign: false,
7106     
7107     getAutoCreate : function(){
7108         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7109         
7110         cfg = {
7111             tag: 'tr'
7112         };
7113             
7114         if(this.cls){
7115             cfg.cls = this.cls;
7116         }
7117         if(this.align){
7118             cfg.align = this.align;
7119         }
7120         if(this.bgcolor){
7121             cfg.bgcolor = this.bgcolor;
7122         }
7123         if(this.charoff){
7124             cfg.charoff = this.charoff;
7125         }
7126         if(this.valign){
7127             cfg.valign = this.valign;
7128         }
7129         
7130         return cfg;
7131     }
7132    
7133 });
7134
7135  
7136
7137  /*
7138  * - LGPL
7139  *
7140  * table body
7141  * 
7142  */
7143
7144 /**
7145  * @class Roo.bootstrap.TableBody
7146  * @extends Roo.bootstrap.Component
7147  * Bootstrap TableBody class
7148  * @cfg {String} cls element class
7149  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7150  * @cfg {String} align Aligns the content inside the element
7151  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7152  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7153  * 
7154  * @constructor
7155  * Create a new TableBody
7156  * @param {Object} config The config object
7157  */
7158
7159 Roo.bootstrap.TableBody = function(config){
7160     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7161 };
7162
7163 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7164     
7165     cls: false,
7166     tag: false,
7167     align: false,
7168     charoff: false,
7169     valign: false,
7170     
7171     getAutoCreate : function(){
7172         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7173         
7174         cfg = {
7175             tag: 'tbody'
7176         };
7177             
7178         if (this.cls) {
7179             cfg.cls=this.cls
7180         }
7181         if(this.tag){
7182             cfg.tag = this.tag;
7183         }
7184         
7185         if(this.align){
7186             cfg.align = this.align;
7187         }
7188         if(this.charoff){
7189             cfg.charoff = this.charoff;
7190         }
7191         if(this.valign){
7192             cfg.valign = this.valign;
7193         }
7194         
7195         return cfg;
7196     }
7197     
7198     
7199 //    initEvents : function()
7200 //    {
7201 //        
7202 //        if(!this.store){
7203 //            return;
7204 //        }
7205 //        
7206 //        this.store = Roo.factory(this.store, Roo.data);
7207 //        this.store.on('load', this.onLoad, this);
7208 //        
7209 //        this.store.load();
7210 //        
7211 //    },
7212 //    
7213 //    onLoad: function () 
7214 //    {   
7215 //        this.fireEvent('load', this);
7216 //    }
7217 //    
7218 //   
7219 });
7220
7221  
7222
7223  /*
7224  * Based on:
7225  * Ext JS Library 1.1.1
7226  * Copyright(c) 2006-2007, Ext JS, LLC.
7227  *
7228  * Originally Released Under LGPL - original licence link has changed is not relivant.
7229  *
7230  * Fork - LGPL
7231  * <script type="text/javascript">
7232  */
7233
7234 // as we use this in bootstrap.
7235 Roo.namespace('Roo.form');
7236  /**
7237  * @class Roo.form.Action
7238  * Internal Class used to handle form actions
7239  * @constructor
7240  * @param {Roo.form.BasicForm} el The form element or its id
7241  * @param {Object} config Configuration options
7242  */
7243
7244  
7245  
7246 // define the action interface
7247 Roo.form.Action = function(form, options){
7248     this.form = form;
7249     this.options = options || {};
7250 };
7251 /**
7252  * Client Validation Failed
7253  * @const 
7254  */
7255 Roo.form.Action.CLIENT_INVALID = 'client';
7256 /**
7257  * Server Validation Failed
7258  * @const 
7259  */
7260 Roo.form.Action.SERVER_INVALID = 'server';
7261  /**
7262  * Connect to Server Failed
7263  * @const 
7264  */
7265 Roo.form.Action.CONNECT_FAILURE = 'connect';
7266 /**
7267  * Reading Data from Server Failed
7268  * @const 
7269  */
7270 Roo.form.Action.LOAD_FAILURE = 'load';
7271
7272 Roo.form.Action.prototype = {
7273     type : 'default',
7274     failureType : undefined,
7275     response : undefined,
7276     result : undefined,
7277
7278     // interface method
7279     run : function(options){
7280
7281     },
7282
7283     // interface method
7284     success : function(response){
7285
7286     },
7287
7288     // interface method
7289     handleResponse : function(response){
7290
7291     },
7292
7293     // default connection failure
7294     failure : function(response){
7295         
7296         this.response = response;
7297         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7298         this.form.afterAction(this, false);
7299     },
7300
7301     processResponse : function(response){
7302         this.response = response;
7303         if(!response.responseText){
7304             return true;
7305         }
7306         this.result = this.handleResponse(response);
7307         return this.result;
7308     },
7309
7310     // utility functions used internally
7311     getUrl : function(appendParams){
7312         var url = this.options.url || this.form.url || this.form.el.dom.action;
7313         if(appendParams){
7314             var p = this.getParams();
7315             if(p){
7316                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7317             }
7318         }
7319         return url;
7320     },
7321
7322     getMethod : function(){
7323         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7324     },
7325
7326     getParams : function(){
7327         var bp = this.form.baseParams;
7328         var p = this.options.params;
7329         if(p){
7330             if(typeof p == "object"){
7331                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7332             }else if(typeof p == 'string' && bp){
7333                 p += '&' + Roo.urlEncode(bp);
7334             }
7335         }else if(bp){
7336             p = Roo.urlEncode(bp);
7337         }
7338         return p;
7339     },
7340
7341     createCallback : function(){
7342         return {
7343             success: this.success,
7344             failure: this.failure,
7345             scope: this,
7346             timeout: (this.form.timeout*1000),
7347             upload: this.form.fileUpload ? this.success : undefined
7348         };
7349     }
7350 };
7351
7352 Roo.form.Action.Submit = function(form, options){
7353     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7354 };
7355
7356 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7357     type : 'submit',
7358
7359     haveProgress : false,
7360     uploadComplete : false,
7361     
7362     // uploadProgress indicator.
7363     uploadProgress : function()
7364     {
7365         if (!this.form.progressUrl) {
7366             return;
7367         }
7368         
7369         if (!this.haveProgress) {
7370             Roo.MessageBox.progress("Uploading", "Uploading");
7371         }
7372         if (this.uploadComplete) {
7373            Roo.MessageBox.hide();
7374            return;
7375         }
7376         
7377         this.haveProgress = true;
7378    
7379         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7380         
7381         var c = new Roo.data.Connection();
7382         c.request({
7383             url : this.form.progressUrl,
7384             params: {
7385                 id : uid
7386             },
7387             method: 'GET',
7388             success : function(req){
7389                //console.log(data);
7390                 var rdata = false;
7391                 var edata;
7392                 try  {
7393                    rdata = Roo.decode(req.responseText)
7394                 } catch (e) {
7395                     Roo.log("Invalid data from server..");
7396                     Roo.log(edata);
7397                     return;
7398                 }
7399                 if (!rdata || !rdata.success) {
7400                     Roo.log(rdata);
7401                     Roo.MessageBox.alert(Roo.encode(rdata));
7402                     return;
7403                 }
7404                 var data = rdata.data;
7405                 
7406                 if (this.uploadComplete) {
7407                    Roo.MessageBox.hide();
7408                    return;
7409                 }
7410                    
7411                 if (data){
7412                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7413                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7414                     );
7415                 }
7416                 this.uploadProgress.defer(2000,this);
7417             },
7418        
7419             failure: function(data) {
7420                 Roo.log('progress url failed ');
7421                 Roo.log(data);
7422             },
7423             scope : this
7424         });
7425            
7426     },
7427     
7428     
7429     run : function()
7430     {
7431         // run get Values on the form, so it syncs any secondary forms.
7432         this.form.getValues();
7433         
7434         var o = this.options;
7435         var method = this.getMethod();
7436         var isPost = method == 'POST';
7437         if(o.clientValidation === false || this.form.isValid()){
7438             
7439             if (this.form.progressUrl) {
7440                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7441                     (new Date() * 1) + '' + Math.random());
7442                     
7443             } 
7444             
7445             
7446             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7447                 form:this.form.el.dom,
7448                 url:this.getUrl(!isPost),
7449                 method: method,
7450                 params:isPost ? this.getParams() : null,
7451                 isUpload: this.form.fileUpload
7452             }));
7453             
7454             this.uploadProgress();
7455
7456         }else if (o.clientValidation !== false){ // client validation failed
7457             this.failureType = Roo.form.Action.CLIENT_INVALID;
7458             this.form.afterAction(this, false);
7459         }
7460     },
7461
7462     success : function(response)
7463     {
7464         this.uploadComplete= true;
7465         if (this.haveProgress) {
7466             Roo.MessageBox.hide();
7467         }
7468         
7469         
7470         var result = this.processResponse(response);
7471         if(result === true || result.success){
7472             this.form.afterAction(this, true);
7473             return;
7474         }
7475         if(result.errors){
7476             this.form.markInvalid(result.errors);
7477             this.failureType = Roo.form.Action.SERVER_INVALID;
7478         }
7479         this.form.afterAction(this, false);
7480     },
7481     failure : function(response)
7482     {
7483         this.uploadComplete= true;
7484         if (this.haveProgress) {
7485             Roo.MessageBox.hide();
7486         }
7487         
7488         this.response = response;
7489         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7490         this.form.afterAction(this, false);
7491     },
7492     
7493     handleResponse : function(response){
7494         if(this.form.errorReader){
7495             var rs = this.form.errorReader.read(response);
7496             var errors = [];
7497             if(rs.records){
7498                 for(var i = 0, len = rs.records.length; i < len; i++) {
7499                     var r = rs.records[i];
7500                     errors[i] = r.data;
7501                 }
7502             }
7503             if(errors.length < 1){
7504                 errors = null;
7505             }
7506             return {
7507                 success : rs.success,
7508                 errors : errors
7509             };
7510         }
7511         var ret = false;
7512         try {
7513             ret = Roo.decode(response.responseText);
7514         } catch (e) {
7515             ret = {
7516                 success: false,
7517                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7518                 errors : []
7519             };
7520         }
7521         return ret;
7522         
7523     }
7524 });
7525
7526
7527 Roo.form.Action.Load = function(form, options){
7528     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7529     this.reader = this.form.reader;
7530 };
7531
7532 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7533     type : 'load',
7534
7535     run : function(){
7536         
7537         Roo.Ajax.request(Roo.apply(
7538                 this.createCallback(), {
7539                     method:this.getMethod(),
7540                     url:this.getUrl(false),
7541                     params:this.getParams()
7542         }));
7543     },
7544
7545     success : function(response){
7546         
7547         var result = this.processResponse(response);
7548         if(result === true || !result.success || !result.data){
7549             this.failureType = Roo.form.Action.LOAD_FAILURE;
7550             this.form.afterAction(this, false);
7551             return;
7552         }
7553         this.form.clearInvalid();
7554         this.form.setValues(result.data);
7555         this.form.afterAction(this, true);
7556     },
7557
7558     handleResponse : function(response){
7559         if(this.form.reader){
7560             var rs = this.form.reader.read(response);
7561             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7562             return {
7563                 success : rs.success,
7564                 data : data
7565             };
7566         }
7567         return Roo.decode(response.responseText);
7568     }
7569 });
7570
7571 Roo.form.Action.ACTION_TYPES = {
7572     'load' : Roo.form.Action.Load,
7573     'submit' : Roo.form.Action.Submit
7574 };/*
7575  * - LGPL
7576  *
7577  * form
7578  *
7579  */
7580
7581 /**
7582  * @class Roo.bootstrap.Form
7583  * @extends Roo.bootstrap.Component
7584  * Bootstrap Form class
7585  * @cfg {String} method  GET | POST (default POST)
7586  * @cfg {String} labelAlign top | left (default top)
7587  * @cfg {String} align left  | right - for navbars
7588  * @cfg {Boolean} loadMask load mask when submit (default true)
7589
7590  *
7591  * @constructor
7592  * Create a new Form
7593  * @param {Object} config The config object
7594  */
7595
7596
7597 Roo.bootstrap.Form = function(config){
7598     
7599     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7600     
7601     Roo.bootstrap.Form.popover.apply();
7602     
7603     this.addEvents({
7604         /**
7605          * @event clientvalidation
7606          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7607          * @param {Form} this
7608          * @param {Boolean} valid true if the form has passed client-side validation
7609          */
7610         clientvalidation: true,
7611         /**
7612          * @event beforeaction
7613          * Fires before any action is performed. Return false to cancel the action.
7614          * @param {Form} this
7615          * @param {Action} action The action to be performed
7616          */
7617         beforeaction: true,
7618         /**
7619          * @event actionfailed
7620          * Fires when an action fails.
7621          * @param {Form} this
7622          * @param {Action} action The action that failed
7623          */
7624         actionfailed : true,
7625         /**
7626          * @event actioncomplete
7627          * Fires when an action is completed.
7628          * @param {Form} this
7629          * @param {Action} action The action that completed
7630          */
7631         actioncomplete : true
7632     });
7633 };
7634
7635 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7636
7637      /**
7638      * @cfg {String} method
7639      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7640      */
7641     method : 'POST',
7642     /**
7643      * @cfg {String} url
7644      * The URL to use for form actions if one isn't supplied in the action options.
7645      */
7646     /**
7647      * @cfg {Boolean} fileUpload
7648      * Set to true if this form is a file upload.
7649      */
7650
7651     /**
7652      * @cfg {Object} baseParams
7653      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7654      */
7655
7656     /**
7657      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7658      */
7659     timeout: 30,
7660     /**
7661      * @cfg {Sting} align (left|right) for navbar forms
7662      */
7663     align : 'left',
7664
7665     // private
7666     activeAction : null,
7667
7668     /**
7669      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7670      * element by passing it or its id or mask the form itself by passing in true.
7671      * @type Mixed
7672      */
7673     waitMsgTarget : false,
7674
7675     loadMask : true,
7676     
7677     /**
7678      * @cfg {Boolean} errorMask (true|false) default false
7679      */
7680     errorMask : false,
7681     
7682     /**
7683      * @cfg {Number} maskOffset Default 100
7684      */
7685     maskOffset : 100,
7686     
7687     /**
7688      * @cfg {Boolean} maskBody
7689      */
7690     maskBody : false,
7691
7692     getAutoCreate : function(){
7693
7694         var cfg = {
7695             tag: 'form',
7696             method : this.method || 'POST',
7697             id : this.id || Roo.id(),
7698             cls : ''
7699         };
7700         if (this.parent().xtype.match(/^Nav/)) {
7701             cfg.cls = 'navbar-form navbar-' + this.align;
7702
7703         }
7704
7705         if (this.labelAlign == 'left' ) {
7706             cfg.cls += ' form-horizontal';
7707         }
7708
7709
7710         return cfg;
7711     },
7712     initEvents : function()
7713     {
7714         this.el.on('submit', this.onSubmit, this);
7715         // this was added as random key presses on the form where triggering form submit.
7716         this.el.on('keypress', function(e) {
7717             if (e.getCharCode() != 13) {
7718                 return true;
7719             }
7720             // we might need to allow it for textareas.. and some other items.
7721             // check e.getTarget().
7722
7723             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7724                 return true;
7725             }
7726
7727             Roo.log("keypress blocked");
7728
7729             e.preventDefault();
7730             return false;
7731         });
7732         
7733     },
7734     // private
7735     onSubmit : function(e){
7736         e.stopEvent();
7737     },
7738
7739      /**
7740      * Returns true if client-side validation on the form is successful.
7741      * @return Boolean
7742      */
7743     isValid : function(){
7744         var items = this.getItems();
7745         var valid = true;
7746         var target = false;
7747         
7748         items.each(function(f){
7749             if(f.validate()){
7750                 return;
7751             }
7752             valid = false;
7753
7754             if(!target && f.el.isVisible(true)){
7755                 target = f;
7756             }
7757            
7758         });
7759         
7760         if(this.errorMask && !valid){
7761             Roo.bootstrap.Form.popover.mask(this, target);
7762         }
7763         
7764         return valid;
7765     },
7766     
7767     /**
7768      * Returns true if any fields in this form have changed since their original load.
7769      * @return Boolean
7770      */
7771     isDirty : function(){
7772         var dirty = false;
7773         var items = this.getItems();
7774         items.each(function(f){
7775            if(f.isDirty()){
7776                dirty = true;
7777                return false;
7778            }
7779            return true;
7780         });
7781         return dirty;
7782     },
7783      /**
7784      * Performs a predefined action (submit or load) or custom actions you define on this form.
7785      * @param {String} actionName The name of the action type
7786      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7787      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7788      * accept other config options):
7789      * <pre>
7790 Property          Type             Description
7791 ----------------  ---------------  ----------------------------------------------------------------------------------
7792 url               String           The url for the action (defaults to the form's url)
7793 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7794 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7795 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7796                                    validate the form on the client (defaults to false)
7797      * </pre>
7798      * @return {BasicForm} this
7799      */
7800     doAction : function(action, options){
7801         if(typeof action == 'string'){
7802             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7803         }
7804         if(this.fireEvent('beforeaction', this, action) !== false){
7805             this.beforeAction(action);
7806             action.run.defer(100, action);
7807         }
7808         return this;
7809     },
7810
7811     // private
7812     beforeAction : function(action){
7813         var o = action.options;
7814         
7815         if(this.loadMask){
7816             
7817             if(this.maskBody){
7818                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7819             } else {
7820                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7821             }
7822         }
7823         // not really supported yet.. ??
7824
7825         //if(this.waitMsgTarget === true){
7826         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7827         //}else if(this.waitMsgTarget){
7828         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7829         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7830         //}else {
7831         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7832        // }
7833
7834     },
7835
7836     // private
7837     afterAction : function(action, success){
7838         this.activeAction = null;
7839         var o = action.options;
7840
7841         if(this.loadMask){
7842             
7843             if(this.maskBody){
7844                 Roo.get(document.body).unmask();
7845             } else {
7846                 this.el.unmask();
7847             }
7848         }
7849         
7850         //if(this.waitMsgTarget === true){
7851 //            this.el.unmask();
7852         //}else if(this.waitMsgTarget){
7853         //    this.waitMsgTarget.unmask();
7854         //}else{
7855         //    Roo.MessageBox.updateProgress(1);
7856         //    Roo.MessageBox.hide();
7857        // }
7858         //
7859         if(success){
7860             if(o.reset){
7861                 this.reset();
7862             }
7863             Roo.callback(o.success, o.scope, [this, action]);
7864             this.fireEvent('actioncomplete', this, action);
7865
7866         }else{
7867
7868             // failure condition..
7869             // we have a scenario where updates need confirming.
7870             // eg. if a locking scenario exists..
7871             // we look for { errors : { needs_confirm : true }} in the response.
7872             if (
7873                 (typeof(action.result) != 'undefined')  &&
7874                 (typeof(action.result.errors) != 'undefined')  &&
7875                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7876            ){
7877                 var _t = this;
7878                 Roo.log("not supported yet");
7879                  /*
7880
7881                 Roo.MessageBox.confirm(
7882                     "Change requires confirmation",
7883                     action.result.errorMsg,
7884                     function(r) {
7885                         if (r != 'yes') {
7886                             return;
7887                         }
7888                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7889                     }
7890
7891                 );
7892                 */
7893
7894
7895                 return;
7896             }
7897
7898             Roo.callback(o.failure, o.scope, [this, action]);
7899             // show an error message if no failed handler is set..
7900             if (!this.hasListener('actionfailed')) {
7901                 Roo.log("need to add dialog support");
7902                 /*
7903                 Roo.MessageBox.alert("Error",
7904                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7905                         action.result.errorMsg :
7906                         "Saving Failed, please check your entries or try again"
7907                 );
7908                 */
7909             }
7910
7911             this.fireEvent('actionfailed', this, action);
7912         }
7913
7914     },
7915     /**
7916      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7917      * @param {String} id The value to search for
7918      * @return Field
7919      */
7920     findField : function(id){
7921         var items = this.getItems();
7922         var field = items.get(id);
7923         if(!field){
7924              items.each(function(f){
7925                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7926                     field = f;
7927                     return false;
7928                 }
7929                 return true;
7930             });
7931         }
7932         return field || null;
7933     },
7934      /**
7935      * Mark fields in this form invalid in bulk.
7936      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7937      * @return {BasicForm} this
7938      */
7939     markInvalid : function(errors){
7940         if(errors instanceof Array){
7941             for(var i = 0, len = errors.length; i < len; i++){
7942                 var fieldError = errors[i];
7943                 var f = this.findField(fieldError.id);
7944                 if(f){
7945                     f.markInvalid(fieldError.msg);
7946                 }
7947             }
7948         }else{
7949             var field, id;
7950             for(id in errors){
7951                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7952                     field.markInvalid(errors[id]);
7953                 }
7954             }
7955         }
7956         //Roo.each(this.childForms || [], function (f) {
7957         //    f.markInvalid(errors);
7958         //});
7959
7960         return this;
7961     },
7962
7963     /**
7964      * Set values for fields in this form in bulk.
7965      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7966      * @return {BasicForm} this
7967      */
7968     setValues : function(values){
7969         if(values instanceof Array){ // array of objects
7970             for(var i = 0, len = values.length; i < len; i++){
7971                 var v = values[i];
7972                 var f = this.findField(v.id);
7973                 if(f){
7974                     f.setValue(v.value);
7975                     if(this.trackResetOnLoad){
7976                         f.originalValue = f.getValue();
7977                     }
7978                 }
7979             }
7980         }else{ // object hash
7981             var field, id;
7982             for(id in values){
7983                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7984
7985                     if (field.setFromData &&
7986                         field.valueField &&
7987                         field.displayField &&
7988                         // combos' with local stores can
7989                         // be queried via setValue()
7990                         // to set their value..
7991                         (field.store && !field.store.isLocal)
7992                         ) {
7993                         // it's a combo
7994                         var sd = { };
7995                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7996                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7997                         field.setFromData(sd);
7998
7999                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8000                         
8001                         field.setFromData(values);
8002                         
8003                     } else {
8004                         field.setValue(values[id]);
8005                     }
8006
8007
8008                     if(this.trackResetOnLoad){
8009                         field.originalValue = field.getValue();
8010                     }
8011                 }
8012             }
8013         }
8014
8015         //Roo.each(this.childForms || [], function (f) {
8016         //    f.setValues(values);
8017         //});
8018
8019         return this;
8020     },
8021
8022     /**
8023      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8024      * they are returned as an array.
8025      * @param {Boolean} asString
8026      * @return {Object}
8027      */
8028     getValues : function(asString){
8029         //if (this.childForms) {
8030             // copy values from the child forms
8031         //    Roo.each(this.childForms, function (f) {
8032         //        this.setValues(f.getValues());
8033         //    }, this);
8034         //}
8035
8036
8037
8038         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8039         if(asString === true){
8040             return fs;
8041         }
8042         return Roo.urlDecode(fs);
8043     },
8044
8045     /**
8046      * Returns the fields in this form as an object with key/value pairs.
8047      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8048      * @return {Object}
8049      */
8050     getFieldValues : function(with_hidden)
8051     {
8052         var items = this.getItems();
8053         var ret = {};
8054         items.each(function(f){
8055             
8056             if (!f.getName()) {
8057                 return;
8058             }
8059             
8060             var v = f.getValue();
8061             
8062             if (f.inputType =='radio') {
8063                 if (typeof(ret[f.getName()]) == 'undefined') {
8064                     ret[f.getName()] = ''; // empty..
8065                 }
8066
8067                 if (!f.el.dom.checked) {
8068                     return;
8069
8070                 }
8071                 v = f.el.dom.value;
8072
8073             }
8074             
8075             if(f.xtype == 'MoneyField'){
8076                 ret[f.currencyName] = f.getCurrency();
8077             }
8078
8079             // not sure if this supported any more..
8080             if ((typeof(v) == 'object') && f.getRawValue) {
8081                 v = f.getRawValue() ; // dates..
8082             }
8083             // combo boxes where name != hiddenName...
8084             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8085                 ret[f.name] = f.getRawValue();
8086             }
8087             ret[f.getName()] = v;
8088         });
8089
8090         return ret;
8091     },
8092
8093     /**
8094      * Clears all invalid messages in this form.
8095      * @return {BasicForm} this
8096      */
8097     clearInvalid : function(){
8098         var items = this.getItems();
8099
8100         items.each(function(f){
8101            f.clearInvalid();
8102         });
8103
8104         return this;
8105     },
8106
8107     /**
8108      * Resets this form.
8109      * @return {BasicForm} this
8110      */
8111     reset : function(){
8112         var items = this.getItems();
8113         items.each(function(f){
8114             f.reset();
8115         });
8116
8117         Roo.each(this.childForms || [], function (f) {
8118             f.reset();
8119         });
8120
8121
8122         return this;
8123     },
8124     
8125     getItems : function()
8126     {
8127         var r=new Roo.util.MixedCollection(false, function(o){
8128             return o.id || (o.id = Roo.id());
8129         });
8130         var iter = function(el) {
8131             if (el.inputEl) {
8132                 r.add(el);
8133             }
8134             if (!el.items) {
8135                 return;
8136             }
8137             Roo.each(el.items,function(e) {
8138                 iter(e);
8139             });
8140         };
8141
8142         iter(this);
8143         return r;
8144     },
8145     
8146     hideFields : function(items)
8147     {
8148         Roo.each(items, function(i){
8149             
8150             var f = this.findField(i);
8151             
8152             if(!f){
8153                 return;
8154             }
8155             
8156             if(f.xtype == 'DateField'){
8157                 f.setVisible(false);
8158                 return;
8159             }
8160             
8161             f.hide();
8162             
8163         }, this);
8164     },
8165     
8166     showFields : function(items)
8167     {
8168         Roo.each(items, function(i){
8169             
8170             var f = this.findField(i);
8171             
8172             if(!f){
8173                 return;
8174             }
8175             
8176             if(f.xtype == 'DateField'){
8177                 f.setVisible(true);
8178                 return;
8179             }
8180             
8181             f.show();
8182             
8183         }, this);
8184     }
8185
8186 });
8187
8188 Roo.apply(Roo.bootstrap.Form, {
8189     
8190     popover : {
8191         
8192         padding : 5,
8193         
8194         isApplied : false,
8195         
8196         isMasked : false,
8197         
8198         form : false,
8199         
8200         target : false,
8201         
8202         toolTip : false,
8203         
8204         intervalID : false,
8205         
8206         maskEl : false,
8207         
8208         apply : function()
8209         {
8210             if(this.isApplied){
8211                 return;
8212             }
8213             
8214             this.maskEl = {
8215                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8216                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8217                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8218                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8219             };
8220             
8221             this.maskEl.top.enableDisplayMode("block");
8222             this.maskEl.left.enableDisplayMode("block");
8223             this.maskEl.bottom.enableDisplayMode("block");
8224             this.maskEl.right.enableDisplayMode("block");
8225             
8226             this.toolTip = new Roo.bootstrap.Tooltip({
8227                 cls : 'roo-form-error-popover',
8228                 alignment : {
8229                     'left' : ['r-l', [-2,0], 'right'],
8230                     'right' : ['l-r', [2,0], 'left'],
8231                     'bottom' : ['tl-bl', [0,2], 'top'],
8232                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8233                 }
8234             });
8235             
8236             this.toolTip.render(Roo.get(document.body));
8237
8238             this.toolTip.el.enableDisplayMode("block");
8239             
8240             Roo.get(document.body).on('click', function(){
8241                 this.unmask();
8242             }, this);
8243             
8244             Roo.get(document.body).on('touchstart', function(){
8245                 this.unmask();
8246             }, this);
8247             
8248             this.isApplied = true
8249         },
8250         
8251         mask : function(form, target)
8252         {
8253             this.form = form;
8254             
8255             this.target = target;
8256             
8257             if(!this.form.errorMask || !target.el){
8258                 return;
8259             }
8260             
8261             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8262             
8263             Roo.log(scrollable);
8264             
8265             var ot = this.target.el.calcOffsetsTo(scrollable);
8266             
8267             var scrollTo = ot[1] - this.form.maskOffset;
8268             
8269             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8270             
8271             scrollable.scrollTo('top', scrollTo);
8272             
8273             var box = this.target.el.getBox();
8274             Roo.log(box);
8275             var zIndex = Roo.bootstrap.Modal.zIndex++;
8276
8277             
8278             this.maskEl.top.setStyle('position', 'absolute');
8279             this.maskEl.top.setStyle('z-index', zIndex);
8280             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8281             this.maskEl.top.setLeft(0);
8282             this.maskEl.top.setTop(0);
8283             this.maskEl.top.show();
8284             
8285             this.maskEl.left.setStyle('position', 'absolute');
8286             this.maskEl.left.setStyle('z-index', zIndex);
8287             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8288             this.maskEl.left.setLeft(0);
8289             this.maskEl.left.setTop(box.y - this.padding);
8290             this.maskEl.left.show();
8291
8292             this.maskEl.bottom.setStyle('position', 'absolute');
8293             this.maskEl.bottom.setStyle('z-index', zIndex);
8294             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8295             this.maskEl.bottom.setLeft(0);
8296             this.maskEl.bottom.setTop(box.bottom + this.padding);
8297             this.maskEl.bottom.show();
8298
8299             this.maskEl.right.setStyle('position', 'absolute');
8300             this.maskEl.right.setStyle('z-index', zIndex);
8301             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8302             this.maskEl.right.setLeft(box.right + this.padding);
8303             this.maskEl.right.setTop(box.y - this.padding);
8304             this.maskEl.right.show();
8305
8306             this.toolTip.bindEl = this.target.el;
8307
8308             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8309
8310             var tip = this.target.blankText;
8311
8312             if(this.target.getValue() !== '' ) {
8313                 
8314                 if (this.target.invalidText.length) {
8315                     tip = this.target.invalidText;
8316                 } else if (this.target.regexText.length){
8317                     tip = this.target.regexText;
8318                 }
8319             }
8320
8321             this.toolTip.show(tip);
8322
8323             this.intervalID = window.setInterval(function() {
8324                 Roo.bootstrap.Form.popover.unmask();
8325             }, 10000);
8326
8327             window.onwheel = function(){ return false;};
8328             
8329             (function(){ this.isMasked = true; }).defer(500, this);
8330             
8331         },
8332         
8333         unmask : function()
8334         {
8335             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8336                 return;
8337             }
8338             
8339             this.maskEl.top.setStyle('position', 'absolute');
8340             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8341             this.maskEl.top.hide();
8342
8343             this.maskEl.left.setStyle('position', 'absolute');
8344             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8345             this.maskEl.left.hide();
8346
8347             this.maskEl.bottom.setStyle('position', 'absolute');
8348             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8349             this.maskEl.bottom.hide();
8350
8351             this.maskEl.right.setStyle('position', 'absolute');
8352             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8353             this.maskEl.right.hide();
8354             
8355             this.toolTip.hide();
8356             
8357             this.toolTip.el.hide();
8358             
8359             window.onwheel = function(){ return true;};
8360             
8361             if(this.intervalID){
8362                 window.clearInterval(this.intervalID);
8363                 this.intervalID = false;
8364             }
8365             
8366             this.isMasked = false;
8367             
8368         }
8369         
8370     }
8371     
8372 });
8373
8374 /*
8375  * Based on:
8376  * Ext JS Library 1.1.1
8377  * Copyright(c) 2006-2007, Ext JS, LLC.
8378  *
8379  * Originally Released Under LGPL - original licence link has changed is not relivant.
8380  *
8381  * Fork - LGPL
8382  * <script type="text/javascript">
8383  */
8384 /**
8385  * @class Roo.form.VTypes
8386  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8387  * @singleton
8388  */
8389 Roo.form.VTypes = function(){
8390     // closure these in so they are only created once.
8391     var alpha = /^[a-zA-Z_]+$/;
8392     var alphanum = /^[a-zA-Z0-9_]+$/;
8393     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8394     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8395
8396     // All these messages and functions are configurable
8397     return {
8398         /**
8399          * The function used to validate email addresses
8400          * @param {String} value The email address
8401          */
8402         'email' : function(v){
8403             return email.test(v);
8404         },
8405         /**
8406          * The error text to display when the email validation function returns false
8407          * @type String
8408          */
8409         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8410         /**
8411          * The keystroke filter mask to be applied on email input
8412          * @type RegExp
8413          */
8414         'emailMask' : /[a-z0-9_\.\-@]/i,
8415
8416         /**
8417          * The function used to validate URLs
8418          * @param {String} value The URL
8419          */
8420         'url' : function(v){
8421             return url.test(v);
8422         },
8423         /**
8424          * The error text to display when the url validation function returns false
8425          * @type String
8426          */
8427         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8428         
8429         /**
8430          * The function used to validate alpha values
8431          * @param {String} value The value
8432          */
8433         'alpha' : function(v){
8434             return alpha.test(v);
8435         },
8436         /**
8437          * The error text to display when the alpha validation function returns false
8438          * @type String
8439          */
8440         'alphaText' : 'This field should only contain letters and _',
8441         /**
8442          * The keystroke filter mask to be applied on alpha input
8443          * @type RegExp
8444          */
8445         'alphaMask' : /[a-z_]/i,
8446
8447         /**
8448          * The function used to validate alphanumeric values
8449          * @param {String} value The value
8450          */
8451         'alphanum' : function(v){
8452             return alphanum.test(v);
8453         },
8454         /**
8455          * The error text to display when the alphanumeric validation function returns false
8456          * @type String
8457          */
8458         'alphanumText' : 'This field should only contain letters, numbers and _',
8459         /**
8460          * The keystroke filter mask to be applied on alphanumeric input
8461          * @type RegExp
8462          */
8463         'alphanumMask' : /[a-z0-9_]/i
8464     };
8465 }();/*
8466  * - LGPL
8467  *
8468  * Input
8469  * 
8470  */
8471
8472 /**
8473  * @class Roo.bootstrap.Input
8474  * @extends Roo.bootstrap.Component
8475  * Bootstrap Input class
8476  * @cfg {Boolean} disabled is it disabled
8477  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8478  * @cfg {String} name name of the input
8479  * @cfg {string} fieldLabel - the label associated
8480  * @cfg {string} placeholder - placeholder to put in text.
8481  * @cfg {string}  before - input group add on before
8482  * @cfg {string} after - input group add on after
8483  * @cfg {string} size - (lg|sm) or leave empty..
8484  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8485  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8486  * @cfg {Number} md colspan out of 12 for computer-sized screens
8487  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8488  * @cfg {string} value default value of the input
8489  * @cfg {Number} labelWidth set the width of label 
8490  * @cfg {Number} labellg set the width of label (1-12)
8491  * @cfg {Number} labelmd set the width of label (1-12)
8492  * @cfg {Number} labelsm set the width of label (1-12)
8493  * @cfg {Number} labelxs set the width of label (1-12)
8494  * @cfg {String} labelAlign (top|left)
8495  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8496  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8497  * @cfg {String} indicatorpos (left|right) default left
8498
8499  * @cfg {String} align (left|center|right) Default left
8500  * @cfg {Boolean} forceFeedback (true|false) Default false
8501  * 
8502  * @constructor
8503  * Create a new Input
8504  * @param {Object} config The config object
8505  */
8506
8507 Roo.bootstrap.Input = function(config){
8508     
8509     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8510     
8511     this.addEvents({
8512         /**
8513          * @event focus
8514          * Fires when this field receives input focus.
8515          * @param {Roo.form.Field} this
8516          */
8517         focus : true,
8518         /**
8519          * @event blur
8520          * Fires when this field loses input focus.
8521          * @param {Roo.form.Field} this
8522          */
8523         blur : true,
8524         /**
8525          * @event specialkey
8526          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8527          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8528          * @param {Roo.form.Field} this
8529          * @param {Roo.EventObject} e The event object
8530          */
8531         specialkey : true,
8532         /**
8533          * @event change
8534          * Fires just before the field blurs if the field value has changed.
8535          * @param {Roo.form.Field} this
8536          * @param {Mixed} newValue The new value
8537          * @param {Mixed} oldValue The original value
8538          */
8539         change : true,
8540         /**
8541          * @event invalid
8542          * Fires after the field has been marked as invalid.
8543          * @param {Roo.form.Field} this
8544          * @param {String} msg The validation message
8545          */
8546         invalid : true,
8547         /**
8548          * @event valid
8549          * Fires after the field has been validated with no errors.
8550          * @param {Roo.form.Field} this
8551          */
8552         valid : true,
8553          /**
8554          * @event keyup
8555          * Fires after the key up
8556          * @param {Roo.form.Field} this
8557          * @param {Roo.EventObject}  e The event Object
8558          */
8559         keyup : true
8560     });
8561 };
8562
8563 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8564      /**
8565      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8566       automatic validation (defaults to "keyup").
8567      */
8568     validationEvent : "keyup",
8569      /**
8570      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8571      */
8572     validateOnBlur : true,
8573     /**
8574      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8575      */
8576     validationDelay : 250,
8577      /**
8578      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8579      */
8580     focusClass : "x-form-focus",  // not needed???
8581     
8582        
8583     /**
8584      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8585      */
8586     invalidClass : "has-warning",
8587     
8588     /**
8589      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8590      */
8591     validClass : "has-success",
8592     
8593     /**
8594      * @cfg {Boolean} hasFeedback (true|false) default true
8595      */
8596     hasFeedback : true,
8597     
8598     /**
8599      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8600      */
8601     invalidFeedbackClass : "glyphicon-warning-sign",
8602     
8603     /**
8604      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8605      */
8606     validFeedbackClass : "glyphicon-ok",
8607     
8608     /**
8609      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8610      */
8611     selectOnFocus : false,
8612     
8613      /**
8614      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8615      */
8616     maskRe : null,
8617        /**
8618      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8619      */
8620     vtype : null,
8621     
8622       /**
8623      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8624      */
8625     disableKeyFilter : false,
8626     
8627        /**
8628      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8629      */
8630     disabled : false,
8631      /**
8632      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8633      */
8634     allowBlank : true,
8635     /**
8636      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8637      */
8638     blankText : "Please complete this mandatory field",
8639     
8640      /**
8641      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8642      */
8643     minLength : 0,
8644     /**
8645      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8646      */
8647     maxLength : Number.MAX_VALUE,
8648     /**
8649      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8650      */
8651     minLengthText : "The minimum length for this field is {0}",
8652     /**
8653      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8654      */
8655     maxLengthText : "The maximum length for this field is {0}",
8656   
8657     
8658     /**
8659      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8660      * If available, this function will be called only after the basic validators all return true, and will be passed the
8661      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8662      */
8663     validator : null,
8664     /**
8665      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8666      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8667      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8668      */
8669     regex : null,
8670     /**
8671      * @cfg {String} regexText -- Depricated - use Invalid Text
8672      */
8673     regexText : "",
8674     
8675     /**
8676      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8677      */
8678     invalidText : "",
8679     
8680     
8681     
8682     autocomplete: false,
8683     
8684     
8685     fieldLabel : '',
8686     inputType : 'text',
8687     
8688     name : false,
8689     placeholder: false,
8690     before : false,
8691     after : false,
8692     size : false,
8693     hasFocus : false,
8694     preventMark: false,
8695     isFormField : true,
8696     value : '',
8697     labelWidth : 2,
8698     labelAlign : false,
8699     readOnly : false,
8700     align : false,
8701     formatedValue : false,
8702     forceFeedback : false,
8703     
8704     indicatorpos : 'left',
8705     
8706     labellg : 0,
8707     labelmd : 0,
8708     labelsm : 0,
8709     labelxs : 0,
8710     
8711     parentLabelAlign : function()
8712     {
8713         var parent = this;
8714         while (parent.parent()) {
8715             parent = parent.parent();
8716             if (typeof(parent.labelAlign) !='undefined') {
8717                 return parent.labelAlign;
8718             }
8719         }
8720         return 'left';
8721         
8722     },
8723     
8724     getAutoCreate : function()
8725     {
8726         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8727         
8728         var id = Roo.id();
8729         
8730         var cfg = {};
8731         
8732         if(this.inputType != 'hidden'){
8733             cfg.cls = 'form-group' //input-group
8734         }
8735         
8736         var input =  {
8737             tag: 'input',
8738             id : id,
8739             type : this.inputType,
8740             value : this.value,
8741             cls : 'form-control',
8742             placeholder : this.placeholder || '',
8743             autocomplete : this.autocomplete || 'new-password'
8744         };
8745         
8746         if(this.align){
8747             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8748         }
8749         
8750         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8751             input.maxLength = this.maxLength;
8752         }
8753         
8754         if (this.disabled) {
8755             input.disabled=true;
8756         }
8757         
8758         if (this.readOnly) {
8759             input.readonly=true;
8760         }
8761         
8762         if (this.name) {
8763             input.name = this.name;
8764         }
8765         
8766         if (this.size) {
8767             input.cls += ' input-' + this.size;
8768         }
8769         
8770         var settings=this;
8771         ['xs','sm','md','lg'].map(function(size){
8772             if (settings[size]) {
8773                 cfg.cls += ' col-' + size + '-' + settings[size];
8774             }
8775         });
8776         
8777         var inputblock = input;
8778         
8779         var feedback = {
8780             tag: 'span',
8781             cls: 'glyphicon form-control-feedback'
8782         };
8783             
8784         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8785             
8786             inputblock = {
8787                 cls : 'has-feedback',
8788                 cn :  [
8789                     input,
8790                     feedback
8791                 ] 
8792             };  
8793         }
8794         
8795         if (this.before || this.after) {
8796             
8797             inputblock = {
8798                 cls : 'input-group',
8799                 cn :  [] 
8800             };
8801             
8802             if (this.before && typeof(this.before) == 'string') {
8803                 
8804                 inputblock.cn.push({
8805                     tag :'span',
8806                     cls : 'roo-input-before input-group-addon',
8807                     html : this.before
8808                 });
8809             }
8810             if (this.before && typeof(this.before) == 'object') {
8811                 this.before = Roo.factory(this.before);
8812                 
8813                 inputblock.cn.push({
8814                     tag :'span',
8815                     cls : 'roo-input-before input-group-' +
8816                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8817                 });
8818             }
8819             
8820             inputblock.cn.push(input);
8821             
8822             if (this.after && typeof(this.after) == 'string') {
8823                 inputblock.cn.push({
8824                     tag :'span',
8825                     cls : 'roo-input-after input-group-addon',
8826                     html : this.after
8827                 });
8828             }
8829             if (this.after && typeof(this.after) == 'object') {
8830                 this.after = Roo.factory(this.after);
8831                 
8832                 inputblock.cn.push({
8833                     tag :'span',
8834                     cls : 'roo-input-after input-group-' +
8835                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8836                 });
8837             }
8838             
8839             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8840                 inputblock.cls += ' has-feedback';
8841                 inputblock.cn.push(feedback);
8842             }
8843         };
8844         
8845         if (align ==='left' && this.fieldLabel.length) {
8846             
8847             cfg.cls += ' roo-form-group-label-left';
8848             
8849             cfg.cn = [
8850                 {
8851                     tag : 'i',
8852                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8853                     tooltip : 'This field is required'
8854                 },
8855                 {
8856                     tag: 'label',
8857                     'for' :  id,
8858                     cls : 'control-label',
8859                     html : this.fieldLabel
8860
8861                 },
8862                 {
8863                     cls : "", 
8864                     cn: [
8865                         inputblock
8866                     ]
8867                 }
8868             ];
8869             
8870             var labelCfg = cfg.cn[1];
8871             var contentCfg = cfg.cn[2];
8872             
8873             if(this.indicatorpos == 'right'){
8874                 cfg.cn = [
8875                     {
8876                         tag: 'label',
8877                         'for' :  id,
8878                         cls : 'control-label',
8879                         cn : [
8880                             {
8881                                 tag : 'span',
8882                                 html : this.fieldLabel
8883                             },
8884                             {
8885                                 tag : 'i',
8886                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8887                                 tooltip : 'This field is required'
8888                             }
8889                         ]
8890                     },
8891                     {
8892                         cls : "",
8893                         cn: [
8894                             inputblock
8895                         ]
8896                     }
8897
8898                 ];
8899                 
8900                 labelCfg = cfg.cn[0];
8901                 contentCfg = cfg.cn[1];
8902             
8903             }
8904             
8905             if(this.labelWidth > 12){
8906                 labelCfg.style = "width: " + this.labelWidth + 'px';
8907             }
8908             
8909             if(this.labelWidth < 13 && this.labelmd == 0){
8910                 this.labelmd = this.labelWidth;
8911             }
8912             
8913             if(this.labellg > 0){
8914                 labelCfg.cls += ' col-lg-' + this.labellg;
8915                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8916             }
8917             
8918             if(this.labelmd > 0){
8919                 labelCfg.cls += ' col-md-' + this.labelmd;
8920                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8921             }
8922             
8923             if(this.labelsm > 0){
8924                 labelCfg.cls += ' col-sm-' + this.labelsm;
8925                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8926             }
8927             
8928             if(this.labelxs > 0){
8929                 labelCfg.cls += ' col-xs-' + this.labelxs;
8930                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8931             }
8932             
8933             
8934         } else if ( this.fieldLabel.length) {
8935                 
8936             cfg.cn = [
8937                 {
8938                     tag : 'i',
8939                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8940                     tooltip : 'This field is required'
8941                 },
8942                 {
8943                     tag: 'label',
8944                    //cls : 'input-group-addon',
8945                     html : this.fieldLabel
8946
8947                 },
8948
8949                inputblock
8950
8951            ];
8952            
8953            if(this.indicatorpos == 'right'){
8954                 
8955                 cfg.cn = [
8956                     {
8957                         tag: 'label',
8958                        //cls : 'input-group-addon',
8959                         html : this.fieldLabel
8960
8961                     },
8962                     {
8963                         tag : 'i',
8964                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8965                         tooltip : 'This field is required'
8966                     },
8967
8968                    inputblock
8969
8970                ];
8971
8972             }
8973
8974         } else {
8975             
8976             cfg.cn = [
8977
8978                     inputblock
8979
8980             ];
8981                 
8982                 
8983         };
8984         
8985         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8986            cfg.cls += ' navbar-form';
8987         }
8988         
8989         if (this.parentType === 'NavGroup') {
8990            cfg.cls += ' navbar-form';
8991            cfg.tag = 'li';
8992         }
8993         
8994         return cfg;
8995         
8996     },
8997     /**
8998      * return the real input element.
8999      */
9000     inputEl: function ()
9001     {
9002         return this.el.select('input.form-control',true).first();
9003     },
9004     
9005     tooltipEl : function()
9006     {
9007         return this.inputEl();
9008     },
9009     
9010     indicatorEl : function()
9011     {
9012         var indicator = this.el.select('i.roo-required-indicator',true).first();
9013         
9014         if(!indicator){
9015             return false;
9016         }
9017         
9018         return indicator;
9019         
9020     },
9021     
9022     setDisabled : function(v)
9023     {
9024         var i  = this.inputEl().dom;
9025         if (!v) {
9026             i.removeAttribute('disabled');
9027             return;
9028             
9029         }
9030         i.setAttribute('disabled','true');
9031     },
9032     initEvents : function()
9033     {
9034           
9035         this.inputEl().on("keydown" , this.fireKey,  this);
9036         this.inputEl().on("focus", this.onFocus,  this);
9037         this.inputEl().on("blur", this.onBlur,  this);
9038         
9039         this.inputEl().relayEvent('keyup', this);
9040         
9041         this.indicator = this.indicatorEl();
9042         
9043         if(this.indicator){
9044             this.indicator.addClass('invisible');
9045             
9046         }
9047  
9048         // reference to original value for reset
9049         this.originalValue = this.getValue();
9050         //Roo.form.TextField.superclass.initEvents.call(this);
9051         if(this.validationEvent == 'keyup'){
9052             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9053             this.inputEl().on('keyup', this.filterValidation, this);
9054         }
9055         else if(this.validationEvent !== false){
9056             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9057         }
9058         
9059         if(this.selectOnFocus){
9060             this.on("focus", this.preFocus, this);
9061             
9062         }
9063         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9064             this.inputEl().on("keypress", this.filterKeys, this);
9065         } else {
9066             this.inputEl().relayEvent('keypress', this);
9067         }
9068        /* if(this.grow){
9069             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9070             this.el.on("click", this.autoSize,  this);
9071         }
9072         */
9073         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9074             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9075         }
9076         
9077         if (typeof(this.before) == 'object') {
9078             this.before.render(this.el.select('.roo-input-before',true).first());
9079         }
9080         if (typeof(this.after) == 'object') {
9081             this.after.render(this.el.select('.roo-input-after',true).first());
9082         }
9083         
9084         
9085     },
9086     filterValidation : function(e){
9087         if(!e.isNavKeyPress()){
9088             this.validationTask.delay(this.validationDelay);
9089         }
9090     },
9091      /**
9092      * Validates the field value
9093      * @return {Boolean} True if the value is valid, else false
9094      */
9095     validate : function(){
9096         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9097         if(this.disabled || this.validateValue(this.getRawValue())){
9098             this.markValid();
9099             return true;
9100         }
9101         
9102         this.markInvalid();
9103         return false;
9104     },
9105     
9106     
9107     /**
9108      * Validates a value according to the field's validation rules and marks the field as invalid
9109      * if the validation fails
9110      * @param {Mixed} value The value to validate
9111      * @return {Boolean} True if the value is valid, else false
9112      */
9113     validateValue : function(value)
9114     {
9115         if(this.getVisibilityEl().hasClass('hidden')){
9116             return true;
9117         }
9118         
9119         if(value.length < 1)  { // if it's blank
9120             if(this.allowBlank){
9121                 return true;
9122             }
9123             return false;
9124         }
9125         
9126         if(value.length < this.minLength){
9127             return false;
9128         }
9129         if(value.length > this.maxLength){
9130             return false;
9131         }
9132         if(this.vtype){
9133             var vt = Roo.form.VTypes;
9134             if(!vt[this.vtype](value, this)){
9135                 return false;
9136             }
9137         }
9138         if(typeof this.validator == "function"){
9139             var msg = this.validator(value);
9140             if(msg !== true){
9141                 return false;
9142             }
9143             if (typeof(msg) == 'string') {
9144                 this.invalidText = msg;
9145             }
9146         }
9147         
9148         if(this.regex && !this.regex.test(value)){
9149             return false;
9150         }
9151         
9152         return true;
9153     },
9154     
9155      // private
9156     fireKey : function(e){
9157         //Roo.log('field ' + e.getKey());
9158         if(e.isNavKeyPress()){
9159             this.fireEvent("specialkey", this, e);
9160         }
9161     },
9162     focus : function (selectText){
9163         if(this.rendered){
9164             this.inputEl().focus();
9165             if(selectText === true){
9166                 this.inputEl().dom.select();
9167             }
9168         }
9169         return this;
9170     } ,
9171     
9172     onFocus : function(){
9173         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9174            // this.el.addClass(this.focusClass);
9175         }
9176         if(!this.hasFocus){
9177             this.hasFocus = true;
9178             this.startValue = this.getValue();
9179             this.fireEvent("focus", this);
9180         }
9181     },
9182     
9183     beforeBlur : Roo.emptyFn,
9184
9185     
9186     // private
9187     onBlur : function(){
9188         this.beforeBlur();
9189         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9190             //this.el.removeClass(this.focusClass);
9191         }
9192         this.hasFocus = false;
9193         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9194             this.validate();
9195         }
9196         var v = this.getValue();
9197         if(String(v) !== String(this.startValue)){
9198             this.fireEvent('change', this, v, this.startValue);
9199         }
9200         this.fireEvent("blur", this);
9201     },
9202     
9203     /**
9204      * Resets the current field value to the originally loaded value and clears any validation messages
9205      */
9206     reset : function(){
9207         this.setValue(this.originalValue);
9208         this.validate();
9209     },
9210      /**
9211      * Returns the name of the field
9212      * @return {Mixed} name The name field
9213      */
9214     getName: function(){
9215         return this.name;
9216     },
9217      /**
9218      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9219      * @return {Mixed} value The field value
9220      */
9221     getValue : function(){
9222         
9223         var v = this.inputEl().getValue();
9224         
9225         return v;
9226     },
9227     /**
9228      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9229      * @return {Mixed} value The field value
9230      */
9231     getRawValue : function(){
9232         var v = this.inputEl().getValue();
9233         
9234         return v;
9235     },
9236     
9237     /**
9238      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9239      * @param {Mixed} value The value to set
9240      */
9241     setRawValue : function(v){
9242         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9243     },
9244     
9245     selectText : function(start, end){
9246         var v = this.getRawValue();
9247         if(v.length > 0){
9248             start = start === undefined ? 0 : start;
9249             end = end === undefined ? v.length : end;
9250             var d = this.inputEl().dom;
9251             if(d.setSelectionRange){
9252                 d.setSelectionRange(start, end);
9253             }else if(d.createTextRange){
9254                 var range = d.createTextRange();
9255                 range.moveStart("character", start);
9256                 range.moveEnd("character", v.length-end);
9257                 range.select();
9258             }
9259         }
9260     },
9261     
9262     /**
9263      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9264      * @param {Mixed} value The value to set
9265      */
9266     setValue : function(v){
9267         this.value = v;
9268         if(this.rendered){
9269             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9270             this.validate();
9271         }
9272     },
9273     
9274     /*
9275     processValue : function(value){
9276         if(this.stripCharsRe){
9277             var newValue = value.replace(this.stripCharsRe, '');
9278             if(newValue !== value){
9279                 this.setRawValue(newValue);
9280                 return newValue;
9281             }
9282         }
9283         return value;
9284     },
9285   */
9286     preFocus : function(){
9287         
9288         if(this.selectOnFocus){
9289             this.inputEl().dom.select();
9290         }
9291     },
9292     filterKeys : function(e){
9293         var k = e.getKey();
9294         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9295             return;
9296         }
9297         var c = e.getCharCode(), cc = String.fromCharCode(c);
9298         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9299             return;
9300         }
9301         if(!this.maskRe.test(cc)){
9302             e.stopEvent();
9303         }
9304     },
9305      /**
9306      * Clear any invalid styles/messages for this field
9307      */
9308     clearInvalid : function(){
9309         
9310         if(!this.el || this.preventMark){ // not rendered
9311             return;
9312         }
9313         
9314      
9315         this.el.removeClass(this.invalidClass);
9316         
9317         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9318             
9319             var feedback = this.el.select('.form-control-feedback', true).first();
9320             
9321             if(feedback){
9322                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9323             }
9324             
9325         }
9326         
9327         this.fireEvent('valid', this);
9328     },
9329     
9330      /**
9331      * Mark this field as valid
9332      */
9333     markValid : function()
9334     {
9335         if(!this.el  || this.preventMark){ // not rendered...
9336             return;
9337         }
9338         
9339         this.el.removeClass([this.invalidClass, this.validClass]);
9340         
9341         var feedback = this.el.select('.form-control-feedback', true).first();
9342             
9343         if(feedback){
9344             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9345         }
9346         
9347         if(this.indicator){
9348             this.indicator.removeClass('visible');
9349             this.indicator.addClass('invisible');
9350         }
9351         
9352         if(this.disabled){
9353             return;
9354         }
9355         
9356         if(this.allowBlank && !this.getRawValue().length){
9357             return;
9358         }
9359         
9360         this.el.addClass(this.validClass);
9361         
9362         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9363             
9364             var feedback = this.el.select('.form-control-feedback', true).first();
9365             
9366             if(feedback){
9367                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9368                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9369             }
9370             
9371         }
9372         
9373         this.fireEvent('valid', this);
9374     },
9375     
9376      /**
9377      * Mark this field as invalid
9378      * @param {String} msg The validation message
9379      */
9380     markInvalid : function(msg)
9381     {
9382         if(!this.el  || this.preventMark){ // not rendered
9383             return;
9384         }
9385         
9386         this.el.removeClass([this.invalidClass, this.validClass]);
9387         
9388         var feedback = this.el.select('.form-control-feedback', true).first();
9389             
9390         if(feedback){
9391             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9392         }
9393
9394         if(this.disabled){
9395             return;
9396         }
9397         
9398         if(this.allowBlank && !this.getRawValue().length){
9399             return;
9400         }
9401         
9402         if(this.indicator){
9403             this.indicator.removeClass('invisible');
9404             this.indicator.addClass('visible');
9405         }
9406         
9407         this.el.addClass(this.invalidClass);
9408         
9409         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9410             
9411             var feedback = this.el.select('.form-control-feedback', true).first();
9412             
9413             if(feedback){
9414                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9415                 
9416                 if(this.getValue().length || this.forceFeedback){
9417                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9418                 }
9419                 
9420             }
9421             
9422         }
9423         
9424         this.fireEvent('invalid', this, msg);
9425     },
9426     // private
9427     SafariOnKeyDown : function(event)
9428     {
9429         // this is a workaround for a password hang bug on chrome/ webkit.
9430         if (this.inputEl().dom.type != 'password') {
9431             return;
9432         }
9433         
9434         var isSelectAll = false;
9435         
9436         if(this.inputEl().dom.selectionEnd > 0){
9437             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9438         }
9439         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9440             event.preventDefault();
9441             this.setValue('');
9442             return;
9443         }
9444         
9445         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9446             
9447             event.preventDefault();
9448             // this is very hacky as keydown always get's upper case.
9449             //
9450             var cc = String.fromCharCode(event.getCharCode());
9451             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9452             
9453         }
9454     },
9455     adjustWidth : function(tag, w){
9456         tag = tag.toLowerCase();
9457         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9458             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9459                 if(tag == 'input'){
9460                     return w + 2;
9461                 }
9462                 if(tag == 'textarea'){
9463                     return w-2;
9464                 }
9465             }else if(Roo.isOpera){
9466                 if(tag == 'input'){
9467                     return w + 2;
9468                 }
9469                 if(tag == 'textarea'){
9470                     return w-2;
9471                 }
9472             }
9473         }
9474         return w;
9475     },
9476     
9477     setFieldLabel : function(v)
9478     {
9479         if(!this.rendered){
9480             return;
9481         }
9482         
9483         if(this.indicator){
9484             var ar = this.el.select('label > span',true);
9485             
9486             if (ar.elements.length) {
9487                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9488                 this.fieldLabel = v;
9489                 return;
9490             }
9491             
9492             var br = this.el.select('label',true);
9493             
9494             if(br.elements.length) {
9495                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9496                 this.fieldLabel = v;
9497                 return;
9498             }
9499             
9500             Roo.log('Cannot Found any of label > span || label in input');
9501             return;
9502         }
9503         
9504         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9505         this.fieldLabel = v;
9506         
9507         
9508     }
9509 });
9510
9511  
9512 /*
9513  * - LGPL
9514  *
9515  * Input
9516  * 
9517  */
9518
9519 /**
9520  * @class Roo.bootstrap.TextArea
9521  * @extends Roo.bootstrap.Input
9522  * Bootstrap TextArea class
9523  * @cfg {Number} cols Specifies the visible width of a text area
9524  * @cfg {Number} rows Specifies the visible number of lines in a text area
9525  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9526  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9527  * @cfg {string} html text
9528  * 
9529  * @constructor
9530  * Create a new TextArea
9531  * @param {Object} config The config object
9532  */
9533
9534 Roo.bootstrap.TextArea = function(config){
9535     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9536    
9537 };
9538
9539 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9540      
9541     cols : false,
9542     rows : 5,
9543     readOnly : false,
9544     warp : 'soft',
9545     resize : false,
9546     value: false,
9547     html: false,
9548     
9549     getAutoCreate : function(){
9550         
9551         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9552         
9553         var id = Roo.id();
9554         
9555         var cfg = {};
9556         
9557         if(this.inputType != 'hidden'){
9558             cfg.cls = 'form-group' //input-group
9559         }
9560         
9561         var input =  {
9562             tag: 'textarea',
9563             id : id,
9564             warp : this.warp,
9565             rows : this.rows,
9566             value : this.value || '',
9567             html: this.html || '',
9568             cls : 'form-control',
9569             placeholder : this.placeholder || '' 
9570             
9571         };
9572         
9573         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9574             input.maxLength = this.maxLength;
9575         }
9576         
9577         if(this.resize){
9578             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9579         }
9580         
9581         if(this.cols){
9582             input.cols = this.cols;
9583         }
9584         
9585         if (this.readOnly) {
9586             input.readonly = true;
9587         }
9588         
9589         if (this.name) {
9590             input.name = this.name;
9591         }
9592         
9593         if (this.size) {
9594             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9595         }
9596         
9597         var settings=this;
9598         ['xs','sm','md','lg'].map(function(size){
9599             if (settings[size]) {
9600                 cfg.cls += ' col-' + size + '-' + settings[size];
9601             }
9602         });
9603         
9604         var inputblock = input;
9605         
9606         if(this.hasFeedback && !this.allowBlank){
9607             
9608             var feedback = {
9609                 tag: 'span',
9610                 cls: 'glyphicon form-control-feedback'
9611             };
9612
9613             inputblock = {
9614                 cls : 'has-feedback',
9615                 cn :  [
9616                     input,
9617                     feedback
9618                 ] 
9619             };  
9620         }
9621         
9622         
9623         if (this.before || this.after) {
9624             
9625             inputblock = {
9626                 cls : 'input-group',
9627                 cn :  [] 
9628             };
9629             if (this.before) {
9630                 inputblock.cn.push({
9631                     tag :'span',
9632                     cls : 'input-group-addon',
9633                     html : this.before
9634                 });
9635             }
9636             
9637             inputblock.cn.push(input);
9638             
9639             if(this.hasFeedback && !this.allowBlank){
9640                 inputblock.cls += ' has-feedback';
9641                 inputblock.cn.push(feedback);
9642             }
9643             
9644             if (this.after) {
9645                 inputblock.cn.push({
9646                     tag :'span',
9647                     cls : 'input-group-addon',
9648                     html : this.after
9649                 });
9650             }
9651             
9652         }
9653         
9654         if (align ==='left' && this.fieldLabel.length) {
9655             cfg.cn = [
9656                 {
9657                     tag: 'label',
9658                     'for' :  id,
9659                     cls : 'control-label',
9660                     html : this.fieldLabel
9661                 },
9662                 {
9663                     cls : "",
9664                     cn: [
9665                         inputblock
9666                     ]
9667                 }
9668
9669             ];
9670             
9671             if(this.labelWidth > 12){
9672                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9673             }
9674
9675             if(this.labelWidth < 13 && this.labelmd == 0){
9676                 this.labelmd = this.labelWidth;
9677             }
9678
9679             if(this.labellg > 0){
9680                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9681                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9682             }
9683
9684             if(this.labelmd > 0){
9685                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9686                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9687             }
9688
9689             if(this.labelsm > 0){
9690                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9691                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9692             }
9693
9694             if(this.labelxs > 0){
9695                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9696                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9697             }
9698             
9699         } else if ( this.fieldLabel.length) {
9700             cfg.cn = [
9701
9702                {
9703                    tag: 'label',
9704                    //cls : 'input-group-addon',
9705                    html : this.fieldLabel
9706
9707                },
9708
9709                inputblock
9710
9711            ];
9712
9713         } else {
9714
9715             cfg.cn = [
9716
9717                 inputblock
9718
9719             ];
9720                 
9721         }
9722         
9723         if (this.disabled) {
9724             input.disabled=true;
9725         }
9726         
9727         return cfg;
9728         
9729     },
9730     /**
9731      * return the real textarea element.
9732      */
9733     inputEl: function ()
9734     {
9735         return this.el.select('textarea.form-control',true).first();
9736     },
9737     
9738     /**
9739      * Clear any invalid styles/messages for this field
9740      */
9741     clearInvalid : function()
9742     {
9743         
9744         if(!this.el || this.preventMark){ // not rendered
9745             return;
9746         }
9747         
9748         var label = this.el.select('label', true).first();
9749         var icon = this.el.select('i.fa-star', true).first();
9750         
9751         if(label && icon){
9752             icon.remove();
9753         }
9754         
9755         this.el.removeClass(this.invalidClass);
9756         
9757         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9758             
9759             var feedback = this.el.select('.form-control-feedback', true).first();
9760             
9761             if(feedback){
9762                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9763             }
9764             
9765         }
9766         
9767         this.fireEvent('valid', this);
9768     },
9769     
9770      /**
9771      * Mark this field as valid
9772      */
9773     markValid : function()
9774     {
9775         if(!this.el  || this.preventMark){ // not rendered
9776             return;
9777         }
9778         
9779         this.el.removeClass([this.invalidClass, this.validClass]);
9780         
9781         var feedback = this.el.select('.form-control-feedback', true).first();
9782             
9783         if(feedback){
9784             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9785         }
9786
9787         if(this.disabled || this.allowBlank){
9788             return;
9789         }
9790         
9791         var label = this.el.select('label', true).first();
9792         var icon = this.el.select('i.fa-star', true).first();
9793         
9794         if(label && icon){
9795             icon.remove();
9796         }
9797         
9798         this.el.addClass(this.validClass);
9799         
9800         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9801             
9802             var feedback = this.el.select('.form-control-feedback', true).first();
9803             
9804             if(feedback){
9805                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9806                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9807             }
9808             
9809         }
9810         
9811         this.fireEvent('valid', this);
9812     },
9813     
9814      /**
9815      * Mark this field as invalid
9816      * @param {String} msg The validation message
9817      */
9818     markInvalid : function(msg)
9819     {
9820         if(!this.el  || this.preventMark){ // not rendered
9821             return;
9822         }
9823         
9824         this.el.removeClass([this.invalidClass, this.validClass]);
9825         
9826         var feedback = this.el.select('.form-control-feedback', true).first();
9827             
9828         if(feedback){
9829             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9830         }
9831
9832         if(this.disabled || this.allowBlank){
9833             return;
9834         }
9835         
9836         var label = this.el.select('label', true).first();
9837         var icon = this.el.select('i.fa-star', true).first();
9838         
9839         if(!this.getValue().length && label && !icon){
9840             this.el.createChild({
9841                 tag : 'i',
9842                 cls : 'text-danger fa fa-lg fa-star',
9843                 tooltip : 'This field is required',
9844                 style : 'margin-right:5px;'
9845             }, label, true);
9846         }
9847
9848         this.el.addClass(this.invalidClass);
9849         
9850         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9851             
9852             var feedback = this.el.select('.form-control-feedback', true).first();
9853             
9854             if(feedback){
9855                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9856                 
9857                 if(this.getValue().length || this.forceFeedback){
9858                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9859                 }
9860                 
9861             }
9862             
9863         }
9864         
9865         this.fireEvent('invalid', this, msg);
9866     }
9867 });
9868
9869  
9870 /*
9871  * - LGPL
9872  *
9873  * trigger field - base class for combo..
9874  * 
9875  */
9876  
9877 /**
9878  * @class Roo.bootstrap.TriggerField
9879  * @extends Roo.bootstrap.Input
9880  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9881  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9882  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9883  * for which you can provide a custom implementation.  For example:
9884  * <pre><code>
9885 var trigger = new Roo.bootstrap.TriggerField();
9886 trigger.onTriggerClick = myTriggerFn;
9887 trigger.applyTo('my-field');
9888 </code></pre>
9889  *
9890  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9891  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9892  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9893  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9894  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9895
9896  * @constructor
9897  * Create a new TriggerField.
9898  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9899  * to the base TextField)
9900  */
9901 Roo.bootstrap.TriggerField = function(config){
9902     this.mimicing = false;
9903     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9904 };
9905
9906 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9907     /**
9908      * @cfg {String} triggerClass A CSS class to apply to the trigger
9909      */
9910      /**
9911      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9912      */
9913     hideTrigger:false,
9914
9915     /**
9916      * @cfg {Boolean} removable (true|false) special filter default false
9917      */
9918     removable : false,
9919     
9920     /** @cfg {Boolean} grow @hide */
9921     /** @cfg {Number} growMin @hide */
9922     /** @cfg {Number} growMax @hide */
9923
9924     /**
9925      * @hide 
9926      * @method
9927      */
9928     autoSize: Roo.emptyFn,
9929     // private
9930     monitorTab : true,
9931     // private
9932     deferHeight : true,
9933
9934     
9935     actionMode : 'wrap',
9936     
9937     caret : false,
9938     
9939     
9940     getAutoCreate : function(){
9941        
9942         var align = this.labelAlign || this.parentLabelAlign();
9943         
9944         var id = Roo.id();
9945         
9946         var cfg = {
9947             cls: 'form-group' //input-group
9948         };
9949         
9950         
9951         var input =  {
9952             tag: 'input',
9953             id : id,
9954             type : this.inputType,
9955             cls : 'form-control',
9956             autocomplete: 'new-password',
9957             placeholder : this.placeholder || '' 
9958             
9959         };
9960         if (this.name) {
9961             input.name = this.name;
9962         }
9963         if (this.size) {
9964             input.cls += ' input-' + this.size;
9965         }
9966         
9967         if (this.disabled) {
9968             input.disabled=true;
9969         }
9970         
9971         var inputblock = input;
9972         
9973         if(this.hasFeedback && !this.allowBlank){
9974             
9975             var feedback = {
9976                 tag: 'span',
9977                 cls: 'glyphicon form-control-feedback'
9978             };
9979             
9980             if(this.removable && !this.editable && !this.tickable){
9981                 inputblock = {
9982                     cls : 'has-feedback',
9983                     cn :  [
9984                         inputblock,
9985                         {
9986                             tag: 'button',
9987                             html : 'x',
9988                             cls : 'roo-combo-removable-btn close'
9989                         },
9990                         feedback
9991                     ] 
9992                 };
9993             } else {
9994                 inputblock = {
9995                     cls : 'has-feedback',
9996                     cn :  [
9997                         inputblock,
9998                         feedback
9999                     ] 
10000                 };
10001             }
10002
10003         } else {
10004             if(this.removable && !this.editable && !this.tickable){
10005                 inputblock = {
10006                     cls : 'roo-removable',
10007                     cn :  [
10008                         inputblock,
10009                         {
10010                             tag: 'button',
10011                             html : 'x',
10012                             cls : 'roo-combo-removable-btn close'
10013                         }
10014                     ] 
10015                 };
10016             }
10017         }
10018         
10019         if (this.before || this.after) {
10020             
10021             inputblock = {
10022                 cls : 'input-group',
10023                 cn :  [] 
10024             };
10025             if (this.before) {
10026                 inputblock.cn.push({
10027                     tag :'span',
10028                     cls : 'input-group-addon',
10029                     html : this.before
10030                 });
10031             }
10032             
10033             inputblock.cn.push(input);
10034             
10035             if(this.hasFeedback && !this.allowBlank){
10036                 inputblock.cls += ' has-feedback';
10037                 inputblock.cn.push(feedback);
10038             }
10039             
10040             if (this.after) {
10041                 inputblock.cn.push({
10042                     tag :'span',
10043                     cls : 'input-group-addon',
10044                     html : this.after
10045                 });
10046             }
10047             
10048         };
10049         
10050         var box = {
10051             tag: 'div',
10052             cn: [
10053                 {
10054                     tag: 'input',
10055                     type : 'hidden',
10056                     cls: 'form-hidden-field'
10057                 },
10058                 inputblock
10059             ]
10060             
10061         };
10062         
10063         if(this.multiple){
10064             box = {
10065                 tag: 'div',
10066                 cn: [
10067                     {
10068                         tag: 'input',
10069                         type : 'hidden',
10070                         cls: 'form-hidden-field'
10071                     },
10072                     {
10073                         tag: 'ul',
10074                         cls: 'roo-select2-choices',
10075                         cn:[
10076                             {
10077                                 tag: 'li',
10078                                 cls: 'roo-select2-search-field',
10079                                 cn: [
10080
10081                                     inputblock
10082                                 ]
10083                             }
10084                         ]
10085                     }
10086                 ]
10087             }
10088         };
10089         
10090         var combobox = {
10091             cls: 'roo-select2-container input-group',
10092             cn: [
10093                 box
10094 //                {
10095 //                    tag: 'ul',
10096 //                    cls: 'typeahead typeahead-long dropdown-menu',
10097 //                    style: 'display:none'
10098 //                }
10099             ]
10100         };
10101         
10102         if(!this.multiple && this.showToggleBtn){
10103             
10104             var caret = {
10105                         tag: 'span',
10106                         cls: 'caret'
10107              };
10108             if (this.caret != false) {
10109                 caret = {
10110                      tag: 'i',
10111                      cls: 'fa fa-' + this.caret
10112                 };
10113                 
10114             }
10115             
10116             combobox.cn.push({
10117                 tag :'span',
10118                 cls : 'input-group-addon btn dropdown-toggle',
10119                 cn : [
10120                     caret,
10121                     {
10122                         tag: 'span',
10123                         cls: 'combobox-clear',
10124                         cn  : [
10125                             {
10126                                 tag : 'i',
10127                                 cls: 'icon-remove'
10128                             }
10129                         ]
10130                     }
10131                 ]
10132
10133             })
10134         }
10135         
10136         if(this.multiple){
10137             combobox.cls += ' roo-select2-container-multi';
10138         }
10139         
10140         if (align ==='left' && this.fieldLabel.length) {
10141             
10142             cfg.cls += ' roo-form-group-label-left';
10143
10144             cfg.cn = [
10145                 {
10146                     tag : 'i',
10147                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10148                     tooltip : 'This field is required'
10149                 },
10150                 {
10151                     tag: 'label',
10152                     'for' :  id,
10153                     cls : 'control-label',
10154                     html : this.fieldLabel
10155
10156                 },
10157                 {
10158                     cls : "", 
10159                     cn: [
10160                         combobox
10161                     ]
10162                 }
10163
10164             ];
10165             
10166             var labelCfg = cfg.cn[1];
10167             var contentCfg = cfg.cn[2];
10168             
10169             if(this.indicatorpos == 'right'){
10170                 cfg.cn = [
10171                     {
10172                         tag: 'label',
10173                         'for' :  id,
10174                         cls : 'control-label',
10175                         cn : [
10176                             {
10177                                 tag : 'span',
10178                                 html : this.fieldLabel
10179                             },
10180                             {
10181                                 tag : 'i',
10182                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10183                                 tooltip : 'This field is required'
10184                             }
10185                         ]
10186                     },
10187                     {
10188                         cls : "", 
10189                         cn: [
10190                             combobox
10191                         ]
10192                     }
10193
10194                 ];
10195                 
10196                 labelCfg = cfg.cn[0];
10197                 contentCfg = cfg.cn[1];
10198             }
10199             
10200             if(this.labelWidth > 12){
10201                 labelCfg.style = "width: " + this.labelWidth + 'px';
10202             }
10203             
10204             if(this.labelWidth < 13 && this.labelmd == 0){
10205                 this.labelmd = this.labelWidth;
10206             }
10207             
10208             if(this.labellg > 0){
10209                 labelCfg.cls += ' col-lg-' + this.labellg;
10210                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10211             }
10212             
10213             if(this.labelmd > 0){
10214                 labelCfg.cls += ' col-md-' + this.labelmd;
10215                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10216             }
10217             
10218             if(this.labelsm > 0){
10219                 labelCfg.cls += ' col-sm-' + this.labelsm;
10220                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10221             }
10222             
10223             if(this.labelxs > 0){
10224                 labelCfg.cls += ' col-xs-' + this.labelxs;
10225                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10226             }
10227             
10228         } else if ( this.fieldLabel.length) {
10229 //                Roo.log(" label");
10230             cfg.cn = [
10231                 {
10232                    tag : 'i',
10233                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10234                    tooltip : 'This field is required'
10235                },
10236                {
10237                    tag: 'label',
10238                    //cls : 'input-group-addon',
10239                    html : this.fieldLabel
10240
10241                },
10242
10243                combobox
10244
10245             ];
10246             
10247             if(this.indicatorpos == 'right'){
10248                 
10249                 cfg.cn = [
10250                     {
10251                        tag: 'label',
10252                        cn : [
10253                            {
10254                                tag : 'span',
10255                                html : this.fieldLabel
10256                            },
10257                            {
10258                               tag : 'i',
10259                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10260                               tooltip : 'This field is required'
10261                            }
10262                        ]
10263
10264                     },
10265                     combobox
10266
10267                 ];
10268
10269             }
10270
10271         } else {
10272             
10273 //                Roo.log(" no label && no align");
10274                 cfg = combobox
10275                      
10276                 
10277         }
10278         
10279         var settings=this;
10280         ['xs','sm','md','lg'].map(function(size){
10281             if (settings[size]) {
10282                 cfg.cls += ' col-' + size + '-' + settings[size];
10283             }
10284         });
10285         
10286         return cfg;
10287         
10288     },
10289     
10290     
10291     
10292     // private
10293     onResize : function(w, h){
10294 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10295 //        if(typeof w == 'number'){
10296 //            var x = w - this.trigger.getWidth();
10297 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10298 //            this.trigger.setStyle('left', x+'px');
10299 //        }
10300     },
10301
10302     // private
10303     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10304
10305     // private
10306     getResizeEl : function(){
10307         return this.inputEl();
10308     },
10309
10310     // private
10311     getPositionEl : function(){
10312         return this.inputEl();
10313     },
10314
10315     // private
10316     alignErrorIcon : function(){
10317         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10318     },
10319
10320     // private
10321     initEvents : function(){
10322         
10323         this.createList();
10324         
10325         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10326         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10327         if(!this.multiple && this.showToggleBtn){
10328             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10329             if(this.hideTrigger){
10330                 this.trigger.setDisplayed(false);
10331             }
10332             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10333         }
10334         
10335         if(this.multiple){
10336             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10337         }
10338         
10339         if(this.removable && !this.editable && !this.tickable){
10340             var close = this.closeTriggerEl();
10341             
10342             if(close){
10343                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10344                 close.on('click', this.removeBtnClick, this, close);
10345             }
10346         }
10347         
10348         //this.trigger.addClassOnOver('x-form-trigger-over');
10349         //this.trigger.addClassOnClick('x-form-trigger-click');
10350         
10351         //if(!this.width){
10352         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10353         //}
10354     },
10355     
10356     closeTriggerEl : function()
10357     {
10358         var close = this.el.select('.roo-combo-removable-btn', true).first();
10359         return close ? close : false;
10360     },
10361     
10362     removeBtnClick : function(e, h, el)
10363     {
10364         e.preventDefault();
10365         
10366         if(this.fireEvent("remove", this) !== false){
10367             this.reset();
10368             this.fireEvent("afterremove", this)
10369         }
10370     },
10371     
10372     createList : function()
10373     {
10374         this.list = Roo.get(document.body).createChild({
10375             tag: 'ul',
10376             cls: 'typeahead typeahead-long dropdown-menu',
10377             style: 'display:none'
10378         });
10379         
10380         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10381         
10382     },
10383
10384     // private
10385     initTrigger : function(){
10386        
10387     },
10388
10389     // private
10390     onDestroy : function(){
10391         if(this.trigger){
10392             this.trigger.removeAllListeners();
10393           //  this.trigger.remove();
10394         }
10395         //if(this.wrap){
10396         //    this.wrap.remove();
10397         //}
10398         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10399     },
10400
10401     // private
10402     onFocus : function(){
10403         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10404         /*
10405         if(!this.mimicing){
10406             this.wrap.addClass('x-trigger-wrap-focus');
10407             this.mimicing = true;
10408             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10409             if(this.monitorTab){
10410                 this.el.on("keydown", this.checkTab, this);
10411             }
10412         }
10413         */
10414     },
10415
10416     // private
10417     checkTab : function(e){
10418         if(e.getKey() == e.TAB){
10419             this.triggerBlur();
10420         }
10421     },
10422
10423     // private
10424     onBlur : function(){
10425         // do nothing
10426     },
10427
10428     // private
10429     mimicBlur : function(e, t){
10430         /*
10431         if(!this.wrap.contains(t) && this.validateBlur()){
10432             this.triggerBlur();
10433         }
10434         */
10435     },
10436
10437     // private
10438     triggerBlur : function(){
10439         this.mimicing = false;
10440         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10441         if(this.monitorTab){
10442             this.el.un("keydown", this.checkTab, this);
10443         }
10444         //this.wrap.removeClass('x-trigger-wrap-focus');
10445         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10446     },
10447
10448     // private
10449     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10450     validateBlur : function(e, t){
10451         return true;
10452     },
10453
10454     // private
10455     onDisable : function(){
10456         this.inputEl().dom.disabled = true;
10457         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10458         //if(this.wrap){
10459         //    this.wrap.addClass('x-item-disabled');
10460         //}
10461     },
10462
10463     // private
10464     onEnable : function(){
10465         this.inputEl().dom.disabled = false;
10466         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10467         //if(this.wrap){
10468         //    this.el.removeClass('x-item-disabled');
10469         //}
10470     },
10471
10472     // private
10473     onShow : function(){
10474         var ae = this.getActionEl();
10475         
10476         if(ae){
10477             ae.dom.style.display = '';
10478             ae.dom.style.visibility = 'visible';
10479         }
10480     },
10481
10482     // private
10483     
10484     onHide : function(){
10485         var ae = this.getActionEl();
10486         ae.dom.style.display = 'none';
10487     },
10488
10489     /**
10490      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10491      * by an implementing function.
10492      * @method
10493      * @param {EventObject} e
10494      */
10495     onTriggerClick : Roo.emptyFn
10496 });
10497  /*
10498  * Based on:
10499  * Ext JS Library 1.1.1
10500  * Copyright(c) 2006-2007, Ext JS, LLC.
10501  *
10502  * Originally Released Under LGPL - original licence link has changed is not relivant.
10503  *
10504  * Fork - LGPL
10505  * <script type="text/javascript">
10506  */
10507
10508
10509 /**
10510  * @class Roo.data.SortTypes
10511  * @singleton
10512  * Defines the default sorting (casting?) comparison functions used when sorting data.
10513  */
10514 Roo.data.SortTypes = {
10515     /**
10516      * Default sort that does nothing
10517      * @param {Mixed} s The value being converted
10518      * @return {Mixed} The comparison value
10519      */
10520     none : function(s){
10521         return s;
10522     },
10523     
10524     /**
10525      * The regular expression used to strip tags
10526      * @type {RegExp}
10527      * @property
10528      */
10529     stripTagsRE : /<\/?[^>]+>/gi,
10530     
10531     /**
10532      * Strips all HTML tags to sort on text only
10533      * @param {Mixed} s The value being converted
10534      * @return {String} The comparison value
10535      */
10536     asText : function(s){
10537         return String(s).replace(this.stripTagsRE, "");
10538     },
10539     
10540     /**
10541      * Strips all HTML tags to sort on text only - Case insensitive
10542      * @param {Mixed} s The value being converted
10543      * @return {String} The comparison value
10544      */
10545     asUCText : function(s){
10546         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10547     },
10548     
10549     /**
10550      * Case insensitive string
10551      * @param {Mixed} s The value being converted
10552      * @return {String} The comparison value
10553      */
10554     asUCString : function(s) {
10555         return String(s).toUpperCase();
10556     },
10557     
10558     /**
10559      * Date sorting
10560      * @param {Mixed} s The value being converted
10561      * @return {Number} The comparison value
10562      */
10563     asDate : function(s) {
10564         if(!s){
10565             return 0;
10566         }
10567         if(s instanceof Date){
10568             return s.getTime();
10569         }
10570         return Date.parse(String(s));
10571     },
10572     
10573     /**
10574      * Float sorting
10575      * @param {Mixed} s The value being converted
10576      * @return {Float} The comparison value
10577      */
10578     asFloat : function(s) {
10579         var val = parseFloat(String(s).replace(/,/g, ""));
10580         if(isNaN(val)) {
10581             val = 0;
10582         }
10583         return val;
10584     },
10585     
10586     /**
10587      * Integer sorting
10588      * @param {Mixed} s The value being converted
10589      * @return {Number} The comparison value
10590      */
10591     asInt : function(s) {
10592         var val = parseInt(String(s).replace(/,/g, ""));
10593         if(isNaN(val)) {
10594             val = 0;
10595         }
10596         return val;
10597     }
10598 };/*
10599  * Based on:
10600  * Ext JS Library 1.1.1
10601  * Copyright(c) 2006-2007, Ext JS, LLC.
10602  *
10603  * Originally Released Under LGPL - original licence link has changed is not relivant.
10604  *
10605  * Fork - LGPL
10606  * <script type="text/javascript">
10607  */
10608
10609 /**
10610 * @class Roo.data.Record
10611  * Instances of this class encapsulate both record <em>definition</em> information, and record
10612  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10613  * to access Records cached in an {@link Roo.data.Store} object.<br>
10614  * <p>
10615  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10616  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10617  * objects.<br>
10618  * <p>
10619  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10620  * @constructor
10621  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10622  * {@link #create}. The parameters are the same.
10623  * @param {Array} data An associative Array of data values keyed by the field name.
10624  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10625  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10626  * not specified an integer id is generated.
10627  */
10628 Roo.data.Record = function(data, id){
10629     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10630     this.data = data;
10631 };
10632
10633 /**
10634  * Generate a constructor for a specific record layout.
10635  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10636  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10637  * Each field definition object may contain the following properties: <ul>
10638  * <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,
10639  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10640  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10641  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10642  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10643  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10644  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10645  * this may be omitted.</p></li>
10646  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10647  * <ul><li>auto (Default, implies no conversion)</li>
10648  * <li>string</li>
10649  * <li>int</li>
10650  * <li>float</li>
10651  * <li>boolean</li>
10652  * <li>date</li></ul></p></li>
10653  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10654  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10655  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10656  * by the Reader into an object that will be stored in the Record. It is passed the
10657  * following parameters:<ul>
10658  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10659  * </ul></p></li>
10660  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10661  * </ul>
10662  * <br>usage:<br><pre><code>
10663 var TopicRecord = Roo.data.Record.create(
10664     {name: 'title', mapping: 'topic_title'},
10665     {name: 'author', mapping: 'username'},
10666     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10667     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10668     {name: 'lastPoster', mapping: 'user2'},
10669     {name: 'excerpt', mapping: 'post_text'}
10670 );
10671
10672 var myNewRecord = new TopicRecord({
10673     title: 'Do my job please',
10674     author: 'noobie',
10675     totalPosts: 1,
10676     lastPost: new Date(),
10677     lastPoster: 'Animal',
10678     excerpt: 'No way dude!'
10679 });
10680 myStore.add(myNewRecord);
10681 </code></pre>
10682  * @method create
10683  * @static
10684  */
10685 Roo.data.Record.create = function(o){
10686     var f = function(){
10687         f.superclass.constructor.apply(this, arguments);
10688     };
10689     Roo.extend(f, Roo.data.Record);
10690     var p = f.prototype;
10691     p.fields = new Roo.util.MixedCollection(false, function(field){
10692         return field.name;
10693     });
10694     for(var i = 0, len = o.length; i < len; i++){
10695         p.fields.add(new Roo.data.Field(o[i]));
10696     }
10697     f.getField = function(name){
10698         return p.fields.get(name);  
10699     };
10700     return f;
10701 };
10702
10703 Roo.data.Record.AUTO_ID = 1000;
10704 Roo.data.Record.EDIT = 'edit';
10705 Roo.data.Record.REJECT = 'reject';
10706 Roo.data.Record.COMMIT = 'commit';
10707
10708 Roo.data.Record.prototype = {
10709     /**
10710      * Readonly flag - true if this record has been modified.
10711      * @type Boolean
10712      */
10713     dirty : false,
10714     editing : false,
10715     error: null,
10716     modified: null,
10717
10718     // private
10719     join : function(store){
10720         this.store = store;
10721     },
10722
10723     /**
10724      * Set the named field to the specified value.
10725      * @param {String} name The name of the field to set.
10726      * @param {Object} value The value to set the field to.
10727      */
10728     set : function(name, value){
10729         if(this.data[name] == value){
10730             return;
10731         }
10732         this.dirty = true;
10733         if(!this.modified){
10734             this.modified = {};
10735         }
10736         if(typeof this.modified[name] == 'undefined'){
10737             this.modified[name] = this.data[name];
10738         }
10739         this.data[name] = value;
10740         if(!this.editing && this.store){
10741             this.store.afterEdit(this);
10742         }       
10743     },
10744
10745     /**
10746      * Get the value of the named field.
10747      * @param {String} name The name of the field to get the value of.
10748      * @return {Object} The value of the field.
10749      */
10750     get : function(name){
10751         return this.data[name]; 
10752     },
10753
10754     // private
10755     beginEdit : function(){
10756         this.editing = true;
10757         this.modified = {}; 
10758     },
10759
10760     // private
10761     cancelEdit : function(){
10762         this.editing = false;
10763         delete this.modified;
10764     },
10765
10766     // private
10767     endEdit : function(){
10768         this.editing = false;
10769         if(this.dirty && this.store){
10770             this.store.afterEdit(this);
10771         }
10772     },
10773
10774     /**
10775      * Usually called by the {@link Roo.data.Store} which owns the Record.
10776      * Rejects all changes made to the Record since either creation, or the last commit operation.
10777      * Modified fields are reverted to their original values.
10778      * <p>
10779      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10780      * of reject operations.
10781      */
10782     reject : function(){
10783         var m = this.modified;
10784         for(var n in m){
10785             if(typeof m[n] != "function"){
10786                 this.data[n] = m[n];
10787             }
10788         }
10789         this.dirty = false;
10790         delete this.modified;
10791         this.editing = false;
10792         if(this.store){
10793             this.store.afterReject(this);
10794         }
10795     },
10796
10797     /**
10798      * Usually called by the {@link Roo.data.Store} which owns the Record.
10799      * Commits all changes made to the Record since either creation, or the last commit operation.
10800      * <p>
10801      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10802      * of commit operations.
10803      */
10804     commit : function(){
10805         this.dirty = false;
10806         delete this.modified;
10807         this.editing = false;
10808         if(this.store){
10809             this.store.afterCommit(this);
10810         }
10811     },
10812
10813     // private
10814     hasError : function(){
10815         return this.error != null;
10816     },
10817
10818     // private
10819     clearError : function(){
10820         this.error = null;
10821     },
10822
10823     /**
10824      * Creates a copy of this record.
10825      * @param {String} id (optional) A new record id if you don't want to use this record's id
10826      * @return {Record}
10827      */
10828     copy : function(newId) {
10829         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10830     }
10831 };/*
10832  * Based on:
10833  * Ext JS Library 1.1.1
10834  * Copyright(c) 2006-2007, Ext JS, LLC.
10835  *
10836  * Originally Released Under LGPL - original licence link has changed is not relivant.
10837  *
10838  * Fork - LGPL
10839  * <script type="text/javascript">
10840  */
10841
10842
10843
10844 /**
10845  * @class Roo.data.Store
10846  * @extends Roo.util.Observable
10847  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10848  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10849  * <p>
10850  * 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
10851  * has no knowledge of the format of the data returned by the Proxy.<br>
10852  * <p>
10853  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10854  * instances from the data object. These records are cached and made available through accessor functions.
10855  * @constructor
10856  * Creates a new Store.
10857  * @param {Object} config A config object containing the objects needed for the Store to access data,
10858  * and read the data into Records.
10859  */
10860 Roo.data.Store = function(config){
10861     this.data = new Roo.util.MixedCollection(false);
10862     this.data.getKey = function(o){
10863         return o.id;
10864     };
10865     this.baseParams = {};
10866     // private
10867     this.paramNames = {
10868         "start" : "start",
10869         "limit" : "limit",
10870         "sort" : "sort",
10871         "dir" : "dir",
10872         "multisort" : "_multisort"
10873     };
10874
10875     if(config && config.data){
10876         this.inlineData = config.data;
10877         delete config.data;
10878     }
10879
10880     Roo.apply(this, config);
10881     
10882     if(this.reader){ // reader passed
10883         this.reader = Roo.factory(this.reader, Roo.data);
10884         this.reader.xmodule = this.xmodule || false;
10885         if(!this.recordType){
10886             this.recordType = this.reader.recordType;
10887         }
10888         if(this.reader.onMetaChange){
10889             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10890         }
10891     }
10892
10893     if(this.recordType){
10894         this.fields = this.recordType.prototype.fields;
10895     }
10896     this.modified = [];
10897
10898     this.addEvents({
10899         /**
10900          * @event datachanged
10901          * Fires when the data cache has changed, and a widget which is using this Store
10902          * as a Record cache should refresh its view.
10903          * @param {Store} this
10904          */
10905         datachanged : true,
10906         /**
10907          * @event metachange
10908          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10909          * @param {Store} this
10910          * @param {Object} meta The JSON metadata
10911          */
10912         metachange : true,
10913         /**
10914          * @event add
10915          * Fires when Records have been added to the Store
10916          * @param {Store} this
10917          * @param {Roo.data.Record[]} records The array of Records added
10918          * @param {Number} index The index at which the record(s) were added
10919          */
10920         add : true,
10921         /**
10922          * @event remove
10923          * Fires when a Record has been removed from the Store
10924          * @param {Store} this
10925          * @param {Roo.data.Record} record The Record that was removed
10926          * @param {Number} index The index at which the record was removed
10927          */
10928         remove : true,
10929         /**
10930          * @event update
10931          * Fires when a Record has been updated
10932          * @param {Store} this
10933          * @param {Roo.data.Record} record The Record that was updated
10934          * @param {String} operation The update operation being performed.  Value may be one of:
10935          * <pre><code>
10936  Roo.data.Record.EDIT
10937  Roo.data.Record.REJECT
10938  Roo.data.Record.COMMIT
10939          * </code></pre>
10940          */
10941         update : true,
10942         /**
10943          * @event clear
10944          * Fires when the data cache has been cleared.
10945          * @param {Store} this
10946          */
10947         clear : true,
10948         /**
10949          * @event beforeload
10950          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10951          * the load action will be canceled.
10952          * @param {Store} this
10953          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10954          */
10955         beforeload : true,
10956         /**
10957          * @event beforeloadadd
10958          * Fires after a new set of Records has been loaded.
10959          * @param {Store} this
10960          * @param {Roo.data.Record[]} records The Records that were loaded
10961          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10962          */
10963         beforeloadadd : true,
10964         /**
10965          * @event load
10966          * Fires after a new set of Records has been loaded, before they are added to the store.
10967          * @param {Store} this
10968          * @param {Roo.data.Record[]} records The Records that were loaded
10969          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10970          * @params {Object} return from reader
10971          */
10972         load : true,
10973         /**
10974          * @event loadexception
10975          * Fires if an exception occurs in the Proxy during loading.
10976          * Called with the signature of the Proxy's "loadexception" event.
10977          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10978          * 
10979          * @param {Proxy} 
10980          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10981          * @param {Object} load options 
10982          * @param {Object} jsonData from your request (normally this contains the Exception)
10983          */
10984         loadexception : true
10985     });
10986     
10987     if(this.proxy){
10988         this.proxy = Roo.factory(this.proxy, Roo.data);
10989         this.proxy.xmodule = this.xmodule || false;
10990         this.relayEvents(this.proxy,  ["loadexception"]);
10991     }
10992     this.sortToggle = {};
10993     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10994
10995     Roo.data.Store.superclass.constructor.call(this);
10996
10997     if(this.inlineData){
10998         this.loadData(this.inlineData);
10999         delete this.inlineData;
11000     }
11001 };
11002
11003 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11004      /**
11005     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11006     * without a remote query - used by combo/forms at present.
11007     */
11008     
11009     /**
11010     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11011     */
11012     /**
11013     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11014     */
11015     /**
11016     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11017     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11018     */
11019     /**
11020     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11021     * on any HTTP request
11022     */
11023     /**
11024     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11025     */
11026     /**
11027     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11028     */
11029     multiSort: false,
11030     /**
11031     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11032     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11033     */
11034     remoteSort : false,
11035
11036     /**
11037     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11038      * loaded or when a record is removed. (defaults to false).
11039     */
11040     pruneModifiedRecords : false,
11041
11042     // private
11043     lastOptions : null,
11044
11045     /**
11046      * Add Records to the Store and fires the add event.
11047      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11048      */
11049     add : function(records){
11050         records = [].concat(records);
11051         for(var i = 0, len = records.length; i < len; i++){
11052             records[i].join(this);
11053         }
11054         var index = this.data.length;
11055         this.data.addAll(records);
11056         this.fireEvent("add", this, records, index);
11057     },
11058
11059     /**
11060      * Remove a Record from the Store and fires the remove event.
11061      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11062      */
11063     remove : function(record){
11064         var index = this.data.indexOf(record);
11065         this.data.removeAt(index);
11066         if(this.pruneModifiedRecords){
11067             this.modified.remove(record);
11068         }
11069         this.fireEvent("remove", this, record, index);
11070     },
11071
11072     /**
11073      * Remove all Records from the Store and fires the clear event.
11074      */
11075     removeAll : function(){
11076         this.data.clear();
11077         if(this.pruneModifiedRecords){
11078             this.modified = [];
11079         }
11080         this.fireEvent("clear", this);
11081     },
11082
11083     /**
11084      * Inserts Records to the Store at the given index and fires the add event.
11085      * @param {Number} index The start index at which to insert the passed Records.
11086      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11087      */
11088     insert : function(index, records){
11089         records = [].concat(records);
11090         for(var i = 0, len = records.length; i < len; i++){
11091             this.data.insert(index, records[i]);
11092             records[i].join(this);
11093         }
11094         this.fireEvent("add", this, records, index);
11095     },
11096
11097     /**
11098      * Get the index within the cache of the passed Record.
11099      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11100      * @return {Number} The index of the passed Record. Returns -1 if not found.
11101      */
11102     indexOf : function(record){
11103         return this.data.indexOf(record);
11104     },
11105
11106     /**
11107      * Get the index within the cache of the Record with the passed id.
11108      * @param {String} id The id of the Record to find.
11109      * @return {Number} The index of the Record. Returns -1 if not found.
11110      */
11111     indexOfId : function(id){
11112         return this.data.indexOfKey(id);
11113     },
11114
11115     /**
11116      * Get the Record with the specified id.
11117      * @param {String} id The id of the Record to find.
11118      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11119      */
11120     getById : function(id){
11121         return this.data.key(id);
11122     },
11123
11124     /**
11125      * Get the Record at the specified index.
11126      * @param {Number} index The index of the Record to find.
11127      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11128      */
11129     getAt : function(index){
11130         return this.data.itemAt(index);
11131     },
11132
11133     /**
11134      * Returns a range of Records between specified indices.
11135      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11136      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11137      * @return {Roo.data.Record[]} An array of Records
11138      */
11139     getRange : function(start, end){
11140         return this.data.getRange(start, end);
11141     },
11142
11143     // private
11144     storeOptions : function(o){
11145         o = Roo.apply({}, o);
11146         delete o.callback;
11147         delete o.scope;
11148         this.lastOptions = o;
11149     },
11150
11151     /**
11152      * Loads the Record cache from the configured Proxy using the configured Reader.
11153      * <p>
11154      * If using remote paging, then the first load call must specify the <em>start</em>
11155      * and <em>limit</em> properties in the options.params property to establish the initial
11156      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11157      * <p>
11158      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11159      * and this call will return before the new data has been loaded. Perform any post-processing
11160      * in a callback function, or in a "load" event handler.</strong>
11161      * <p>
11162      * @param {Object} options An object containing properties which control loading options:<ul>
11163      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11164      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11165      * passed the following arguments:<ul>
11166      * <li>r : Roo.data.Record[]</li>
11167      * <li>options: Options object from the load call</li>
11168      * <li>success: Boolean success indicator</li></ul></li>
11169      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11170      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11171      * </ul>
11172      */
11173     load : function(options){
11174         options = options || {};
11175         if(this.fireEvent("beforeload", this, options) !== false){
11176             this.storeOptions(options);
11177             var p = Roo.apply(options.params || {}, this.baseParams);
11178             // if meta was not loaded from remote source.. try requesting it.
11179             if (!this.reader.metaFromRemote) {
11180                 p._requestMeta = 1;
11181             }
11182             if(this.sortInfo && this.remoteSort){
11183                 var pn = this.paramNames;
11184                 p[pn["sort"]] = this.sortInfo.field;
11185                 p[pn["dir"]] = this.sortInfo.direction;
11186             }
11187             if (this.multiSort) {
11188                 var pn = this.paramNames;
11189                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11190             }
11191             
11192             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11193         }
11194     },
11195
11196     /**
11197      * Reloads the Record cache from the configured Proxy using the configured Reader and
11198      * the options from the last load operation performed.
11199      * @param {Object} options (optional) An object containing properties which may override the options
11200      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11201      * the most recently used options are reused).
11202      */
11203     reload : function(options){
11204         this.load(Roo.applyIf(options||{}, this.lastOptions));
11205     },
11206
11207     // private
11208     // Called as a callback by the Reader during a load operation.
11209     loadRecords : function(o, options, success){
11210         if(!o || success === false){
11211             if(success !== false){
11212                 this.fireEvent("load", this, [], options, o);
11213             }
11214             if(options.callback){
11215                 options.callback.call(options.scope || this, [], options, false);
11216             }
11217             return;
11218         }
11219         // if data returned failure - throw an exception.
11220         if (o.success === false) {
11221             // show a message if no listener is registered.
11222             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11223                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11224             }
11225             // loadmask wil be hooked into this..
11226             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11227             return;
11228         }
11229         var r = o.records, t = o.totalRecords || r.length;
11230         
11231         this.fireEvent("beforeloadadd", this, r, options, o);
11232         
11233         if(!options || options.add !== true){
11234             if(this.pruneModifiedRecords){
11235                 this.modified = [];
11236             }
11237             for(var i = 0, len = r.length; i < len; i++){
11238                 r[i].join(this);
11239             }
11240             if(this.snapshot){
11241                 this.data = this.snapshot;
11242                 delete this.snapshot;
11243             }
11244             this.data.clear();
11245             this.data.addAll(r);
11246             this.totalLength = t;
11247             this.applySort();
11248             this.fireEvent("datachanged", this);
11249         }else{
11250             this.totalLength = Math.max(t, this.data.length+r.length);
11251             this.add(r);
11252         }
11253         
11254         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11255                 
11256             var e = new Roo.data.Record({});
11257
11258             e.set(this.parent.displayField, this.parent.emptyTitle);
11259             e.set(this.parent.valueField, '');
11260
11261             this.insert(0, e);
11262         }
11263             
11264         this.fireEvent("load", this, r, options, o);
11265         if(options.callback){
11266             options.callback.call(options.scope || this, r, options, true);
11267         }
11268     },
11269
11270
11271     /**
11272      * Loads data from a passed data block. A Reader which understands the format of the data
11273      * must have been configured in the constructor.
11274      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11275      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11276      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11277      */
11278     loadData : function(o, append){
11279         var r = this.reader.readRecords(o);
11280         this.loadRecords(r, {add: append}, true);
11281     },
11282
11283     /**
11284      * Gets the number of cached records.
11285      * <p>
11286      * <em>If using paging, this may not be the total size of the dataset. If the data object
11287      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11288      * the data set size</em>
11289      */
11290     getCount : function(){
11291         return this.data.length || 0;
11292     },
11293
11294     /**
11295      * Gets the total number of records in the dataset as returned by the server.
11296      * <p>
11297      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11298      * the dataset size</em>
11299      */
11300     getTotalCount : function(){
11301         return this.totalLength || 0;
11302     },
11303
11304     /**
11305      * Returns the sort state of the Store as an object with two properties:
11306      * <pre><code>
11307  field {String} The name of the field by which the Records are sorted
11308  direction {String} The sort order, "ASC" or "DESC"
11309      * </code></pre>
11310      */
11311     getSortState : function(){
11312         return this.sortInfo;
11313     },
11314
11315     // private
11316     applySort : function(){
11317         if(this.sortInfo && !this.remoteSort){
11318             var s = this.sortInfo, f = s.field;
11319             var st = this.fields.get(f).sortType;
11320             var fn = function(r1, r2){
11321                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11322                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11323             };
11324             this.data.sort(s.direction, fn);
11325             if(this.snapshot && this.snapshot != this.data){
11326                 this.snapshot.sort(s.direction, fn);
11327             }
11328         }
11329     },
11330
11331     /**
11332      * Sets the default sort column and order to be used by the next load operation.
11333      * @param {String} fieldName The name of the field to sort by.
11334      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11335      */
11336     setDefaultSort : function(field, dir){
11337         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11338     },
11339
11340     /**
11341      * Sort the Records.
11342      * If remote sorting is used, the sort is performed on the server, and the cache is
11343      * reloaded. If local sorting is used, the cache is sorted internally.
11344      * @param {String} fieldName The name of the field to sort by.
11345      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11346      */
11347     sort : function(fieldName, dir){
11348         var f = this.fields.get(fieldName);
11349         if(!dir){
11350             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11351             
11352             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11353                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11354             }else{
11355                 dir = f.sortDir;
11356             }
11357         }
11358         this.sortToggle[f.name] = dir;
11359         this.sortInfo = {field: f.name, direction: dir};
11360         if(!this.remoteSort){
11361             this.applySort();
11362             this.fireEvent("datachanged", this);
11363         }else{
11364             this.load(this.lastOptions);
11365         }
11366     },
11367
11368     /**
11369      * Calls the specified function for each of the Records in the cache.
11370      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11371      * Returning <em>false</em> aborts and exits the iteration.
11372      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11373      */
11374     each : function(fn, scope){
11375         this.data.each(fn, scope);
11376     },
11377
11378     /**
11379      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11380      * (e.g., during paging).
11381      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11382      */
11383     getModifiedRecords : function(){
11384         return this.modified;
11385     },
11386
11387     // private
11388     createFilterFn : function(property, value, anyMatch){
11389         if(!value.exec){ // not a regex
11390             value = String(value);
11391             if(value.length == 0){
11392                 return false;
11393             }
11394             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11395         }
11396         return function(r){
11397             return value.test(r.data[property]);
11398         };
11399     },
11400
11401     /**
11402      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11403      * @param {String} property A field on your records
11404      * @param {Number} start The record index to start at (defaults to 0)
11405      * @param {Number} end The last record index to include (defaults to length - 1)
11406      * @return {Number} The sum
11407      */
11408     sum : function(property, start, end){
11409         var rs = this.data.items, v = 0;
11410         start = start || 0;
11411         end = (end || end === 0) ? end : rs.length-1;
11412
11413         for(var i = start; i <= end; i++){
11414             v += (rs[i].data[property] || 0);
11415         }
11416         return v;
11417     },
11418
11419     /**
11420      * Filter the records by a specified property.
11421      * @param {String} field A field on your records
11422      * @param {String/RegExp} value Either a string that the field
11423      * should start with or a RegExp to test against the field
11424      * @param {Boolean} anyMatch True to match any part not just the beginning
11425      */
11426     filter : function(property, value, anyMatch){
11427         var fn = this.createFilterFn(property, value, anyMatch);
11428         return fn ? this.filterBy(fn) : this.clearFilter();
11429     },
11430
11431     /**
11432      * Filter by a function. The specified function will be called with each
11433      * record in this data source. If the function returns true the record is included,
11434      * otherwise it is filtered.
11435      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11436      * @param {Object} scope (optional) The scope of the function (defaults to this)
11437      */
11438     filterBy : function(fn, scope){
11439         this.snapshot = this.snapshot || this.data;
11440         this.data = this.queryBy(fn, scope||this);
11441         this.fireEvent("datachanged", this);
11442     },
11443
11444     /**
11445      * Query the records by a specified property.
11446      * @param {String} field A field on your records
11447      * @param {String/RegExp} value Either a string that the field
11448      * should start with or a RegExp to test against the field
11449      * @param {Boolean} anyMatch True to match any part not just the beginning
11450      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11451      */
11452     query : function(property, value, anyMatch){
11453         var fn = this.createFilterFn(property, value, anyMatch);
11454         return fn ? this.queryBy(fn) : this.data.clone();
11455     },
11456
11457     /**
11458      * Query by a function. The specified function will be called with each
11459      * record in this data source. If the function returns true the record is included
11460      * in the results.
11461      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11462      * @param {Object} scope (optional) The scope of the function (defaults to this)
11463       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11464      **/
11465     queryBy : function(fn, scope){
11466         var data = this.snapshot || this.data;
11467         return data.filterBy(fn, scope||this);
11468     },
11469
11470     /**
11471      * Collects unique values for a particular dataIndex from this store.
11472      * @param {String} dataIndex The property to collect
11473      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11474      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11475      * @return {Array} An array of the unique values
11476      **/
11477     collect : function(dataIndex, allowNull, bypassFilter){
11478         var d = (bypassFilter === true && this.snapshot) ?
11479                 this.snapshot.items : this.data.items;
11480         var v, sv, r = [], l = {};
11481         for(var i = 0, len = d.length; i < len; i++){
11482             v = d[i].data[dataIndex];
11483             sv = String(v);
11484             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11485                 l[sv] = true;
11486                 r[r.length] = v;
11487             }
11488         }
11489         return r;
11490     },
11491
11492     /**
11493      * Revert to a view of the Record cache with no filtering applied.
11494      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11495      */
11496     clearFilter : function(suppressEvent){
11497         if(this.snapshot && this.snapshot != this.data){
11498             this.data = this.snapshot;
11499             delete this.snapshot;
11500             if(suppressEvent !== true){
11501                 this.fireEvent("datachanged", this);
11502             }
11503         }
11504     },
11505
11506     // private
11507     afterEdit : function(record){
11508         if(this.modified.indexOf(record) == -1){
11509             this.modified.push(record);
11510         }
11511         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11512     },
11513     
11514     // private
11515     afterReject : function(record){
11516         this.modified.remove(record);
11517         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11518     },
11519
11520     // private
11521     afterCommit : function(record){
11522         this.modified.remove(record);
11523         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11524     },
11525
11526     /**
11527      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11528      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11529      */
11530     commitChanges : function(){
11531         var m = this.modified.slice(0);
11532         this.modified = [];
11533         for(var i = 0, len = m.length; i < len; i++){
11534             m[i].commit();
11535         }
11536     },
11537
11538     /**
11539      * Cancel outstanding changes on all changed records.
11540      */
11541     rejectChanges : function(){
11542         var m = this.modified.slice(0);
11543         this.modified = [];
11544         for(var i = 0, len = m.length; i < len; i++){
11545             m[i].reject();
11546         }
11547     },
11548
11549     onMetaChange : function(meta, rtype, o){
11550         this.recordType = rtype;
11551         this.fields = rtype.prototype.fields;
11552         delete this.snapshot;
11553         this.sortInfo = meta.sortInfo || this.sortInfo;
11554         this.modified = [];
11555         this.fireEvent('metachange', this, this.reader.meta);
11556     },
11557     
11558     moveIndex : function(data, type)
11559     {
11560         var index = this.indexOf(data);
11561         
11562         var newIndex = index + type;
11563         
11564         this.remove(data);
11565         
11566         this.insert(newIndex, data);
11567         
11568     }
11569 });/*
11570  * Based on:
11571  * Ext JS Library 1.1.1
11572  * Copyright(c) 2006-2007, Ext JS, LLC.
11573  *
11574  * Originally Released Under LGPL - original licence link has changed is not relivant.
11575  *
11576  * Fork - LGPL
11577  * <script type="text/javascript">
11578  */
11579
11580 /**
11581  * @class Roo.data.SimpleStore
11582  * @extends Roo.data.Store
11583  * Small helper class to make creating Stores from Array data easier.
11584  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11585  * @cfg {Array} fields An array of field definition objects, or field name strings.
11586  * @cfg {Array} data The multi-dimensional array of data
11587  * @constructor
11588  * @param {Object} config
11589  */
11590 Roo.data.SimpleStore = function(config){
11591     Roo.data.SimpleStore.superclass.constructor.call(this, {
11592         isLocal : true,
11593         reader: new Roo.data.ArrayReader({
11594                 id: config.id
11595             },
11596             Roo.data.Record.create(config.fields)
11597         ),
11598         proxy : new Roo.data.MemoryProxy(config.data)
11599     });
11600     this.load();
11601 };
11602 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11603  * Based on:
11604  * Ext JS Library 1.1.1
11605  * Copyright(c) 2006-2007, Ext JS, LLC.
11606  *
11607  * Originally Released Under LGPL - original licence link has changed is not relivant.
11608  *
11609  * Fork - LGPL
11610  * <script type="text/javascript">
11611  */
11612
11613 /**
11614 /**
11615  * @extends Roo.data.Store
11616  * @class Roo.data.JsonStore
11617  * Small helper class to make creating Stores for JSON data easier. <br/>
11618 <pre><code>
11619 var store = new Roo.data.JsonStore({
11620     url: 'get-images.php',
11621     root: 'images',
11622     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11623 });
11624 </code></pre>
11625  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11626  * JsonReader and HttpProxy (unless inline data is provided).</b>
11627  * @cfg {Array} fields An array of field definition objects, or field name strings.
11628  * @constructor
11629  * @param {Object} config
11630  */
11631 Roo.data.JsonStore = function(c){
11632     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11633         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11634         reader: new Roo.data.JsonReader(c, c.fields)
11635     }));
11636 };
11637 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11638  * Based on:
11639  * Ext JS Library 1.1.1
11640  * Copyright(c) 2006-2007, Ext JS, LLC.
11641  *
11642  * Originally Released Under LGPL - original licence link has changed is not relivant.
11643  *
11644  * Fork - LGPL
11645  * <script type="text/javascript">
11646  */
11647
11648  
11649 Roo.data.Field = function(config){
11650     if(typeof config == "string"){
11651         config = {name: config};
11652     }
11653     Roo.apply(this, config);
11654     
11655     if(!this.type){
11656         this.type = "auto";
11657     }
11658     
11659     var st = Roo.data.SortTypes;
11660     // named sortTypes are supported, here we look them up
11661     if(typeof this.sortType == "string"){
11662         this.sortType = st[this.sortType];
11663     }
11664     
11665     // set default sortType for strings and dates
11666     if(!this.sortType){
11667         switch(this.type){
11668             case "string":
11669                 this.sortType = st.asUCString;
11670                 break;
11671             case "date":
11672                 this.sortType = st.asDate;
11673                 break;
11674             default:
11675                 this.sortType = st.none;
11676         }
11677     }
11678
11679     // define once
11680     var stripRe = /[\$,%]/g;
11681
11682     // prebuilt conversion function for this field, instead of
11683     // switching every time we're reading a value
11684     if(!this.convert){
11685         var cv, dateFormat = this.dateFormat;
11686         switch(this.type){
11687             case "":
11688             case "auto":
11689             case undefined:
11690                 cv = function(v){ return v; };
11691                 break;
11692             case "string":
11693                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11694                 break;
11695             case "int":
11696                 cv = function(v){
11697                     return v !== undefined && v !== null && v !== '' ?
11698                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11699                     };
11700                 break;
11701             case "float":
11702                 cv = function(v){
11703                     return v !== undefined && v !== null && v !== '' ?
11704                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11705                     };
11706                 break;
11707             case "bool":
11708             case "boolean":
11709                 cv = function(v){ return v === true || v === "true" || v == 1; };
11710                 break;
11711             case "date":
11712                 cv = function(v){
11713                     if(!v){
11714                         return '';
11715                     }
11716                     if(v instanceof Date){
11717                         return v;
11718                     }
11719                     if(dateFormat){
11720                         if(dateFormat == "timestamp"){
11721                             return new Date(v*1000);
11722                         }
11723                         return Date.parseDate(v, dateFormat);
11724                     }
11725                     var parsed = Date.parse(v);
11726                     return parsed ? new Date(parsed) : null;
11727                 };
11728              break;
11729             
11730         }
11731         this.convert = cv;
11732     }
11733 };
11734
11735 Roo.data.Field.prototype = {
11736     dateFormat: null,
11737     defaultValue: "",
11738     mapping: null,
11739     sortType : null,
11740     sortDir : "ASC"
11741 };/*
11742  * Based on:
11743  * Ext JS Library 1.1.1
11744  * Copyright(c) 2006-2007, Ext JS, LLC.
11745  *
11746  * Originally Released Under LGPL - original licence link has changed is not relivant.
11747  *
11748  * Fork - LGPL
11749  * <script type="text/javascript">
11750  */
11751  
11752 // Base class for reading structured data from a data source.  This class is intended to be
11753 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11754
11755 /**
11756  * @class Roo.data.DataReader
11757  * Base class for reading structured data from a data source.  This class is intended to be
11758  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11759  */
11760
11761 Roo.data.DataReader = function(meta, recordType){
11762     
11763     this.meta = meta;
11764     
11765     this.recordType = recordType instanceof Array ? 
11766         Roo.data.Record.create(recordType) : recordType;
11767 };
11768
11769 Roo.data.DataReader.prototype = {
11770      /**
11771      * Create an empty record
11772      * @param {Object} data (optional) - overlay some values
11773      * @return {Roo.data.Record} record created.
11774      */
11775     newRow :  function(d) {
11776         var da =  {};
11777         this.recordType.prototype.fields.each(function(c) {
11778             switch( c.type) {
11779                 case 'int' : da[c.name] = 0; break;
11780                 case 'date' : da[c.name] = new Date(); break;
11781                 case 'float' : da[c.name] = 0.0; break;
11782                 case 'boolean' : da[c.name] = false; break;
11783                 default : da[c.name] = ""; break;
11784             }
11785             
11786         });
11787         return new this.recordType(Roo.apply(da, d));
11788     }
11789     
11790 };/*
11791  * Based on:
11792  * Ext JS Library 1.1.1
11793  * Copyright(c) 2006-2007, Ext JS, LLC.
11794  *
11795  * Originally Released Under LGPL - original licence link has changed is not relivant.
11796  *
11797  * Fork - LGPL
11798  * <script type="text/javascript">
11799  */
11800
11801 /**
11802  * @class Roo.data.DataProxy
11803  * @extends Roo.data.Observable
11804  * This class is an abstract base class for implementations which provide retrieval of
11805  * unformatted data objects.<br>
11806  * <p>
11807  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11808  * (of the appropriate type which knows how to parse the data object) to provide a block of
11809  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11810  * <p>
11811  * Custom implementations must implement the load method as described in
11812  * {@link Roo.data.HttpProxy#load}.
11813  */
11814 Roo.data.DataProxy = function(){
11815     this.addEvents({
11816         /**
11817          * @event beforeload
11818          * Fires before a network request is made to retrieve a data object.
11819          * @param {Object} This DataProxy object.
11820          * @param {Object} params The params parameter to the load function.
11821          */
11822         beforeload : true,
11823         /**
11824          * @event load
11825          * Fires before the load method's callback is called.
11826          * @param {Object} This DataProxy object.
11827          * @param {Object} o The data object.
11828          * @param {Object} arg The callback argument object passed to the load function.
11829          */
11830         load : true,
11831         /**
11832          * @event loadexception
11833          * Fires if an Exception occurs during data retrieval.
11834          * @param {Object} This DataProxy object.
11835          * @param {Object} o The data object.
11836          * @param {Object} arg The callback argument object passed to the load function.
11837          * @param {Object} e The Exception.
11838          */
11839         loadexception : true
11840     });
11841     Roo.data.DataProxy.superclass.constructor.call(this);
11842 };
11843
11844 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11845
11846     /**
11847      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11848      */
11849 /*
11850  * Based on:
11851  * Ext JS Library 1.1.1
11852  * Copyright(c) 2006-2007, Ext JS, LLC.
11853  *
11854  * Originally Released Under LGPL - original licence link has changed is not relivant.
11855  *
11856  * Fork - LGPL
11857  * <script type="text/javascript">
11858  */
11859 /**
11860  * @class Roo.data.MemoryProxy
11861  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11862  * to the Reader when its load method is called.
11863  * @constructor
11864  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11865  */
11866 Roo.data.MemoryProxy = function(data){
11867     if (data.data) {
11868         data = data.data;
11869     }
11870     Roo.data.MemoryProxy.superclass.constructor.call(this);
11871     this.data = data;
11872 };
11873
11874 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11875     
11876     /**
11877      * Load data from the requested source (in this case an in-memory
11878      * data object passed to the constructor), read the data object into
11879      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11880      * process that block using the passed callback.
11881      * @param {Object} params This parameter is not used by the MemoryProxy class.
11882      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11883      * object into a block of Roo.data.Records.
11884      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11885      * The function must be passed <ul>
11886      * <li>The Record block object</li>
11887      * <li>The "arg" argument from the load function</li>
11888      * <li>A boolean success indicator</li>
11889      * </ul>
11890      * @param {Object} scope The scope in which to call the callback
11891      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11892      */
11893     load : function(params, reader, callback, scope, arg){
11894         params = params || {};
11895         var result;
11896         try {
11897             result = reader.readRecords(this.data);
11898         }catch(e){
11899             this.fireEvent("loadexception", this, arg, null, e);
11900             callback.call(scope, null, arg, false);
11901             return;
11902         }
11903         callback.call(scope, result, arg, true);
11904     },
11905     
11906     // private
11907     update : function(params, records){
11908         
11909     }
11910 });/*
11911  * Based on:
11912  * Ext JS Library 1.1.1
11913  * Copyright(c) 2006-2007, Ext JS, LLC.
11914  *
11915  * Originally Released Under LGPL - original licence link has changed is not relivant.
11916  *
11917  * Fork - LGPL
11918  * <script type="text/javascript">
11919  */
11920 /**
11921  * @class Roo.data.HttpProxy
11922  * @extends Roo.data.DataProxy
11923  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11924  * configured to reference a certain URL.<br><br>
11925  * <p>
11926  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11927  * from which the running page was served.<br><br>
11928  * <p>
11929  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11930  * <p>
11931  * Be aware that to enable the browser to parse an XML document, the server must set
11932  * the Content-Type header in the HTTP response to "text/xml".
11933  * @constructor
11934  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11935  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11936  * will be used to make the request.
11937  */
11938 Roo.data.HttpProxy = function(conn){
11939     Roo.data.HttpProxy.superclass.constructor.call(this);
11940     // is conn a conn config or a real conn?
11941     this.conn = conn;
11942     this.useAjax = !conn || !conn.events;
11943   
11944 };
11945
11946 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11947     // thse are take from connection...
11948     
11949     /**
11950      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11951      */
11952     /**
11953      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11954      * extra parameters to each request made by this object. (defaults to undefined)
11955      */
11956     /**
11957      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11958      *  to each request made by this object. (defaults to undefined)
11959      */
11960     /**
11961      * @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)
11962      */
11963     /**
11964      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11965      */
11966      /**
11967      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11968      * @type Boolean
11969      */
11970   
11971
11972     /**
11973      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11974      * @type Boolean
11975      */
11976     /**
11977      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11978      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11979      * a finer-grained basis than the DataProxy events.
11980      */
11981     getConnection : function(){
11982         return this.useAjax ? Roo.Ajax : this.conn;
11983     },
11984
11985     /**
11986      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11987      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11988      * process that block using the passed callback.
11989      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11990      * for the request to the remote server.
11991      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11992      * object into a block of Roo.data.Records.
11993      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11994      * The function must be passed <ul>
11995      * <li>The Record block object</li>
11996      * <li>The "arg" argument from the load function</li>
11997      * <li>A boolean success indicator</li>
11998      * </ul>
11999      * @param {Object} scope The scope in which to call the callback
12000      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12001      */
12002     load : function(params, reader, callback, scope, arg){
12003         if(this.fireEvent("beforeload", this, params) !== false){
12004             var  o = {
12005                 params : params || {},
12006                 request: {
12007                     callback : callback,
12008                     scope : scope,
12009                     arg : arg
12010                 },
12011                 reader: reader,
12012                 callback : this.loadResponse,
12013                 scope: this
12014             };
12015             if(this.useAjax){
12016                 Roo.applyIf(o, this.conn);
12017                 if(this.activeRequest){
12018                     Roo.Ajax.abort(this.activeRequest);
12019                 }
12020                 this.activeRequest = Roo.Ajax.request(o);
12021             }else{
12022                 this.conn.request(o);
12023             }
12024         }else{
12025             callback.call(scope||this, null, arg, false);
12026         }
12027     },
12028
12029     // private
12030     loadResponse : function(o, success, response){
12031         delete this.activeRequest;
12032         if(!success){
12033             this.fireEvent("loadexception", this, o, response);
12034             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12035             return;
12036         }
12037         var result;
12038         try {
12039             result = o.reader.read(response);
12040         }catch(e){
12041             this.fireEvent("loadexception", this, o, response, e);
12042             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12043             return;
12044         }
12045         
12046         this.fireEvent("load", this, o, o.request.arg);
12047         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12048     },
12049
12050     // private
12051     update : function(dataSet){
12052
12053     },
12054
12055     // private
12056     updateResponse : function(dataSet){
12057
12058     }
12059 });/*
12060  * Based on:
12061  * Ext JS Library 1.1.1
12062  * Copyright(c) 2006-2007, Ext JS, LLC.
12063  *
12064  * Originally Released Under LGPL - original licence link has changed is not relivant.
12065  *
12066  * Fork - LGPL
12067  * <script type="text/javascript">
12068  */
12069
12070 /**
12071  * @class Roo.data.ScriptTagProxy
12072  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12073  * other than the originating domain of the running page.<br><br>
12074  * <p>
12075  * <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
12076  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12077  * <p>
12078  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12079  * source code that is used as the source inside a &lt;script> tag.<br><br>
12080  * <p>
12081  * In order for the browser to process the returned data, the server must wrap the data object
12082  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12083  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12084  * depending on whether the callback name was passed:
12085  * <p>
12086  * <pre><code>
12087 boolean scriptTag = false;
12088 String cb = request.getParameter("callback");
12089 if (cb != null) {
12090     scriptTag = true;
12091     response.setContentType("text/javascript");
12092 } else {
12093     response.setContentType("application/x-json");
12094 }
12095 Writer out = response.getWriter();
12096 if (scriptTag) {
12097     out.write(cb + "(");
12098 }
12099 out.print(dataBlock.toJsonString());
12100 if (scriptTag) {
12101     out.write(");");
12102 }
12103 </pre></code>
12104  *
12105  * @constructor
12106  * @param {Object} config A configuration object.
12107  */
12108 Roo.data.ScriptTagProxy = function(config){
12109     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12110     Roo.apply(this, config);
12111     this.head = document.getElementsByTagName("head")[0];
12112 };
12113
12114 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12115
12116 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12117     /**
12118      * @cfg {String} url The URL from which to request the data object.
12119      */
12120     /**
12121      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12122      */
12123     timeout : 30000,
12124     /**
12125      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12126      * the server the name of the callback function set up by the load call to process the returned data object.
12127      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12128      * javascript output which calls this named function passing the data object as its only parameter.
12129      */
12130     callbackParam : "callback",
12131     /**
12132      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12133      * name to the request.
12134      */
12135     nocache : true,
12136
12137     /**
12138      * Load data from the configured URL, read the data object into
12139      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12140      * process that block using the passed callback.
12141      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12142      * for the request to the remote server.
12143      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12144      * object into a block of Roo.data.Records.
12145      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12146      * The function must be passed <ul>
12147      * <li>The Record block object</li>
12148      * <li>The "arg" argument from the load function</li>
12149      * <li>A boolean success indicator</li>
12150      * </ul>
12151      * @param {Object} scope The scope in which to call the callback
12152      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12153      */
12154     load : function(params, reader, callback, scope, arg){
12155         if(this.fireEvent("beforeload", this, params) !== false){
12156
12157             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12158
12159             var url = this.url;
12160             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12161             if(this.nocache){
12162                 url += "&_dc=" + (new Date().getTime());
12163             }
12164             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12165             var trans = {
12166                 id : transId,
12167                 cb : "stcCallback"+transId,
12168                 scriptId : "stcScript"+transId,
12169                 params : params,
12170                 arg : arg,
12171                 url : url,
12172                 callback : callback,
12173                 scope : scope,
12174                 reader : reader
12175             };
12176             var conn = this;
12177
12178             window[trans.cb] = function(o){
12179                 conn.handleResponse(o, trans);
12180             };
12181
12182             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12183
12184             if(this.autoAbort !== false){
12185                 this.abort();
12186             }
12187
12188             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12189
12190             var script = document.createElement("script");
12191             script.setAttribute("src", url);
12192             script.setAttribute("type", "text/javascript");
12193             script.setAttribute("id", trans.scriptId);
12194             this.head.appendChild(script);
12195
12196             this.trans = trans;
12197         }else{
12198             callback.call(scope||this, null, arg, false);
12199         }
12200     },
12201
12202     // private
12203     isLoading : function(){
12204         return this.trans ? true : false;
12205     },
12206
12207     /**
12208      * Abort the current server request.
12209      */
12210     abort : function(){
12211         if(this.isLoading()){
12212             this.destroyTrans(this.trans);
12213         }
12214     },
12215
12216     // private
12217     destroyTrans : function(trans, isLoaded){
12218         this.head.removeChild(document.getElementById(trans.scriptId));
12219         clearTimeout(trans.timeoutId);
12220         if(isLoaded){
12221             window[trans.cb] = undefined;
12222             try{
12223                 delete window[trans.cb];
12224             }catch(e){}
12225         }else{
12226             // if hasn't been loaded, wait for load to remove it to prevent script error
12227             window[trans.cb] = function(){
12228                 window[trans.cb] = undefined;
12229                 try{
12230                     delete window[trans.cb];
12231                 }catch(e){}
12232             };
12233         }
12234     },
12235
12236     // private
12237     handleResponse : function(o, trans){
12238         this.trans = false;
12239         this.destroyTrans(trans, true);
12240         var result;
12241         try {
12242             result = trans.reader.readRecords(o);
12243         }catch(e){
12244             this.fireEvent("loadexception", this, o, trans.arg, e);
12245             trans.callback.call(trans.scope||window, null, trans.arg, false);
12246             return;
12247         }
12248         this.fireEvent("load", this, o, trans.arg);
12249         trans.callback.call(trans.scope||window, result, trans.arg, true);
12250     },
12251
12252     // private
12253     handleFailure : function(trans){
12254         this.trans = false;
12255         this.destroyTrans(trans, false);
12256         this.fireEvent("loadexception", this, null, trans.arg);
12257         trans.callback.call(trans.scope||window, null, trans.arg, false);
12258     }
12259 });/*
12260  * Based on:
12261  * Ext JS Library 1.1.1
12262  * Copyright(c) 2006-2007, Ext JS, LLC.
12263  *
12264  * Originally Released Under LGPL - original licence link has changed is not relivant.
12265  *
12266  * Fork - LGPL
12267  * <script type="text/javascript">
12268  */
12269
12270 /**
12271  * @class Roo.data.JsonReader
12272  * @extends Roo.data.DataReader
12273  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12274  * based on mappings in a provided Roo.data.Record constructor.
12275  * 
12276  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12277  * in the reply previously. 
12278  * 
12279  * <p>
12280  * Example code:
12281  * <pre><code>
12282 var RecordDef = Roo.data.Record.create([
12283     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12284     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12285 ]);
12286 var myReader = new Roo.data.JsonReader({
12287     totalProperty: "results",    // The property which contains the total dataset size (optional)
12288     root: "rows",                // The property which contains an Array of row objects
12289     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12290 }, RecordDef);
12291 </code></pre>
12292  * <p>
12293  * This would consume a JSON file like this:
12294  * <pre><code>
12295 { 'results': 2, 'rows': [
12296     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12297     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12298 }
12299 </code></pre>
12300  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12301  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12302  * paged from the remote server.
12303  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12304  * @cfg {String} root name of the property which contains the Array of row objects.
12305  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12306  * @cfg {Array} fields Array of field definition objects
12307  * @constructor
12308  * Create a new JsonReader
12309  * @param {Object} meta Metadata configuration options
12310  * @param {Object} recordType Either an Array of field definition objects,
12311  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12312  */
12313 Roo.data.JsonReader = function(meta, recordType){
12314     
12315     meta = meta || {};
12316     // set some defaults:
12317     Roo.applyIf(meta, {
12318         totalProperty: 'total',
12319         successProperty : 'success',
12320         root : 'data',
12321         id : 'id'
12322     });
12323     
12324     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12325 };
12326 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12327     
12328     /**
12329      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12330      * Used by Store query builder to append _requestMeta to params.
12331      * 
12332      */
12333     metaFromRemote : false,
12334     /**
12335      * This method is only used by a DataProxy which has retrieved data from a remote server.
12336      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12337      * @return {Object} data A data block which is used by an Roo.data.Store object as
12338      * a cache of Roo.data.Records.
12339      */
12340     read : function(response){
12341         var json = response.responseText;
12342        
12343         var o = /* eval:var:o */ eval("("+json+")");
12344         if(!o) {
12345             throw {message: "JsonReader.read: Json object not found"};
12346         }
12347         
12348         if(o.metaData){
12349             
12350             delete this.ef;
12351             this.metaFromRemote = true;
12352             this.meta = o.metaData;
12353             this.recordType = Roo.data.Record.create(o.metaData.fields);
12354             this.onMetaChange(this.meta, this.recordType, o);
12355         }
12356         return this.readRecords(o);
12357     },
12358
12359     // private function a store will implement
12360     onMetaChange : function(meta, recordType, o){
12361
12362     },
12363
12364     /**
12365          * @ignore
12366          */
12367     simpleAccess: function(obj, subsc) {
12368         return obj[subsc];
12369     },
12370
12371         /**
12372          * @ignore
12373          */
12374     getJsonAccessor: function(){
12375         var re = /[\[\.]/;
12376         return function(expr) {
12377             try {
12378                 return(re.test(expr))
12379                     ? new Function("obj", "return obj." + expr)
12380                     : function(obj){
12381                         return obj[expr];
12382                     };
12383             } catch(e){}
12384             return Roo.emptyFn;
12385         };
12386     }(),
12387
12388     /**
12389      * Create a data block containing Roo.data.Records from an XML document.
12390      * @param {Object} o An object which contains an Array of row objects in the property specified
12391      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12392      * which contains the total size of the dataset.
12393      * @return {Object} data A data block which is used by an Roo.data.Store object as
12394      * a cache of Roo.data.Records.
12395      */
12396     readRecords : function(o){
12397         /**
12398          * After any data loads, the raw JSON data is available for further custom processing.
12399          * @type Object
12400          */
12401         this.o = o;
12402         var s = this.meta, Record = this.recordType,
12403             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12404
12405 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12406         if (!this.ef) {
12407             if(s.totalProperty) {
12408                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12409                 }
12410                 if(s.successProperty) {
12411                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12412                 }
12413                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12414                 if (s.id) {
12415                         var g = this.getJsonAccessor(s.id);
12416                         this.getId = function(rec) {
12417                                 var r = g(rec);  
12418                                 return (r === undefined || r === "") ? null : r;
12419                         };
12420                 } else {
12421                         this.getId = function(){return null;};
12422                 }
12423             this.ef = [];
12424             for(var jj = 0; jj < fl; jj++){
12425                 f = fi[jj];
12426                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12427                 this.ef[jj] = this.getJsonAccessor(map);
12428             }
12429         }
12430
12431         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12432         if(s.totalProperty){
12433             var vt = parseInt(this.getTotal(o), 10);
12434             if(!isNaN(vt)){
12435                 totalRecords = vt;
12436             }
12437         }
12438         if(s.successProperty){
12439             var vs = this.getSuccess(o);
12440             if(vs === false || vs === 'false'){
12441                 success = false;
12442             }
12443         }
12444         var records = [];
12445         for(var i = 0; i < c; i++){
12446                 var n = root[i];
12447             var values = {};
12448             var id = this.getId(n);
12449             for(var j = 0; j < fl; j++){
12450                 f = fi[j];
12451             var v = this.ef[j](n);
12452             if (!f.convert) {
12453                 Roo.log('missing convert for ' + f.name);
12454                 Roo.log(f);
12455                 continue;
12456             }
12457             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12458             }
12459             var record = new Record(values, id);
12460             record.json = n;
12461             records[i] = record;
12462         }
12463         return {
12464             raw : o,
12465             success : success,
12466             records : records,
12467             totalRecords : totalRecords
12468         };
12469     }
12470 });/*
12471  * Based on:
12472  * Ext JS Library 1.1.1
12473  * Copyright(c) 2006-2007, Ext JS, LLC.
12474  *
12475  * Originally Released Under LGPL - original licence link has changed is not relivant.
12476  *
12477  * Fork - LGPL
12478  * <script type="text/javascript">
12479  */
12480
12481 /**
12482  * @class Roo.data.ArrayReader
12483  * @extends Roo.data.DataReader
12484  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12485  * Each element of that Array represents a row of data fields. The
12486  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12487  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12488  * <p>
12489  * Example code:.
12490  * <pre><code>
12491 var RecordDef = Roo.data.Record.create([
12492     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12493     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12494 ]);
12495 var myReader = new Roo.data.ArrayReader({
12496     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12497 }, RecordDef);
12498 </code></pre>
12499  * <p>
12500  * This would consume an Array like this:
12501  * <pre><code>
12502 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12503   </code></pre>
12504  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12505  * @constructor
12506  * Create a new JsonReader
12507  * @param {Object} meta Metadata configuration options.
12508  * @param {Object} recordType Either an Array of field definition objects
12509  * as specified to {@link Roo.data.Record#create},
12510  * or an {@link Roo.data.Record} object
12511  * created using {@link Roo.data.Record#create}.
12512  */
12513 Roo.data.ArrayReader = function(meta, recordType){
12514     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12515 };
12516
12517 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12518     /**
12519      * Create a data block containing Roo.data.Records from an XML document.
12520      * @param {Object} o An Array of row objects which represents the dataset.
12521      * @return {Object} data A data block which is used by an Roo.data.Store object as
12522      * a cache of Roo.data.Records.
12523      */
12524     readRecords : function(o){
12525         var sid = this.meta ? this.meta.id : null;
12526         var recordType = this.recordType, fields = recordType.prototype.fields;
12527         var records = [];
12528         var root = o;
12529             for(var i = 0; i < root.length; i++){
12530                     var n = root[i];
12531                 var values = {};
12532                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12533                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12534                 var f = fields.items[j];
12535                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12536                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12537                 v = f.convert(v);
12538                 values[f.name] = v;
12539             }
12540                 var record = new recordType(values, id);
12541                 record.json = n;
12542                 records[records.length] = record;
12543             }
12544             return {
12545                 records : records,
12546                 totalRecords : records.length
12547             };
12548     }
12549 });/*
12550  * - LGPL
12551  * * 
12552  */
12553
12554 /**
12555  * @class Roo.bootstrap.ComboBox
12556  * @extends Roo.bootstrap.TriggerField
12557  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12558  * @cfg {Boolean} append (true|false) default false
12559  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12560  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12561  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12562  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12563  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12564  * @cfg {Boolean} animate default true
12565  * @cfg {Boolean} emptyResultText only for touch device
12566  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12567  * @cfg {String} emptyTitle default ''
12568  * @constructor
12569  * Create a new ComboBox.
12570  * @param {Object} config Configuration options
12571  */
12572 Roo.bootstrap.ComboBox = function(config){
12573     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12574     this.addEvents({
12575         /**
12576          * @event expand
12577          * Fires when the dropdown list is expanded
12578         * @param {Roo.bootstrap.ComboBox} combo This combo box
12579         */
12580         'expand' : true,
12581         /**
12582          * @event collapse
12583          * Fires when the dropdown list is collapsed
12584         * @param {Roo.bootstrap.ComboBox} combo This combo box
12585         */
12586         'collapse' : true,
12587         /**
12588          * @event beforeselect
12589          * Fires before a list item is selected. Return false to cancel the selection.
12590         * @param {Roo.bootstrap.ComboBox} combo This combo box
12591         * @param {Roo.data.Record} record The data record returned from the underlying store
12592         * @param {Number} index The index of the selected item in the dropdown list
12593         */
12594         'beforeselect' : true,
12595         /**
12596          * @event select
12597          * Fires when a list item is selected
12598         * @param {Roo.bootstrap.ComboBox} combo This combo box
12599         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12600         * @param {Number} index The index of the selected item in the dropdown list
12601         */
12602         'select' : true,
12603         /**
12604          * @event beforequery
12605          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12606          * The event object passed has these properties:
12607         * @param {Roo.bootstrap.ComboBox} combo This combo box
12608         * @param {String} query The query
12609         * @param {Boolean} forceAll true to force "all" query
12610         * @param {Boolean} cancel true to cancel the query
12611         * @param {Object} e The query event object
12612         */
12613         'beforequery': true,
12614          /**
12615          * @event add
12616          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12617         * @param {Roo.bootstrap.ComboBox} combo This combo box
12618         */
12619         'add' : true,
12620         /**
12621          * @event edit
12622          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12623         * @param {Roo.bootstrap.ComboBox} combo This combo box
12624         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12625         */
12626         'edit' : true,
12627         /**
12628          * @event remove
12629          * Fires when the remove value from the combobox array
12630         * @param {Roo.bootstrap.ComboBox} combo This combo box
12631         */
12632         'remove' : true,
12633         /**
12634          * @event afterremove
12635          * Fires when the remove value from the combobox array
12636         * @param {Roo.bootstrap.ComboBox} combo This combo box
12637         */
12638         'afterremove' : true,
12639         /**
12640          * @event specialfilter
12641          * Fires when specialfilter
12642             * @param {Roo.bootstrap.ComboBox} combo This combo box
12643             */
12644         'specialfilter' : true,
12645         /**
12646          * @event tick
12647          * Fires when tick the element
12648             * @param {Roo.bootstrap.ComboBox} combo This combo box
12649             */
12650         'tick' : true,
12651         /**
12652          * @event touchviewdisplay
12653          * Fires when touch view require special display (default is using displayField)
12654             * @param {Roo.bootstrap.ComboBox} combo This combo box
12655             * @param {Object} cfg set html .
12656             */
12657         'touchviewdisplay' : true
12658         
12659     });
12660     
12661     this.item = [];
12662     this.tickItems = [];
12663     
12664     this.selectedIndex = -1;
12665     if(this.mode == 'local'){
12666         if(config.queryDelay === undefined){
12667             this.queryDelay = 10;
12668         }
12669         if(config.minChars === undefined){
12670             this.minChars = 0;
12671         }
12672     }
12673 };
12674
12675 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12676      
12677     /**
12678      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12679      * rendering into an Roo.Editor, defaults to false)
12680      */
12681     /**
12682      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12683      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12684      */
12685     /**
12686      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12687      */
12688     /**
12689      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12690      * the dropdown list (defaults to undefined, with no header element)
12691      */
12692
12693      /**
12694      * @cfg {String/Roo.Template} tpl The template to use to render the output
12695      */
12696      
12697      /**
12698      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12699      */
12700     listWidth: undefined,
12701     /**
12702      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12703      * mode = 'remote' or 'text' if mode = 'local')
12704      */
12705     displayField: undefined,
12706     
12707     /**
12708      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12709      * mode = 'remote' or 'value' if mode = 'local'). 
12710      * Note: use of a valueField requires the user make a selection
12711      * in order for a value to be mapped.
12712      */
12713     valueField: undefined,
12714     /**
12715      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12716      */
12717     modalTitle : '',
12718     
12719     /**
12720      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12721      * field's data value (defaults to the underlying DOM element's name)
12722      */
12723     hiddenName: undefined,
12724     /**
12725      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12726      */
12727     listClass: '',
12728     /**
12729      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12730      */
12731     selectedClass: 'active',
12732     
12733     /**
12734      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12735      */
12736     shadow:'sides',
12737     /**
12738      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12739      * anchor positions (defaults to 'tl-bl')
12740      */
12741     listAlign: 'tl-bl?',
12742     /**
12743      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12744      */
12745     maxHeight: 300,
12746     /**
12747      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12748      * query specified by the allQuery config option (defaults to 'query')
12749      */
12750     triggerAction: 'query',
12751     /**
12752      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12753      * (defaults to 4, does not apply if editable = false)
12754      */
12755     minChars : 4,
12756     /**
12757      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12758      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12759      */
12760     typeAhead: false,
12761     /**
12762      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12763      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12764      */
12765     queryDelay: 500,
12766     /**
12767      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12768      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12769      */
12770     pageSize: 0,
12771     /**
12772      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12773      * when editable = true (defaults to false)
12774      */
12775     selectOnFocus:false,
12776     /**
12777      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12778      */
12779     queryParam: 'query',
12780     /**
12781      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12782      * when mode = 'remote' (defaults to 'Loading...')
12783      */
12784     loadingText: 'Loading...',
12785     /**
12786      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12787      */
12788     resizable: false,
12789     /**
12790      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12791      */
12792     handleHeight : 8,
12793     /**
12794      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12795      * traditional select (defaults to true)
12796      */
12797     editable: true,
12798     /**
12799      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12800      */
12801     allQuery: '',
12802     /**
12803      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12804      */
12805     mode: 'remote',
12806     /**
12807      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12808      * listWidth has a higher value)
12809      */
12810     minListWidth : 70,
12811     /**
12812      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12813      * allow the user to set arbitrary text into the field (defaults to false)
12814      */
12815     forceSelection:false,
12816     /**
12817      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12818      * if typeAhead = true (defaults to 250)
12819      */
12820     typeAheadDelay : 250,
12821     /**
12822      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12823      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12824      */
12825     valueNotFoundText : undefined,
12826     /**
12827      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12828      */
12829     blockFocus : false,
12830     
12831     /**
12832      * @cfg {Boolean} disableClear Disable showing of clear button.
12833      */
12834     disableClear : false,
12835     /**
12836      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12837      */
12838     alwaysQuery : false,
12839     
12840     /**
12841      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12842      */
12843     multiple : false,
12844     
12845     /**
12846      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12847      */
12848     invalidClass : "has-warning",
12849     
12850     /**
12851      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12852      */
12853     validClass : "has-success",
12854     
12855     /**
12856      * @cfg {Boolean} specialFilter (true|false) special filter default false
12857      */
12858     specialFilter : false,
12859     
12860     /**
12861      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12862      */
12863     mobileTouchView : true,
12864     
12865     /**
12866      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12867      */
12868     useNativeIOS : false,
12869     
12870     ios_options : false,
12871     
12872     //private
12873     addicon : false,
12874     editicon: false,
12875     
12876     page: 0,
12877     hasQuery: false,
12878     append: false,
12879     loadNext: false,
12880     autoFocus : true,
12881     tickable : false,
12882     btnPosition : 'right',
12883     triggerList : true,
12884     showToggleBtn : true,
12885     animate : true,
12886     emptyResultText: 'Empty',
12887     triggerText : 'Select',
12888     emptyTitle : '',
12889     
12890     // element that contains real text value.. (when hidden is used..)
12891     
12892     getAutoCreate : function()
12893     {   
12894         var cfg = false;
12895         //render
12896         /*
12897          * Render classic select for iso
12898          */
12899         
12900         if(Roo.isIOS && this.useNativeIOS){
12901             cfg = this.getAutoCreateNativeIOS();
12902             return cfg;
12903         }
12904         
12905         /*
12906          * Touch Devices
12907          */
12908         
12909         if(Roo.isTouch && this.mobileTouchView){
12910             cfg = this.getAutoCreateTouchView();
12911             return cfg;;
12912         }
12913         
12914         /*
12915          *  Normal ComboBox
12916          */
12917         if(!this.tickable){
12918             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12919             return cfg;
12920         }
12921         
12922         /*
12923          *  ComboBox with tickable selections
12924          */
12925              
12926         var align = this.labelAlign || this.parentLabelAlign();
12927         
12928         cfg = {
12929             cls : 'form-group roo-combobox-tickable' //input-group
12930         };
12931         
12932         var btn_text_select = '';
12933         var btn_text_done = '';
12934         var btn_text_cancel = '';
12935         
12936         if (this.btn_text_show) {
12937             btn_text_select = 'Select';
12938             btn_text_done = 'Done';
12939             btn_text_cancel = 'Cancel'; 
12940         }
12941         
12942         var buttons = {
12943             tag : 'div',
12944             cls : 'tickable-buttons',
12945             cn : [
12946                 {
12947                     tag : 'button',
12948                     type : 'button',
12949                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12950                     //html : this.triggerText
12951                     html: btn_text_select
12952                 },
12953                 {
12954                     tag : 'button',
12955                     type : 'button',
12956                     name : 'ok',
12957                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12958                     //html : 'Done'
12959                     html: btn_text_done
12960                 },
12961                 {
12962                     tag : 'button',
12963                     type : 'button',
12964                     name : 'cancel',
12965                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12966                     //html : 'Cancel'
12967                     html: btn_text_cancel
12968                 }
12969             ]
12970         };
12971         
12972         if(this.editable){
12973             buttons.cn.unshift({
12974                 tag: 'input',
12975                 cls: 'roo-select2-search-field-input'
12976             });
12977         }
12978         
12979         var _this = this;
12980         
12981         Roo.each(buttons.cn, function(c){
12982             if (_this.size) {
12983                 c.cls += ' btn-' + _this.size;
12984             }
12985
12986             if (_this.disabled) {
12987                 c.disabled = true;
12988             }
12989         });
12990         
12991         var box = {
12992             tag: 'div',
12993             cn: [
12994                 {
12995                     tag: 'input',
12996                     type : 'hidden',
12997                     cls: 'form-hidden-field'
12998                 },
12999                 {
13000                     tag: 'ul',
13001                     cls: 'roo-select2-choices',
13002                     cn:[
13003                         {
13004                             tag: 'li',
13005                             cls: 'roo-select2-search-field',
13006                             cn: [
13007                                 buttons
13008                             ]
13009                         }
13010                     ]
13011                 }
13012             ]
13013         };
13014         
13015         var combobox = {
13016             cls: 'roo-select2-container input-group roo-select2-container-multi',
13017             cn: [
13018                 box
13019 //                {
13020 //                    tag: 'ul',
13021 //                    cls: 'typeahead typeahead-long dropdown-menu',
13022 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13023 //                }
13024             ]
13025         };
13026         
13027         if(this.hasFeedback && !this.allowBlank){
13028             
13029             var feedback = {
13030                 tag: 'span',
13031                 cls: 'glyphicon form-control-feedback'
13032             };
13033
13034             combobox.cn.push(feedback);
13035         }
13036         
13037         
13038         if (align ==='left' && this.fieldLabel.length) {
13039             
13040             cfg.cls += ' roo-form-group-label-left';
13041             
13042             cfg.cn = [
13043                 {
13044                     tag : 'i',
13045                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13046                     tooltip : 'This field is required'
13047                 },
13048                 {
13049                     tag: 'label',
13050                     'for' :  id,
13051                     cls : 'control-label',
13052                     html : this.fieldLabel
13053
13054                 },
13055                 {
13056                     cls : "", 
13057                     cn: [
13058                         combobox
13059                     ]
13060                 }
13061
13062             ];
13063             
13064             var labelCfg = cfg.cn[1];
13065             var contentCfg = cfg.cn[2];
13066             
13067
13068             if(this.indicatorpos == 'right'){
13069                 
13070                 cfg.cn = [
13071                     {
13072                         tag: 'label',
13073                         'for' :  id,
13074                         cls : 'control-label',
13075                         cn : [
13076                             {
13077                                 tag : 'span',
13078                                 html : this.fieldLabel
13079                             },
13080                             {
13081                                 tag : 'i',
13082                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13083                                 tooltip : 'This field is required'
13084                             }
13085                         ]
13086                     },
13087                     {
13088                         cls : "",
13089                         cn: [
13090                             combobox
13091                         ]
13092                     }
13093
13094                 ];
13095                 
13096                 
13097                 
13098                 labelCfg = cfg.cn[0];
13099                 contentCfg = cfg.cn[1];
13100             
13101             }
13102             
13103             if(this.labelWidth > 12){
13104                 labelCfg.style = "width: " + this.labelWidth + 'px';
13105             }
13106             
13107             if(this.labelWidth < 13 && this.labelmd == 0){
13108                 this.labelmd = this.labelWidth;
13109             }
13110             
13111             if(this.labellg > 0){
13112                 labelCfg.cls += ' col-lg-' + this.labellg;
13113                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13114             }
13115             
13116             if(this.labelmd > 0){
13117                 labelCfg.cls += ' col-md-' + this.labelmd;
13118                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13119             }
13120             
13121             if(this.labelsm > 0){
13122                 labelCfg.cls += ' col-sm-' + this.labelsm;
13123                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13124             }
13125             
13126             if(this.labelxs > 0){
13127                 labelCfg.cls += ' col-xs-' + this.labelxs;
13128                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13129             }
13130                 
13131                 
13132         } else if ( this.fieldLabel.length) {
13133 //                Roo.log(" label");
13134                  cfg.cn = [
13135                     {
13136                         tag : 'i',
13137                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13138                         tooltip : 'This field is required'
13139                     },
13140                     {
13141                         tag: 'label',
13142                         //cls : 'input-group-addon',
13143                         html : this.fieldLabel
13144                     },
13145                     combobox
13146                 ];
13147                 
13148                 if(this.indicatorpos == 'right'){
13149                     cfg.cn = [
13150                         {
13151                             tag: 'label',
13152                             //cls : 'input-group-addon',
13153                             html : this.fieldLabel
13154                         },
13155                         {
13156                             tag : 'i',
13157                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13158                             tooltip : 'This field is required'
13159                         },
13160                         combobox
13161                     ];
13162                     
13163                 }
13164
13165         } else {
13166             
13167 //                Roo.log(" no label && no align");
13168                 cfg = combobox
13169                      
13170                 
13171         }
13172          
13173         var settings=this;
13174         ['xs','sm','md','lg'].map(function(size){
13175             if (settings[size]) {
13176                 cfg.cls += ' col-' + size + '-' + settings[size];
13177             }
13178         });
13179         
13180         return cfg;
13181         
13182     },
13183     
13184     _initEventsCalled : false,
13185     
13186     // private
13187     initEvents: function()
13188     {   
13189         if (this._initEventsCalled) { // as we call render... prevent looping...
13190             return;
13191         }
13192         this._initEventsCalled = true;
13193         
13194         if (!this.store) {
13195             throw "can not find store for combo";
13196         }
13197         
13198         this.indicator = this.indicatorEl();
13199         
13200         this.store = Roo.factory(this.store, Roo.data);
13201         this.store.parent = this;
13202         
13203         // if we are building from html. then this element is so complex, that we can not really
13204         // use the rendered HTML.
13205         // so we have to trash and replace the previous code.
13206         if (Roo.XComponent.build_from_html) {
13207             // remove this element....
13208             var e = this.el.dom, k=0;
13209             while (e ) { e = e.previousSibling;  ++k;}
13210
13211             this.el.remove();
13212             
13213             this.el=false;
13214             this.rendered = false;
13215             
13216             this.render(this.parent().getChildContainer(true), k);
13217         }
13218         
13219         if(Roo.isIOS && this.useNativeIOS){
13220             this.initIOSView();
13221             return;
13222         }
13223         
13224         /*
13225          * Touch Devices
13226          */
13227         
13228         if(Roo.isTouch && this.mobileTouchView){
13229             this.initTouchView();
13230             return;
13231         }
13232         
13233         if(this.tickable){
13234             this.initTickableEvents();
13235             return;
13236         }
13237         
13238         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13239         
13240         if(this.hiddenName){
13241             
13242             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13243             
13244             this.hiddenField.dom.value =
13245                 this.hiddenValue !== undefined ? this.hiddenValue :
13246                 this.value !== undefined ? this.value : '';
13247
13248             // prevent input submission
13249             this.el.dom.removeAttribute('name');
13250             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13251              
13252              
13253         }
13254         //if(Roo.isGecko){
13255         //    this.el.dom.setAttribute('autocomplete', 'off');
13256         //}
13257         
13258         var cls = 'x-combo-list';
13259         
13260         //this.list = new Roo.Layer({
13261         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13262         //});
13263         
13264         var _this = this;
13265         
13266         (function(){
13267             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13268             _this.list.setWidth(lw);
13269         }).defer(100);
13270         
13271         this.list.on('mouseover', this.onViewOver, this);
13272         this.list.on('mousemove', this.onViewMove, this);
13273         this.list.on('scroll', this.onViewScroll, this);
13274         
13275         /*
13276         this.list.swallowEvent('mousewheel');
13277         this.assetHeight = 0;
13278
13279         if(this.title){
13280             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13281             this.assetHeight += this.header.getHeight();
13282         }
13283
13284         this.innerList = this.list.createChild({cls:cls+'-inner'});
13285         this.innerList.on('mouseover', this.onViewOver, this);
13286         this.innerList.on('mousemove', this.onViewMove, this);
13287         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13288         
13289         if(this.allowBlank && !this.pageSize && !this.disableClear){
13290             this.footer = this.list.createChild({cls:cls+'-ft'});
13291             this.pageTb = new Roo.Toolbar(this.footer);
13292            
13293         }
13294         if(this.pageSize){
13295             this.footer = this.list.createChild({cls:cls+'-ft'});
13296             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13297                     {pageSize: this.pageSize});
13298             
13299         }
13300         
13301         if (this.pageTb && this.allowBlank && !this.disableClear) {
13302             var _this = this;
13303             this.pageTb.add(new Roo.Toolbar.Fill(), {
13304                 cls: 'x-btn-icon x-btn-clear',
13305                 text: '&#160;',
13306                 handler: function()
13307                 {
13308                     _this.collapse();
13309                     _this.clearValue();
13310                     _this.onSelect(false, -1);
13311                 }
13312             });
13313         }
13314         if (this.footer) {
13315             this.assetHeight += this.footer.getHeight();
13316         }
13317         */
13318             
13319         if(!this.tpl){
13320             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13321         }
13322
13323         this.view = new Roo.View(this.list, this.tpl, {
13324             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13325         });
13326         //this.view.wrapEl.setDisplayed(false);
13327         this.view.on('click', this.onViewClick, this);
13328         
13329         
13330         this.store.on('beforeload', this.onBeforeLoad, this);
13331         this.store.on('load', this.onLoad, this);
13332         this.store.on('loadexception', this.onLoadException, this);
13333         /*
13334         if(this.resizable){
13335             this.resizer = new Roo.Resizable(this.list,  {
13336                pinned:true, handles:'se'
13337             });
13338             this.resizer.on('resize', function(r, w, h){
13339                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13340                 this.listWidth = w;
13341                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13342                 this.restrictHeight();
13343             }, this);
13344             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13345         }
13346         */
13347         if(!this.editable){
13348             this.editable = true;
13349             this.setEditable(false);
13350         }
13351         
13352         /*
13353         
13354         if (typeof(this.events.add.listeners) != 'undefined') {
13355             
13356             this.addicon = this.wrap.createChild(
13357                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13358        
13359             this.addicon.on('click', function(e) {
13360                 this.fireEvent('add', this);
13361             }, this);
13362         }
13363         if (typeof(this.events.edit.listeners) != 'undefined') {
13364             
13365             this.editicon = this.wrap.createChild(
13366                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13367             if (this.addicon) {
13368                 this.editicon.setStyle('margin-left', '40px');
13369             }
13370             this.editicon.on('click', function(e) {
13371                 
13372                 // we fire even  if inothing is selected..
13373                 this.fireEvent('edit', this, this.lastData );
13374                 
13375             }, this);
13376         }
13377         */
13378         
13379         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13380             "up" : function(e){
13381                 this.inKeyMode = true;
13382                 this.selectPrev();
13383             },
13384
13385             "down" : function(e){
13386                 if(!this.isExpanded()){
13387                     this.onTriggerClick();
13388                 }else{
13389                     this.inKeyMode = true;
13390                     this.selectNext();
13391                 }
13392             },
13393
13394             "enter" : function(e){
13395 //                this.onViewClick();
13396                 //return true;
13397                 this.collapse();
13398                 
13399                 if(this.fireEvent("specialkey", this, e)){
13400                     this.onViewClick(false);
13401                 }
13402                 
13403                 return true;
13404             },
13405
13406             "esc" : function(e){
13407                 this.collapse();
13408             },
13409
13410             "tab" : function(e){
13411                 this.collapse();
13412                 
13413                 if(this.fireEvent("specialkey", this, e)){
13414                     this.onViewClick(false);
13415                 }
13416                 
13417                 return true;
13418             },
13419
13420             scope : this,
13421
13422             doRelay : function(foo, bar, hname){
13423                 if(hname == 'down' || this.scope.isExpanded()){
13424                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13425                 }
13426                 return true;
13427             },
13428
13429             forceKeyDown: true
13430         });
13431         
13432         
13433         this.queryDelay = Math.max(this.queryDelay || 10,
13434                 this.mode == 'local' ? 10 : 250);
13435         
13436         
13437         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13438         
13439         if(this.typeAhead){
13440             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13441         }
13442         if(this.editable !== false){
13443             this.inputEl().on("keyup", this.onKeyUp, this);
13444         }
13445         if(this.forceSelection){
13446             this.inputEl().on('blur', this.doForce, this);
13447         }
13448         
13449         if(this.multiple){
13450             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13451             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13452         }
13453     },
13454     
13455     initTickableEvents: function()
13456     {   
13457         this.createList();
13458         
13459         if(this.hiddenName){
13460             
13461             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13462             
13463             this.hiddenField.dom.value =
13464                 this.hiddenValue !== undefined ? this.hiddenValue :
13465                 this.value !== undefined ? this.value : '';
13466
13467             // prevent input submission
13468             this.el.dom.removeAttribute('name');
13469             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13470              
13471              
13472         }
13473         
13474 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13475         
13476         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13477         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13478         if(this.triggerList){
13479             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13480         }
13481          
13482         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13483         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13484         
13485         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13486         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13487         
13488         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13489         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13490         
13491         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13492         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13493         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13494         
13495         this.okBtn.hide();
13496         this.cancelBtn.hide();
13497         
13498         var _this = this;
13499         
13500         (function(){
13501             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13502             _this.list.setWidth(lw);
13503         }).defer(100);
13504         
13505         this.list.on('mouseover', this.onViewOver, this);
13506         this.list.on('mousemove', this.onViewMove, this);
13507         
13508         this.list.on('scroll', this.onViewScroll, this);
13509         
13510         if(!this.tpl){
13511             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>';
13512         }
13513
13514         this.view = new Roo.View(this.list, this.tpl, {
13515             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13516         });
13517         
13518         //this.view.wrapEl.setDisplayed(false);
13519         this.view.on('click', this.onViewClick, this);
13520         
13521         
13522         
13523         this.store.on('beforeload', this.onBeforeLoad, this);
13524         this.store.on('load', this.onLoad, this);
13525         this.store.on('loadexception', this.onLoadException, this);
13526         
13527         if(this.editable){
13528             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13529                 "up" : function(e){
13530                     this.inKeyMode = true;
13531                     this.selectPrev();
13532                 },
13533
13534                 "down" : function(e){
13535                     this.inKeyMode = true;
13536                     this.selectNext();
13537                 },
13538
13539                 "enter" : function(e){
13540                     if(this.fireEvent("specialkey", this, e)){
13541                         this.onViewClick(false);
13542                     }
13543                     
13544                     return true;
13545                 },
13546
13547                 "esc" : function(e){
13548                     this.onTickableFooterButtonClick(e, false, false);
13549                 },
13550
13551                 "tab" : function(e){
13552                     this.fireEvent("specialkey", this, e);
13553                     
13554                     this.onTickableFooterButtonClick(e, false, false);
13555                     
13556                     return true;
13557                 },
13558
13559                 scope : this,
13560
13561                 doRelay : function(e, fn, key){
13562                     if(this.scope.isExpanded()){
13563                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13564                     }
13565                     return true;
13566                 },
13567
13568                 forceKeyDown: true
13569             });
13570         }
13571         
13572         this.queryDelay = Math.max(this.queryDelay || 10,
13573                 this.mode == 'local' ? 10 : 250);
13574         
13575         
13576         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13577         
13578         if(this.typeAhead){
13579             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13580         }
13581         
13582         if(this.editable !== false){
13583             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13584         }
13585         
13586         this.indicator = this.indicatorEl();
13587         
13588         if(this.indicator){
13589             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13590             this.indicator.hide();
13591         }
13592         
13593     },
13594
13595     onDestroy : function(){
13596         if(this.view){
13597             this.view.setStore(null);
13598             this.view.el.removeAllListeners();
13599             this.view.el.remove();
13600             this.view.purgeListeners();
13601         }
13602         if(this.list){
13603             this.list.dom.innerHTML  = '';
13604         }
13605         
13606         if(this.store){
13607             this.store.un('beforeload', this.onBeforeLoad, this);
13608             this.store.un('load', this.onLoad, this);
13609             this.store.un('loadexception', this.onLoadException, this);
13610         }
13611         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13612     },
13613
13614     // private
13615     fireKey : function(e){
13616         if(e.isNavKeyPress() && !this.list.isVisible()){
13617             this.fireEvent("specialkey", this, e);
13618         }
13619     },
13620
13621     // private
13622     onResize: function(w, h){
13623 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13624 //        
13625 //        if(typeof w != 'number'){
13626 //            // we do not handle it!?!?
13627 //            return;
13628 //        }
13629 //        var tw = this.trigger.getWidth();
13630 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13631 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13632 //        var x = w - tw;
13633 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13634 //            
13635 //        //this.trigger.setStyle('left', x+'px');
13636 //        
13637 //        if(this.list && this.listWidth === undefined){
13638 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13639 //            this.list.setWidth(lw);
13640 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13641 //        }
13642         
13643     
13644         
13645     },
13646
13647     /**
13648      * Allow or prevent the user from directly editing the field text.  If false is passed,
13649      * the user will only be able to select from the items defined in the dropdown list.  This method
13650      * is the runtime equivalent of setting the 'editable' config option at config time.
13651      * @param {Boolean} value True to allow the user to directly edit the field text
13652      */
13653     setEditable : function(value){
13654         if(value == this.editable){
13655             return;
13656         }
13657         this.editable = value;
13658         if(!value){
13659             this.inputEl().dom.setAttribute('readOnly', true);
13660             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13661             this.inputEl().addClass('x-combo-noedit');
13662         }else{
13663             this.inputEl().dom.setAttribute('readOnly', false);
13664             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13665             this.inputEl().removeClass('x-combo-noedit');
13666         }
13667     },
13668
13669     // private
13670     
13671     onBeforeLoad : function(combo,opts){
13672         if(!this.hasFocus){
13673             return;
13674         }
13675          if (!opts.add) {
13676             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13677          }
13678         this.restrictHeight();
13679         this.selectedIndex = -1;
13680     },
13681
13682     // private
13683     onLoad : function(){
13684         
13685         this.hasQuery = false;
13686         
13687         if(!this.hasFocus){
13688             return;
13689         }
13690         
13691         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13692             this.loading.hide();
13693         }
13694         
13695         if(this.store.getCount() > 0){
13696             
13697             this.expand();
13698             this.restrictHeight();
13699             if(this.lastQuery == this.allQuery){
13700                 if(this.editable && !this.tickable){
13701                     this.inputEl().dom.select();
13702                 }
13703                 
13704                 if(
13705                     !this.selectByValue(this.value, true) &&
13706                     this.autoFocus && 
13707                     (
13708                         !this.store.lastOptions ||
13709                         typeof(this.store.lastOptions.add) == 'undefined' || 
13710                         this.store.lastOptions.add != true
13711                     )
13712                 ){
13713                     this.select(0, true);
13714                 }
13715             }else{
13716                 if(this.autoFocus){
13717                     this.selectNext();
13718                 }
13719                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13720                     this.taTask.delay(this.typeAheadDelay);
13721                 }
13722             }
13723         }else{
13724             this.onEmptyResults();
13725         }
13726         
13727         //this.el.focus();
13728     },
13729     // private
13730     onLoadException : function()
13731     {
13732         this.hasQuery = false;
13733         
13734         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13735             this.loading.hide();
13736         }
13737         
13738         if(this.tickable && this.editable){
13739             return;
13740         }
13741         
13742         this.collapse();
13743         // only causes errors at present
13744         //Roo.log(this.store.reader.jsonData);
13745         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13746             // fixme
13747             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13748         //}
13749         
13750         
13751     },
13752     // private
13753     onTypeAhead : function(){
13754         if(this.store.getCount() > 0){
13755             var r = this.store.getAt(0);
13756             var newValue = r.data[this.displayField];
13757             var len = newValue.length;
13758             var selStart = this.getRawValue().length;
13759             
13760             if(selStart != len){
13761                 this.setRawValue(newValue);
13762                 this.selectText(selStart, newValue.length);
13763             }
13764         }
13765     },
13766
13767     // private
13768     onSelect : function(record, index){
13769         
13770         if(this.fireEvent('beforeselect', this, record, index) !== false){
13771         
13772             this.setFromData(index > -1 ? record.data : false);
13773             
13774             this.collapse();
13775             this.fireEvent('select', this, record, index);
13776         }
13777     },
13778
13779     /**
13780      * Returns the currently selected field value or empty string if no value is set.
13781      * @return {String} value The selected value
13782      */
13783     getValue : function()
13784     {
13785         if(Roo.isIOS && this.useNativeIOS){
13786             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13787         }
13788         
13789         if(this.multiple){
13790             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13791         }
13792         
13793         if(this.valueField){
13794             return typeof this.value != 'undefined' ? this.value : '';
13795         }else{
13796             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13797         }
13798     },
13799     
13800     getRawValue : function()
13801     {
13802         if(Roo.isIOS && this.useNativeIOS){
13803             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13804         }
13805         
13806         var v = this.inputEl().getValue();
13807         
13808         return v;
13809     },
13810
13811     /**
13812      * Clears any text/value currently set in the field
13813      */
13814     clearValue : function(){
13815         
13816         if(this.hiddenField){
13817             this.hiddenField.dom.value = '';
13818         }
13819         this.value = '';
13820         this.setRawValue('');
13821         this.lastSelectionText = '';
13822         this.lastData = false;
13823         
13824         var close = this.closeTriggerEl();
13825         
13826         if(close){
13827             close.hide();
13828         }
13829         
13830         this.validate();
13831         
13832     },
13833
13834     /**
13835      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13836      * will be displayed in the field.  If the value does not match the data value of an existing item,
13837      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13838      * Otherwise the field will be blank (although the value will still be set).
13839      * @param {String} value The value to match
13840      */
13841     setValue : function(v)
13842     {
13843         if(Roo.isIOS && this.useNativeIOS){
13844             this.setIOSValue(v);
13845             return;
13846         }
13847         
13848         if(this.multiple){
13849             this.syncValue();
13850             return;
13851         }
13852         
13853         var text = v;
13854         if(this.valueField){
13855             var r = this.findRecord(this.valueField, v);
13856             if(r){
13857                 text = r.data[this.displayField];
13858             }else if(this.valueNotFoundText !== undefined){
13859                 text = this.valueNotFoundText;
13860             }
13861         }
13862         this.lastSelectionText = text;
13863         if(this.hiddenField){
13864             this.hiddenField.dom.value = v;
13865         }
13866         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13867         this.value = v;
13868         
13869         var close = this.closeTriggerEl();
13870         
13871         if(close){
13872             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13873         }
13874         
13875         this.validate();
13876     },
13877     /**
13878      * @property {Object} the last set data for the element
13879      */
13880     
13881     lastData : false,
13882     /**
13883      * Sets the value of the field based on a object which is related to the record format for the store.
13884      * @param {Object} value the value to set as. or false on reset?
13885      */
13886     setFromData : function(o){
13887         
13888         if(this.multiple){
13889             this.addItem(o);
13890             return;
13891         }
13892             
13893         var dv = ''; // display value
13894         var vv = ''; // value value..
13895         this.lastData = o;
13896         if (this.displayField) {
13897             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13898         } else {
13899             // this is an error condition!!!
13900             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13901         }
13902         
13903         if(this.valueField){
13904             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13905         }
13906         
13907         var close = this.closeTriggerEl();
13908         
13909         if(close){
13910             if(dv.length || vv * 1 > 0){
13911                 close.show() ;
13912                 this.blockFocus=true;
13913             } else {
13914                 close.hide();
13915             }             
13916         }
13917         
13918         if(this.hiddenField){
13919             this.hiddenField.dom.value = vv;
13920             
13921             this.lastSelectionText = dv;
13922             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13923             this.value = vv;
13924             return;
13925         }
13926         // no hidden field.. - we store the value in 'value', but still display
13927         // display field!!!!
13928         this.lastSelectionText = dv;
13929         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13930         this.value = vv;
13931         
13932         
13933         
13934     },
13935     // private
13936     reset : function(){
13937         // overridden so that last data is reset..
13938         
13939         if(this.multiple){
13940             this.clearItem();
13941             return;
13942         }
13943         
13944         this.setValue(this.originalValue);
13945         //this.clearInvalid();
13946         this.lastData = false;
13947         if (this.view) {
13948             this.view.clearSelections();
13949         }
13950         
13951         this.validate();
13952     },
13953     // private
13954     findRecord : function(prop, value){
13955         var record;
13956         if(this.store.getCount() > 0){
13957             this.store.each(function(r){
13958                 if(r.data[prop] == value){
13959                     record = r;
13960                     return false;
13961                 }
13962                 return true;
13963             });
13964         }
13965         return record;
13966     },
13967     
13968     getName: function()
13969     {
13970         // returns hidden if it's set..
13971         if (!this.rendered) {return ''};
13972         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13973         
13974     },
13975     // private
13976     onViewMove : function(e, t){
13977         this.inKeyMode = false;
13978     },
13979
13980     // private
13981     onViewOver : function(e, t){
13982         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13983             return;
13984         }
13985         var item = this.view.findItemFromChild(t);
13986         
13987         if(item){
13988             var index = this.view.indexOf(item);
13989             this.select(index, false);
13990         }
13991     },
13992
13993     // private
13994     onViewClick : function(view, doFocus, el, e)
13995     {
13996         var index = this.view.getSelectedIndexes()[0];
13997         
13998         var r = this.store.getAt(index);
13999         
14000         if(this.tickable){
14001             
14002             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14003                 return;
14004             }
14005             
14006             var rm = false;
14007             var _this = this;
14008             
14009             Roo.each(this.tickItems, function(v,k){
14010                 
14011                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14012                     Roo.log(v);
14013                     _this.tickItems.splice(k, 1);
14014                     
14015                     if(typeof(e) == 'undefined' && view == false){
14016                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14017                     }
14018                     
14019                     rm = true;
14020                     return;
14021                 }
14022             });
14023             
14024             if(rm){
14025                 return;
14026             }
14027             
14028             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14029                 this.tickItems.push(r.data);
14030             }
14031             
14032             if(typeof(e) == 'undefined' && view == false){
14033                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14034             }
14035                     
14036             return;
14037         }
14038         
14039         if(r){
14040             this.onSelect(r, index);
14041         }
14042         if(doFocus !== false && !this.blockFocus){
14043             this.inputEl().focus();
14044         }
14045     },
14046
14047     // private
14048     restrictHeight : function(){
14049         //this.innerList.dom.style.height = '';
14050         //var inner = this.innerList.dom;
14051         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14052         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14053         //this.list.beginUpdate();
14054         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14055         this.list.alignTo(this.inputEl(), this.listAlign);
14056         this.list.alignTo(this.inputEl(), this.listAlign);
14057         //this.list.endUpdate();
14058     },
14059
14060     // private
14061     onEmptyResults : function(){
14062         
14063         if(this.tickable && this.editable){
14064             this.hasFocus = false;
14065             this.restrictHeight();
14066             return;
14067         }
14068         
14069         this.collapse();
14070     },
14071
14072     /**
14073      * Returns true if the dropdown list is expanded, else false.
14074      */
14075     isExpanded : function(){
14076         return this.list.isVisible();
14077     },
14078
14079     /**
14080      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14081      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14082      * @param {String} value The data value of the item to select
14083      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14084      * selected item if it is not currently in view (defaults to true)
14085      * @return {Boolean} True if the value matched an item in the list, else false
14086      */
14087     selectByValue : function(v, scrollIntoView){
14088         if(v !== undefined && v !== null){
14089             var r = this.findRecord(this.valueField || this.displayField, v);
14090             if(r){
14091                 this.select(this.store.indexOf(r), scrollIntoView);
14092                 return true;
14093             }
14094         }
14095         return false;
14096     },
14097
14098     /**
14099      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14100      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14101      * @param {Number} index The zero-based index of the list item to select
14102      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14103      * selected item if it is not currently in view (defaults to true)
14104      */
14105     select : function(index, scrollIntoView){
14106         this.selectedIndex = index;
14107         this.view.select(index);
14108         if(scrollIntoView !== false){
14109             var el = this.view.getNode(index);
14110             /*
14111              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14112              */
14113             if(el){
14114                 this.list.scrollChildIntoView(el, false);
14115             }
14116         }
14117     },
14118
14119     // private
14120     selectNext : function(){
14121         var ct = this.store.getCount();
14122         if(ct > 0){
14123             if(this.selectedIndex == -1){
14124                 this.select(0);
14125             }else if(this.selectedIndex < ct-1){
14126                 this.select(this.selectedIndex+1);
14127             }
14128         }
14129     },
14130
14131     // private
14132     selectPrev : function(){
14133         var ct = this.store.getCount();
14134         if(ct > 0){
14135             if(this.selectedIndex == -1){
14136                 this.select(0);
14137             }else if(this.selectedIndex != 0){
14138                 this.select(this.selectedIndex-1);
14139             }
14140         }
14141     },
14142
14143     // private
14144     onKeyUp : function(e){
14145         if(this.editable !== false && !e.isSpecialKey()){
14146             this.lastKey = e.getKey();
14147             this.dqTask.delay(this.queryDelay);
14148         }
14149     },
14150
14151     // private
14152     validateBlur : function(){
14153         return !this.list || !this.list.isVisible();   
14154     },
14155
14156     // private
14157     initQuery : function(){
14158         
14159         var v = this.getRawValue();
14160         
14161         if(this.tickable && this.editable){
14162             v = this.tickableInputEl().getValue();
14163         }
14164         
14165         this.doQuery(v);
14166     },
14167
14168     // private
14169     doForce : function(){
14170         if(this.inputEl().dom.value.length > 0){
14171             this.inputEl().dom.value =
14172                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14173              
14174         }
14175     },
14176
14177     /**
14178      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14179      * query allowing the query action to be canceled if needed.
14180      * @param {String} query The SQL query to execute
14181      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14182      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14183      * saved in the current store (defaults to false)
14184      */
14185     doQuery : function(q, forceAll){
14186         
14187         if(q === undefined || q === null){
14188             q = '';
14189         }
14190         var qe = {
14191             query: q,
14192             forceAll: forceAll,
14193             combo: this,
14194             cancel:false
14195         };
14196         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14197             return false;
14198         }
14199         q = qe.query;
14200         
14201         forceAll = qe.forceAll;
14202         if(forceAll === true || (q.length >= this.minChars)){
14203             
14204             this.hasQuery = true;
14205             
14206             if(this.lastQuery != q || this.alwaysQuery){
14207                 this.lastQuery = q;
14208                 if(this.mode == 'local'){
14209                     this.selectedIndex = -1;
14210                     if(forceAll){
14211                         this.store.clearFilter();
14212                     }else{
14213                         
14214                         if(this.specialFilter){
14215                             this.fireEvent('specialfilter', this);
14216                             this.onLoad();
14217                             return;
14218                         }
14219                         
14220                         this.store.filter(this.displayField, q);
14221                     }
14222                     
14223                     this.store.fireEvent("datachanged", this.store);
14224                     
14225                     this.onLoad();
14226                     
14227                     
14228                 }else{
14229                     
14230                     this.store.baseParams[this.queryParam] = q;
14231                     
14232                     var options = {params : this.getParams(q)};
14233                     
14234                     if(this.loadNext){
14235                         options.add = true;
14236                         options.params.start = this.page * this.pageSize;
14237                     }
14238                     
14239                     this.store.load(options);
14240                     
14241                     /*
14242                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14243                      *  we should expand the list on onLoad
14244                      *  so command out it
14245                      */
14246 //                    this.expand();
14247                 }
14248             }else{
14249                 this.selectedIndex = -1;
14250                 this.onLoad();   
14251             }
14252         }
14253         
14254         this.loadNext = false;
14255     },
14256     
14257     // private
14258     getParams : function(q){
14259         var p = {};
14260         //p[this.queryParam] = q;
14261         
14262         if(this.pageSize){
14263             p.start = 0;
14264             p.limit = this.pageSize;
14265         }
14266         return p;
14267     },
14268
14269     /**
14270      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14271      */
14272     collapse : function(){
14273         if(!this.isExpanded()){
14274             return;
14275         }
14276         
14277         this.list.hide();
14278         
14279         this.hasFocus = false;
14280         
14281         if(this.tickable){
14282             this.okBtn.hide();
14283             this.cancelBtn.hide();
14284             this.trigger.show();
14285             
14286             if(this.editable){
14287                 this.tickableInputEl().dom.value = '';
14288                 this.tickableInputEl().blur();
14289             }
14290             
14291         }
14292         
14293         Roo.get(document).un('mousedown', this.collapseIf, this);
14294         Roo.get(document).un('mousewheel', this.collapseIf, this);
14295         if (!this.editable) {
14296             Roo.get(document).un('keydown', this.listKeyPress, this);
14297         }
14298         this.fireEvent('collapse', this);
14299         
14300         this.validate();
14301     },
14302
14303     // private
14304     collapseIf : function(e){
14305         var in_combo  = e.within(this.el);
14306         var in_list =  e.within(this.list);
14307         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14308         
14309         if (in_combo || in_list || is_list) {
14310             //e.stopPropagation();
14311             return;
14312         }
14313         
14314         if(this.tickable){
14315             this.onTickableFooterButtonClick(e, false, false);
14316         }
14317
14318         this.collapse();
14319         
14320     },
14321
14322     /**
14323      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14324      */
14325     expand : function(){
14326        
14327         if(this.isExpanded() || !this.hasFocus){
14328             return;
14329         }
14330         
14331         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14332         this.list.setWidth(lw);
14333         
14334         Roo.log('expand');
14335         
14336         this.list.show();
14337         
14338         this.restrictHeight();
14339         
14340         if(this.tickable){
14341             
14342             this.tickItems = Roo.apply([], this.item);
14343             
14344             this.okBtn.show();
14345             this.cancelBtn.show();
14346             this.trigger.hide();
14347             
14348             if(this.editable){
14349                 this.tickableInputEl().focus();
14350             }
14351             
14352         }
14353         
14354         Roo.get(document).on('mousedown', this.collapseIf, this);
14355         Roo.get(document).on('mousewheel', this.collapseIf, this);
14356         if (!this.editable) {
14357             Roo.get(document).on('keydown', this.listKeyPress, this);
14358         }
14359         
14360         this.fireEvent('expand', this);
14361     },
14362
14363     // private
14364     // Implements the default empty TriggerField.onTriggerClick function
14365     onTriggerClick : function(e)
14366     {
14367         Roo.log('trigger click');
14368         
14369         if(this.disabled || !this.triggerList){
14370             return;
14371         }
14372         
14373         this.page = 0;
14374         this.loadNext = false;
14375         
14376         if(this.isExpanded()){
14377             this.collapse();
14378             if (!this.blockFocus) {
14379                 this.inputEl().focus();
14380             }
14381             
14382         }else {
14383             this.hasFocus = true;
14384             if(this.triggerAction == 'all') {
14385                 this.doQuery(this.allQuery, true);
14386             } else {
14387                 this.doQuery(this.getRawValue());
14388             }
14389             if (!this.blockFocus) {
14390                 this.inputEl().focus();
14391             }
14392         }
14393     },
14394     
14395     onTickableTriggerClick : function(e)
14396     {
14397         if(this.disabled){
14398             return;
14399         }
14400         
14401         this.page = 0;
14402         this.loadNext = false;
14403         this.hasFocus = true;
14404         
14405         if(this.triggerAction == 'all') {
14406             this.doQuery(this.allQuery, true);
14407         } else {
14408             this.doQuery(this.getRawValue());
14409         }
14410     },
14411     
14412     onSearchFieldClick : function(e)
14413     {
14414         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14415             this.onTickableFooterButtonClick(e, false, false);
14416             return;
14417         }
14418         
14419         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14420             return;
14421         }
14422         
14423         this.page = 0;
14424         this.loadNext = false;
14425         this.hasFocus = true;
14426         
14427         if(this.triggerAction == 'all') {
14428             this.doQuery(this.allQuery, true);
14429         } else {
14430             this.doQuery(this.getRawValue());
14431         }
14432     },
14433     
14434     listKeyPress : function(e)
14435     {
14436         //Roo.log('listkeypress');
14437         // scroll to first matching element based on key pres..
14438         if (e.isSpecialKey()) {
14439             return false;
14440         }
14441         var k = String.fromCharCode(e.getKey()).toUpperCase();
14442         //Roo.log(k);
14443         var match  = false;
14444         var csel = this.view.getSelectedNodes();
14445         var cselitem = false;
14446         if (csel.length) {
14447             var ix = this.view.indexOf(csel[0]);
14448             cselitem  = this.store.getAt(ix);
14449             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14450                 cselitem = false;
14451             }
14452             
14453         }
14454         
14455         this.store.each(function(v) { 
14456             if (cselitem) {
14457                 // start at existing selection.
14458                 if (cselitem.id == v.id) {
14459                     cselitem = false;
14460                 }
14461                 return true;
14462             }
14463                 
14464             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14465                 match = this.store.indexOf(v);
14466                 return false;
14467             }
14468             return true;
14469         }, this);
14470         
14471         if (match === false) {
14472             return true; // no more action?
14473         }
14474         // scroll to?
14475         this.view.select(match);
14476         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14477         sn.scrollIntoView(sn.dom.parentNode, false);
14478     },
14479     
14480     onViewScroll : function(e, t){
14481         
14482         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){
14483             return;
14484         }
14485         
14486         this.hasQuery = true;
14487         
14488         this.loading = this.list.select('.loading', true).first();
14489         
14490         if(this.loading === null){
14491             this.list.createChild({
14492                 tag: 'div',
14493                 cls: 'loading roo-select2-more-results roo-select2-active',
14494                 html: 'Loading more results...'
14495             });
14496             
14497             this.loading = this.list.select('.loading', true).first();
14498             
14499             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14500             
14501             this.loading.hide();
14502         }
14503         
14504         this.loading.show();
14505         
14506         var _combo = this;
14507         
14508         this.page++;
14509         this.loadNext = true;
14510         
14511         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14512         
14513         return;
14514     },
14515     
14516     addItem : function(o)
14517     {   
14518         var dv = ''; // display value
14519         
14520         if (this.displayField) {
14521             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14522         } else {
14523             // this is an error condition!!!
14524             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14525         }
14526         
14527         if(!dv.length){
14528             return;
14529         }
14530         
14531         var choice = this.choices.createChild({
14532             tag: 'li',
14533             cls: 'roo-select2-search-choice',
14534             cn: [
14535                 {
14536                     tag: 'div',
14537                     html: dv
14538                 },
14539                 {
14540                     tag: 'a',
14541                     href: '#',
14542                     cls: 'roo-select2-search-choice-close fa fa-times',
14543                     tabindex: '-1'
14544                 }
14545             ]
14546             
14547         }, this.searchField);
14548         
14549         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14550         
14551         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14552         
14553         this.item.push(o);
14554         
14555         this.lastData = o;
14556         
14557         this.syncValue();
14558         
14559         this.inputEl().dom.value = '';
14560         
14561         this.validate();
14562     },
14563     
14564     onRemoveItem : function(e, _self, o)
14565     {
14566         e.preventDefault();
14567         
14568         this.lastItem = Roo.apply([], this.item);
14569         
14570         var index = this.item.indexOf(o.data) * 1;
14571         
14572         if( index < 0){
14573             Roo.log('not this item?!');
14574             return;
14575         }
14576         
14577         this.item.splice(index, 1);
14578         o.item.remove();
14579         
14580         this.syncValue();
14581         
14582         this.fireEvent('remove', this, e);
14583         
14584         this.validate();
14585         
14586     },
14587     
14588     syncValue : function()
14589     {
14590         if(!this.item.length){
14591             this.clearValue();
14592             return;
14593         }
14594             
14595         var value = [];
14596         var _this = this;
14597         Roo.each(this.item, function(i){
14598             if(_this.valueField){
14599                 value.push(i[_this.valueField]);
14600                 return;
14601             }
14602
14603             value.push(i);
14604         });
14605
14606         this.value = value.join(',');
14607
14608         if(this.hiddenField){
14609             this.hiddenField.dom.value = this.value;
14610         }
14611         
14612         this.store.fireEvent("datachanged", this.store);
14613         
14614         this.validate();
14615     },
14616     
14617     clearItem : function()
14618     {
14619         if(!this.multiple){
14620             return;
14621         }
14622         
14623         this.item = [];
14624         
14625         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14626            c.remove();
14627         });
14628         
14629         this.syncValue();
14630         
14631         this.validate();
14632         
14633         if(this.tickable && !Roo.isTouch){
14634             this.view.refresh();
14635         }
14636     },
14637     
14638     inputEl: function ()
14639     {
14640         if(Roo.isIOS && this.useNativeIOS){
14641             return this.el.select('select.roo-ios-select', true).first();
14642         }
14643         
14644         if(Roo.isTouch && this.mobileTouchView){
14645             return this.el.select('input.form-control',true).first();
14646         }
14647         
14648         if(this.tickable){
14649             return this.searchField;
14650         }
14651         
14652         return this.el.select('input.form-control',true).first();
14653     },
14654     
14655     onTickableFooterButtonClick : function(e, btn, el)
14656     {
14657         e.preventDefault();
14658         
14659         this.lastItem = Roo.apply([], this.item);
14660         
14661         if(btn && btn.name == 'cancel'){
14662             this.tickItems = Roo.apply([], this.item);
14663             this.collapse();
14664             return;
14665         }
14666         
14667         this.clearItem();
14668         
14669         var _this = this;
14670         
14671         Roo.each(this.tickItems, function(o){
14672             _this.addItem(o);
14673         });
14674         
14675         this.collapse();
14676         
14677     },
14678     
14679     validate : function()
14680     {
14681         if(this.getVisibilityEl().hasClass('hidden')){
14682             return true;
14683         }
14684         
14685         var v = this.getRawValue();
14686         
14687         if(this.multiple){
14688             v = this.getValue();
14689         }
14690         
14691         if(this.disabled || this.allowBlank || v.length){
14692             this.markValid();
14693             return true;
14694         }
14695         
14696         this.markInvalid();
14697         return false;
14698     },
14699     
14700     tickableInputEl : function()
14701     {
14702         if(!this.tickable || !this.editable){
14703             return this.inputEl();
14704         }
14705         
14706         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14707     },
14708     
14709     
14710     getAutoCreateTouchView : function()
14711     {
14712         var id = Roo.id();
14713         
14714         var cfg = {
14715             cls: 'form-group' //input-group
14716         };
14717         
14718         var input =  {
14719             tag: 'input',
14720             id : id,
14721             type : this.inputType,
14722             cls : 'form-control x-combo-noedit',
14723             autocomplete: 'new-password',
14724             placeholder : this.placeholder || '',
14725             readonly : true
14726         };
14727         
14728         if (this.name) {
14729             input.name = this.name;
14730         }
14731         
14732         if (this.size) {
14733             input.cls += ' input-' + this.size;
14734         }
14735         
14736         if (this.disabled) {
14737             input.disabled = true;
14738         }
14739         
14740         var inputblock = {
14741             cls : '',
14742             cn : [
14743                 input
14744             ]
14745         };
14746         
14747         if(this.before){
14748             inputblock.cls += ' input-group';
14749             
14750             inputblock.cn.unshift({
14751                 tag :'span',
14752                 cls : 'input-group-addon',
14753                 html : this.before
14754             });
14755         }
14756         
14757         if(this.removable && !this.multiple){
14758             inputblock.cls += ' roo-removable';
14759             
14760             inputblock.cn.push({
14761                 tag: 'button',
14762                 html : 'x',
14763                 cls : 'roo-combo-removable-btn close'
14764             });
14765         }
14766
14767         if(this.hasFeedback && !this.allowBlank){
14768             
14769             inputblock.cls += ' has-feedback';
14770             
14771             inputblock.cn.push({
14772                 tag: 'span',
14773                 cls: 'glyphicon form-control-feedback'
14774             });
14775             
14776         }
14777         
14778         if (this.after) {
14779             
14780             inputblock.cls += (this.before) ? '' : ' input-group';
14781             
14782             inputblock.cn.push({
14783                 tag :'span',
14784                 cls : 'input-group-addon',
14785                 html : this.after
14786             });
14787         }
14788
14789         var box = {
14790             tag: 'div',
14791             cn: [
14792                 {
14793                     tag: 'input',
14794                     type : 'hidden',
14795                     cls: 'form-hidden-field'
14796                 },
14797                 inputblock
14798             ]
14799             
14800         };
14801         
14802         if(this.multiple){
14803             box = {
14804                 tag: 'div',
14805                 cn: [
14806                     {
14807                         tag: 'input',
14808                         type : 'hidden',
14809                         cls: 'form-hidden-field'
14810                     },
14811                     {
14812                         tag: 'ul',
14813                         cls: 'roo-select2-choices',
14814                         cn:[
14815                             {
14816                                 tag: 'li',
14817                                 cls: 'roo-select2-search-field',
14818                                 cn: [
14819
14820                                     inputblock
14821                                 ]
14822                             }
14823                         ]
14824                     }
14825                 ]
14826             }
14827         };
14828         
14829         var combobox = {
14830             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14831             cn: [
14832                 box
14833             ]
14834         };
14835         
14836         if(!this.multiple && this.showToggleBtn){
14837             
14838             var caret = {
14839                         tag: 'span',
14840                         cls: 'caret'
14841             };
14842             
14843             if (this.caret != false) {
14844                 caret = {
14845                      tag: 'i',
14846                      cls: 'fa fa-' + this.caret
14847                 };
14848                 
14849             }
14850             
14851             combobox.cn.push({
14852                 tag :'span',
14853                 cls : 'input-group-addon btn dropdown-toggle',
14854                 cn : [
14855                     caret,
14856                     {
14857                         tag: 'span',
14858                         cls: 'combobox-clear',
14859                         cn  : [
14860                             {
14861                                 tag : 'i',
14862                                 cls: 'icon-remove'
14863                             }
14864                         ]
14865                     }
14866                 ]
14867
14868             })
14869         }
14870         
14871         if(this.multiple){
14872             combobox.cls += ' roo-select2-container-multi';
14873         }
14874         
14875         var align = this.labelAlign || this.parentLabelAlign();
14876         
14877         if (align ==='left' && this.fieldLabel.length) {
14878
14879             cfg.cn = [
14880                 {
14881                    tag : 'i',
14882                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14883                    tooltip : 'This field is required'
14884                 },
14885                 {
14886                     tag: 'label',
14887                     cls : 'control-label',
14888                     html : this.fieldLabel
14889
14890                 },
14891                 {
14892                     cls : '', 
14893                     cn: [
14894                         combobox
14895                     ]
14896                 }
14897             ];
14898             
14899             var labelCfg = cfg.cn[1];
14900             var contentCfg = cfg.cn[2];
14901             
14902
14903             if(this.indicatorpos == 'right'){
14904                 cfg.cn = [
14905                     {
14906                         tag: 'label',
14907                         'for' :  id,
14908                         cls : 'control-label',
14909                         cn : [
14910                             {
14911                                 tag : 'span',
14912                                 html : this.fieldLabel
14913                             },
14914                             {
14915                                 tag : 'i',
14916                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14917                                 tooltip : 'This field is required'
14918                             }
14919                         ]
14920                     },
14921                     {
14922                         cls : "",
14923                         cn: [
14924                             combobox
14925                         ]
14926                     }
14927
14928                 ];
14929                 
14930                 labelCfg = cfg.cn[0];
14931                 contentCfg = cfg.cn[1];
14932             }
14933             
14934            
14935             
14936             if(this.labelWidth > 12){
14937                 labelCfg.style = "width: " + this.labelWidth + 'px';
14938             }
14939             
14940             if(this.labelWidth < 13 && this.labelmd == 0){
14941                 this.labelmd = this.labelWidth;
14942             }
14943             
14944             if(this.labellg > 0){
14945                 labelCfg.cls += ' col-lg-' + this.labellg;
14946                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14947             }
14948             
14949             if(this.labelmd > 0){
14950                 labelCfg.cls += ' col-md-' + this.labelmd;
14951                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14952             }
14953             
14954             if(this.labelsm > 0){
14955                 labelCfg.cls += ' col-sm-' + this.labelsm;
14956                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14957             }
14958             
14959             if(this.labelxs > 0){
14960                 labelCfg.cls += ' col-xs-' + this.labelxs;
14961                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14962             }
14963                 
14964                 
14965         } else if ( this.fieldLabel.length) {
14966             cfg.cn = [
14967                 {
14968                    tag : 'i',
14969                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14970                    tooltip : 'This field is required'
14971                 },
14972                 {
14973                     tag: 'label',
14974                     cls : 'control-label',
14975                     html : this.fieldLabel
14976
14977                 },
14978                 {
14979                     cls : '', 
14980                     cn: [
14981                         combobox
14982                     ]
14983                 }
14984             ];
14985             
14986             if(this.indicatorpos == 'right'){
14987                 cfg.cn = [
14988                     {
14989                         tag: 'label',
14990                         cls : 'control-label',
14991                         html : this.fieldLabel,
14992                         cn : [
14993                             {
14994                                tag : 'i',
14995                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14996                                tooltip : 'This field is required'
14997                             }
14998                         ]
14999                     },
15000                     {
15001                         cls : '', 
15002                         cn: [
15003                             combobox
15004                         ]
15005                     }
15006                 ];
15007             }
15008         } else {
15009             cfg.cn = combobox;    
15010         }
15011         
15012         
15013         var settings = this;
15014         
15015         ['xs','sm','md','lg'].map(function(size){
15016             if (settings[size]) {
15017                 cfg.cls += ' col-' + size + '-' + settings[size];
15018             }
15019         });
15020         
15021         return cfg;
15022     },
15023     
15024     initTouchView : function()
15025     {
15026         this.renderTouchView();
15027         
15028         this.touchViewEl.on('scroll', function(){
15029             this.el.dom.scrollTop = 0;
15030         }, this);
15031         
15032         this.originalValue = this.getValue();
15033         
15034         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15035         
15036         this.inputEl().on("click", this.showTouchView, this);
15037         if (this.triggerEl) {
15038             this.triggerEl.on("click", this.showTouchView, this);
15039         }
15040         
15041         
15042         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15043         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15044         
15045         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15046         
15047         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15048         this.store.on('load', this.onTouchViewLoad, this);
15049         this.store.on('loadexception', this.onTouchViewLoadException, this);
15050         
15051         if(this.hiddenName){
15052             
15053             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15054             
15055             this.hiddenField.dom.value =
15056                 this.hiddenValue !== undefined ? this.hiddenValue :
15057                 this.value !== undefined ? this.value : '';
15058         
15059             this.el.dom.removeAttribute('name');
15060             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15061         }
15062         
15063         if(this.multiple){
15064             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15065             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15066         }
15067         
15068         if(this.removable && !this.multiple){
15069             var close = this.closeTriggerEl();
15070             if(close){
15071                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15072                 close.on('click', this.removeBtnClick, this, close);
15073             }
15074         }
15075         /*
15076          * fix the bug in Safari iOS8
15077          */
15078         this.inputEl().on("focus", function(e){
15079             document.activeElement.blur();
15080         }, this);
15081         
15082         return;
15083         
15084         
15085     },
15086     
15087     renderTouchView : function()
15088     {
15089         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15090         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15091         
15092         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15093         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15094         
15095         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15096         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15097         this.touchViewBodyEl.setStyle('overflow', 'auto');
15098         
15099         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15100         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15101         
15102         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15103         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15104         
15105     },
15106     
15107     showTouchView : function()
15108     {
15109         if(this.disabled){
15110             return;
15111         }
15112         
15113         this.touchViewHeaderEl.hide();
15114
15115         if(this.modalTitle.length){
15116             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15117             this.touchViewHeaderEl.show();
15118         }
15119
15120         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15121         this.touchViewEl.show();
15122
15123         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15124         
15125         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15126         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15127
15128         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15129
15130         if(this.modalTitle.length){
15131             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15132         }
15133         
15134         this.touchViewBodyEl.setHeight(bodyHeight);
15135
15136         if(this.animate){
15137             var _this = this;
15138             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15139         }else{
15140             this.touchViewEl.addClass('in');
15141         }
15142
15143         this.doTouchViewQuery();
15144         
15145     },
15146     
15147     hideTouchView : function()
15148     {
15149         this.touchViewEl.removeClass('in');
15150
15151         if(this.animate){
15152             var _this = this;
15153             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15154         }else{
15155             this.touchViewEl.setStyle('display', 'none');
15156         }
15157         
15158     },
15159     
15160     setTouchViewValue : function()
15161     {
15162         if(this.multiple){
15163             this.clearItem();
15164         
15165             var _this = this;
15166
15167             Roo.each(this.tickItems, function(o){
15168                 this.addItem(o);
15169             }, this);
15170         }
15171         
15172         this.hideTouchView();
15173     },
15174     
15175     doTouchViewQuery : function()
15176     {
15177         var qe = {
15178             query: '',
15179             forceAll: true,
15180             combo: this,
15181             cancel:false
15182         };
15183         
15184         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15185             return false;
15186         }
15187         
15188         if(!this.alwaysQuery || this.mode == 'local'){
15189             this.onTouchViewLoad();
15190             return;
15191         }
15192         
15193         this.store.load();
15194     },
15195     
15196     onTouchViewBeforeLoad : function(combo,opts)
15197     {
15198         return;
15199     },
15200
15201     // private
15202     onTouchViewLoad : function()
15203     {
15204         if(this.store.getCount() < 1){
15205             this.onTouchViewEmptyResults();
15206             return;
15207         }
15208         
15209         this.clearTouchView();
15210         
15211         var rawValue = this.getRawValue();
15212         
15213         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15214         
15215         this.tickItems = [];
15216         
15217         this.store.data.each(function(d, rowIndex){
15218             var row = this.touchViewListGroup.createChild(template);
15219             
15220             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15221                 row.addClass(d.data.cls);
15222             }
15223             
15224             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15225                 var cfg = {
15226                     data : d.data,
15227                     html : d.data[this.displayField]
15228                 };
15229                 
15230                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15231                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15232                 }
15233             }
15234             row.removeClass('selected');
15235             if(!this.multiple && this.valueField &&
15236                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15237             {
15238                 // radio buttons..
15239                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15240                 row.addClass('selected');
15241             }
15242             
15243             if(this.multiple && this.valueField &&
15244                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15245             {
15246                 
15247                 // checkboxes...
15248                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15249                 this.tickItems.push(d.data);
15250             }
15251             
15252             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15253             
15254         }, this);
15255         
15256         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15257         
15258         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15259
15260         if(this.modalTitle.length){
15261             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15262         }
15263
15264         var listHeight = this.touchViewListGroup.getHeight();
15265         
15266         var _this = this;
15267         
15268         if(firstChecked && listHeight > bodyHeight){
15269             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15270         }
15271         
15272     },
15273     
15274     onTouchViewLoadException : function()
15275     {
15276         this.hideTouchView();
15277     },
15278     
15279     onTouchViewEmptyResults : function()
15280     {
15281         this.clearTouchView();
15282         
15283         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15284         
15285         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15286         
15287     },
15288     
15289     clearTouchView : function()
15290     {
15291         this.touchViewListGroup.dom.innerHTML = '';
15292     },
15293     
15294     onTouchViewClick : function(e, el, o)
15295     {
15296         e.preventDefault();
15297         
15298         var row = o.row;
15299         var rowIndex = o.rowIndex;
15300         
15301         var r = this.store.getAt(rowIndex);
15302         
15303         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15304             
15305             if(!this.multiple){
15306                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15307                     c.dom.removeAttribute('checked');
15308                 }, this);
15309
15310                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15311
15312                 this.setFromData(r.data);
15313
15314                 var close = this.closeTriggerEl();
15315
15316                 if(close){
15317                     close.show();
15318                 }
15319
15320                 this.hideTouchView();
15321
15322                 this.fireEvent('select', this, r, rowIndex);
15323
15324                 return;
15325             }
15326
15327             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15328                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15329                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15330                 return;
15331             }
15332
15333             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15334             this.addItem(r.data);
15335             this.tickItems.push(r.data);
15336         }
15337     },
15338     
15339     getAutoCreateNativeIOS : function()
15340     {
15341         var cfg = {
15342             cls: 'form-group' //input-group,
15343         };
15344         
15345         var combobox =  {
15346             tag: 'select',
15347             cls : 'roo-ios-select'
15348         };
15349         
15350         if (this.name) {
15351             combobox.name = this.name;
15352         }
15353         
15354         if (this.disabled) {
15355             combobox.disabled = true;
15356         }
15357         
15358         var settings = this;
15359         
15360         ['xs','sm','md','lg'].map(function(size){
15361             if (settings[size]) {
15362                 cfg.cls += ' col-' + size + '-' + settings[size];
15363             }
15364         });
15365         
15366         cfg.cn = combobox;
15367         
15368         return cfg;
15369         
15370     },
15371     
15372     initIOSView : function()
15373     {
15374         this.store.on('load', this.onIOSViewLoad, this);
15375         
15376         return;
15377     },
15378     
15379     onIOSViewLoad : function()
15380     {
15381         if(this.store.getCount() < 1){
15382             return;
15383         }
15384         
15385         this.clearIOSView();
15386         
15387         if(this.allowBlank) {
15388             
15389             var default_text = '-- SELECT --';
15390             
15391             if(this.placeholder.length){
15392                 default_text = this.placeholder;
15393             }
15394             
15395             if(this.emptyTitle.length){
15396                 default_text += ' - ' + this.emptyTitle + ' -';
15397             }
15398             
15399             var opt = this.inputEl().createChild({
15400                 tag: 'option',
15401                 value : 0,
15402                 html : default_text
15403             });
15404             
15405             var o = {};
15406             o[this.valueField] = 0;
15407             o[this.displayField] = default_text;
15408             
15409             this.ios_options.push({
15410                 data : o,
15411                 el : opt
15412             });
15413             
15414         }
15415         
15416         this.store.data.each(function(d, rowIndex){
15417             
15418             var html = '';
15419             
15420             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15421                 html = d.data[this.displayField];
15422             }
15423             
15424             var value = '';
15425             
15426             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15427                 value = d.data[this.valueField];
15428             }
15429             
15430             var option = {
15431                 tag: 'option',
15432                 value : value,
15433                 html : html
15434             };
15435             
15436             if(this.value == d.data[this.valueField]){
15437                 option['selected'] = true;
15438             }
15439             
15440             var opt = this.inputEl().createChild(option);
15441             
15442             this.ios_options.push({
15443                 data : d.data,
15444                 el : opt
15445             });
15446             
15447         }, this);
15448         
15449         this.inputEl().on('change', function(){
15450            this.fireEvent('select', this);
15451         }, this);
15452         
15453     },
15454     
15455     clearIOSView: function()
15456     {
15457         this.inputEl().dom.innerHTML = '';
15458         
15459         this.ios_options = [];
15460     },
15461     
15462     setIOSValue: function(v)
15463     {
15464         this.value = v;
15465         
15466         if(!this.ios_options){
15467             return;
15468         }
15469         
15470         Roo.each(this.ios_options, function(opts){
15471            
15472            opts.el.dom.removeAttribute('selected');
15473            
15474            if(opts.data[this.valueField] != v){
15475                return;
15476            }
15477            
15478            opts.el.dom.setAttribute('selected', true);
15479            
15480         }, this);
15481     }
15482
15483     /** 
15484     * @cfg {Boolean} grow 
15485     * @hide 
15486     */
15487     /** 
15488     * @cfg {Number} growMin 
15489     * @hide 
15490     */
15491     /** 
15492     * @cfg {Number} growMax 
15493     * @hide 
15494     */
15495     /**
15496      * @hide
15497      * @method autoSize
15498      */
15499 });
15500
15501 Roo.apply(Roo.bootstrap.ComboBox,  {
15502     
15503     header : {
15504         tag: 'div',
15505         cls: 'modal-header',
15506         cn: [
15507             {
15508                 tag: 'h4',
15509                 cls: 'modal-title'
15510             }
15511         ]
15512     },
15513     
15514     body : {
15515         tag: 'div',
15516         cls: 'modal-body',
15517         cn: [
15518             {
15519                 tag: 'ul',
15520                 cls: 'list-group'
15521             }
15522         ]
15523     },
15524     
15525     listItemRadio : {
15526         tag: 'li',
15527         cls: 'list-group-item',
15528         cn: [
15529             {
15530                 tag: 'span',
15531                 cls: 'roo-combobox-list-group-item-value'
15532             },
15533             {
15534                 tag: 'div',
15535                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15536                 cn: [
15537                     {
15538                         tag: 'input',
15539                         type: 'radio'
15540                     },
15541                     {
15542                         tag: 'label'
15543                     }
15544                 ]
15545             }
15546         ]
15547     },
15548     
15549     listItemCheckbox : {
15550         tag: 'li',
15551         cls: 'list-group-item',
15552         cn: [
15553             {
15554                 tag: 'span',
15555                 cls: 'roo-combobox-list-group-item-value'
15556             },
15557             {
15558                 tag: 'div',
15559                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15560                 cn: [
15561                     {
15562                         tag: 'input',
15563                         type: 'checkbox'
15564                     },
15565                     {
15566                         tag: 'label'
15567                     }
15568                 ]
15569             }
15570         ]
15571     },
15572     
15573     emptyResult : {
15574         tag: 'div',
15575         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15576     },
15577     
15578     footer : {
15579         tag: 'div',
15580         cls: 'modal-footer',
15581         cn: [
15582             {
15583                 tag: 'div',
15584                 cls: 'row',
15585                 cn: [
15586                     {
15587                         tag: 'div',
15588                         cls: 'col-xs-6 text-left',
15589                         cn: {
15590                             tag: 'button',
15591                             cls: 'btn btn-danger roo-touch-view-cancel',
15592                             html: 'Cancel'
15593                         }
15594                     },
15595                     {
15596                         tag: 'div',
15597                         cls: 'col-xs-6 text-right',
15598                         cn: {
15599                             tag: 'button',
15600                             cls: 'btn btn-success roo-touch-view-ok',
15601                             html: 'OK'
15602                         }
15603                     }
15604                 ]
15605             }
15606         ]
15607         
15608     }
15609 });
15610
15611 Roo.apply(Roo.bootstrap.ComboBox,  {
15612     
15613     touchViewTemplate : {
15614         tag: 'div',
15615         cls: 'modal fade roo-combobox-touch-view',
15616         cn: [
15617             {
15618                 tag: 'div',
15619                 cls: 'modal-dialog',
15620                 style : 'position:fixed', // we have to fix position....
15621                 cn: [
15622                     {
15623                         tag: 'div',
15624                         cls: 'modal-content',
15625                         cn: [
15626                             Roo.bootstrap.ComboBox.header,
15627                             Roo.bootstrap.ComboBox.body,
15628                             Roo.bootstrap.ComboBox.footer
15629                         ]
15630                     }
15631                 ]
15632             }
15633         ]
15634     }
15635 });/*
15636  * Based on:
15637  * Ext JS Library 1.1.1
15638  * Copyright(c) 2006-2007, Ext JS, LLC.
15639  *
15640  * Originally Released Under LGPL - original licence link has changed is not relivant.
15641  *
15642  * Fork - LGPL
15643  * <script type="text/javascript">
15644  */
15645
15646 /**
15647  * @class Roo.View
15648  * @extends Roo.util.Observable
15649  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15650  * This class also supports single and multi selection modes. <br>
15651  * Create a data model bound view:
15652  <pre><code>
15653  var store = new Roo.data.Store(...);
15654
15655  var view = new Roo.View({
15656     el : "my-element",
15657     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15658  
15659     singleSelect: true,
15660     selectedClass: "ydataview-selected",
15661     store: store
15662  });
15663
15664  // listen for node click?
15665  view.on("click", function(vw, index, node, e){
15666  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15667  });
15668
15669  // load XML data
15670  dataModel.load("foobar.xml");
15671  </code></pre>
15672  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15673  * <br><br>
15674  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15675  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15676  * 
15677  * Note: old style constructor is still suported (container, template, config)
15678  * 
15679  * @constructor
15680  * Create a new View
15681  * @param {Object} config The config object
15682  * 
15683  */
15684 Roo.View = function(config, depreciated_tpl, depreciated_config){
15685     
15686     this.parent = false;
15687     
15688     if (typeof(depreciated_tpl) == 'undefined') {
15689         // new way.. - universal constructor.
15690         Roo.apply(this, config);
15691         this.el  = Roo.get(this.el);
15692     } else {
15693         // old format..
15694         this.el  = Roo.get(config);
15695         this.tpl = depreciated_tpl;
15696         Roo.apply(this, depreciated_config);
15697     }
15698     this.wrapEl  = this.el.wrap().wrap();
15699     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15700     
15701     
15702     if(typeof(this.tpl) == "string"){
15703         this.tpl = new Roo.Template(this.tpl);
15704     } else {
15705         // support xtype ctors..
15706         this.tpl = new Roo.factory(this.tpl, Roo);
15707     }
15708     
15709     
15710     this.tpl.compile();
15711     
15712     /** @private */
15713     this.addEvents({
15714         /**
15715          * @event beforeclick
15716          * Fires before a click is processed. Returns false to cancel the default action.
15717          * @param {Roo.View} this
15718          * @param {Number} index The index of the target node
15719          * @param {HTMLElement} node The target node
15720          * @param {Roo.EventObject} e The raw event object
15721          */
15722             "beforeclick" : true,
15723         /**
15724          * @event click
15725          * Fires when a template node is clicked.
15726          * @param {Roo.View} this
15727          * @param {Number} index The index of the target node
15728          * @param {HTMLElement} node The target node
15729          * @param {Roo.EventObject} e The raw event object
15730          */
15731             "click" : true,
15732         /**
15733          * @event dblclick
15734          * Fires when a template node is double clicked.
15735          * @param {Roo.View} this
15736          * @param {Number} index The index of the target node
15737          * @param {HTMLElement} node The target node
15738          * @param {Roo.EventObject} e The raw event object
15739          */
15740             "dblclick" : true,
15741         /**
15742          * @event contextmenu
15743          * Fires when a template node is right clicked.
15744          * @param {Roo.View} this
15745          * @param {Number} index The index of the target node
15746          * @param {HTMLElement} node The target node
15747          * @param {Roo.EventObject} e The raw event object
15748          */
15749             "contextmenu" : true,
15750         /**
15751          * @event selectionchange
15752          * Fires when the selected nodes change.
15753          * @param {Roo.View} this
15754          * @param {Array} selections Array of the selected nodes
15755          */
15756             "selectionchange" : true,
15757     
15758         /**
15759          * @event beforeselect
15760          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15761          * @param {Roo.View} this
15762          * @param {HTMLElement} node The node to be selected
15763          * @param {Array} selections Array of currently selected nodes
15764          */
15765             "beforeselect" : true,
15766         /**
15767          * @event preparedata
15768          * Fires on every row to render, to allow you to change the data.
15769          * @param {Roo.View} this
15770          * @param {Object} data to be rendered (change this)
15771          */
15772           "preparedata" : true
15773           
15774           
15775         });
15776
15777
15778
15779     this.el.on({
15780         "click": this.onClick,
15781         "dblclick": this.onDblClick,
15782         "contextmenu": this.onContextMenu,
15783         scope:this
15784     });
15785
15786     this.selections = [];
15787     this.nodes = [];
15788     this.cmp = new Roo.CompositeElementLite([]);
15789     if(this.store){
15790         this.store = Roo.factory(this.store, Roo.data);
15791         this.setStore(this.store, true);
15792     }
15793     
15794     if ( this.footer && this.footer.xtype) {
15795            
15796          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15797         
15798         this.footer.dataSource = this.store;
15799         this.footer.container = fctr;
15800         this.footer = Roo.factory(this.footer, Roo);
15801         fctr.insertFirst(this.el);
15802         
15803         // this is a bit insane - as the paging toolbar seems to detach the el..
15804 //        dom.parentNode.parentNode.parentNode
15805          // they get detached?
15806     }
15807     
15808     
15809     Roo.View.superclass.constructor.call(this);
15810     
15811     
15812 };
15813
15814 Roo.extend(Roo.View, Roo.util.Observable, {
15815     
15816      /**
15817      * @cfg {Roo.data.Store} store Data store to load data from.
15818      */
15819     store : false,
15820     
15821     /**
15822      * @cfg {String|Roo.Element} el The container element.
15823      */
15824     el : '',
15825     
15826     /**
15827      * @cfg {String|Roo.Template} tpl The template used by this View 
15828      */
15829     tpl : false,
15830     /**
15831      * @cfg {String} dataName the named area of the template to use as the data area
15832      *                          Works with domtemplates roo-name="name"
15833      */
15834     dataName: false,
15835     /**
15836      * @cfg {String} selectedClass The css class to add to selected nodes
15837      */
15838     selectedClass : "x-view-selected",
15839      /**
15840      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15841      */
15842     emptyText : "",
15843     
15844     /**
15845      * @cfg {String} text to display on mask (default Loading)
15846      */
15847     mask : false,
15848     /**
15849      * @cfg {Boolean} multiSelect Allow multiple selection
15850      */
15851     multiSelect : false,
15852     /**
15853      * @cfg {Boolean} singleSelect Allow single selection
15854      */
15855     singleSelect:  false,
15856     
15857     /**
15858      * @cfg {Boolean} toggleSelect - selecting 
15859      */
15860     toggleSelect : false,
15861     
15862     /**
15863      * @cfg {Boolean} tickable - selecting 
15864      */
15865     tickable : false,
15866     
15867     /**
15868      * Returns the element this view is bound to.
15869      * @return {Roo.Element}
15870      */
15871     getEl : function(){
15872         return this.wrapEl;
15873     },
15874     
15875     
15876
15877     /**
15878      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15879      */
15880     refresh : function(){
15881         //Roo.log('refresh');
15882         var t = this.tpl;
15883         
15884         // if we are using something like 'domtemplate', then
15885         // the what gets used is:
15886         // t.applySubtemplate(NAME, data, wrapping data..)
15887         // the outer template then get' applied with
15888         //     the store 'extra data'
15889         // and the body get's added to the
15890         //      roo-name="data" node?
15891         //      <span class='roo-tpl-{name}'></span> ?????
15892         
15893         
15894         
15895         this.clearSelections();
15896         this.el.update("");
15897         var html = [];
15898         var records = this.store.getRange();
15899         if(records.length < 1) {
15900             
15901             // is this valid??  = should it render a template??
15902             
15903             this.el.update(this.emptyText);
15904             return;
15905         }
15906         var el = this.el;
15907         if (this.dataName) {
15908             this.el.update(t.apply(this.store.meta)); //????
15909             el = this.el.child('.roo-tpl-' + this.dataName);
15910         }
15911         
15912         for(var i = 0, len = records.length; i < len; i++){
15913             var data = this.prepareData(records[i].data, i, records[i]);
15914             this.fireEvent("preparedata", this, data, i, records[i]);
15915             
15916             var d = Roo.apply({}, data);
15917             
15918             if(this.tickable){
15919                 Roo.apply(d, {'roo-id' : Roo.id()});
15920                 
15921                 var _this = this;
15922             
15923                 Roo.each(this.parent.item, function(item){
15924                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15925                         return;
15926                     }
15927                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15928                 });
15929             }
15930             
15931             html[html.length] = Roo.util.Format.trim(
15932                 this.dataName ?
15933                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15934                     t.apply(d)
15935             );
15936         }
15937         
15938         
15939         
15940         el.update(html.join(""));
15941         this.nodes = el.dom.childNodes;
15942         this.updateIndexes(0);
15943     },
15944     
15945
15946     /**
15947      * Function to override to reformat the data that is sent to
15948      * the template for each node.
15949      * DEPRICATED - use the preparedata event handler.
15950      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15951      * a JSON object for an UpdateManager bound view).
15952      */
15953     prepareData : function(data, index, record)
15954     {
15955         this.fireEvent("preparedata", this, data, index, record);
15956         return data;
15957     },
15958
15959     onUpdate : function(ds, record){
15960         // Roo.log('on update');   
15961         this.clearSelections();
15962         var index = this.store.indexOf(record);
15963         var n = this.nodes[index];
15964         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15965         n.parentNode.removeChild(n);
15966         this.updateIndexes(index, index);
15967     },
15968
15969     
15970     
15971 // --------- FIXME     
15972     onAdd : function(ds, records, index)
15973     {
15974         //Roo.log(['on Add', ds, records, index] );        
15975         this.clearSelections();
15976         if(this.nodes.length == 0){
15977             this.refresh();
15978             return;
15979         }
15980         var n = this.nodes[index];
15981         for(var i = 0, len = records.length; i < len; i++){
15982             var d = this.prepareData(records[i].data, i, records[i]);
15983             if(n){
15984                 this.tpl.insertBefore(n, d);
15985             }else{
15986                 
15987                 this.tpl.append(this.el, d);
15988             }
15989         }
15990         this.updateIndexes(index);
15991     },
15992
15993     onRemove : function(ds, record, index){
15994        // Roo.log('onRemove');
15995         this.clearSelections();
15996         var el = this.dataName  ?
15997             this.el.child('.roo-tpl-' + this.dataName) :
15998             this.el; 
15999         
16000         el.dom.removeChild(this.nodes[index]);
16001         this.updateIndexes(index);
16002     },
16003
16004     /**
16005      * Refresh an individual node.
16006      * @param {Number} index
16007      */
16008     refreshNode : function(index){
16009         this.onUpdate(this.store, this.store.getAt(index));
16010     },
16011
16012     updateIndexes : function(startIndex, endIndex){
16013         var ns = this.nodes;
16014         startIndex = startIndex || 0;
16015         endIndex = endIndex || ns.length - 1;
16016         for(var i = startIndex; i <= endIndex; i++){
16017             ns[i].nodeIndex = i;
16018         }
16019     },
16020
16021     /**
16022      * Changes the data store this view uses and refresh the view.
16023      * @param {Store} store
16024      */
16025     setStore : function(store, initial){
16026         if(!initial && this.store){
16027             this.store.un("datachanged", this.refresh);
16028             this.store.un("add", this.onAdd);
16029             this.store.un("remove", this.onRemove);
16030             this.store.un("update", this.onUpdate);
16031             this.store.un("clear", this.refresh);
16032             this.store.un("beforeload", this.onBeforeLoad);
16033             this.store.un("load", this.onLoad);
16034             this.store.un("loadexception", this.onLoad);
16035         }
16036         if(store){
16037           
16038             store.on("datachanged", this.refresh, this);
16039             store.on("add", this.onAdd, this);
16040             store.on("remove", this.onRemove, this);
16041             store.on("update", this.onUpdate, this);
16042             store.on("clear", this.refresh, this);
16043             store.on("beforeload", this.onBeforeLoad, this);
16044             store.on("load", this.onLoad, this);
16045             store.on("loadexception", this.onLoad, this);
16046         }
16047         
16048         if(store){
16049             this.refresh();
16050         }
16051     },
16052     /**
16053      * onbeforeLoad - masks the loading area.
16054      *
16055      */
16056     onBeforeLoad : function(store,opts)
16057     {
16058          //Roo.log('onBeforeLoad');   
16059         if (!opts.add) {
16060             this.el.update("");
16061         }
16062         this.el.mask(this.mask ? this.mask : "Loading" ); 
16063     },
16064     onLoad : function ()
16065     {
16066         this.el.unmask();
16067     },
16068     
16069
16070     /**
16071      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16072      * @param {HTMLElement} node
16073      * @return {HTMLElement} The template node
16074      */
16075     findItemFromChild : function(node){
16076         var el = this.dataName  ?
16077             this.el.child('.roo-tpl-' + this.dataName,true) :
16078             this.el.dom; 
16079         
16080         if(!node || node.parentNode == el){
16081                     return node;
16082             }
16083             var p = node.parentNode;
16084             while(p && p != el){
16085             if(p.parentNode == el){
16086                 return p;
16087             }
16088             p = p.parentNode;
16089         }
16090             return null;
16091     },
16092
16093     /** @ignore */
16094     onClick : function(e){
16095         var item = this.findItemFromChild(e.getTarget());
16096         if(item){
16097             var index = this.indexOf(item);
16098             if(this.onItemClick(item, index, e) !== false){
16099                 this.fireEvent("click", this, index, item, e);
16100             }
16101         }else{
16102             this.clearSelections();
16103         }
16104     },
16105
16106     /** @ignore */
16107     onContextMenu : function(e){
16108         var item = this.findItemFromChild(e.getTarget());
16109         if(item){
16110             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16111         }
16112     },
16113
16114     /** @ignore */
16115     onDblClick : function(e){
16116         var item = this.findItemFromChild(e.getTarget());
16117         if(item){
16118             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16119         }
16120     },
16121
16122     onItemClick : function(item, index, e)
16123     {
16124         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16125             return false;
16126         }
16127         if (this.toggleSelect) {
16128             var m = this.isSelected(item) ? 'unselect' : 'select';
16129             //Roo.log(m);
16130             var _t = this;
16131             _t[m](item, true, false);
16132             return true;
16133         }
16134         if(this.multiSelect || this.singleSelect){
16135             if(this.multiSelect && e.shiftKey && this.lastSelection){
16136                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16137             }else{
16138                 this.select(item, this.multiSelect && e.ctrlKey);
16139                 this.lastSelection = item;
16140             }
16141             
16142             if(!this.tickable){
16143                 e.preventDefault();
16144             }
16145             
16146         }
16147         return true;
16148     },
16149
16150     /**
16151      * Get the number of selected nodes.
16152      * @return {Number}
16153      */
16154     getSelectionCount : function(){
16155         return this.selections.length;
16156     },
16157
16158     /**
16159      * Get the currently selected nodes.
16160      * @return {Array} An array of HTMLElements
16161      */
16162     getSelectedNodes : function(){
16163         return this.selections;
16164     },
16165
16166     /**
16167      * Get the indexes of the selected nodes.
16168      * @return {Array}
16169      */
16170     getSelectedIndexes : function(){
16171         var indexes = [], s = this.selections;
16172         for(var i = 0, len = s.length; i < len; i++){
16173             indexes.push(s[i].nodeIndex);
16174         }
16175         return indexes;
16176     },
16177
16178     /**
16179      * Clear all selections
16180      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16181      */
16182     clearSelections : function(suppressEvent){
16183         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16184             this.cmp.elements = this.selections;
16185             this.cmp.removeClass(this.selectedClass);
16186             this.selections = [];
16187             if(!suppressEvent){
16188                 this.fireEvent("selectionchange", this, this.selections);
16189             }
16190         }
16191     },
16192
16193     /**
16194      * Returns true if the passed node is selected
16195      * @param {HTMLElement/Number} node The node or node index
16196      * @return {Boolean}
16197      */
16198     isSelected : function(node){
16199         var s = this.selections;
16200         if(s.length < 1){
16201             return false;
16202         }
16203         node = this.getNode(node);
16204         return s.indexOf(node) !== -1;
16205     },
16206
16207     /**
16208      * Selects nodes.
16209      * @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
16210      * @param {Boolean} keepExisting (optional) true to keep existing selections
16211      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16212      */
16213     select : function(nodeInfo, keepExisting, suppressEvent){
16214         if(nodeInfo instanceof Array){
16215             if(!keepExisting){
16216                 this.clearSelections(true);
16217             }
16218             for(var i = 0, len = nodeInfo.length; i < len; i++){
16219                 this.select(nodeInfo[i], true, true);
16220             }
16221             return;
16222         } 
16223         var node = this.getNode(nodeInfo);
16224         if(!node || this.isSelected(node)){
16225             return; // already selected.
16226         }
16227         if(!keepExisting){
16228             this.clearSelections(true);
16229         }
16230         
16231         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16232             Roo.fly(node).addClass(this.selectedClass);
16233             this.selections.push(node);
16234             if(!suppressEvent){
16235                 this.fireEvent("selectionchange", this, this.selections);
16236             }
16237         }
16238         
16239         
16240     },
16241       /**
16242      * Unselects nodes.
16243      * @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
16244      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16245      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16246      */
16247     unselect : function(nodeInfo, keepExisting, suppressEvent)
16248     {
16249         if(nodeInfo instanceof Array){
16250             Roo.each(this.selections, function(s) {
16251                 this.unselect(s, nodeInfo);
16252             }, this);
16253             return;
16254         }
16255         var node = this.getNode(nodeInfo);
16256         if(!node || !this.isSelected(node)){
16257             //Roo.log("not selected");
16258             return; // not selected.
16259         }
16260         // fireevent???
16261         var ns = [];
16262         Roo.each(this.selections, function(s) {
16263             if (s == node ) {
16264                 Roo.fly(node).removeClass(this.selectedClass);
16265
16266                 return;
16267             }
16268             ns.push(s);
16269         },this);
16270         
16271         this.selections= ns;
16272         this.fireEvent("selectionchange", this, this.selections);
16273     },
16274
16275     /**
16276      * Gets a template node.
16277      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16278      * @return {HTMLElement} The node or null if it wasn't found
16279      */
16280     getNode : function(nodeInfo){
16281         if(typeof nodeInfo == "string"){
16282             return document.getElementById(nodeInfo);
16283         }else if(typeof nodeInfo == "number"){
16284             return this.nodes[nodeInfo];
16285         }
16286         return nodeInfo;
16287     },
16288
16289     /**
16290      * Gets a range template nodes.
16291      * @param {Number} startIndex
16292      * @param {Number} endIndex
16293      * @return {Array} An array of nodes
16294      */
16295     getNodes : function(start, end){
16296         var ns = this.nodes;
16297         start = start || 0;
16298         end = typeof end == "undefined" ? ns.length - 1 : end;
16299         var nodes = [];
16300         if(start <= end){
16301             for(var i = start; i <= end; i++){
16302                 nodes.push(ns[i]);
16303             }
16304         } else{
16305             for(var i = start; i >= end; i--){
16306                 nodes.push(ns[i]);
16307             }
16308         }
16309         return nodes;
16310     },
16311
16312     /**
16313      * Finds the index of the passed node
16314      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16315      * @return {Number} The index of the node or -1
16316      */
16317     indexOf : function(node){
16318         node = this.getNode(node);
16319         if(typeof node.nodeIndex == "number"){
16320             return node.nodeIndex;
16321         }
16322         var ns = this.nodes;
16323         for(var i = 0, len = ns.length; i < len; i++){
16324             if(ns[i] == node){
16325                 return i;
16326             }
16327         }
16328         return -1;
16329     }
16330 });
16331 /*
16332  * - LGPL
16333  *
16334  * based on jquery fullcalendar
16335  * 
16336  */
16337
16338 Roo.bootstrap = Roo.bootstrap || {};
16339 /**
16340  * @class Roo.bootstrap.Calendar
16341  * @extends Roo.bootstrap.Component
16342  * Bootstrap Calendar class
16343  * @cfg {Boolean} loadMask (true|false) default false
16344  * @cfg {Object} header generate the user specific header of the calendar, default false
16345
16346  * @constructor
16347  * Create a new Container
16348  * @param {Object} config The config object
16349  */
16350
16351
16352
16353 Roo.bootstrap.Calendar = function(config){
16354     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16355      this.addEvents({
16356         /**
16357              * @event select
16358              * Fires when a date is selected
16359              * @param {DatePicker} this
16360              * @param {Date} date The selected date
16361              */
16362         'select': true,
16363         /**
16364              * @event monthchange
16365              * Fires when the displayed month changes 
16366              * @param {DatePicker} this
16367              * @param {Date} date The selected month
16368              */
16369         'monthchange': true,
16370         /**
16371              * @event evententer
16372              * Fires when mouse over an event
16373              * @param {Calendar} this
16374              * @param {event} Event
16375              */
16376         'evententer': true,
16377         /**
16378              * @event eventleave
16379              * Fires when the mouse leaves an
16380              * @param {Calendar} this
16381              * @param {event}
16382              */
16383         'eventleave': true,
16384         /**
16385              * @event eventclick
16386              * Fires when the mouse click an
16387              * @param {Calendar} this
16388              * @param {event}
16389              */
16390         'eventclick': true
16391         
16392     });
16393
16394 };
16395
16396 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16397     
16398      /**
16399      * @cfg {Number} startDay
16400      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16401      */
16402     startDay : 0,
16403     
16404     loadMask : false,
16405     
16406     header : false,
16407       
16408     getAutoCreate : function(){
16409         
16410         
16411         var fc_button = function(name, corner, style, content ) {
16412             return Roo.apply({},{
16413                 tag : 'span',
16414                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16415                          (corner.length ?
16416                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16417                             ''
16418                         ),
16419                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16420                 unselectable: 'on'
16421             });
16422         };
16423         
16424         var header = {};
16425         
16426         if(!this.header){
16427             header = {
16428                 tag : 'table',
16429                 cls : 'fc-header',
16430                 style : 'width:100%',
16431                 cn : [
16432                     {
16433                         tag: 'tr',
16434                         cn : [
16435                             {
16436                                 tag : 'td',
16437                                 cls : 'fc-header-left',
16438                                 cn : [
16439                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16440                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16441                                     { tag: 'span', cls: 'fc-header-space' },
16442                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16443
16444
16445                                 ]
16446                             },
16447
16448                             {
16449                                 tag : 'td',
16450                                 cls : 'fc-header-center',
16451                                 cn : [
16452                                     {
16453                                         tag: 'span',
16454                                         cls: 'fc-header-title',
16455                                         cn : {
16456                                             tag: 'H2',
16457                                             html : 'month / year'
16458                                         }
16459                                     }
16460
16461                                 ]
16462                             },
16463                             {
16464                                 tag : 'td',
16465                                 cls : 'fc-header-right',
16466                                 cn : [
16467                               /*      fc_button('month', 'left', '', 'month' ),
16468                                     fc_button('week', '', '', 'week' ),
16469                                     fc_button('day', 'right', '', 'day' )
16470                                 */    
16471
16472                                 ]
16473                             }
16474
16475                         ]
16476                     }
16477                 ]
16478             };
16479         }
16480         
16481         header = this.header;
16482         
16483        
16484         var cal_heads = function() {
16485             var ret = [];
16486             // fixme - handle this.
16487             
16488             for (var i =0; i < Date.dayNames.length; i++) {
16489                 var d = Date.dayNames[i];
16490                 ret.push({
16491                     tag: 'th',
16492                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16493                     html : d.substring(0,3)
16494                 });
16495                 
16496             }
16497             ret[0].cls += ' fc-first';
16498             ret[6].cls += ' fc-last';
16499             return ret;
16500         };
16501         var cal_cell = function(n) {
16502             return  {
16503                 tag: 'td',
16504                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16505                 cn : [
16506                     {
16507                         cn : [
16508                             {
16509                                 cls: 'fc-day-number',
16510                                 html: 'D'
16511                             },
16512                             {
16513                                 cls: 'fc-day-content',
16514                              
16515                                 cn : [
16516                                      {
16517                                         style: 'position: relative;' // height: 17px;
16518                                     }
16519                                 ]
16520                             }
16521                             
16522                             
16523                         ]
16524                     }
16525                 ]
16526                 
16527             }
16528         };
16529         var cal_rows = function() {
16530             
16531             var ret = [];
16532             for (var r = 0; r < 6; r++) {
16533                 var row= {
16534                     tag : 'tr',
16535                     cls : 'fc-week',
16536                     cn : []
16537                 };
16538                 
16539                 for (var i =0; i < Date.dayNames.length; i++) {
16540                     var d = Date.dayNames[i];
16541                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16542
16543                 }
16544                 row.cn[0].cls+=' fc-first';
16545                 row.cn[0].cn[0].style = 'min-height:90px';
16546                 row.cn[6].cls+=' fc-last';
16547                 ret.push(row);
16548                 
16549             }
16550             ret[0].cls += ' fc-first';
16551             ret[4].cls += ' fc-prev-last';
16552             ret[5].cls += ' fc-last';
16553             return ret;
16554             
16555         };
16556         
16557         var cal_table = {
16558             tag: 'table',
16559             cls: 'fc-border-separate',
16560             style : 'width:100%',
16561             cellspacing  : 0,
16562             cn : [
16563                 { 
16564                     tag: 'thead',
16565                     cn : [
16566                         { 
16567                             tag: 'tr',
16568                             cls : 'fc-first fc-last',
16569                             cn : cal_heads()
16570                         }
16571                     ]
16572                 },
16573                 { 
16574                     tag: 'tbody',
16575                     cn : cal_rows()
16576                 }
16577                   
16578             ]
16579         };
16580          
16581          var cfg = {
16582             cls : 'fc fc-ltr',
16583             cn : [
16584                 header,
16585                 {
16586                     cls : 'fc-content',
16587                     style : "position: relative;",
16588                     cn : [
16589                         {
16590                             cls : 'fc-view fc-view-month fc-grid',
16591                             style : 'position: relative',
16592                             unselectable : 'on',
16593                             cn : [
16594                                 {
16595                                     cls : 'fc-event-container',
16596                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16597                                 },
16598                                 cal_table
16599                             ]
16600                         }
16601                     ]
16602     
16603                 }
16604            ] 
16605             
16606         };
16607         
16608          
16609         
16610         return cfg;
16611     },
16612     
16613     
16614     initEvents : function()
16615     {
16616         if(!this.store){
16617             throw "can not find store for calendar";
16618         }
16619         
16620         var mark = {
16621             tag: "div",
16622             cls:"x-dlg-mask",
16623             style: "text-align:center",
16624             cn: [
16625                 {
16626                     tag: "div",
16627                     style: "background-color:white;width:50%;margin:250 auto",
16628                     cn: [
16629                         {
16630                             tag: "img",
16631                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16632                         },
16633                         {
16634                             tag: "span",
16635                             html: "Loading"
16636                         }
16637                         
16638                     ]
16639                 }
16640             ]
16641         };
16642         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16643         
16644         var size = this.el.select('.fc-content', true).first().getSize();
16645         this.maskEl.setSize(size.width, size.height);
16646         this.maskEl.enableDisplayMode("block");
16647         if(!this.loadMask){
16648             this.maskEl.hide();
16649         }
16650         
16651         this.store = Roo.factory(this.store, Roo.data);
16652         this.store.on('load', this.onLoad, this);
16653         this.store.on('beforeload', this.onBeforeLoad, this);
16654         
16655         this.resize();
16656         
16657         this.cells = this.el.select('.fc-day',true);
16658         //Roo.log(this.cells);
16659         this.textNodes = this.el.query('.fc-day-number');
16660         this.cells.addClassOnOver('fc-state-hover');
16661         
16662         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16663         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16664         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16665         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16666         
16667         this.on('monthchange', this.onMonthChange, this);
16668         
16669         this.update(new Date().clearTime());
16670     },
16671     
16672     resize : function() {
16673         var sz  = this.el.getSize();
16674         
16675         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16676         this.el.select('.fc-day-content div',true).setHeight(34);
16677     },
16678     
16679     
16680     // private
16681     showPrevMonth : function(e){
16682         this.update(this.activeDate.add("mo", -1));
16683     },
16684     showToday : function(e){
16685         this.update(new Date().clearTime());
16686     },
16687     // private
16688     showNextMonth : function(e){
16689         this.update(this.activeDate.add("mo", 1));
16690     },
16691
16692     // private
16693     showPrevYear : function(){
16694         this.update(this.activeDate.add("y", -1));
16695     },
16696
16697     // private
16698     showNextYear : function(){
16699         this.update(this.activeDate.add("y", 1));
16700     },
16701
16702     
16703    // private
16704     update : function(date)
16705     {
16706         var vd = this.activeDate;
16707         this.activeDate = date;
16708 //        if(vd && this.el){
16709 //            var t = date.getTime();
16710 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16711 //                Roo.log('using add remove');
16712 //                
16713 //                this.fireEvent('monthchange', this, date);
16714 //                
16715 //                this.cells.removeClass("fc-state-highlight");
16716 //                this.cells.each(function(c){
16717 //                   if(c.dateValue == t){
16718 //                       c.addClass("fc-state-highlight");
16719 //                       setTimeout(function(){
16720 //                            try{c.dom.firstChild.focus();}catch(e){}
16721 //                       }, 50);
16722 //                       return false;
16723 //                   }
16724 //                   return true;
16725 //                });
16726 //                return;
16727 //            }
16728 //        }
16729         
16730         var days = date.getDaysInMonth();
16731         
16732         var firstOfMonth = date.getFirstDateOfMonth();
16733         var startingPos = firstOfMonth.getDay()-this.startDay;
16734         
16735         if(startingPos < this.startDay){
16736             startingPos += 7;
16737         }
16738         
16739         var pm = date.add(Date.MONTH, -1);
16740         var prevStart = pm.getDaysInMonth()-startingPos;
16741 //        
16742         this.cells = this.el.select('.fc-day',true);
16743         this.textNodes = this.el.query('.fc-day-number');
16744         this.cells.addClassOnOver('fc-state-hover');
16745         
16746         var cells = this.cells.elements;
16747         var textEls = this.textNodes;
16748         
16749         Roo.each(cells, function(cell){
16750             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16751         });
16752         
16753         days += startingPos;
16754
16755         // convert everything to numbers so it's fast
16756         var day = 86400000;
16757         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16758         //Roo.log(d);
16759         //Roo.log(pm);
16760         //Roo.log(prevStart);
16761         
16762         var today = new Date().clearTime().getTime();
16763         var sel = date.clearTime().getTime();
16764         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16765         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16766         var ddMatch = this.disabledDatesRE;
16767         var ddText = this.disabledDatesText;
16768         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16769         var ddaysText = this.disabledDaysText;
16770         var format = this.format;
16771         
16772         var setCellClass = function(cal, cell){
16773             cell.row = 0;
16774             cell.events = [];
16775             cell.more = [];
16776             //Roo.log('set Cell Class');
16777             cell.title = "";
16778             var t = d.getTime();
16779             
16780             //Roo.log(d);
16781             
16782             cell.dateValue = t;
16783             if(t == today){
16784                 cell.className += " fc-today";
16785                 cell.className += " fc-state-highlight";
16786                 cell.title = cal.todayText;
16787             }
16788             if(t == sel){
16789                 // disable highlight in other month..
16790                 //cell.className += " fc-state-highlight";
16791                 
16792             }
16793             // disabling
16794             if(t < min) {
16795                 cell.className = " fc-state-disabled";
16796                 cell.title = cal.minText;
16797                 return;
16798             }
16799             if(t > max) {
16800                 cell.className = " fc-state-disabled";
16801                 cell.title = cal.maxText;
16802                 return;
16803             }
16804             if(ddays){
16805                 if(ddays.indexOf(d.getDay()) != -1){
16806                     cell.title = ddaysText;
16807                     cell.className = " fc-state-disabled";
16808                 }
16809             }
16810             if(ddMatch && format){
16811                 var fvalue = d.dateFormat(format);
16812                 if(ddMatch.test(fvalue)){
16813                     cell.title = ddText.replace("%0", fvalue);
16814                     cell.className = " fc-state-disabled";
16815                 }
16816             }
16817             
16818             if (!cell.initialClassName) {
16819                 cell.initialClassName = cell.dom.className;
16820             }
16821             
16822             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16823         };
16824
16825         var i = 0;
16826         
16827         for(; i < startingPos; i++) {
16828             textEls[i].innerHTML = (++prevStart);
16829             d.setDate(d.getDate()+1);
16830             
16831             cells[i].className = "fc-past fc-other-month";
16832             setCellClass(this, cells[i]);
16833         }
16834         
16835         var intDay = 0;
16836         
16837         for(; i < days; i++){
16838             intDay = i - startingPos + 1;
16839             textEls[i].innerHTML = (intDay);
16840             d.setDate(d.getDate()+1);
16841             
16842             cells[i].className = ''; // "x-date-active";
16843             setCellClass(this, cells[i]);
16844         }
16845         var extraDays = 0;
16846         
16847         for(; i < 42; i++) {
16848             textEls[i].innerHTML = (++extraDays);
16849             d.setDate(d.getDate()+1);
16850             
16851             cells[i].className = "fc-future fc-other-month";
16852             setCellClass(this, cells[i]);
16853         }
16854         
16855         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16856         
16857         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16858         
16859         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16860         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16861         
16862         if(totalRows != 6){
16863             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16864             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16865         }
16866         
16867         this.fireEvent('monthchange', this, date);
16868         
16869         
16870         /*
16871         if(!this.internalRender){
16872             var main = this.el.dom.firstChild;
16873             var w = main.offsetWidth;
16874             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16875             Roo.fly(main).setWidth(w);
16876             this.internalRender = true;
16877             // opera does not respect the auto grow header center column
16878             // then, after it gets a width opera refuses to recalculate
16879             // without a second pass
16880             if(Roo.isOpera && !this.secondPass){
16881                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16882                 this.secondPass = true;
16883                 this.update.defer(10, this, [date]);
16884             }
16885         }
16886         */
16887         
16888     },
16889     
16890     findCell : function(dt) {
16891         dt = dt.clearTime().getTime();
16892         var ret = false;
16893         this.cells.each(function(c){
16894             //Roo.log("check " +c.dateValue + '?=' + dt);
16895             if(c.dateValue == dt){
16896                 ret = c;
16897                 return false;
16898             }
16899             return true;
16900         });
16901         
16902         return ret;
16903     },
16904     
16905     findCells : function(ev) {
16906         var s = ev.start.clone().clearTime().getTime();
16907        // Roo.log(s);
16908         var e= ev.end.clone().clearTime().getTime();
16909        // Roo.log(e);
16910         var ret = [];
16911         this.cells.each(function(c){
16912              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16913             
16914             if(c.dateValue > e){
16915                 return ;
16916             }
16917             if(c.dateValue < s){
16918                 return ;
16919             }
16920             ret.push(c);
16921         });
16922         
16923         return ret;    
16924     },
16925     
16926 //    findBestRow: function(cells)
16927 //    {
16928 //        var ret = 0;
16929 //        
16930 //        for (var i =0 ; i < cells.length;i++) {
16931 //            ret  = Math.max(cells[i].rows || 0,ret);
16932 //        }
16933 //        return ret;
16934 //        
16935 //    },
16936     
16937     
16938     addItem : function(ev)
16939     {
16940         // look for vertical location slot in
16941         var cells = this.findCells(ev);
16942         
16943 //        ev.row = this.findBestRow(cells);
16944         
16945         // work out the location.
16946         
16947         var crow = false;
16948         var rows = [];
16949         for(var i =0; i < cells.length; i++) {
16950             
16951             cells[i].row = cells[0].row;
16952             
16953             if(i == 0){
16954                 cells[i].row = cells[i].row + 1;
16955             }
16956             
16957             if (!crow) {
16958                 crow = {
16959                     start : cells[i],
16960                     end :  cells[i]
16961                 };
16962                 continue;
16963             }
16964             if (crow.start.getY() == cells[i].getY()) {
16965                 // on same row.
16966                 crow.end = cells[i];
16967                 continue;
16968             }
16969             // different row.
16970             rows.push(crow);
16971             crow = {
16972                 start: cells[i],
16973                 end : cells[i]
16974             };
16975             
16976         }
16977         
16978         rows.push(crow);
16979         ev.els = [];
16980         ev.rows = rows;
16981         ev.cells = cells;
16982         
16983         cells[0].events.push(ev);
16984         
16985         this.calevents.push(ev);
16986     },
16987     
16988     clearEvents: function() {
16989         
16990         if(!this.calevents){
16991             return;
16992         }
16993         
16994         Roo.each(this.cells.elements, function(c){
16995             c.row = 0;
16996             c.events = [];
16997             c.more = [];
16998         });
16999         
17000         Roo.each(this.calevents, function(e) {
17001             Roo.each(e.els, function(el) {
17002                 el.un('mouseenter' ,this.onEventEnter, this);
17003                 el.un('mouseleave' ,this.onEventLeave, this);
17004                 el.remove();
17005             },this);
17006         },this);
17007         
17008         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17009             e.remove();
17010         });
17011         
17012     },
17013     
17014     renderEvents: function()
17015     {   
17016         var _this = this;
17017         
17018         this.cells.each(function(c) {
17019             
17020             if(c.row < 5){
17021                 return;
17022             }
17023             
17024             var ev = c.events;
17025             
17026             var r = 4;
17027             if(c.row != c.events.length){
17028                 r = 4 - (4 - (c.row - c.events.length));
17029             }
17030             
17031             c.events = ev.slice(0, r);
17032             c.more = ev.slice(r);
17033             
17034             if(c.more.length && c.more.length == 1){
17035                 c.events.push(c.more.pop());
17036             }
17037             
17038             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17039             
17040         });
17041             
17042         this.cells.each(function(c) {
17043             
17044             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17045             
17046             
17047             for (var e = 0; e < c.events.length; e++){
17048                 var ev = c.events[e];
17049                 var rows = ev.rows;
17050                 
17051                 for(var i = 0; i < rows.length; i++) {
17052                 
17053                     // how many rows should it span..
17054
17055                     var  cfg = {
17056                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17057                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17058
17059                         unselectable : "on",
17060                         cn : [
17061                             {
17062                                 cls: 'fc-event-inner',
17063                                 cn : [
17064     //                                {
17065     //                                  tag:'span',
17066     //                                  cls: 'fc-event-time',
17067     //                                  html : cells.length > 1 ? '' : ev.time
17068     //                                },
17069                                     {
17070                                       tag:'span',
17071                                       cls: 'fc-event-title',
17072                                       html : String.format('{0}', ev.title)
17073                                     }
17074
17075
17076                                 ]
17077                             },
17078                             {
17079                                 cls: 'ui-resizable-handle ui-resizable-e',
17080                                 html : '&nbsp;&nbsp;&nbsp'
17081                             }
17082
17083                         ]
17084                     };
17085
17086                     if (i == 0) {
17087                         cfg.cls += ' fc-event-start';
17088                     }
17089                     if ((i+1) == rows.length) {
17090                         cfg.cls += ' fc-event-end';
17091                     }
17092
17093                     var ctr = _this.el.select('.fc-event-container',true).first();
17094                     var cg = ctr.createChild(cfg);
17095
17096                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17097                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17098
17099                     var r = (c.more.length) ? 1 : 0;
17100                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17101                     cg.setWidth(ebox.right - sbox.x -2);
17102
17103                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17104                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17105                     cg.on('click', _this.onEventClick, _this, ev);
17106
17107                     ev.els.push(cg);
17108                     
17109                 }
17110                 
17111             }
17112             
17113             
17114             if(c.more.length){
17115                 var  cfg = {
17116                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17117                     style : 'position: absolute',
17118                     unselectable : "on",
17119                     cn : [
17120                         {
17121                             cls: 'fc-event-inner',
17122                             cn : [
17123                                 {
17124                                   tag:'span',
17125                                   cls: 'fc-event-title',
17126                                   html : 'More'
17127                                 }
17128
17129
17130                             ]
17131                         },
17132                         {
17133                             cls: 'ui-resizable-handle ui-resizable-e',
17134                             html : '&nbsp;&nbsp;&nbsp'
17135                         }
17136
17137                     ]
17138                 };
17139
17140                 var ctr = _this.el.select('.fc-event-container',true).first();
17141                 var cg = ctr.createChild(cfg);
17142
17143                 var sbox = c.select('.fc-day-content',true).first().getBox();
17144                 var ebox = c.select('.fc-day-content',true).first().getBox();
17145                 //Roo.log(cg);
17146                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17147                 cg.setWidth(ebox.right - sbox.x -2);
17148
17149                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17150                 
17151             }
17152             
17153         });
17154         
17155         
17156         
17157     },
17158     
17159     onEventEnter: function (e, el,event,d) {
17160         this.fireEvent('evententer', this, el, event);
17161     },
17162     
17163     onEventLeave: function (e, el,event,d) {
17164         this.fireEvent('eventleave', this, el, event);
17165     },
17166     
17167     onEventClick: function (e, el,event,d) {
17168         this.fireEvent('eventclick', this, el, event);
17169     },
17170     
17171     onMonthChange: function () {
17172         this.store.load();
17173     },
17174     
17175     onMoreEventClick: function(e, el, more)
17176     {
17177         var _this = this;
17178         
17179         this.calpopover.placement = 'right';
17180         this.calpopover.setTitle('More');
17181         
17182         this.calpopover.setContent('');
17183         
17184         var ctr = this.calpopover.el.select('.popover-content', true).first();
17185         
17186         Roo.each(more, function(m){
17187             var cfg = {
17188                 cls : 'fc-event-hori fc-event-draggable',
17189                 html : m.title
17190             };
17191             var cg = ctr.createChild(cfg);
17192             
17193             cg.on('click', _this.onEventClick, _this, m);
17194         });
17195         
17196         this.calpopover.show(el);
17197         
17198         
17199     },
17200     
17201     onLoad: function () 
17202     {   
17203         this.calevents = [];
17204         var cal = this;
17205         
17206         if(this.store.getCount() > 0){
17207             this.store.data.each(function(d){
17208                cal.addItem({
17209                     id : d.data.id,
17210                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17211                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17212                     time : d.data.start_time,
17213                     title : d.data.title,
17214                     description : d.data.description,
17215                     venue : d.data.venue
17216                 });
17217             });
17218         }
17219         
17220         this.renderEvents();
17221         
17222         if(this.calevents.length && this.loadMask){
17223             this.maskEl.hide();
17224         }
17225     },
17226     
17227     onBeforeLoad: function()
17228     {
17229         this.clearEvents();
17230         if(this.loadMask){
17231             this.maskEl.show();
17232         }
17233     }
17234 });
17235
17236  
17237  /*
17238  * - LGPL
17239  *
17240  * element
17241  * 
17242  */
17243
17244 /**
17245  * @class Roo.bootstrap.Popover
17246  * @extends Roo.bootstrap.Component
17247  * Bootstrap Popover class
17248  * @cfg {String} html contents of the popover   (or false to use children..)
17249  * @cfg {String} title of popover (or false to hide)
17250  * @cfg {String} placement how it is placed
17251  * @cfg {String} trigger click || hover (or false to trigger manually)
17252  * @cfg {String} over what (parent or false to trigger manually.)
17253  * @cfg {Number} delay - delay before showing
17254  
17255  * @constructor
17256  * Create a new Popover
17257  * @param {Object} config The config object
17258  */
17259
17260 Roo.bootstrap.Popover = function(config){
17261     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17262     
17263     this.addEvents({
17264         // raw events
17265          /**
17266          * @event show
17267          * After the popover show
17268          * 
17269          * @param {Roo.bootstrap.Popover} this
17270          */
17271         "show" : true,
17272         /**
17273          * @event hide
17274          * After the popover hide
17275          * 
17276          * @param {Roo.bootstrap.Popover} this
17277          */
17278         "hide" : true
17279     });
17280 };
17281
17282 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17283     
17284     title: 'Fill in a title',
17285     html: false,
17286     
17287     placement : 'right',
17288     trigger : 'hover', // hover
17289     
17290     delay : 0,
17291     
17292     over: 'parent',
17293     
17294     can_build_overlaid : false,
17295     
17296     getChildContainer : function()
17297     {
17298         return this.el.select('.popover-content',true).first();
17299     },
17300     
17301     getAutoCreate : function(){
17302          
17303         var cfg = {
17304            cls : 'popover roo-dynamic',
17305            style: 'display:block',
17306            cn : [
17307                 {
17308                     cls : 'arrow'
17309                 },
17310                 {
17311                     cls : 'popover-inner',
17312                     cn : [
17313                         {
17314                             tag: 'h3',
17315                             cls: 'popover-title',
17316                             html : this.title
17317                         },
17318                         {
17319                             cls : 'popover-content',
17320                             html : this.html
17321                         }
17322                     ]
17323                     
17324                 }
17325            ]
17326         };
17327         
17328         return cfg;
17329     },
17330     setTitle: function(str)
17331     {
17332         this.title = str;
17333         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17334     },
17335     setContent: function(str)
17336     {
17337         this.html = str;
17338         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17339     },
17340     // as it get's added to the bottom of the page.
17341     onRender : function(ct, position)
17342     {
17343         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17344         if(!this.el){
17345             var cfg = Roo.apply({},  this.getAutoCreate());
17346             cfg.id = Roo.id();
17347             
17348             if (this.cls) {
17349                 cfg.cls += ' ' + this.cls;
17350             }
17351             if (this.style) {
17352                 cfg.style = this.style;
17353             }
17354             //Roo.log("adding to ");
17355             this.el = Roo.get(document.body).createChild(cfg, position);
17356 //            Roo.log(this.el);
17357         }
17358         this.initEvents();
17359     },
17360     
17361     initEvents : function()
17362     {
17363         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17364         this.el.enableDisplayMode('block');
17365         this.el.hide();
17366         if (this.over === false) {
17367             return; 
17368         }
17369         if (this.triggers === false) {
17370             return;
17371         }
17372         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17373         var triggers = this.trigger ? this.trigger.split(' ') : [];
17374         Roo.each(triggers, function(trigger) {
17375         
17376             if (trigger == 'click') {
17377                 on_el.on('click', this.toggle, this);
17378             } else if (trigger != 'manual') {
17379                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17380                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17381       
17382                 on_el.on(eventIn  ,this.enter, this);
17383                 on_el.on(eventOut, this.leave, this);
17384             }
17385         }, this);
17386         
17387     },
17388     
17389     
17390     // private
17391     timeout : null,
17392     hoverState : null,
17393     
17394     toggle : function () {
17395         this.hoverState == 'in' ? this.leave() : this.enter();
17396     },
17397     
17398     enter : function () {
17399         
17400         clearTimeout(this.timeout);
17401     
17402         this.hoverState = 'in';
17403     
17404         if (!this.delay || !this.delay.show) {
17405             this.show();
17406             return;
17407         }
17408         var _t = this;
17409         this.timeout = setTimeout(function () {
17410             if (_t.hoverState == 'in') {
17411                 _t.show();
17412             }
17413         }, this.delay.show)
17414     },
17415     
17416     leave : function() {
17417         clearTimeout(this.timeout);
17418     
17419         this.hoverState = 'out';
17420     
17421         if (!this.delay || !this.delay.hide) {
17422             this.hide();
17423             return;
17424         }
17425         var _t = this;
17426         this.timeout = setTimeout(function () {
17427             if (_t.hoverState == 'out') {
17428                 _t.hide();
17429             }
17430         }, this.delay.hide)
17431     },
17432     
17433     show : function (on_el)
17434     {
17435         if (!on_el) {
17436             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17437         }
17438         
17439         // set content.
17440         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17441         if (this.html !== false) {
17442             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17443         }
17444         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17445         if (!this.title.length) {
17446             this.el.select('.popover-title',true).hide();
17447         }
17448         
17449         var placement = typeof this.placement == 'function' ?
17450             this.placement.call(this, this.el, on_el) :
17451             this.placement;
17452             
17453         var autoToken = /\s?auto?\s?/i;
17454         var autoPlace = autoToken.test(placement);
17455         if (autoPlace) {
17456             placement = placement.replace(autoToken, '') || 'top';
17457         }
17458         
17459         //this.el.detach()
17460         //this.el.setXY([0,0]);
17461         this.el.show();
17462         this.el.dom.style.display='block';
17463         this.el.addClass(placement);
17464         
17465         //this.el.appendTo(on_el);
17466         
17467         var p = this.getPosition();
17468         var box = this.el.getBox();
17469         
17470         if (autoPlace) {
17471             // fixme..
17472         }
17473         var align = Roo.bootstrap.Popover.alignment[placement];
17474         
17475 //        Roo.log(align);
17476         this.el.alignTo(on_el, align[0],align[1]);
17477         //var arrow = this.el.select('.arrow',true).first();
17478         //arrow.set(align[2], 
17479         
17480         this.el.addClass('in');
17481         
17482         
17483         if (this.el.hasClass('fade')) {
17484             // fade it?
17485         }
17486         
17487         this.hoverState = 'in';
17488         
17489         this.fireEvent('show', this);
17490         
17491     },
17492     hide : function()
17493     {
17494         this.el.setXY([0,0]);
17495         this.el.removeClass('in');
17496         this.el.hide();
17497         this.hoverState = null;
17498         
17499         this.fireEvent('hide', this);
17500     }
17501     
17502 });
17503
17504 Roo.bootstrap.Popover.alignment = {
17505     'left' : ['r-l', [-10,0], 'right'],
17506     'right' : ['l-r', [10,0], 'left'],
17507     'bottom' : ['t-b', [0,10], 'top'],
17508     'top' : [ 'b-t', [0,-10], 'bottom']
17509 };
17510
17511  /*
17512  * - LGPL
17513  *
17514  * Progress
17515  * 
17516  */
17517
17518 /**
17519  * @class Roo.bootstrap.Progress
17520  * @extends Roo.bootstrap.Component
17521  * Bootstrap Progress class
17522  * @cfg {Boolean} striped striped of the progress bar
17523  * @cfg {Boolean} active animated of the progress bar
17524  * 
17525  * 
17526  * @constructor
17527  * Create a new Progress
17528  * @param {Object} config The config object
17529  */
17530
17531 Roo.bootstrap.Progress = function(config){
17532     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17533 };
17534
17535 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17536     
17537     striped : false,
17538     active: false,
17539     
17540     getAutoCreate : function(){
17541         var cfg = {
17542             tag: 'div',
17543             cls: 'progress'
17544         };
17545         
17546         
17547         if(this.striped){
17548             cfg.cls += ' progress-striped';
17549         }
17550       
17551         if(this.active){
17552             cfg.cls += ' active';
17553         }
17554         
17555         
17556         return cfg;
17557     }
17558    
17559 });
17560
17561  
17562
17563  /*
17564  * - LGPL
17565  *
17566  * ProgressBar
17567  * 
17568  */
17569
17570 /**
17571  * @class Roo.bootstrap.ProgressBar
17572  * @extends Roo.bootstrap.Component
17573  * Bootstrap ProgressBar class
17574  * @cfg {Number} aria_valuenow aria-value now
17575  * @cfg {Number} aria_valuemin aria-value min
17576  * @cfg {Number} aria_valuemax aria-value max
17577  * @cfg {String} label label for the progress bar
17578  * @cfg {String} panel (success | info | warning | danger )
17579  * @cfg {String} role role of the progress bar
17580  * @cfg {String} sr_only text
17581  * 
17582  * 
17583  * @constructor
17584  * Create a new ProgressBar
17585  * @param {Object} config The config object
17586  */
17587
17588 Roo.bootstrap.ProgressBar = function(config){
17589     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17590 };
17591
17592 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17593     
17594     aria_valuenow : 0,
17595     aria_valuemin : 0,
17596     aria_valuemax : 100,
17597     label : false,
17598     panel : false,
17599     role : false,
17600     sr_only: false,
17601     
17602     getAutoCreate : function()
17603     {
17604         
17605         var cfg = {
17606             tag: 'div',
17607             cls: 'progress-bar',
17608             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17609         };
17610         
17611         if(this.sr_only){
17612             cfg.cn = {
17613                 tag: 'span',
17614                 cls: 'sr-only',
17615                 html: this.sr_only
17616             }
17617         }
17618         
17619         if(this.role){
17620             cfg.role = this.role;
17621         }
17622         
17623         if(this.aria_valuenow){
17624             cfg['aria-valuenow'] = this.aria_valuenow;
17625         }
17626         
17627         if(this.aria_valuemin){
17628             cfg['aria-valuemin'] = this.aria_valuemin;
17629         }
17630         
17631         if(this.aria_valuemax){
17632             cfg['aria-valuemax'] = this.aria_valuemax;
17633         }
17634         
17635         if(this.label && !this.sr_only){
17636             cfg.html = this.label;
17637         }
17638         
17639         if(this.panel){
17640             cfg.cls += ' progress-bar-' + this.panel;
17641         }
17642         
17643         return cfg;
17644     },
17645     
17646     update : function(aria_valuenow)
17647     {
17648         this.aria_valuenow = aria_valuenow;
17649         
17650         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17651     }
17652    
17653 });
17654
17655  
17656
17657  /*
17658  * - LGPL
17659  *
17660  * column
17661  * 
17662  */
17663
17664 /**
17665  * @class Roo.bootstrap.TabGroup
17666  * @extends Roo.bootstrap.Column
17667  * Bootstrap Column class
17668  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17669  * @cfg {Boolean} carousel true to make the group behave like a carousel
17670  * @cfg {Boolean} bullets show bullets for the panels
17671  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17672  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17673  * @cfg {Boolean} showarrow (true|false) show arrow default true
17674  * 
17675  * @constructor
17676  * Create a new TabGroup
17677  * @param {Object} config The config object
17678  */
17679
17680 Roo.bootstrap.TabGroup = function(config){
17681     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17682     if (!this.navId) {
17683         this.navId = Roo.id();
17684     }
17685     this.tabs = [];
17686     Roo.bootstrap.TabGroup.register(this);
17687     
17688 };
17689
17690 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17691     
17692     carousel : false,
17693     transition : false,
17694     bullets : 0,
17695     timer : 0,
17696     autoslide : false,
17697     slideFn : false,
17698     slideOnTouch : false,
17699     showarrow : true,
17700     
17701     getAutoCreate : function()
17702     {
17703         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17704         
17705         cfg.cls += ' tab-content';
17706         
17707         if (this.carousel) {
17708             cfg.cls += ' carousel slide';
17709             
17710             cfg.cn = [{
17711                cls : 'carousel-inner',
17712                cn : []
17713             }];
17714         
17715             if(this.bullets  && !Roo.isTouch){
17716                 
17717                 var bullets = {
17718                     cls : 'carousel-bullets',
17719                     cn : []
17720                 };
17721                
17722                 if(this.bullets_cls){
17723                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17724                 }
17725                 
17726                 bullets.cn.push({
17727                     cls : 'clear'
17728                 });
17729                 
17730                 cfg.cn[0].cn.push(bullets);
17731             }
17732             
17733             if(this.showarrow){
17734                 cfg.cn[0].cn.push({
17735                     tag : 'div',
17736                     class : 'carousel-arrow',
17737                     cn : [
17738                         {
17739                             tag : 'div',
17740                             class : 'carousel-prev',
17741                             cn : [
17742                                 {
17743                                     tag : 'i',
17744                                     class : 'fa fa-chevron-left'
17745                                 }
17746                             ]
17747                         },
17748                         {
17749                             tag : 'div',
17750                             class : 'carousel-next',
17751                             cn : [
17752                                 {
17753                                     tag : 'i',
17754                                     class : 'fa fa-chevron-right'
17755                                 }
17756                             ]
17757                         }
17758                     ]
17759                 });
17760             }
17761             
17762         }
17763         
17764         return cfg;
17765     },
17766     
17767     initEvents:  function()
17768     {
17769 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17770 //            this.el.on("touchstart", this.onTouchStart, this);
17771 //        }
17772         
17773         if(this.autoslide){
17774             var _this = this;
17775             
17776             this.slideFn = window.setInterval(function() {
17777                 _this.showPanelNext();
17778             }, this.timer);
17779         }
17780         
17781         if(this.showarrow){
17782             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17783             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17784         }
17785         
17786         
17787     },
17788     
17789 //    onTouchStart : function(e, el, o)
17790 //    {
17791 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17792 //            return;
17793 //        }
17794 //        
17795 //        this.showPanelNext();
17796 //    },
17797     
17798     
17799     getChildContainer : function()
17800     {
17801         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17802     },
17803     
17804     /**
17805     * register a Navigation item
17806     * @param {Roo.bootstrap.NavItem} the navitem to add
17807     */
17808     register : function(item)
17809     {
17810         this.tabs.push( item);
17811         item.navId = this.navId; // not really needed..
17812         this.addBullet();
17813     
17814     },
17815     
17816     getActivePanel : function()
17817     {
17818         var r = false;
17819         Roo.each(this.tabs, function(t) {
17820             if (t.active) {
17821                 r = t;
17822                 return false;
17823             }
17824             return null;
17825         });
17826         return r;
17827         
17828     },
17829     getPanelByName : function(n)
17830     {
17831         var r = false;
17832         Roo.each(this.tabs, function(t) {
17833             if (t.tabId == n) {
17834                 r = t;
17835                 return false;
17836             }
17837             return null;
17838         });
17839         return r;
17840     },
17841     indexOfPanel : function(p)
17842     {
17843         var r = false;
17844         Roo.each(this.tabs, function(t,i) {
17845             if (t.tabId == p.tabId) {
17846                 r = i;
17847                 return false;
17848             }
17849             return null;
17850         });
17851         return r;
17852     },
17853     /**
17854      * show a specific panel
17855      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17856      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17857      */
17858     showPanel : function (pan)
17859     {
17860         if(this.transition || typeof(pan) == 'undefined'){
17861             Roo.log("waiting for the transitionend");
17862             return;
17863         }
17864         
17865         if (typeof(pan) == 'number') {
17866             pan = this.tabs[pan];
17867         }
17868         
17869         if (typeof(pan) == 'string') {
17870             pan = this.getPanelByName(pan);
17871         }
17872         
17873         var cur = this.getActivePanel();
17874         
17875         if(!pan || !cur){
17876             Roo.log('pan or acitve pan is undefined');
17877             return false;
17878         }
17879         
17880         if (pan.tabId == this.getActivePanel().tabId) {
17881             return true;
17882         }
17883         
17884         if (false === cur.fireEvent('beforedeactivate')) {
17885             return false;
17886         }
17887         
17888         if(this.bullets > 0 && !Roo.isTouch){
17889             this.setActiveBullet(this.indexOfPanel(pan));
17890         }
17891         
17892         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17893             
17894             this.transition = true;
17895             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17896             var lr = dir == 'next' ? 'left' : 'right';
17897             pan.el.addClass(dir); // or prev
17898             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17899             cur.el.addClass(lr); // or right
17900             pan.el.addClass(lr);
17901             
17902             var _this = this;
17903             cur.el.on('transitionend', function() {
17904                 Roo.log("trans end?");
17905                 
17906                 pan.el.removeClass([lr,dir]);
17907                 pan.setActive(true);
17908                 
17909                 cur.el.removeClass([lr]);
17910                 cur.setActive(false);
17911                 
17912                 _this.transition = false;
17913                 
17914             }, this, { single:  true } );
17915             
17916             return true;
17917         }
17918         
17919         cur.setActive(false);
17920         pan.setActive(true);
17921         
17922         return true;
17923         
17924     },
17925     showPanelNext : function()
17926     {
17927         var i = this.indexOfPanel(this.getActivePanel());
17928         
17929         if (i >= this.tabs.length - 1 && !this.autoslide) {
17930             return;
17931         }
17932         
17933         if (i >= this.tabs.length - 1 && this.autoslide) {
17934             i = -1;
17935         }
17936         
17937         this.showPanel(this.tabs[i+1]);
17938     },
17939     
17940     showPanelPrev : function()
17941     {
17942         var i = this.indexOfPanel(this.getActivePanel());
17943         
17944         if (i  < 1 && !this.autoslide) {
17945             return;
17946         }
17947         
17948         if (i < 1 && this.autoslide) {
17949             i = this.tabs.length;
17950         }
17951         
17952         this.showPanel(this.tabs[i-1]);
17953     },
17954     
17955     
17956     addBullet: function()
17957     {
17958         if(!this.bullets || Roo.isTouch){
17959             return;
17960         }
17961         var ctr = this.el.select('.carousel-bullets',true).first();
17962         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17963         var bullet = ctr.createChild({
17964             cls : 'bullet bullet-' + i
17965         },ctr.dom.lastChild);
17966         
17967         
17968         var _this = this;
17969         
17970         bullet.on('click', (function(e, el, o, ii, t){
17971
17972             e.preventDefault();
17973
17974             this.showPanel(ii);
17975
17976             if(this.autoslide && this.slideFn){
17977                 clearInterval(this.slideFn);
17978                 this.slideFn = window.setInterval(function() {
17979                     _this.showPanelNext();
17980                 }, this.timer);
17981             }
17982
17983         }).createDelegate(this, [i, bullet], true));
17984                 
17985         
17986     },
17987      
17988     setActiveBullet : function(i)
17989     {
17990         if(Roo.isTouch){
17991             return;
17992         }
17993         
17994         Roo.each(this.el.select('.bullet', true).elements, function(el){
17995             el.removeClass('selected');
17996         });
17997
17998         var bullet = this.el.select('.bullet-' + i, true).first();
17999         
18000         if(!bullet){
18001             return;
18002         }
18003         
18004         bullet.addClass('selected');
18005     }
18006     
18007     
18008   
18009 });
18010
18011  
18012
18013  
18014  
18015 Roo.apply(Roo.bootstrap.TabGroup, {
18016     
18017     groups: {},
18018      /**
18019     * register a Navigation Group
18020     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18021     */
18022     register : function(navgrp)
18023     {
18024         this.groups[navgrp.navId] = navgrp;
18025         
18026     },
18027     /**
18028     * fetch a Navigation Group based on the navigation ID
18029     * if one does not exist , it will get created.
18030     * @param {string} the navgroup to add
18031     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18032     */
18033     get: function(navId) {
18034         if (typeof(this.groups[navId]) == 'undefined') {
18035             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18036         }
18037         return this.groups[navId] ;
18038     }
18039     
18040     
18041     
18042 });
18043
18044  /*
18045  * - LGPL
18046  *
18047  * TabPanel
18048  * 
18049  */
18050
18051 /**
18052  * @class Roo.bootstrap.TabPanel
18053  * @extends Roo.bootstrap.Component
18054  * Bootstrap TabPanel class
18055  * @cfg {Boolean} active panel active
18056  * @cfg {String} html panel content
18057  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18058  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18059  * @cfg {String} href click to link..
18060  * 
18061  * 
18062  * @constructor
18063  * Create a new TabPanel
18064  * @param {Object} config The config object
18065  */
18066
18067 Roo.bootstrap.TabPanel = function(config){
18068     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18069     this.addEvents({
18070         /**
18071              * @event changed
18072              * Fires when the active status changes
18073              * @param {Roo.bootstrap.TabPanel} this
18074              * @param {Boolean} state the new state
18075             
18076          */
18077         'changed': true,
18078         /**
18079              * @event beforedeactivate
18080              * Fires before a tab is de-activated - can be used to do validation on a form.
18081              * @param {Roo.bootstrap.TabPanel} this
18082              * @return {Boolean} false if there is an error
18083             
18084          */
18085         'beforedeactivate': true
18086      });
18087     
18088     this.tabId = this.tabId || Roo.id();
18089   
18090 };
18091
18092 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18093     
18094     active: false,
18095     html: false,
18096     tabId: false,
18097     navId : false,
18098     href : '',
18099     
18100     getAutoCreate : function(){
18101         var cfg = {
18102             tag: 'div',
18103             // item is needed for carousel - not sure if it has any effect otherwise
18104             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18105             html: this.html || ''
18106         };
18107         
18108         if(this.active){
18109             cfg.cls += ' active';
18110         }
18111         
18112         if(this.tabId){
18113             cfg.tabId = this.tabId;
18114         }
18115         
18116         
18117         return cfg;
18118     },
18119     
18120     initEvents:  function()
18121     {
18122         var p = this.parent();
18123         
18124         this.navId = this.navId || p.navId;
18125         
18126         if (typeof(this.navId) != 'undefined') {
18127             // not really needed.. but just in case.. parent should be a NavGroup.
18128             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18129             
18130             tg.register(this);
18131             
18132             var i = tg.tabs.length - 1;
18133             
18134             if(this.active && tg.bullets > 0 && i < tg.bullets){
18135                 tg.setActiveBullet(i);
18136             }
18137         }
18138         
18139         this.el.on('click', this.onClick, this);
18140         
18141         if(Roo.isTouch){
18142             this.el.on("touchstart", this.onTouchStart, this);
18143             this.el.on("touchmove", this.onTouchMove, this);
18144             this.el.on("touchend", this.onTouchEnd, this);
18145         }
18146         
18147     },
18148     
18149     onRender : function(ct, position)
18150     {
18151         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18152     },
18153     
18154     setActive : function(state)
18155     {
18156         Roo.log("panel - set active " + this.tabId + "=" + state);
18157         
18158         this.active = state;
18159         if (!state) {
18160             this.el.removeClass('active');
18161             
18162         } else  if (!this.el.hasClass('active')) {
18163             this.el.addClass('active');
18164         }
18165         
18166         this.fireEvent('changed', this, state);
18167     },
18168     
18169     onClick : function(e)
18170     {
18171         e.preventDefault();
18172         
18173         if(!this.href.length){
18174             return;
18175         }
18176         
18177         window.location.href = this.href;
18178     },
18179     
18180     startX : 0,
18181     startY : 0,
18182     endX : 0,
18183     endY : 0,
18184     swiping : false,
18185     
18186     onTouchStart : function(e)
18187     {
18188         this.swiping = false;
18189         
18190         this.startX = e.browserEvent.touches[0].clientX;
18191         this.startY = e.browserEvent.touches[0].clientY;
18192     },
18193     
18194     onTouchMove : function(e)
18195     {
18196         this.swiping = true;
18197         
18198         this.endX = e.browserEvent.touches[0].clientX;
18199         this.endY = e.browserEvent.touches[0].clientY;
18200     },
18201     
18202     onTouchEnd : function(e)
18203     {
18204         if(!this.swiping){
18205             this.onClick(e);
18206             return;
18207         }
18208         
18209         var tabGroup = this.parent();
18210         
18211         if(this.endX > this.startX){ // swiping right
18212             tabGroup.showPanelPrev();
18213             return;
18214         }
18215         
18216         if(this.startX > this.endX){ // swiping left
18217             tabGroup.showPanelNext();
18218             return;
18219         }
18220     }
18221     
18222     
18223 });
18224  
18225
18226  
18227
18228  /*
18229  * - LGPL
18230  *
18231  * DateField
18232  * 
18233  */
18234
18235 /**
18236  * @class Roo.bootstrap.DateField
18237  * @extends Roo.bootstrap.Input
18238  * Bootstrap DateField class
18239  * @cfg {Number} weekStart default 0
18240  * @cfg {String} viewMode default empty, (months|years)
18241  * @cfg {String} minViewMode default empty, (months|years)
18242  * @cfg {Number} startDate default -Infinity
18243  * @cfg {Number} endDate default Infinity
18244  * @cfg {Boolean} todayHighlight default false
18245  * @cfg {Boolean} todayBtn default false
18246  * @cfg {Boolean} calendarWeeks default false
18247  * @cfg {Object} daysOfWeekDisabled default empty
18248  * @cfg {Boolean} singleMode default false (true | false)
18249  * 
18250  * @cfg {Boolean} keyboardNavigation default true
18251  * @cfg {String} language default en
18252  * 
18253  * @constructor
18254  * Create a new DateField
18255  * @param {Object} config The config object
18256  */
18257
18258 Roo.bootstrap.DateField = function(config){
18259     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18260      this.addEvents({
18261             /**
18262              * @event show
18263              * Fires when this field show.
18264              * @param {Roo.bootstrap.DateField} this
18265              * @param {Mixed} date The date value
18266              */
18267             show : true,
18268             /**
18269              * @event show
18270              * Fires when this field hide.
18271              * @param {Roo.bootstrap.DateField} this
18272              * @param {Mixed} date The date value
18273              */
18274             hide : true,
18275             /**
18276              * @event select
18277              * Fires when select a date.
18278              * @param {Roo.bootstrap.DateField} this
18279              * @param {Mixed} date The date value
18280              */
18281             select : true,
18282             /**
18283              * @event beforeselect
18284              * Fires when before select a date.
18285              * @param {Roo.bootstrap.DateField} this
18286              * @param {Mixed} date The date value
18287              */
18288             beforeselect : true
18289         });
18290 };
18291
18292 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18293     
18294     /**
18295      * @cfg {String} format
18296      * The default date format string which can be overriden for localization support.  The format must be
18297      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18298      */
18299     format : "m/d/y",
18300     /**
18301      * @cfg {String} altFormats
18302      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18303      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18304      */
18305     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18306     
18307     weekStart : 0,
18308     
18309     viewMode : '',
18310     
18311     minViewMode : '',
18312     
18313     todayHighlight : false,
18314     
18315     todayBtn: false,
18316     
18317     language: 'en',
18318     
18319     keyboardNavigation: true,
18320     
18321     calendarWeeks: false,
18322     
18323     startDate: -Infinity,
18324     
18325     endDate: Infinity,
18326     
18327     daysOfWeekDisabled: [],
18328     
18329     _events: [],
18330     
18331     singleMode : false,
18332     
18333     UTCDate: function()
18334     {
18335         return new Date(Date.UTC.apply(Date, arguments));
18336     },
18337     
18338     UTCToday: function()
18339     {
18340         var today = new Date();
18341         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18342     },
18343     
18344     getDate: function() {
18345             var d = this.getUTCDate();
18346             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18347     },
18348     
18349     getUTCDate: function() {
18350             return this.date;
18351     },
18352     
18353     setDate: function(d) {
18354             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18355     },
18356     
18357     setUTCDate: function(d) {
18358             this.date = d;
18359             this.setValue(this.formatDate(this.date));
18360     },
18361         
18362     onRender: function(ct, position)
18363     {
18364         
18365         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18366         
18367         this.language = this.language || 'en';
18368         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18369         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18370         
18371         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18372         this.format = this.format || 'm/d/y';
18373         this.isInline = false;
18374         this.isInput = true;
18375         this.component = this.el.select('.add-on', true).first() || false;
18376         this.component = (this.component && this.component.length === 0) ? false : this.component;
18377         this.hasInput = this.component && this.inputEl().length;
18378         
18379         if (typeof(this.minViewMode === 'string')) {
18380             switch (this.minViewMode) {
18381                 case 'months':
18382                     this.minViewMode = 1;
18383                     break;
18384                 case 'years':
18385                     this.minViewMode = 2;
18386                     break;
18387                 default:
18388                     this.minViewMode = 0;
18389                     break;
18390             }
18391         }
18392         
18393         if (typeof(this.viewMode === 'string')) {
18394             switch (this.viewMode) {
18395                 case 'months':
18396                     this.viewMode = 1;
18397                     break;
18398                 case 'years':
18399                     this.viewMode = 2;
18400                     break;
18401                 default:
18402                     this.viewMode = 0;
18403                     break;
18404             }
18405         }
18406                 
18407         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18408         
18409 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18410         
18411         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18412         
18413         this.picker().on('mousedown', this.onMousedown, this);
18414         this.picker().on('click', this.onClick, this);
18415         
18416         this.picker().addClass('datepicker-dropdown');
18417         
18418         this.startViewMode = this.viewMode;
18419         
18420         if(this.singleMode){
18421             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18422                 v.setVisibilityMode(Roo.Element.DISPLAY);
18423                 v.hide();
18424             });
18425             
18426             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18427                 v.setStyle('width', '189px');
18428             });
18429         }
18430         
18431         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18432             if(!this.calendarWeeks){
18433                 v.remove();
18434                 return;
18435             }
18436             
18437             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18438             v.attr('colspan', function(i, val){
18439                 return parseInt(val) + 1;
18440             });
18441         });
18442                         
18443         
18444         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18445         
18446         this.setStartDate(this.startDate);
18447         this.setEndDate(this.endDate);
18448         
18449         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18450         
18451         this.fillDow();
18452         this.fillMonths();
18453         this.update();
18454         this.showMode();
18455         
18456         if(this.isInline) {
18457             this.show();
18458         }
18459     },
18460     
18461     picker : function()
18462     {
18463         return this.pickerEl;
18464 //        return this.el.select('.datepicker', true).first();
18465     },
18466     
18467     fillDow: function()
18468     {
18469         var dowCnt = this.weekStart;
18470         
18471         var dow = {
18472             tag: 'tr',
18473             cn: [
18474                 
18475             ]
18476         };
18477         
18478         if(this.calendarWeeks){
18479             dow.cn.push({
18480                 tag: 'th',
18481                 cls: 'cw',
18482                 html: '&nbsp;'
18483             })
18484         }
18485         
18486         while (dowCnt < this.weekStart + 7) {
18487             dow.cn.push({
18488                 tag: 'th',
18489                 cls: 'dow',
18490                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18491             });
18492         }
18493         
18494         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18495     },
18496     
18497     fillMonths: function()
18498     {    
18499         var i = 0;
18500         var months = this.picker().select('>.datepicker-months td', true).first();
18501         
18502         months.dom.innerHTML = '';
18503         
18504         while (i < 12) {
18505             var month = {
18506                 tag: 'span',
18507                 cls: 'month',
18508                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18509             };
18510             
18511             months.createChild(month);
18512         }
18513         
18514     },
18515     
18516     update: function()
18517     {
18518         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;
18519         
18520         if (this.date < this.startDate) {
18521             this.viewDate = new Date(this.startDate);
18522         } else if (this.date > this.endDate) {
18523             this.viewDate = new Date(this.endDate);
18524         } else {
18525             this.viewDate = new Date(this.date);
18526         }
18527         
18528         this.fill();
18529     },
18530     
18531     fill: function() 
18532     {
18533         var d = new Date(this.viewDate),
18534                 year = d.getUTCFullYear(),
18535                 month = d.getUTCMonth(),
18536                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18537                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18538                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18539                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18540                 currentDate = this.date && this.date.valueOf(),
18541                 today = this.UTCToday();
18542         
18543         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18544         
18545 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18546         
18547 //        this.picker.select('>tfoot th.today').
18548 //                                              .text(dates[this.language].today)
18549 //                                              .toggle(this.todayBtn !== false);
18550     
18551         this.updateNavArrows();
18552         this.fillMonths();
18553                                                 
18554         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18555         
18556         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18557          
18558         prevMonth.setUTCDate(day);
18559         
18560         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18561         
18562         var nextMonth = new Date(prevMonth);
18563         
18564         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18565         
18566         nextMonth = nextMonth.valueOf();
18567         
18568         var fillMonths = false;
18569         
18570         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18571         
18572         while(prevMonth.valueOf() < nextMonth) {
18573             var clsName = '';
18574             
18575             if (prevMonth.getUTCDay() === this.weekStart) {
18576                 if(fillMonths){
18577                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18578                 }
18579                     
18580                 fillMonths = {
18581                     tag: 'tr',
18582                     cn: []
18583                 };
18584                 
18585                 if(this.calendarWeeks){
18586                     // ISO 8601: First week contains first thursday.
18587                     // ISO also states week starts on Monday, but we can be more abstract here.
18588                     var
18589                     // Start of current week: based on weekstart/current date
18590                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18591                     // Thursday of this week
18592                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18593                     // First Thursday of year, year from thursday
18594                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18595                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18596                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18597                     
18598                     fillMonths.cn.push({
18599                         tag: 'td',
18600                         cls: 'cw',
18601                         html: calWeek
18602                     });
18603                 }
18604             }
18605             
18606             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18607                 clsName += ' old';
18608             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18609                 clsName += ' new';
18610             }
18611             if (this.todayHighlight &&
18612                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18613                 prevMonth.getUTCMonth() == today.getMonth() &&
18614                 prevMonth.getUTCDate() == today.getDate()) {
18615                 clsName += ' today';
18616             }
18617             
18618             if (currentDate && prevMonth.valueOf() === currentDate) {
18619                 clsName += ' active';
18620             }
18621             
18622             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18623                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18624                     clsName += ' disabled';
18625             }
18626             
18627             fillMonths.cn.push({
18628                 tag: 'td',
18629                 cls: 'day ' + clsName,
18630                 html: prevMonth.getDate()
18631             });
18632             
18633             prevMonth.setDate(prevMonth.getDate()+1);
18634         }
18635           
18636         var currentYear = this.date && this.date.getUTCFullYear();
18637         var currentMonth = this.date && this.date.getUTCMonth();
18638         
18639         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18640         
18641         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18642             v.removeClass('active');
18643             
18644             if(currentYear === year && k === currentMonth){
18645                 v.addClass('active');
18646             }
18647             
18648             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18649                 v.addClass('disabled');
18650             }
18651             
18652         });
18653         
18654         
18655         year = parseInt(year/10, 10) * 10;
18656         
18657         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18658         
18659         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18660         
18661         year -= 1;
18662         for (var i = -1; i < 11; i++) {
18663             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18664                 tag: 'span',
18665                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18666                 html: year
18667             });
18668             
18669             year += 1;
18670         }
18671     },
18672     
18673     showMode: function(dir) 
18674     {
18675         if (dir) {
18676             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18677         }
18678         
18679         Roo.each(this.picker().select('>div',true).elements, function(v){
18680             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18681             v.hide();
18682         });
18683         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18684     },
18685     
18686     place: function()
18687     {
18688         if(this.isInline) {
18689             return;
18690         }
18691         
18692         this.picker().removeClass(['bottom', 'top']);
18693         
18694         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18695             /*
18696              * place to the top of element!
18697              *
18698              */
18699             
18700             this.picker().addClass('top');
18701             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18702             
18703             return;
18704         }
18705         
18706         this.picker().addClass('bottom');
18707         
18708         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18709     },
18710     
18711     parseDate : function(value)
18712     {
18713         if(!value || value instanceof Date){
18714             return value;
18715         }
18716         var v = Date.parseDate(value, this.format);
18717         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18718             v = Date.parseDate(value, 'Y-m-d');
18719         }
18720         if(!v && this.altFormats){
18721             if(!this.altFormatsArray){
18722                 this.altFormatsArray = this.altFormats.split("|");
18723             }
18724             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18725                 v = Date.parseDate(value, this.altFormatsArray[i]);
18726             }
18727         }
18728         return v;
18729     },
18730     
18731     formatDate : function(date, fmt)
18732     {   
18733         return (!date || !(date instanceof Date)) ?
18734         date : date.dateFormat(fmt || this.format);
18735     },
18736     
18737     onFocus : function()
18738     {
18739         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18740         this.show();
18741     },
18742     
18743     onBlur : function()
18744     {
18745         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18746         
18747         var d = this.inputEl().getValue();
18748         
18749         this.setValue(d);
18750                 
18751         this.hide();
18752     },
18753     
18754     show : function()
18755     {
18756         this.picker().show();
18757         this.update();
18758         this.place();
18759         
18760         this.fireEvent('show', this, this.date);
18761     },
18762     
18763     hide : function()
18764     {
18765         if(this.isInline) {
18766             return;
18767         }
18768         this.picker().hide();
18769         this.viewMode = this.startViewMode;
18770         this.showMode();
18771         
18772         this.fireEvent('hide', this, this.date);
18773         
18774     },
18775     
18776     onMousedown: function(e)
18777     {
18778         e.stopPropagation();
18779         e.preventDefault();
18780     },
18781     
18782     keyup: function(e)
18783     {
18784         Roo.bootstrap.DateField.superclass.keyup.call(this);
18785         this.update();
18786     },
18787
18788     setValue: function(v)
18789     {
18790         if(this.fireEvent('beforeselect', this, v) !== false){
18791             var d = new Date(this.parseDate(v) ).clearTime();
18792         
18793             if(isNaN(d.getTime())){
18794                 this.date = this.viewDate = '';
18795                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18796                 return;
18797             }
18798
18799             v = this.formatDate(d);
18800
18801             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18802
18803             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18804
18805             this.update();
18806
18807             this.fireEvent('select', this, this.date);
18808         }
18809     },
18810     
18811     getValue: function()
18812     {
18813         return this.formatDate(this.date);
18814     },
18815     
18816     fireKey: function(e)
18817     {
18818         if (!this.picker().isVisible()){
18819             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18820                 this.show();
18821             }
18822             return;
18823         }
18824         
18825         var dateChanged = false,
18826         dir, day, month,
18827         newDate, newViewDate;
18828         
18829         switch(e.keyCode){
18830             case 27: // escape
18831                 this.hide();
18832                 e.preventDefault();
18833                 break;
18834             case 37: // left
18835             case 39: // right
18836                 if (!this.keyboardNavigation) {
18837                     break;
18838                 }
18839                 dir = e.keyCode == 37 ? -1 : 1;
18840                 
18841                 if (e.ctrlKey){
18842                     newDate = this.moveYear(this.date, dir);
18843                     newViewDate = this.moveYear(this.viewDate, dir);
18844                 } else if (e.shiftKey){
18845                     newDate = this.moveMonth(this.date, dir);
18846                     newViewDate = this.moveMonth(this.viewDate, dir);
18847                 } else {
18848                     newDate = new Date(this.date);
18849                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18850                     newViewDate = new Date(this.viewDate);
18851                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18852                 }
18853                 if (this.dateWithinRange(newDate)){
18854                     this.date = newDate;
18855                     this.viewDate = newViewDate;
18856                     this.setValue(this.formatDate(this.date));
18857 //                    this.update();
18858                     e.preventDefault();
18859                     dateChanged = true;
18860                 }
18861                 break;
18862             case 38: // up
18863             case 40: // down
18864                 if (!this.keyboardNavigation) {
18865                     break;
18866                 }
18867                 dir = e.keyCode == 38 ? -1 : 1;
18868                 if (e.ctrlKey){
18869                     newDate = this.moveYear(this.date, dir);
18870                     newViewDate = this.moveYear(this.viewDate, dir);
18871                 } else if (e.shiftKey){
18872                     newDate = this.moveMonth(this.date, dir);
18873                     newViewDate = this.moveMonth(this.viewDate, dir);
18874                 } else {
18875                     newDate = new Date(this.date);
18876                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18877                     newViewDate = new Date(this.viewDate);
18878                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18879                 }
18880                 if (this.dateWithinRange(newDate)){
18881                     this.date = newDate;
18882                     this.viewDate = newViewDate;
18883                     this.setValue(this.formatDate(this.date));
18884 //                    this.update();
18885                     e.preventDefault();
18886                     dateChanged = true;
18887                 }
18888                 break;
18889             case 13: // enter
18890                 this.setValue(this.formatDate(this.date));
18891                 this.hide();
18892                 e.preventDefault();
18893                 break;
18894             case 9: // tab
18895                 this.setValue(this.formatDate(this.date));
18896                 this.hide();
18897                 break;
18898             case 16: // shift
18899             case 17: // ctrl
18900             case 18: // alt
18901                 break;
18902             default :
18903                 this.hide();
18904                 
18905         }
18906     },
18907     
18908     
18909     onClick: function(e) 
18910     {
18911         e.stopPropagation();
18912         e.preventDefault();
18913         
18914         var target = e.getTarget();
18915         
18916         if(target.nodeName.toLowerCase() === 'i'){
18917             target = Roo.get(target).dom.parentNode;
18918         }
18919         
18920         var nodeName = target.nodeName;
18921         var className = target.className;
18922         var html = target.innerHTML;
18923         //Roo.log(nodeName);
18924         
18925         switch(nodeName.toLowerCase()) {
18926             case 'th':
18927                 switch(className) {
18928                     case 'switch':
18929                         this.showMode(1);
18930                         break;
18931                     case 'prev':
18932                     case 'next':
18933                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18934                         switch(this.viewMode){
18935                                 case 0:
18936                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18937                                         break;
18938                                 case 1:
18939                                 case 2:
18940                                         this.viewDate = this.moveYear(this.viewDate, dir);
18941                                         break;
18942                         }
18943                         this.fill();
18944                         break;
18945                     case 'today':
18946                         var date = new Date();
18947                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18948 //                        this.fill()
18949                         this.setValue(this.formatDate(this.date));
18950                         
18951                         this.hide();
18952                         break;
18953                 }
18954                 break;
18955             case 'span':
18956                 if (className.indexOf('disabled') < 0) {
18957                     this.viewDate.setUTCDate(1);
18958                     if (className.indexOf('month') > -1) {
18959                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18960                     } else {
18961                         var year = parseInt(html, 10) || 0;
18962                         this.viewDate.setUTCFullYear(year);
18963                         
18964                     }
18965                     
18966                     if(this.singleMode){
18967                         this.setValue(this.formatDate(this.viewDate));
18968                         this.hide();
18969                         return;
18970                     }
18971                     
18972                     this.showMode(-1);
18973                     this.fill();
18974                 }
18975                 break;
18976                 
18977             case 'td':
18978                 //Roo.log(className);
18979                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18980                     var day = parseInt(html, 10) || 1;
18981                     var year = this.viewDate.getUTCFullYear(),
18982                         month = this.viewDate.getUTCMonth();
18983
18984                     if (className.indexOf('old') > -1) {
18985                         if(month === 0 ){
18986                             month = 11;
18987                             year -= 1;
18988                         }else{
18989                             month -= 1;
18990                         }
18991                     } else if (className.indexOf('new') > -1) {
18992                         if (month == 11) {
18993                             month = 0;
18994                             year += 1;
18995                         } else {
18996                             month += 1;
18997                         }
18998                     }
18999                     //Roo.log([year,month,day]);
19000                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19001                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19002 //                    this.fill();
19003                     //Roo.log(this.formatDate(this.date));
19004                     this.setValue(this.formatDate(this.date));
19005                     this.hide();
19006                 }
19007                 break;
19008         }
19009     },
19010     
19011     setStartDate: function(startDate)
19012     {
19013         this.startDate = startDate || -Infinity;
19014         if (this.startDate !== -Infinity) {
19015             this.startDate = this.parseDate(this.startDate);
19016         }
19017         this.update();
19018         this.updateNavArrows();
19019     },
19020
19021     setEndDate: function(endDate)
19022     {
19023         this.endDate = endDate || Infinity;
19024         if (this.endDate !== Infinity) {
19025             this.endDate = this.parseDate(this.endDate);
19026         }
19027         this.update();
19028         this.updateNavArrows();
19029     },
19030     
19031     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19032     {
19033         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19034         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19035             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19036         }
19037         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19038             return parseInt(d, 10);
19039         });
19040         this.update();
19041         this.updateNavArrows();
19042     },
19043     
19044     updateNavArrows: function() 
19045     {
19046         if(this.singleMode){
19047             return;
19048         }
19049         
19050         var d = new Date(this.viewDate),
19051         year = d.getUTCFullYear(),
19052         month = d.getUTCMonth();
19053         
19054         Roo.each(this.picker().select('.prev', true).elements, function(v){
19055             v.show();
19056             switch (this.viewMode) {
19057                 case 0:
19058
19059                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19060                         v.hide();
19061                     }
19062                     break;
19063                 case 1:
19064                 case 2:
19065                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19066                         v.hide();
19067                     }
19068                     break;
19069             }
19070         });
19071         
19072         Roo.each(this.picker().select('.next', true).elements, function(v){
19073             v.show();
19074             switch (this.viewMode) {
19075                 case 0:
19076
19077                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19078                         v.hide();
19079                     }
19080                     break;
19081                 case 1:
19082                 case 2:
19083                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19084                         v.hide();
19085                     }
19086                     break;
19087             }
19088         })
19089     },
19090     
19091     moveMonth: function(date, dir)
19092     {
19093         if (!dir) {
19094             return date;
19095         }
19096         var new_date = new Date(date.valueOf()),
19097         day = new_date.getUTCDate(),
19098         month = new_date.getUTCMonth(),
19099         mag = Math.abs(dir),
19100         new_month, test;
19101         dir = dir > 0 ? 1 : -1;
19102         if (mag == 1){
19103             test = dir == -1
19104             // If going back one month, make sure month is not current month
19105             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19106             ? function(){
19107                 return new_date.getUTCMonth() == month;
19108             }
19109             // If going forward one month, make sure month is as expected
19110             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19111             : function(){
19112                 return new_date.getUTCMonth() != new_month;
19113             };
19114             new_month = month + dir;
19115             new_date.setUTCMonth(new_month);
19116             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19117             if (new_month < 0 || new_month > 11) {
19118                 new_month = (new_month + 12) % 12;
19119             }
19120         } else {
19121             // For magnitudes >1, move one month at a time...
19122             for (var i=0; i<mag; i++) {
19123                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19124                 new_date = this.moveMonth(new_date, dir);
19125             }
19126             // ...then reset the day, keeping it in the new month
19127             new_month = new_date.getUTCMonth();
19128             new_date.setUTCDate(day);
19129             test = function(){
19130                 return new_month != new_date.getUTCMonth();
19131             };
19132         }
19133         // Common date-resetting loop -- if date is beyond end of month, make it
19134         // end of month
19135         while (test()){
19136             new_date.setUTCDate(--day);
19137             new_date.setUTCMonth(new_month);
19138         }
19139         return new_date;
19140     },
19141
19142     moveYear: function(date, dir)
19143     {
19144         return this.moveMonth(date, dir*12);
19145     },
19146
19147     dateWithinRange: function(date)
19148     {
19149         return date >= this.startDate && date <= this.endDate;
19150     },
19151
19152     
19153     remove: function() 
19154     {
19155         this.picker().remove();
19156     },
19157     
19158     validateValue : function(value)
19159     {
19160         if(this.getVisibilityEl().hasClass('hidden')){
19161             return true;
19162         }
19163         
19164         if(value.length < 1)  {
19165             if(this.allowBlank){
19166                 return true;
19167             }
19168             return false;
19169         }
19170         
19171         if(value.length < this.minLength){
19172             return false;
19173         }
19174         if(value.length > this.maxLength){
19175             return false;
19176         }
19177         if(this.vtype){
19178             var vt = Roo.form.VTypes;
19179             if(!vt[this.vtype](value, this)){
19180                 return false;
19181             }
19182         }
19183         if(typeof this.validator == "function"){
19184             var msg = this.validator(value);
19185             if(msg !== true){
19186                 return false;
19187             }
19188         }
19189         
19190         if(this.regex && !this.regex.test(value)){
19191             return false;
19192         }
19193         
19194         if(typeof(this.parseDate(value)) == 'undefined'){
19195             return false;
19196         }
19197         
19198         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19199             return false;
19200         }      
19201         
19202         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19203             return false;
19204         } 
19205         
19206         
19207         return true;
19208     },
19209     
19210     setVisible : function(visible)
19211     {
19212         if(!this.getEl()){
19213             return;
19214         }
19215         
19216         this.getEl().removeClass('hidden');
19217         
19218         if(visible){
19219             return;
19220         }
19221         
19222         this.getEl().addClass('hidden');
19223     }
19224    
19225 });
19226
19227 Roo.apply(Roo.bootstrap.DateField,  {
19228     
19229     head : {
19230         tag: 'thead',
19231         cn: [
19232         {
19233             tag: 'tr',
19234             cn: [
19235             {
19236                 tag: 'th',
19237                 cls: 'prev',
19238                 html: '<i class="fa fa-arrow-left"/>'
19239             },
19240             {
19241                 tag: 'th',
19242                 cls: 'switch',
19243                 colspan: '5'
19244             },
19245             {
19246                 tag: 'th',
19247                 cls: 'next',
19248                 html: '<i class="fa fa-arrow-right"/>'
19249             }
19250
19251             ]
19252         }
19253         ]
19254     },
19255     
19256     content : {
19257         tag: 'tbody',
19258         cn: [
19259         {
19260             tag: 'tr',
19261             cn: [
19262             {
19263                 tag: 'td',
19264                 colspan: '7'
19265             }
19266             ]
19267         }
19268         ]
19269     },
19270     
19271     footer : {
19272         tag: 'tfoot',
19273         cn: [
19274         {
19275             tag: 'tr',
19276             cn: [
19277             {
19278                 tag: 'th',
19279                 colspan: '7',
19280                 cls: 'today'
19281             }
19282                     
19283             ]
19284         }
19285         ]
19286     },
19287     
19288     dates:{
19289         en: {
19290             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19291             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19292             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19293             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19294             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19295             today: "Today"
19296         }
19297     },
19298     
19299     modes: [
19300     {
19301         clsName: 'days',
19302         navFnc: 'Month',
19303         navStep: 1
19304     },
19305     {
19306         clsName: 'months',
19307         navFnc: 'FullYear',
19308         navStep: 1
19309     },
19310     {
19311         clsName: 'years',
19312         navFnc: 'FullYear',
19313         navStep: 10
19314     }]
19315 });
19316
19317 Roo.apply(Roo.bootstrap.DateField,  {
19318   
19319     template : {
19320         tag: 'div',
19321         cls: 'datepicker dropdown-menu roo-dynamic',
19322         cn: [
19323         {
19324             tag: 'div',
19325             cls: 'datepicker-days',
19326             cn: [
19327             {
19328                 tag: 'table',
19329                 cls: 'table-condensed',
19330                 cn:[
19331                 Roo.bootstrap.DateField.head,
19332                 {
19333                     tag: 'tbody'
19334                 },
19335                 Roo.bootstrap.DateField.footer
19336                 ]
19337             }
19338             ]
19339         },
19340         {
19341             tag: 'div',
19342             cls: 'datepicker-months',
19343             cn: [
19344             {
19345                 tag: 'table',
19346                 cls: 'table-condensed',
19347                 cn:[
19348                 Roo.bootstrap.DateField.head,
19349                 Roo.bootstrap.DateField.content,
19350                 Roo.bootstrap.DateField.footer
19351                 ]
19352             }
19353             ]
19354         },
19355         {
19356             tag: 'div',
19357             cls: 'datepicker-years',
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     }
19372 });
19373
19374  
19375
19376  /*
19377  * - LGPL
19378  *
19379  * TimeField
19380  * 
19381  */
19382
19383 /**
19384  * @class Roo.bootstrap.TimeField
19385  * @extends Roo.bootstrap.Input
19386  * Bootstrap DateField class
19387  * 
19388  * 
19389  * @constructor
19390  * Create a new TimeField
19391  * @param {Object} config The config object
19392  */
19393
19394 Roo.bootstrap.TimeField = function(config){
19395     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19396     this.addEvents({
19397             /**
19398              * @event show
19399              * Fires when this field show.
19400              * @param {Roo.bootstrap.DateField} thisthis
19401              * @param {Mixed} date The date value
19402              */
19403             show : true,
19404             /**
19405              * @event show
19406              * Fires when this field hide.
19407              * @param {Roo.bootstrap.DateField} this
19408              * @param {Mixed} date The date value
19409              */
19410             hide : true,
19411             /**
19412              * @event select
19413              * Fires when select a date.
19414              * @param {Roo.bootstrap.DateField} this
19415              * @param {Mixed} date The date value
19416              */
19417             select : true
19418         });
19419 };
19420
19421 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19422     
19423     /**
19424      * @cfg {String} format
19425      * The default time format string which can be overriden for localization support.  The format must be
19426      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19427      */
19428     format : "H:i",
19429        
19430     onRender: function(ct, position)
19431     {
19432         
19433         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19434                 
19435         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19436         
19437         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19438         
19439         this.pop = this.picker().select('>.datepicker-time',true).first();
19440         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19441         
19442         this.picker().on('mousedown', this.onMousedown, this);
19443         this.picker().on('click', this.onClick, this);
19444         
19445         this.picker().addClass('datepicker-dropdown');
19446     
19447         this.fillTime();
19448         this.update();
19449             
19450         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19451         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19452         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19453         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19454         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19455         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19456
19457     },
19458     
19459     fireKey: function(e){
19460         if (!this.picker().isVisible()){
19461             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19462                 this.show();
19463             }
19464             return;
19465         }
19466
19467         e.preventDefault();
19468         
19469         switch(e.keyCode){
19470             case 27: // escape
19471                 this.hide();
19472                 break;
19473             case 37: // left
19474             case 39: // right
19475                 this.onTogglePeriod();
19476                 break;
19477             case 38: // up
19478                 this.onIncrementMinutes();
19479                 break;
19480             case 40: // down
19481                 this.onDecrementMinutes();
19482                 break;
19483             case 13: // enter
19484             case 9: // tab
19485                 this.setTime();
19486                 break;
19487         }
19488     },
19489     
19490     onClick: function(e) {
19491         e.stopPropagation();
19492         e.preventDefault();
19493     },
19494     
19495     picker : function()
19496     {
19497         return this.el.select('.datepicker', true).first();
19498     },
19499     
19500     fillTime: function()
19501     {    
19502         var time = this.pop.select('tbody', true).first();
19503         
19504         time.dom.innerHTML = '';
19505         
19506         time.createChild({
19507             tag: 'tr',
19508             cn: [
19509                 {
19510                     tag: 'td',
19511                     cn: [
19512                         {
19513                             tag: 'a',
19514                             href: '#',
19515                             cls: 'btn',
19516                             cn: [
19517                                 {
19518                                     tag: 'span',
19519                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19520                                 }
19521                             ]
19522                         } 
19523                     ]
19524                 },
19525                 {
19526                     tag: 'td',
19527                     cls: 'separator'
19528                 },
19529                 {
19530                     tag: 'td',
19531                     cn: [
19532                         {
19533                             tag: 'a',
19534                             href: '#',
19535                             cls: 'btn',
19536                             cn: [
19537                                 {
19538                                     tag: 'span',
19539                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19540                                 }
19541                             ]
19542                         }
19543                     ]
19544                 },
19545                 {
19546                     tag: 'td',
19547                     cls: 'separator'
19548                 }
19549             ]
19550         });
19551         
19552         time.createChild({
19553             tag: 'tr',
19554             cn: [
19555                 {
19556                     tag: 'td',
19557                     cn: [
19558                         {
19559                             tag: 'span',
19560                             cls: 'timepicker-hour',
19561                             html: '00'
19562                         }  
19563                     ]
19564                 },
19565                 {
19566                     tag: 'td',
19567                     cls: 'separator',
19568                     html: ':'
19569                 },
19570                 {
19571                     tag: 'td',
19572                     cn: [
19573                         {
19574                             tag: 'span',
19575                             cls: 'timepicker-minute',
19576                             html: '00'
19577                         }  
19578                     ]
19579                 },
19580                 {
19581                     tag: 'td',
19582                     cls: 'separator'
19583                 },
19584                 {
19585                     tag: 'td',
19586                     cn: [
19587                         {
19588                             tag: 'button',
19589                             type: 'button',
19590                             cls: 'btn btn-primary period',
19591                             html: 'AM'
19592                             
19593                         }
19594                     ]
19595                 }
19596             ]
19597         });
19598         
19599         time.createChild({
19600             tag: 'tr',
19601             cn: [
19602                 {
19603                     tag: 'td',
19604                     cn: [
19605                         {
19606                             tag: 'a',
19607                             href: '#',
19608                             cls: 'btn',
19609                             cn: [
19610                                 {
19611                                     tag: 'span',
19612                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19613                                 }
19614                             ]
19615                         }
19616                     ]
19617                 },
19618                 {
19619                     tag: 'td',
19620                     cls: 'separator'
19621                 },
19622                 {
19623                     tag: 'td',
19624                     cn: [
19625                         {
19626                             tag: 'a',
19627                             href: '#',
19628                             cls: 'btn',
19629                             cn: [
19630                                 {
19631                                     tag: 'span',
19632                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19633                                 }
19634                             ]
19635                         }
19636                     ]
19637                 },
19638                 {
19639                     tag: 'td',
19640                     cls: 'separator'
19641                 }
19642             ]
19643         });
19644         
19645     },
19646     
19647     update: function()
19648     {
19649         
19650         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19651         
19652         this.fill();
19653     },
19654     
19655     fill: function() 
19656     {
19657         var hours = this.time.getHours();
19658         var minutes = this.time.getMinutes();
19659         var period = 'AM';
19660         
19661         if(hours > 11){
19662             period = 'PM';
19663         }
19664         
19665         if(hours == 0){
19666             hours = 12;
19667         }
19668         
19669         
19670         if(hours > 12){
19671             hours = hours - 12;
19672         }
19673         
19674         if(hours < 10){
19675             hours = '0' + hours;
19676         }
19677         
19678         if(minutes < 10){
19679             minutes = '0' + minutes;
19680         }
19681         
19682         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19683         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19684         this.pop.select('button', true).first().dom.innerHTML = period;
19685         
19686     },
19687     
19688     place: function()
19689     {   
19690         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19691         
19692         var cls = ['bottom'];
19693         
19694         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19695             cls.pop();
19696             cls.push('top');
19697         }
19698         
19699         cls.push('right');
19700         
19701         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19702             cls.pop();
19703             cls.push('left');
19704         }
19705         
19706         this.picker().addClass(cls.join('-'));
19707         
19708         var _this = this;
19709         
19710         Roo.each(cls, function(c){
19711             if(c == 'bottom'){
19712                 _this.picker().setTop(_this.inputEl().getHeight());
19713                 return;
19714             }
19715             if(c == 'top'){
19716                 _this.picker().setTop(0 - _this.picker().getHeight());
19717                 return;
19718             }
19719             
19720             if(c == 'left'){
19721                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19722                 return;
19723             }
19724             if(c == 'right'){
19725                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19726                 return;
19727             }
19728         });
19729         
19730     },
19731   
19732     onFocus : function()
19733     {
19734         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19735         this.show();
19736     },
19737     
19738     onBlur : function()
19739     {
19740         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19741         this.hide();
19742     },
19743     
19744     show : function()
19745     {
19746         this.picker().show();
19747         this.pop.show();
19748         this.update();
19749         this.place();
19750         
19751         this.fireEvent('show', this, this.date);
19752     },
19753     
19754     hide : function()
19755     {
19756         this.picker().hide();
19757         this.pop.hide();
19758         
19759         this.fireEvent('hide', this, this.date);
19760     },
19761     
19762     setTime : function()
19763     {
19764         this.hide();
19765         this.setValue(this.time.format(this.format));
19766         
19767         this.fireEvent('select', this, this.date);
19768         
19769         
19770     },
19771     
19772     onMousedown: function(e){
19773         e.stopPropagation();
19774         e.preventDefault();
19775     },
19776     
19777     onIncrementHours: function()
19778     {
19779         Roo.log('onIncrementHours');
19780         this.time = this.time.add(Date.HOUR, 1);
19781         this.update();
19782         
19783     },
19784     
19785     onDecrementHours: function()
19786     {
19787         Roo.log('onDecrementHours');
19788         this.time = this.time.add(Date.HOUR, -1);
19789         this.update();
19790     },
19791     
19792     onIncrementMinutes: function()
19793     {
19794         Roo.log('onIncrementMinutes');
19795         this.time = this.time.add(Date.MINUTE, 1);
19796         this.update();
19797     },
19798     
19799     onDecrementMinutes: function()
19800     {
19801         Roo.log('onDecrementMinutes');
19802         this.time = this.time.add(Date.MINUTE, -1);
19803         this.update();
19804     },
19805     
19806     onTogglePeriod: function()
19807     {
19808         Roo.log('onTogglePeriod');
19809         this.time = this.time.add(Date.HOUR, 12);
19810         this.update();
19811     }
19812     
19813    
19814 });
19815
19816 Roo.apply(Roo.bootstrap.TimeField,  {
19817     
19818     content : {
19819         tag: 'tbody',
19820         cn: [
19821             {
19822                 tag: 'tr',
19823                 cn: [
19824                 {
19825                     tag: 'td',
19826                     colspan: '7'
19827                 }
19828                 ]
19829             }
19830         ]
19831     },
19832     
19833     footer : {
19834         tag: 'tfoot',
19835         cn: [
19836             {
19837                 tag: 'tr',
19838                 cn: [
19839                 {
19840                     tag: 'th',
19841                     colspan: '7',
19842                     cls: '',
19843                     cn: [
19844                         {
19845                             tag: 'button',
19846                             cls: 'btn btn-info ok',
19847                             html: 'OK'
19848                         }
19849                     ]
19850                 }
19851
19852                 ]
19853             }
19854         ]
19855     }
19856 });
19857
19858 Roo.apply(Roo.bootstrap.TimeField,  {
19859   
19860     template : {
19861         tag: 'div',
19862         cls: 'datepicker dropdown-menu',
19863         cn: [
19864             {
19865                 tag: 'div',
19866                 cls: 'datepicker-time',
19867                 cn: [
19868                 {
19869                     tag: 'table',
19870                     cls: 'table-condensed',
19871                     cn:[
19872                     Roo.bootstrap.TimeField.content,
19873                     Roo.bootstrap.TimeField.footer
19874                     ]
19875                 }
19876                 ]
19877             }
19878         ]
19879     }
19880 });
19881
19882  
19883
19884  /*
19885  * - LGPL
19886  *
19887  * MonthField
19888  * 
19889  */
19890
19891 /**
19892  * @class Roo.bootstrap.MonthField
19893  * @extends Roo.bootstrap.Input
19894  * Bootstrap MonthField class
19895  * 
19896  * @cfg {String} language default en
19897  * 
19898  * @constructor
19899  * Create a new MonthField
19900  * @param {Object} config The config object
19901  */
19902
19903 Roo.bootstrap.MonthField = function(config){
19904     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19905     
19906     this.addEvents({
19907         /**
19908          * @event show
19909          * Fires when this field show.
19910          * @param {Roo.bootstrap.MonthField} this
19911          * @param {Mixed} date The date value
19912          */
19913         show : true,
19914         /**
19915          * @event show
19916          * Fires when this field hide.
19917          * @param {Roo.bootstrap.MonthField} this
19918          * @param {Mixed} date The date value
19919          */
19920         hide : true,
19921         /**
19922          * @event select
19923          * Fires when select a date.
19924          * @param {Roo.bootstrap.MonthField} this
19925          * @param {String} oldvalue The old value
19926          * @param {String} newvalue The new value
19927          */
19928         select : true
19929     });
19930 };
19931
19932 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19933     
19934     onRender: function(ct, position)
19935     {
19936         
19937         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19938         
19939         this.language = this.language || 'en';
19940         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19941         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19942         
19943         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19944         this.isInline = false;
19945         this.isInput = true;
19946         this.component = this.el.select('.add-on', true).first() || false;
19947         this.component = (this.component && this.component.length === 0) ? false : this.component;
19948         this.hasInput = this.component && this.inputEL().length;
19949         
19950         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19951         
19952         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19953         
19954         this.picker().on('mousedown', this.onMousedown, this);
19955         this.picker().on('click', this.onClick, this);
19956         
19957         this.picker().addClass('datepicker-dropdown');
19958         
19959         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19960             v.setStyle('width', '189px');
19961         });
19962         
19963         this.fillMonths();
19964         
19965         this.update();
19966         
19967         if(this.isInline) {
19968             this.show();
19969         }
19970         
19971     },
19972     
19973     setValue: function(v, suppressEvent)
19974     {   
19975         var o = this.getValue();
19976         
19977         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19978         
19979         this.update();
19980
19981         if(suppressEvent !== true){
19982             this.fireEvent('select', this, o, v);
19983         }
19984         
19985     },
19986     
19987     getValue: function()
19988     {
19989         return this.value;
19990     },
19991     
19992     onClick: function(e) 
19993     {
19994         e.stopPropagation();
19995         e.preventDefault();
19996         
19997         var target = e.getTarget();
19998         
19999         if(target.nodeName.toLowerCase() === 'i'){
20000             target = Roo.get(target).dom.parentNode;
20001         }
20002         
20003         var nodeName = target.nodeName;
20004         var className = target.className;
20005         var html = target.innerHTML;
20006         
20007         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20008             return;
20009         }
20010         
20011         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20012         
20013         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20014         
20015         this.hide();
20016                         
20017     },
20018     
20019     picker : function()
20020     {
20021         return this.pickerEl;
20022     },
20023     
20024     fillMonths: function()
20025     {    
20026         var i = 0;
20027         var months = this.picker().select('>.datepicker-months td', true).first();
20028         
20029         months.dom.innerHTML = '';
20030         
20031         while (i < 12) {
20032             var month = {
20033                 tag: 'span',
20034                 cls: 'month',
20035                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20036             };
20037             
20038             months.createChild(month);
20039         }
20040         
20041     },
20042     
20043     update: function()
20044     {
20045         var _this = this;
20046         
20047         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20048             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20049         }
20050         
20051         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20052             e.removeClass('active');
20053             
20054             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20055                 e.addClass('active');
20056             }
20057         })
20058     },
20059     
20060     place: function()
20061     {
20062         if(this.isInline) {
20063             return;
20064         }
20065         
20066         this.picker().removeClass(['bottom', 'top']);
20067         
20068         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20069             /*
20070              * place to the top of element!
20071              *
20072              */
20073             
20074             this.picker().addClass('top');
20075             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20076             
20077             return;
20078         }
20079         
20080         this.picker().addClass('bottom');
20081         
20082         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20083     },
20084     
20085     onFocus : function()
20086     {
20087         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20088         this.show();
20089     },
20090     
20091     onBlur : function()
20092     {
20093         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20094         
20095         var d = this.inputEl().getValue();
20096         
20097         this.setValue(d);
20098                 
20099         this.hide();
20100     },
20101     
20102     show : function()
20103     {
20104         this.picker().show();
20105         this.picker().select('>.datepicker-months', true).first().show();
20106         this.update();
20107         this.place();
20108         
20109         this.fireEvent('show', this, this.date);
20110     },
20111     
20112     hide : function()
20113     {
20114         if(this.isInline) {
20115             return;
20116         }
20117         this.picker().hide();
20118         this.fireEvent('hide', this, this.date);
20119         
20120     },
20121     
20122     onMousedown: function(e)
20123     {
20124         e.stopPropagation();
20125         e.preventDefault();
20126     },
20127     
20128     keyup: function(e)
20129     {
20130         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20131         this.update();
20132     },
20133
20134     fireKey: function(e)
20135     {
20136         if (!this.picker().isVisible()){
20137             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20138                 this.show();
20139             }
20140             return;
20141         }
20142         
20143         var dir;
20144         
20145         switch(e.keyCode){
20146             case 27: // escape
20147                 this.hide();
20148                 e.preventDefault();
20149                 break;
20150             case 37: // left
20151             case 39: // right
20152                 dir = e.keyCode == 37 ? -1 : 1;
20153                 
20154                 this.vIndex = this.vIndex + dir;
20155                 
20156                 if(this.vIndex < 0){
20157                     this.vIndex = 0;
20158                 }
20159                 
20160                 if(this.vIndex > 11){
20161                     this.vIndex = 11;
20162                 }
20163                 
20164                 if(isNaN(this.vIndex)){
20165                     this.vIndex = 0;
20166                 }
20167                 
20168                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20169                 
20170                 break;
20171             case 38: // up
20172             case 40: // down
20173                 
20174                 dir = e.keyCode == 38 ? -1 : 1;
20175                 
20176                 this.vIndex = this.vIndex + dir * 4;
20177                 
20178                 if(this.vIndex < 0){
20179                     this.vIndex = 0;
20180                 }
20181                 
20182                 if(this.vIndex > 11){
20183                     this.vIndex = 11;
20184                 }
20185                 
20186                 if(isNaN(this.vIndex)){
20187                     this.vIndex = 0;
20188                 }
20189                 
20190                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20191                 break;
20192                 
20193             case 13: // enter
20194                 
20195                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20196                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20197                 }
20198                 
20199                 this.hide();
20200                 e.preventDefault();
20201                 break;
20202             case 9: // tab
20203                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20204                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20205                 }
20206                 this.hide();
20207                 break;
20208             case 16: // shift
20209             case 17: // ctrl
20210             case 18: // alt
20211                 break;
20212             default :
20213                 this.hide();
20214                 
20215         }
20216     },
20217     
20218     remove: function() 
20219     {
20220         this.picker().remove();
20221     }
20222    
20223 });
20224
20225 Roo.apply(Roo.bootstrap.MonthField,  {
20226     
20227     content : {
20228         tag: 'tbody',
20229         cn: [
20230         {
20231             tag: 'tr',
20232             cn: [
20233             {
20234                 tag: 'td',
20235                 colspan: '7'
20236             }
20237             ]
20238         }
20239         ]
20240     },
20241     
20242     dates:{
20243         en: {
20244             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20245             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20246         }
20247     }
20248 });
20249
20250 Roo.apply(Roo.bootstrap.MonthField,  {
20251   
20252     template : {
20253         tag: 'div',
20254         cls: 'datepicker dropdown-menu roo-dynamic',
20255         cn: [
20256             {
20257                 tag: 'div',
20258                 cls: 'datepicker-months',
20259                 cn: [
20260                 {
20261                     tag: 'table',
20262                     cls: 'table-condensed',
20263                     cn:[
20264                         Roo.bootstrap.DateField.content
20265                     ]
20266                 }
20267                 ]
20268             }
20269         ]
20270     }
20271 });
20272
20273  
20274
20275  
20276  /*
20277  * - LGPL
20278  *
20279  * CheckBox
20280  * 
20281  */
20282
20283 /**
20284  * @class Roo.bootstrap.CheckBox
20285  * @extends Roo.bootstrap.Input
20286  * Bootstrap CheckBox class
20287  * 
20288  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20289  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20290  * @cfg {String} boxLabel The text that appears beside the checkbox
20291  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20292  * @cfg {Boolean} checked initnal the element
20293  * @cfg {Boolean} inline inline the element (default false)
20294  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20295  * @cfg {String} tooltip label tooltip
20296  * 
20297  * @constructor
20298  * Create a new CheckBox
20299  * @param {Object} config The config object
20300  */
20301
20302 Roo.bootstrap.CheckBox = function(config){
20303     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20304    
20305     this.addEvents({
20306         /**
20307         * @event check
20308         * Fires when the element is checked or unchecked.
20309         * @param {Roo.bootstrap.CheckBox} this This input
20310         * @param {Boolean} checked The new checked value
20311         */
20312        check : true,
20313        /**
20314         * @event click
20315         * Fires when the element is click.
20316         * @param {Roo.bootstrap.CheckBox} this This input
20317         */
20318        click : true
20319     });
20320     
20321 };
20322
20323 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20324   
20325     inputType: 'checkbox',
20326     inputValue: 1,
20327     valueOff: 0,
20328     boxLabel: false,
20329     checked: false,
20330     weight : false,
20331     inline: false,
20332     tooltip : '',
20333     
20334     getAutoCreate : function()
20335     {
20336         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20337         
20338         var id = Roo.id();
20339         
20340         var cfg = {};
20341         
20342         cfg.cls = 'form-group ' + this.inputType; //input-group
20343         
20344         if(this.inline){
20345             cfg.cls += ' ' + this.inputType + '-inline';
20346         }
20347         
20348         var input =  {
20349             tag: 'input',
20350             id : id,
20351             type : this.inputType,
20352             value : this.inputValue,
20353             cls : 'roo-' + this.inputType, //'form-box',
20354             placeholder : this.placeholder || ''
20355             
20356         };
20357         
20358         if(this.inputType != 'radio'){
20359             var hidden =  {
20360                 tag: 'input',
20361                 type : 'hidden',
20362                 cls : 'roo-hidden-value',
20363                 value : this.checked ? this.inputValue : this.valueOff
20364             };
20365         }
20366         
20367             
20368         if (this.weight) { // Validity check?
20369             cfg.cls += " " + this.inputType + "-" + this.weight;
20370         }
20371         
20372         if (this.disabled) {
20373             input.disabled=true;
20374         }
20375         
20376         if(this.checked){
20377             input.checked = this.checked;
20378         }
20379         
20380         if (this.name) {
20381             
20382             input.name = this.name;
20383             
20384             if(this.inputType != 'radio'){
20385                 hidden.name = this.name;
20386                 input.name = '_hidden_' + this.name;
20387             }
20388         }
20389         
20390         if (this.size) {
20391             input.cls += ' input-' + this.size;
20392         }
20393         
20394         var settings=this;
20395         
20396         ['xs','sm','md','lg'].map(function(size){
20397             if (settings[size]) {
20398                 cfg.cls += ' col-' + size + '-' + settings[size];
20399             }
20400         });
20401         
20402         var inputblock = input;
20403          
20404         if (this.before || this.after) {
20405             
20406             inputblock = {
20407                 cls : 'input-group',
20408                 cn :  [] 
20409             };
20410             
20411             if (this.before) {
20412                 inputblock.cn.push({
20413                     tag :'span',
20414                     cls : 'input-group-addon',
20415                     html : this.before
20416                 });
20417             }
20418             
20419             inputblock.cn.push(input);
20420             
20421             if(this.inputType != 'radio'){
20422                 inputblock.cn.push(hidden);
20423             }
20424             
20425             if (this.after) {
20426                 inputblock.cn.push({
20427                     tag :'span',
20428                     cls : 'input-group-addon',
20429                     html : this.after
20430                 });
20431             }
20432             
20433         }
20434         
20435         if (align ==='left' && this.fieldLabel.length) {
20436 //                Roo.log("left and has label");
20437             cfg.cn = [
20438                 {
20439                     tag: 'label',
20440                     'for' :  id,
20441                     cls : 'control-label',
20442                     html : this.fieldLabel
20443                 },
20444                 {
20445                     cls : "", 
20446                     cn: [
20447                         inputblock
20448                     ]
20449                 }
20450             ];
20451             
20452             if(this.labelWidth > 12){
20453                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20454             }
20455             
20456             if(this.labelWidth < 13 && this.labelmd == 0){
20457                 this.labelmd = this.labelWidth;
20458             }
20459             
20460             if(this.labellg > 0){
20461                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20462                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20463             }
20464             
20465             if(this.labelmd > 0){
20466                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20467                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20468             }
20469             
20470             if(this.labelsm > 0){
20471                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20472                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20473             }
20474             
20475             if(this.labelxs > 0){
20476                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20477                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20478             }
20479             
20480         } else if ( this.fieldLabel.length) {
20481 //                Roo.log(" label");
20482                 cfg.cn = [
20483                    
20484                     {
20485                         tag: this.boxLabel ? 'span' : 'label',
20486                         'for': id,
20487                         cls: 'control-label box-input-label',
20488                         //cls : 'input-group-addon',
20489                         html : this.fieldLabel
20490                     },
20491                     
20492                     inputblock
20493                     
20494                 ];
20495
20496         } else {
20497             
20498 //                Roo.log(" no label && no align");
20499                 cfg.cn = [  inputblock ] ;
20500                 
20501                 
20502         }
20503         
20504         if(this.boxLabel){
20505              var boxLabelCfg = {
20506                 tag: 'label',
20507                 //'for': id, // box label is handled by onclick - so no for...
20508                 cls: 'box-label',
20509                 html: this.boxLabel
20510             };
20511             
20512             if(this.tooltip){
20513                 boxLabelCfg.tooltip = this.tooltip;
20514             }
20515              
20516             cfg.cn.push(boxLabelCfg);
20517         }
20518         
20519         if(this.inputType != 'radio'){
20520             cfg.cn.push(hidden);
20521         }
20522         
20523         return cfg;
20524         
20525     },
20526     
20527     /**
20528      * return the real input element.
20529      */
20530     inputEl: function ()
20531     {
20532         return this.el.select('input.roo-' + this.inputType,true).first();
20533     },
20534     hiddenEl: function ()
20535     {
20536         return this.el.select('input.roo-hidden-value',true).first();
20537     },
20538     
20539     labelEl: function()
20540     {
20541         return this.el.select('label.control-label',true).first();
20542     },
20543     /* depricated... */
20544     
20545     label: function()
20546     {
20547         return this.labelEl();
20548     },
20549     
20550     boxLabelEl: function()
20551     {
20552         return this.el.select('label.box-label',true).first();
20553     },
20554     
20555     initEvents : function()
20556     {
20557 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20558         
20559         this.inputEl().on('click', this.onClick,  this);
20560         
20561         if (this.boxLabel) { 
20562             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20563         }
20564         
20565         this.startValue = this.getValue();
20566         
20567         if(this.groupId){
20568             Roo.bootstrap.CheckBox.register(this);
20569         }
20570     },
20571     
20572     onClick : function(e)
20573     {   
20574         if(this.fireEvent('click', this, e) !== false){
20575             this.setChecked(!this.checked);
20576         }
20577         
20578     },
20579     
20580     setChecked : function(state,suppressEvent)
20581     {
20582         this.startValue = this.getValue();
20583
20584         if(this.inputType == 'radio'){
20585             
20586             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20587                 e.dom.checked = false;
20588             });
20589             
20590             this.inputEl().dom.checked = true;
20591             
20592             this.inputEl().dom.value = this.inputValue;
20593             
20594             if(suppressEvent !== true){
20595                 this.fireEvent('check', this, true);
20596             }
20597             
20598             this.validate();
20599             
20600             return;
20601         }
20602         
20603         this.checked = state;
20604         
20605         this.inputEl().dom.checked = state;
20606         
20607         
20608         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20609         
20610         if(suppressEvent !== true){
20611             this.fireEvent('check', this, state);
20612         }
20613         
20614         this.validate();
20615     },
20616     
20617     getValue : function()
20618     {
20619         if(this.inputType == 'radio'){
20620             return this.getGroupValue();
20621         }
20622         
20623         return this.hiddenEl().dom.value;
20624         
20625     },
20626     
20627     getGroupValue : function()
20628     {
20629         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20630             return '';
20631         }
20632         
20633         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20634     },
20635     
20636     setValue : function(v,suppressEvent)
20637     {
20638         if(this.inputType == 'radio'){
20639             this.setGroupValue(v, suppressEvent);
20640             return;
20641         }
20642         
20643         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20644         
20645         this.validate();
20646     },
20647     
20648     setGroupValue : function(v, suppressEvent)
20649     {
20650         this.startValue = this.getValue();
20651         
20652         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20653             e.dom.checked = false;
20654             
20655             if(e.dom.value == v){
20656                 e.dom.checked = true;
20657             }
20658         });
20659         
20660         if(suppressEvent !== true){
20661             this.fireEvent('check', this, true);
20662         }
20663
20664         this.validate();
20665         
20666         return;
20667     },
20668     
20669     validate : function()
20670     {
20671         if(this.getVisibilityEl().hasClass('hidden')){
20672             return true;
20673         }
20674         
20675         if(
20676                 this.disabled || 
20677                 (this.inputType == 'radio' && this.validateRadio()) ||
20678                 (this.inputType == 'checkbox' && this.validateCheckbox())
20679         ){
20680             this.markValid();
20681             return true;
20682         }
20683         
20684         this.markInvalid();
20685         return false;
20686     },
20687     
20688     validateRadio : function()
20689     {
20690         if(this.getVisibilityEl().hasClass('hidden')){
20691             return true;
20692         }
20693         
20694         if(this.allowBlank){
20695             return true;
20696         }
20697         
20698         var valid = false;
20699         
20700         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20701             if(!e.dom.checked){
20702                 return;
20703             }
20704             
20705             valid = true;
20706             
20707             return false;
20708         });
20709         
20710         return valid;
20711     },
20712     
20713     validateCheckbox : function()
20714     {
20715         if(!this.groupId){
20716             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20717             //return (this.getValue() == this.inputValue) ? true : false;
20718         }
20719         
20720         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20721         
20722         if(!group){
20723             return false;
20724         }
20725         
20726         var r = false;
20727         
20728         for(var i in group){
20729             if(group[i].el.isVisible(true)){
20730                 r = false;
20731                 break;
20732             }
20733             
20734             r = true;
20735         }
20736         
20737         for(var i in group){
20738             if(r){
20739                 break;
20740             }
20741             
20742             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20743         }
20744         
20745         return r;
20746     },
20747     
20748     /**
20749      * Mark this field as valid
20750      */
20751     markValid : function()
20752     {
20753         var _this = this;
20754         
20755         this.fireEvent('valid', this);
20756         
20757         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20758         
20759         if(this.groupId){
20760             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20761         }
20762         
20763         if(label){
20764             label.markValid();
20765         }
20766
20767         if(this.inputType == 'radio'){
20768             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20769                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20770                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20771             });
20772             
20773             return;
20774         }
20775
20776         if(!this.groupId){
20777             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20778             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20779             return;
20780         }
20781         
20782         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20783         
20784         if(!group){
20785             return;
20786         }
20787         
20788         for(var i in group){
20789             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20790             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20791         }
20792     },
20793     
20794      /**
20795      * Mark this field as invalid
20796      * @param {String} msg The validation message
20797      */
20798     markInvalid : function(msg)
20799     {
20800         if(this.allowBlank){
20801             return;
20802         }
20803         
20804         var _this = this;
20805         
20806         this.fireEvent('invalid', this, msg);
20807         
20808         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20809         
20810         if(this.groupId){
20811             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20812         }
20813         
20814         if(label){
20815             label.markInvalid();
20816         }
20817             
20818         if(this.inputType == 'radio'){
20819             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20820                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20821                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20822             });
20823             
20824             return;
20825         }
20826         
20827         if(!this.groupId){
20828             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20829             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20830             return;
20831         }
20832         
20833         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20834         
20835         if(!group){
20836             return;
20837         }
20838         
20839         for(var i in group){
20840             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20841             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20842         }
20843         
20844     },
20845     
20846     clearInvalid : function()
20847     {
20848         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20849         
20850         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20851         
20852         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20853         
20854         if (label && label.iconEl) {
20855             label.iconEl.removeClass(label.validClass);
20856             label.iconEl.removeClass(label.invalidClass);
20857         }
20858     },
20859     
20860     disable : function()
20861     {
20862         if(this.inputType != 'radio'){
20863             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20864             return;
20865         }
20866         
20867         var _this = this;
20868         
20869         if(this.rendered){
20870             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20871                 _this.getActionEl().addClass(this.disabledClass);
20872                 e.dom.disabled = true;
20873             });
20874         }
20875         
20876         this.disabled = true;
20877         this.fireEvent("disable", this);
20878         return this;
20879     },
20880
20881     enable : function()
20882     {
20883         if(this.inputType != 'radio'){
20884             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20885             return;
20886         }
20887         
20888         var _this = this;
20889         
20890         if(this.rendered){
20891             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20892                 _this.getActionEl().removeClass(this.disabledClass);
20893                 e.dom.disabled = false;
20894             });
20895         }
20896         
20897         this.disabled = false;
20898         this.fireEvent("enable", this);
20899         return this;
20900     },
20901     
20902     setBoxLabel : function(v)
20903     {
20904         this.boxLabel = v;
20905         
20906         if(this.rendered){
20907             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20908         }
20909     }
20910
20911 });
20912
20913 Roo.apply(Roo.bootstrap.CheckBox, {
20914     
20915     groups: {},
20916     
20917      /**
20918     * register a CheckBox Group
20919     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20920     */
20921     register : function(checkbox)
20922     {
20923         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20924             this.groups[checkbox.groupId] = {};
20925         }
20926         
20927         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20928             return;
20929         }
20930         
20931         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20932         
20933     },
20934     /**
20935     * fetch a CheckBox Group based on the group ID
20936     * @param {string} the group ID
20937     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20938     */
20939     get: function(groupId) {
20940         if (typeof(this.groups[groupId]) == 'undefined') {
20941             return false;
20942         }
20943         
20944         return this.groups[groupId] ;
20945     }
20946     
20947     
20948 });
20949 /*
20950  * - LGPL
20951  *
20952  * RadioItem
20953  * 
20954  */
20955
20956 /**
20957  * @class Roo.bootstrap.Radio
20958  * @extends Roo.bootstrap.Component
20959  * Bootstrap Radio class
20960  * @cfg {String} boxLabel - the label associated
20961  * @cfg {String} value - the value of radio
20962  * 
20963  * @constructor
20964  * Create a new Radio
20965  * @param {Object} config The config object
20966  */
20967 Roo.bootstrap.Radio = function(config){
20968     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20969     
20970 };
20971
20972 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20973     
20974     boxLabel : '',
20975     
20976     value : '',
20977     
20978     getAutoCreate : function()
20979     {
20980         var cfg = {
20981             tag : 'div',
20982             cls : 'form-group radio',
20983             cn : [
20984                 {
20985                     tag : 'label',
20986                     cls : 'box-label',
20987                     html : this.boxLabel
20988                 }
20989             ]
20990         };
20991         
20992         return cfg;
20993     },
20994     
20995     initEvents : function() 
20996     {
20997         this.parent().register(this);
20998         
20999         this.el.on('click', this.onClick, this);
21000         
21001     },
21002     
21003     onClick : function(e)
21004     {
21005         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21006             this.setChecked(true);
21007         }
21008     },
21009     
21010     setChecked : function(state, suppressEvent)
21011     {
21012         this.parent().setValue(this.value, suppressEvent);
21013         
21014     },
21015     
21016     setBoxLabel : function(v)
21017     {
21018         this.boxLabel = v;
21019         
21020         if(this.rendered){
21021             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21022         }
21023     }
21024     
21025 });
21026  
21027
21028  /*
21029  * - LGPL
21030  *
21031  * Input
21032  * 
21033  */
21034
21035 /**
21036  * @class Roo.bootstrap.SecurePass
21037  * @extends Roo.bootstrap.Input
21038  * Bootstrap SecurePass class
21039  *
21040  * 
21041  * @constructor
21042  * Create a new SecurePass
21043  * @param {Object} config The config object
21044  */
21045  
21046 Roo.bootstrap.SecurePass = function (config) {
21047     // these go here, so the translation tool can replace them..
21048     this.errors = {
21049         PwdEmpty: "Please type a password, and then retype it to confirm.",
21050         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21051         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21052         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21053         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21054         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21055         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21056         TooWeak: "Your password is Too Weak."
21057     },
21058     this.meterLabel = "Password strength:";
21059     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21060     this.meterClass = [
21061         "roo-password-meter-tooweak", 
21062         "roo-password-meter-weak", 
21063         "roo-password-meter-medium", 
21064         "roo-password-meter-strong", 
21065         "roo-password-meter-grey"
21066     ];
21067     
21068     this.errors = {};
21069     
21070     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21071 }
21072
21073 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21074     /**
21075      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21076      * {
21077      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21078      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21079      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21080      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21081      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21082      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21083      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21084      * })
21085      */
21086     // private
21087     
21088     meterWidth: 300,
21089     errorMsg :'',    
21090     errors: false,
21091     imageRoot: '/',
21092     /**
21093      * @cfg {String/Object} Label for the strength meter (defaults to
21094      * 'Password strength:')
21095      */
21096     // private
21097     meterLabel: '',
21098     /**
21099      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21100      * ['Weak', 'Medium', 'Strong'])
21101      */
21102     // private    
21103     pwdStrengths: false,    
21104     // private
21105     strength: 0,
21106     // private
21107     _lastPwd: null,
21108     // private
21109     kCapitalLetter: 0,
21110     kSmallLetter: 1,
21111     kDigit: 2,
21112     kPunctuation: 3,
21113     
21114     insecure: false,
21115     // private
21116     initEvents: function ()
21117     {
21118         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21119
21120         if (this.el.is('input[type=password]') && Roo.isSafari) {
21121             this.el.on('keydown', this.SafariOnKeyDown, this);
21122         }
21123
21124         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21125     },
21126     // private
21127     onRender: function (ct, position)
21128     {
21129         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21130         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21131         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21132
21133         this.trigger.createChild({
21134                    cn: [
21135                     {
21136                     //id: 'PwdMeter',
21137                     tag: 'div',
21138                     cls: 'roo-password-meter-grey col-xs-12',
21139                     style: {
21140                         //width: 0,
21141                         //width: this.meterWidth + 'px'                                                
21142                         }
21143                     },
21144                     {                            
21145                          cls: 'roo-password-meter-text'                          
21146                     }
21147                 ]            
21148         });
21149
21150          
21151         if (this.hideTrigger) {
21152             this.trigger.setDisplayed(false);
21153         }
21154         this.setSize(this.width || '', this.height || '');
21155     },
21156     // private
21157     onDestroy: function ()
21158     {
21159         if (this.trigger) {
21160             this.trigger.removeAllListeners();
21161             this.trigger.remove();
21162         }
21163         if (this.wrap) {
21164             this.wrap.remove();
21165         }
21166         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21167     },
21168     // private
21169     checkStrength: function ()
21170     {
21171         var pwd = this.inputEl().getValue();
21172         if (pwd == this._lastPwd) {
21173             return;
21174         }
21175
21176         var strength;
21177         if (this.ClientSideStrongPassword(pwd)) {
21178             strength = 3;
21179         } else if (this.ClientSideMediumPassword(pwd)) {
21180             strength = 2;
21181         } else if (this.ClientSideWeakPassword(pwd)) {
21182             strength = 1;
21183         } else {
21184             strength = 0;
21185         }
21186         
21187         Roo.log('strength1: ' + strength);
21188         
21189         //var pm = this.trigger.child('div/div/div').dom;
21190         var pm = this.trigger.child('div/div');
21191         pm.removeClass(this.meterClass);
21192         pm.addClass(this.meterClass[strength]);
21193                 
21194         
21195         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21196                 
21197         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21198         
21199         this._lastPwd = pwd;
21200     },
21201     reset: function ()
21202     {
21203         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21204         
21205         this._lastPwd = '';
21206         
21207         var pm = this.trigger.child('div/div');
21208         pm.removeClass(this.meterClass);
21209         pm.addClass('roo-password-meter-grey');        
21210         
21211         
21212         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21213         
21214         pt.innerHTML = '';
21215         this.inputEl().dom.type='password';
21216     },
21217     // private
21218     validateValue: function (value)
21219     {
21220         
21221         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21222             return false;
21223         }
21224         if (value.length == 0) {
21225             if (this.allowBlank) {
21226                 this.clearInvalid();
21227                 return true;
21228             }
21229
21230             this.markInvalid(this.errors.PwdEmpty);
21231             this.errorMsg = this.errors.PwdEmpty;
21232             return false;
21233         }
21234         
21235         if(this.insecure){
21236             return true;
21237         }
21238         
21239         if ('[\x21-\x7e]*'.match(value)) {
21240             this.markInvalid(this.errors.PwdBadChar);
21241             this.errorMsg = this.errors.PwdBadChar;
21242             return false;
21243         }
21244         if (value.length < 6) {
21245             this.markInvalid(this.errors.PwdShort);
21246             this.errorMsg = this.errors.PwdShort;
21247             return false;
21248         }
21249         if (value.length > 16) {
21250             this.markInvalid(this.errors.PwdLong);
21251             this.errorMsg = this.errors.PwdLong;
21252             return false;
21253         }
21254         var strength;
21255         if (this.ClientSideStrongPassword(value)) {
21256             strength = 3;
21257         } else if (this.ClientSideMediumPassword(value)) {
21258             strength = 2;
21259         } else if (this.ClientSideWeakPassword(value)) {
21260             strength = 1;
21261         } else {
21262             strength = 0;
21263         }
21264
21265         
21266         if (strength < 2) {
21267             //this.markInvalid(this.errors.TooWeak);
21268             this.errorMsg = this.errors.TooWeak;
21269             //return false;
21270         }
21271         
21272         
21273         console.log('strength2: ' + strength);
21274         
21275         //var pm = this.trigger.child('div/div/div').dom;
21276         
21277         var pm = this.trigger.child('div/div');
21278         pm.removeClass(this.meterClass);
21279         pm.addClass(this.meterClass[strength]);
21280                 
21281         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21282                 
21283         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21284         
21285         this.errorMsg = ''; 
21286         return true;
21287     },
21288     // private
21289     CharacterSetChecks: function (type)
21290     {
21291         this.type = type;
21292         this.fResult = false;
21293     },
21294     // private
21295     isctype: function (character, type)
21296     {
21297         switch (type) {  
21298             case this.kCapitalLetter:
21299                 if (character >= 'A' && character <= 'Z') {
21300                     return true;
21301                 }
21302                 break;
21303             
21304             case this.kSmallLetter:
21305                 if (character >= 'a' && character <= 'z') {
21306                     return true;
21307                 }
21308                 break;
21309             
21310             case this.kDigit:
21311                 if (character >= '0' && character <= '9') {
21312                     return true;
21313                 }
21314                 break;
21315             
21316             case this.kPunctuation:
21317                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21318                     return true;
21319                 }
21320                 break;
21321             
21322             default:
21323                 return false;
21324         }
21325
21326     },
21327     // private
21328     IsLongEnough: function (pwd, size)
21329     {
21330         return !(pwd == null || isNaN(size) || pwd.length < size);
21331     },
21332     // private
21333     SpansEnoughCharacterSets: function (word, nb)
21334     {
21335         if (!this.IsLongEnough(word, nb))
21336         {
21337             return false;
21338         }
21339
21340         var characterSetChecks = new Array(
21341             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21342             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21343         );
21344         
21345         for (var index = 0; index < word.length; ++index) {
21346             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21347                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21348                     characterSetChecks[nCharSet].fResult = true;
21349                     break;
21350                 }
21351             }
21352         }
21353
21354         var nCharSets = 0;
21355         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21356             if (characterSetChecks[nCharSet].fResult) {
21357                 ++nCharSets;
21358             }
21359         }
21360
21361         if (nCharSets < nb) {
21362             return false;
21363         }
21364         return true;
21365     },
21366     // private
21367     ClientSideStrongPassword: function (pwd)
21368     {
21369         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21370     },
21371     // private
21372     ClientSideMediumPassword: function (pwd)
21373     {
21374         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21375     },
21376     // private
21377     ClientSideWeakPassword: function (pwd)
21378     {
21379         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21380     }
21381           
21382 })//<script type="text/javascript">
21383
21384 /*
21385  * Based  Ext JS Library 1.1.1
21386  * Copyright(c) 2006-2007, Ext JS, LLC.
21387  * LGPL
21388  *
21389  */
21390  
21391 /**
21392  * @class Roo.HtmlEditorCore
21393  * @extends Roo.Component
21394  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21395  *
21396  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21397  */
21398
21399 Roo.HtmlEditorCore = function(config){
21400     
21401     
21402     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21403     
21404     
21405     this.addEvents({
21406         /**
21407          * @event initialize
21408          * Fires when the editor is fully initialized (including the iframe)
21409          * @param {Roo.HtmlEditorCore} this
21410          */
21411         initialize: true,
21412         /**
21413          * @event activate
21414          * Fires when the editor is first receives the focus. Any insertion must wait
21415          * until after this event.
21416          * @param {Roo.HtmlEditorCore} this
21417          */
21418         activate: true,
21419          /**
21420          * @event beforesync
21421          * Fires before the textarea is updated with content from the editor iframe. Return false
21422          * to cancel the sync.
21423          * @param {Roo.HtmlEditorCore} this
21424          * @param {String} html
21425          */
21426         beforesync: true,
21427          /**
21428          * @event beforepush
21429          * Fires before the iframe editor is updated with content from the textarea. Return false
21430          * to cancel the push.
21431          * @param {Roo.HtmlEditorCore} this
21432          * @param {String} html
21433          */
21434         beforepush: true,
21435          /**
21436          * @event sync
21437          * Fires when the textarea is updated with content from the editor iframe.
21438          * @param {Roo.HtmlEditorCore} this
21439          * @param {String} html
21440          */
21441         sync: true,
21442          /**
21443          * @event push
21444          * Fires when the iframe editor is updated with content from the textarea.
21445          * @param {Roo.HtmlEditorCore} this
21446          * @param {String} html
21447          */
21448         push: true,
21449         
21450         /**
21451          * @event editorevent
21452          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21453          * @param {Roo.HtmlEditorCore} this
21454          */
21455         editorevent: true
21456         
21457     });
21458     
21459     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21460     
21461     // defaults : white / black...
21462     this.applyBlacklists();
21463     
21464     
21465     
21466 };
21467
21468
21469 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21470
21471
21472      /**
21473      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21474      */
21475     
21476     owner : false,
21477     
21478      /**
21479      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21480      *                        Roo.resizable.
21481      */
21482     resizable : false,
21483      /**
21484      * @cfg {Number} height (in pixels)
21485      */   
21486     height: 300,
21487    /**
21488      * @cfg {Number} width (in pixels)
21489      */   
21490     width: 500,
21491     
21492     /**
21493      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21494      * 
21495      */
21496     stylesheets: false,
21497     
21498     // id of frame..
21499     frameId: false,
21500     
21501     // private properties
21502     validationEvent : false,
21503     deferHeight: true,
21504     initialized : false,
21505     activated : false,
21506     sourceEditMode : false,
21507     onFocus : Roo.emptyFn,
21508     iframePad:3,
21509     hideMode:'offsets',
21510     
21511     clearUp: true,
21512     
21513     // blacklist + whitelisted elements..
21514     black: false,
21515     white: false,
21516      
21517     bodyCls : '',
21518
21519     /**
21520      * Protected method that will not generally be called directly. It
21521      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21522      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21523      */
21524     getDocMarkup : function(){
21525         // body styles..
21526         var st = '';
21527         
21528         // inherit styels from page...?? 
21529         if (this.stylesheets === false) {
21530             
21531             Roo.get(document.head).select('style').each(function(node) {
21532                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21533             });
21534             
21535             Roo.get(document.head).select('link').each(function(node) { 
21536                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21537             });
21538             
21539         } else if (!this.stylesheets.length) {
21540                 // simple..
21541                 st = '<style type="text/css">' +
21542                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21543                    '</style>';
21544         } else { 
21545             st = '<style type="text/css">' +
21546                     this.stylesheets +
21547                 '</style>';
21548         }
21549         
21550         st +=  '<style type="text/css">' +
21551             'IMG { cursor: pointer } ' +
21552         '</style>';
21553
21554         var cls = 'roo-htmleditor-body';
21555         
21556         if(this.bodyCls.length){
21557             cls += ' ' + this.bodyCls;
21558         }
21559         
21560         return '<html><head>' + st  +
21561             //<style type="text/css">' +
21562             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21563             //'</style>' +
21564             ' </head><body class="' +  cls + '"></body></html>';
21565     },
21566
21567     // private
21568     onRender : function(ct, position)
21569     {
21570         var _t = this;
21571         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21572         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21573         
21574         
21575         this.el.dom.style.border = '0 none';
21576         this.el.dom.setAttribute('tabIndex', -1);
21577         this.el.addClass('x-hidden hide');
21578         
21579         
21580         
21581         if(Roo.isIE){ // fix IE 1px bogus margin
21582             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21583         }
21584        
21585         
21586         this.frameId = Roo.id();
21587         
21588          
21589         
21590         var iframe = this.owner.wrap.createChild({
21591             tag: 'iframe',
21592             cls: 'form-control', // bootstrap..
21593             id: this.frameId,
21594             name: this.frameId,
21595             frameBorder : 'no',
21596             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21597         }, this.el
21598         );
21599         
21600         
21601         this.iframe = iframe.dom;
21602
21603          this.assignDocWin();
21604         
21605         this.doc.designMode = 'on';
21606        
21607         this.doc.open();
21608         this.doc.write(this.getDocMarkup());
21609         this.doc.close();
21610
21611         
21612         var task = { // must defer to wait for browser to be ready
21613             run : function(){
21614                 //console.log("run task?" + this.doc.readyState);
21615                 this.assignDocWin();
21616                 if(this.doc.body || this.doc.readyState == 'complete'){
21617                     try {
21618                         this.doc.designMode="on";
21619                     } catch (e) {
21620                         return;
21621                     }
21622                     Roo.TaskMgr.stop(task);
21623                     this.initEditor.defer(10, this);
21624                 }
21625             },
21626             interval : 10,
21627             duration: 10000,
21628             scope: this
21629         };
21630         Roo.TaskMgr.start(task);
21631
21632     },
21633
21634     // private
21635     onResize : function(w, h)
21636     {
21637          Roo.log('resize: ' +w + ',' + h );
21638         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21639         if(!this.iframe){
21640             return;
21641         }
21642         if(typeof w == 'number'){
21643             
21644             this.iframe.style.width = w + 'px';
21645         }
21646         if(typeof h == 'number'){
21647             
21648             this.iframe.style.height = h + 'px';
21649             if(this.doc){
21650                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21651             }
21652         }
21653         
21654     },
21655
21656     /**
21657      * Toggles the editor between standard and source edit mode.
21658      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21659      */
21660     toggleSourceEdit : function(sourceEditMode){
21661         
21662         this.sourceEditMode = sourceEditMode === true;
21663         
21664         if(this.sourceEditMode){
21665  
21666             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21667             
21668         }else{
21669             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21670             //this.iframe.className = '';
21671             this.deferFocus();
21672         }
21673         //this.setSize(this.owner.wrap.getSize());
21674         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21675     },
21676
21677     
21678   
21679
21680     /**
21681      * Protected method that will not generally be called directly. If you need/want
21682      * custom HTML cleanup, this is the method you should override.
21683      * @param {String} html The HTML to be cleaned
21684      * return {String} The cleaned HTML
21685      */
21686     cleanHtml : function(html){
21687         html = String(html);
21688         if(html.length > 5){
21689             if(Roo.isSafari){ // strip safari nonsense
21690                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21691             }
21692         }
21693         if(html == '&nbsp;'){
21694             html = '';
21695         }
21696         return html;
21697     },
21698
21699     /**
21700      * HTML Editor -> Textarea
21701      * Protected method that will not generally be called directly. Syncs the contents
21702      * of the editor iframe with the textarea.
21703      */
21704     syncValue : function(){
21705         if(this.initialized){
21706             var bd = (this.doc.body || this.doc.documentElement);
21707             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21708             var html = bd.innerHTML;
21709             if(Roo.isSafari){
21710                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21711                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21712                 if(m && m[1]){
21713                     html = '<div style="'+m[0]+'">' + html + '</div>';
21714                 }
21715             }
21716             html = this.cleanHtml(html);
21717             // fix up the special chars.. normaly like back quotes in word...
21718             // however we do not want to do this with chinese..
21719             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21720                 var cc = b.charCodeAt();
21721                 if (
21722                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21723                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21724                     (cc >= 0xf900 && cc < 0xfb00 )
21725                 ) {
21726                         return b;
21727                 }
21728                 return "&#"+cc+";" 
21729             });
21730             if(this.owner.fireEvent('beforesync', this, html) !== false){
21731                 this.el.dom.value = html;
21732                 this.owner.fireEvent('sync', this, html);
21733             }
21734         }
21735     },
21736
21737     /**
21738      * Protected method that will not generally be called directly. Pushes the value of the textarea
21739      * into the iframe editor.
21740      */
21741     pushValue : function(){
21742         if(this.initialized){
21743             var v = this.el.dom.value.trim();
21744             
21745 //            if(v.length < 1){
21746 //                v = '&#160;';
21747 //            }
21748             
21749             if(this.owner.fireEvent('beforepush', this, v) !== false){
21750                 var d = (this.doc.body || this.doc.documentElement);
21751                 d.innerHTML = v;
21752                 this.cleanUpPaste();
21753                 this.el.dom.value = d.innerHTML;
21754                 this.owner.fireEvent('push', this, v);
21755             }
21756         }
21757     },
21758
21759     // private
21760     deferFocus : function(){
21761         this.focus.defer(10, this);
21762     },
21763
21764     // doc'ed in Field
21765     focus : function(){
21766         if(this.win && !this.sourceEditMode){
21767             this.win.focus();
21768         }else{
21769             this.el.focus();
21770         }
21771     },
21772     
21773     assignDocWin: function()
21774     {
21775         var iframe = this.iframe;
21776         
21777          if(Roo.isIE){
21778             this.doc = iframe.contentWindow.document;
21779             this.win = iframe.contentWindow;
21780         } else {
21781 //            if (!Roo.get(this.frameId)) {
21782 //                return;
21783 //            }
21784 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21785 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21786             
21787             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21788                 return;
21789             }
21790             
21791             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21792             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21793         }
21794     },
21795     
21796     // private
21797     initEditor : function(){
21798         //console.log("INIT EDITOR");
21799         this.assignDocWin();
21800         
21801         
21802         
21803         this.doc.designMode="on";
21804         this.doc.open();
21805         this.doc.write(this.getDocMarkup());
21806         this.doc.close();
21807         
21808         var dbody = (this.doc.body || this.doc.documentElement);
21809         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21810         // this copies styles from the containing element into thsi one..
21811         // not sure why we need all of this..
21812         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21813         
21814         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21815         //ss['background-attachment'] = 'fixed'; // w3c
21816         dbody.bgProperties = 'fixed'; // ie
21817         //Roo.DomHelper.applyStyles(dbody, ss);
21818         Roo.EventManager.on(this.doc, {
21819             //'mousedown': this.onEditorEvent,
21820             'mouseup': this.onEditorEvent,
21821             'dblclick': this.onEditorEvent,
21822             'click': this.onEditorEvent,
21823             'keyup': this.onEditorEvent,
21824             buffer:100,
21825             scope: this
21826         });
21827         if(Roo.isGecko){
21828             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21829         }
21830         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21831             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21832         }
21833         this.initialized = true;
21834
21835         this.owner.fireEvent('initialize', this);
21836         this.pushValue();
21837     },
21838
21839     // private
21840     onDestroy : function(){
21841         
21842         
21843         
21844         if(this.rendered){
21845             
21846             //for (var i =0; i < this.toolbars.length;i++) {
21847             //    // fixme - ask toolbars for heights?
21848             //    this.toolbars[i].onDestroy();
21849            // }
21850             
21851             //this.wrap.dom.innerHTML = '';
21852             //this.wrap.remove();
21853         }
21854     },
21855
21856     // private
21857     onFirstFocus : function(){
21858         
21859         this.assignDocWin();
21860         
21861         
21862         this.activated = true;
21863          
21864     
21865         if(Roo.isGecko){ // prevent silly gecko errors
21866             this.win.focus();
21867             var s = this.win.getSelection();
21868             if(!s.focusNode || s.focusNode.nodeType != 3){
21869                 var r = s.getRangeAt(0);
21870                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21871                 r.collapse(true);
21872                 this.deferFocus();
21873             }
21874             try{
21875                 this.execCmd('useCSS', true);
21876                 this.execCmd('styleWithCSS', false);
21877             }catch(e){}
21878         }
21879         this.owner.fireEvent('activate', this);
21880     },
21881
21882     // private
21883     adjustFont: function(btn){
21884         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21885         //if(Roo.isSafari){ // safari
21886         //    adjust *= 2;
21887        // }
21888         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21889         if(Roo.isSafari){ // safari
21890             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21891             v =  (v < 10) ? 10 : v;
21892             v =  (v > 48) ? 48 : v;
21893             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21894             
21895         }
21896         
21897         
21898         v = Math.max(1, v+adjust);
21899         
21900         this.execCmd('FontSize', v  );
21901     },
21902
21903     onEditorEvent : function(e)
21904     {
21905         this.owner.fireEvent('editorevent', this, e);
21906       //  this.updateToolbar();
21907         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21908     },
21909
21910     insertTag : function(tg)
21911     {
21912         // could be a bit smarter... -> wrap the current selected tRoo..
21913         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21914             
21915             range = this.createRange(this.getSelection());
21916             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21917             wrappingNode.appendChild(range.extractContents());
21918             range.insertNode(wrappingNode);
21919
21920             return;
21921             
21922             
21923             
21924         }
21925         this.execCmd("formatblock",   tg);
21926         
21927     },
21928     
21929     insertText : function(txt)
21930     {
21931         
21932         
21933         var range = this.createRange();
21934         range.deleteContents();
21935                //alert(Sender.getAttribute('label'));
21936                
21937         range.insertNode(this.doc.createTextNode(txt));
21938     } ,
21939     
21940      
21941
21942     /**
21943      * Executes a Midas editor command on the editor document and performs necessary focus and
21944      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21945      * @param {String} cmd The Midas command
21946      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21947      */
21948     relayCmd : function(cmd, value){
21949         this.win.focus();
21950         this.execCmd(cmd, value);
21951         this.owner.fireEvent('editorevent', this);
21952         //this.updateToolbar();
21953         this.owner.deferFocus();
21954     },
21955
21956     /**
21957      * Executes a Midas editor command directly on the editor document.
21958      * For visual commands, you should use {@link #relayCmd} instead.
21959      * <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     execCmd : function(cmd, value){
21964         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21965         this.syncValue();
21966     },
21967  
21968  
21969    
21970     /**
21971      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21972      * to insert tRoo.
21973      * @param {String} text | dom node.. 
21974      */
21975     insertAtCursor : function(text)
21976     {
21977         
21978         if(!this.activated){
21979             return;
21980         }
21981         /*
21982         if(Roo.isIE){
21983             this.win.focus();
21984             var r = this.doc.selection.createRange();
21985             if(r){
21986                 r.collapse(true);
21987                 r.pasteHTML(text);
21988                 this.syncValue();
21989                 this.deferFocus();
21990             
21991             }
21992             return;
21993         }
21994         */
21995         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21996             this.win.focus();
21997             
21998             
21999             // from jquery ui (MIT licenced)
22000             var range, node;
22001             var win = this.win;
22002             
22003             if (win.getSelection && win.getSelection().getRangeAt) {
22004                 range = win.getSelection().getRangeAt(0);
22005                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22006                 range.insertNode(node);
22007             } else if (win.document.selection && win.document.selection.createRange) {
22008                 // no firefox support
22009                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22010                 win.document.selection.createRange().pasteHTML(txt);
22011             } else {
22012                 // no firefox support
22013                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22014                 this.execCmd('InsertHTML', txt);
22015             } 
22016             
22017             this.syncValue();
22018             
22019             this.deferFocus();
22020         }
22021     },
22022  // private
22023     mozKeyPress : function(e){
22024         if(e.ctrlKey){
22025             var c = e.getCharCode(), cmd;
22026           
22027             if(c > 0){
22028                 c = String.fromCharCode(c).toLowerCase();
22029                 switch(c){
22030                     case 'b':
22031                         cmd = 'bold';
22032                         break;
22033                     case 'i':
22034                         cmd = 'italic';
22035                         break;
22036                     
22037                     case 'u':
22038                         cmd = 'underline';
22039                         break;
22040                     
22041                     case 'v':
22042                         this.cleanUpPaste.defer(100, this);
22043                         return;
22044                         
22045                 }
22046                 if(cmd){
22047                     this.win.focus();
22048                     this.execCmd(cmd);
22049                     this.deferFocus();
22050                     e.preventDefault();
22051                 }
22052                 
22053             }
22054         }
22055     },
22056
22057     // private
22058     fixKeys : function(){ // load time branching for fastest keydown performance
22059         if(Roo.isIE){
22060             return function(e){
22061                 var k = e.getKey(), r;
22062                 if(k == e.TAB){
22063                     e.stopEvent();
22064                     r = this.doc.selection.createRange();
22065                     if(r){
22066                         r.collapse(true);
22067                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22068                         this.deferFocus();
22069                     }
22070                     return;
22071                 }
22072                 
22073                 if(k == e.ENTER){
22074                     r = this.doc.selection.createRange();
22075                     if(r){
22076                         var target = r.parentElement();
22077                         if(!target || target.tagName.toLowerCase() != 'li'){
22078                             e.stopEvent();
22079                             r.pasteHTML('<br />');
22080                             r.collapse(false);
22081                             r.select();
22082                         }
22083                     }
22084                 }
22085                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22086                     this.cleanUpPaste.defer(100, this);
22087                     return;
22088                 }
22089                 
22090                 
22091             };
22092         }else if(Roo.isOpera){
22093             return function(e){
22094                 var k = e.getKey();
22095                 if(k == e.TAB){
22096                     e.stopEvent();
22097                     this.win.focus();
22098                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22099                     this.deferFocus();
22100                 }
22101                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22102                     this.cleanUpPaste.defer(100, this);
22103                     return;
22104                 }
22105                 
22106             };
22107         }else if(Roo.isSafari){
22108             return function(e){
22109                 var k = e.getKey();
22110                 
22111                 if(k == e.TAB){
22112                     e.stopEvent();
22113                     this.execCmd('InsertText','\t');
22114                     this.deferFocus();
22115                     return;
22116                 }
22117                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22118                     this.cleanUpPaste.defer(100, this);
22119                     return;
22120                 }
22121                 
22122              };
22123         }
22124     }(),
22125     
22126     getAllAncestors: function()
22127     {
22128         var p = this.getSelectedNode();
22129         var a = [];
22130         if (!p) {
22131             a.push(p); // push blank onto stack..
22132             p = this.getParentElement();
22133         }
22134         
22135         
22136         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22137             a.push(p);
22138             p = p.parentNode;
22139         }
22140         a.push(this.doc.body);
22141         return a;
22142     },
22143     lastSel : false,
22144     lastSelNode : false,
22145     
22146     
22147     getSelection : function() 
22148     {
22149         this.assignDocWin();
22150         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22151     },
22152     
22153     getSelectedNode: function() 
22154     {
22155         // this may only work on Gecko!!!
22156         
22157         // should we cache this!!!!
22158         
22159         
22160         
22161          
22162         var range = this.createRange(this.getSelection()).cloneRange();
22163         
22164         if (Roo.isIE) {
22165             var parent = range.parentElement();
22166             while (true) {
22167                 var testRange = range.duplicate();
22168                 testRange.moveToElementText(parent);
22169                 if (testRange.inRange(range)) {
22170                     break;
22171                 }
22172                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22173                     break;
22174                 }
22175                 parent = parent.parentElement;
22176             }
22177             return parent;
22178         }
22179         
22180         // is ancestor a text element.
22181         var ac =  range.commonAncestorContainer;
22182         if (ac.nodeType == 3) {
22183             ac = ac.parentNode;
22184         }
22185         
22186         var ar = ac.childNodes;
22187          
22188         var nodes = [];
22189         var other_nodes = [];
22190         var has_other_nodes = false;
22191         for (var i=0;i<ar.length;i++) {
22192             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22193                 continue;
22194             }
22195             // fullly contained node.
22196             
22197             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22198                 nodes.push(ar[i]);
22199                 continue;
22200             }
22201             
22202             // probably selected..
22203             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22204                 other_nodes.push(ar[i]);
22205                 continue;
22206             }
22207             // outer..
22208             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22209                 continue;
22210             }
22211             
22212             
22213             has_other_nodes = true;
22214         }
22215         if (!nodes.length && other_nodes.length) {
22216             nodes= other_nodes;
22217         }
22218         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22219             return false;
22220         }
22221         
22222         return nodes[0];
22223     },
22224     createRange: function(sel)
22225     {
22226         // this has strange effects when using with 
22227         // top toolbar - not sure if it's a great idea.
22228         //this.editor.contentWindow.focus();
22229         if (typeof sel != "undefined") {
22230             try {
22231                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22232             } catch(e) {
22233                 return this.doc.createRange();
22234             }
22235         } else {
22236             return this.doc.createRange();
22237         }
22238     },
22239     getParentElement: function()
22240     {
22241         
22242         this.assignDocWin();
22243         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22244         
22245         var range = this.createRange(sel);
22246          
22247         try {
22248             var p = range.commonAncestorContainer;
22249             while (p.nodeType == 3) { // text node
22250                 p = p.parentNode;
22251             }
22252             return p;
22253         } catch (e) {
22254             return null;
22255         }
22256     
22257     },
22258     /***
22259      *
22260      * Range intersection.. the hard stuff...
22261      *  '-1' = before
22262      *  '0' = hits..
22263      *  '1' = after.
22264      *         [ -- selected range --- ]
22265      *   [fail]                        [fail]
22266      *
22267      *    basically..
22268      *      if end is before start or  hits it. fail.
22269      *      if start is after end or hits it fail.
22270      *
22271      *   if either hits (but other is outside. - then it's not 
22272      *   
22273      *    
22274      **/
22275     
22276     
22277     // @see http://www.thismuchiknow.co.uk/?p=64.
22278     rangeIntersectsNode : function(range, node)
22279     {
22280         var nodeRange = node.ownerDocument.createRange();
22281         try {
22282             nodeRange.selectNode(node);
22283         } catch (e) {
22284             nodeRange.selectNodeContents(node);
22285         }
22286     
22287         var rangeStartRange = range.cloneRange();
22288         rangeStartRange.collapse(true);
22289     
22290         var rangeEndRange = range.cloneRange();
22291         rangeEndRange.collapse(false);
22292     
22293         var nodeStartRange = nodeRange.cloneRange();
22294         nodeStartRange.collapse(true);
22295     
22296         var nodeEndRange = nodeRange.cloneRange();
22297         nodeEndRange.collapse(false);
22298     
22299         return rangeStartRange.compareBoundaryPoints(
22300                  Range.START_TO_START, nodeEndRange) == -1 &&
22301                rangeEndRange.compareBoundaryPoints(
22302                  Range.START_TO_START, nodeStartRange) == 1;
22303         
22304          
22305     },
22306     rangeCompareNode : function(range, node)
22307     {
22308         var nodeRange = node.ownerDocument.createRange();
22309         try {
22310             nodeRange.selectNode(node);
22311         } catch (e) {
22312             nodeRange.selectNodeContents(node);
22313         }
22314         
22315         
22316         range.collapse(true);
22317     
22318         nodeRange.collapse(true);
22319      
22320         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22321         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22322          
22323         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22324         
22325         var nodeIsBefore   =  ss == 1;
22326         var nodeIsAfter    = ee == -1;
22327         
22328         if (nodeIsBefore && nodeIsAfter) {
22329             return 0; // outer
22330         }
22331         if (!nodeIsBefore && nodeIsAfter) {
22332             return 1; //right trailed.
22333         }
22334         
22335         if (nodeIsBefore && !nodeIsAfter) {
22336             return 2;  // left trailed.
22337         }
22338         // fully contined.
22339         return 3;
22340     },
22341
22342     // private? - in a new class?
22343     cleanUpPaste :  function()
22344     {
22345         // cleans up the whole document..
22346         Roo.log('cleanuppaste');
22347         
22348         this.cleanUpChildren(this.doc.body);
22349         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22350         if (clean != this.doc.body.innerHTML) {
22351             this.doc.body.innerHTML = clean;
22352         }
22353         
22354     },
22355     
22356     cleanWordChars : function(input) {// change the chars to hex code
22357         var he = Roo.HtmlEditorCore;
22358         
22359         var output = input;
22360         Roo.each(he.swapCodes, function(sw) { 
22361             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22362             
22363             output = output.replace(swapper, sw[1]);
22364         });
22365         
22366         return output;
22367     },
22368     
22369     
22370     cleanUpChildren : function (n)
22371     {
22372         if (!n.childNodes.length) {
22373             return;
22374         }
22375         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22376            this.cleanUpChild(n.childNodes[i]);
22377         }
22378     },
22379     
22380     
22381         
22382     
22383     cleanUpChild : function (node)
22384     {
22385         var ed = this;
22386         //console.log(node);
22387         if (node.nodeName == "#text") {
22388             // clean up silly Windows -- stuff?
22389             return; 
22390         }
22391         if (node.nodeName == "#comment") {
22392             node.parentNode.removeChild(node);
22393             // clean up silly Windows -- stuff?
22394             return; 
22395         }
22396         var lcname = node.tagName.toLowerCase();
22397         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22398         // whitelist of tags..
22399         
22400         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22401             // remove node.
22402             node.parentNode.removeChild(node);
22403             return;
22404             
22405         }
22406         
22407         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22408         
22409         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22410         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22411         
22412         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22413         //    remove_keep_children = true;
22414         //}
22415         
22416         if (remove_keep_children) {
22417             this.cleanUpChildren(node);
22418             // inserts everything just before this node...
22419             while (node.childNodes.length) {
22420                 var cn = node.childNodes[0];
22421                 node.removeChild(cn);
22422                 node.parentNode.insertBefore(cn, node);
22423             }
22424             node.parentNode.removeChild(node);
22425             return;
22426         }
22427         
22428         if (!node.attributes || !node.attributes.length) {
22429             this.cleanUpChildren(node);
22430             return;
22431         }
22432         
22433         function cleanAttr(n,v)
22434         {
22435             
22436             if (v.match(/^\./) || v.match(/^\//)) {
22437                 return;
22438             }
22439             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22440                 return;
22441             }
22442             if (v.match(/^#/)) {
22443                 return;
22444             }
22445 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22446             node.removeAttribute(n);
22447             
22448         }
22449         
22450         var cwhite = this.cwhite;
22451         var cblack = this.cblack;
22452             
22453         function cleanStyle(n,v)
22454         {
22455             if (v.match(/expression/)) { //XSS?? should we even bother..
22456                 node.removeAttribute(n);
22457                 return;
22458             }
22459             
22460             var parts = v.split(/;/);
22461             var clean = [];
22462             
22463             Roo.each(parts, function(p) {
22464                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22465                 if (!p.length) {
22466                     return true;
22467                 }
22468                 var l = p.split(':').shift().replace(/\s+/g,'');
22469                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22470                 
22471                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22472 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22473                     //node.removeAttribute(n);
22474                     return true;
22475                 }
22476                 //Roo.log()
22477                 // only allow 'c whitelisted system attributes'
22478                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22479 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22480                     //node.removeAttribute(n);
22481                     return true;
22482                 }
22483                 
22484                 
22485                  
22486                 
22487                 clean.push(p);
22488                 return true;
22489             });
22490             if (clean.length) { 
22491                 node.setAttribute(n, clean.join(';'));
22492             } else {
22493                 node.removeAttribute(n);
22494             }
22495             
22496         }
22497         
22498         
22499         for (var i = node.attributes.length-1; i > -1 ; i--) {
22500             var a = node.attributes[i];
22501             //console.log(a);
22502             
22503             if (a.name.toLowerCase().substr(0,2)=='on')  {
22504                 node.removeAttribute(a.name);
22505                 continue;
22506             }
22507             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22508                 node.removeAttribute(a.name);
22509                 continue;
22510             }
22511             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22512                 cleanAttr(a.name,a.value); // fixme..
22513                 continue;
22514             }
22515             if (a.name == 'style') {
22516                 cleanStyle(a.name,a.value);
22517                 continue;
22518             }
22519             /// clean up MS crap..
22520             // tecnically this should be a list of valid class'es..
22521             
22522             
22523             if (a.name == 'class') {
22524                 if (a.value.match(/^Mso/)) {
22525                     node.className = '';
22526                 }
22527                 
22528                 if (a.value.match(/^body$/)) {
22529                     node.className = '';
22530                 }
22531                 continue;
22532             }
22533             
22534             // style cleanup!?
22535             // class cleanup?
22536             
22537         }
22538         
22539         
22540         this.cleanUpChildren(node);
22541         
22542         
22543     },
22544     
22545     /**
22546      * Clean up MS wordisms...
22547      */
22548     cleanWord : function(node)
22549     {
22550         
22551         
22552         if (!node) {
22553             this.cleanWord(this.doc.body);
22554             return;
22555         }
22556         if (node.nodeName == "#text") {
22557             // clean up silly Windows -- stuff?
22558             return; 
22559         }
22560         if (node.nodeName == "#comment") {
22561             node.parentNode.removeChild(node);
22562             // clean up silly Windows -- stuff?
22563             return; 
22564         }
22565         
22566         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22567             node.parentNode.removeChild(node);
22568             return;
22569         }
22570         
22571         // remove - but keep children..
22572         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22573             while (node.childNodes.length) {
22574                 var cn = node.childNodes[0];
22575                 node.removeChild(cn);
22576                 node.parentNode.insertBefore(cn, node);
22577             }
22578             node.parentNode.removeChild(node);
22579             this.iterateChildren(node, this.cleanWord);
22580             return;
22581         }
22582         // clean styles
22583         if (node.className.length) {
22584             
22585             var cn = node.className.split(/\W+/);
22586             var cna = [];
22587             Roo.each(cn, function(cls) {
22588                 if (cls.match(/Mso[a-zA-Z]+/)) {
22589                     return;
22590                 }
22591                 cna.push(cls);
22592             });
22593             node.className = cna.length ? cna.join(' ') : '';
22594             if (!cna.length) {
22595                 node.removeAttribute("class");
22596             }
22597         }
22598         
22599         if (node.hasAttribute("lang")) {
22600             node.removeAttribute("lang");
22601         }
22602         
22603         if (node.hasAttribute("style")) {
22604             
22605             var styles = node.getAttribute("style").split(";");
22606             var nstyle = [];
22607             Roo.each(styles, function(s) {
22608                 if (!s.match(/:/)) {
22609                     return;
22610                 }
22611                 var kv = s.split(":");
22612                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22613                     return;
22614                 }
22615                 // what ever is left... we allow.
22616                 nstyle.push(s);
22617             });
22618             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22619             if (!nstyle.length) {
22620                 node.removeAttribute('style');
22621             }
22622         }
22623         this.iterateChildren(node, this.cleanWord);
22624         
22625         
22626         
22627     },
22628     /**
22629      * iterateChildren of a Node, calling fn each time, using this as the scole..
22630      * @param {DomNode} node node to iterate children of.
22631      * @param {Function} fn method of this class to call on each item.
22632      */
22633     iterateChildren : function(node, fn)
22634     {
22635         if (!node.childNodes.length) {
22636                 return;
22637         }
22638         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22639            fn.call(this, node.childNodes[i])
22640         }
22641     },
22642     
22643     
22644     /**
22645      * cleanTableWidths.
22646      *
22647      * Quite often pasting from word etc.. results in tables with column and widths.
22648      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22649      *
22650      */
22651     cleanTableWidths : function(node)
22652     {
22653          
22654          
22655         if (!node) {
22656             this.cleanTableWidths(this.doc.body);
22657             return;
22658         }
22659         
22660         // ignore list...
22661         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22662             return; 
22663         }
22664         Roo.log(node.tagName);
22665         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22666             this.iterateChildren(node, this.cleanTableWidths);
22667             return;
22668         }
22669         if (node.hasAttribute('width')) {
22670             node.removeAttribute('width');
22671         }
22672         
22673          
22674         if (node.hasAttribute("style")) {
22675             // pretty basic...
22676             
22677             var styles = node.getAttribute("style").split(";");
22678             var nstyle = [];
22679             Roo.each(styles, function(s) {
22680                 if (!s.match(/:/)) {
22681                     return;
22682                 }
22683                 var kv = s.split(":");
22684                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22685                     return;
22686                 }
22687                 // what ever is left... we allow.
22688                 nstyle.push(s);
22689             });
22690             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22691             if (!nstyle.length) {
22692                 node.removeAttribute('style');
22693             }
22694         }
22695         
22696         this.iterateChildren(node, this.cleanTableWidths);
22697         
22698         
22699     },
22700     
22701     
22702     
22703     
22704     domToHTML : function(currentElement, depth, nopadtext) {
22705         
22706         depth = depth || 0;
22707         nopadtext = nopadtext || false;
22708     
22709         if (!currentElement) {
22710             return this.domToHTML(this.doc.body);
22711         }
22712         
22713         //Roo.log(currentElement);
22714         var j;
22715         var allText = false;
22716         var nodeName = currentElement.nodeName;
22717         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22718         
22719         if  (nodeName == '#text') {
22720             
22721             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22722         }
22723         
22724         
22725         var ret = '';
22726         if (nodeName != 'BODY') {
22727              
22728             var i = 0;
22729             // Prints the node tagName, such as <A>, <IMG>, etc
22730             if (tagName) {
22731                 var attr = [];
22732                 for(i = 0; i < currentElement.attributes.length;i++) {
22733                     // quoting?
22734                     var aname = currentElement.attributes.item(i).name;
22735                     if (!currentElement.attributes.item(i).value.length) {
22736                         continue;
22737                     }
22738                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22739                 }
22740                 
22741                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22742             } 
22743             else {
22744                 
22745                 // eack
22746             }
22747         } else {
22748             tagName = false;
22749         }
22750         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22751             return ret;
22752         }
22753         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22754             nopadtext = true;
22755         }
22756         
22757         
22758         // Traverse the tree
22759         i = 0;
22760         var currentElementChild = currentElement.childNodes.item(i);
22761         var allText = true;
22762         var innerHTML  = '';
22763         lastnode = '';
22764         while (currentElementChild) {
22765             // Formatting code (indent the tree so it looks nice on the screen)
22766             var nopad = nopadtext;
22767             if (lastnode == 'SPAN') {
22768                 nopad  = true;
22769             }
22770             // text
22771             if  (currentElementChild.nodeName == '#text') {
22772                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22773                 toadd = nopadtext ? toadd : toadd.trim();
22774                 if (!nopad && toadd.length > 80) {
22775                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22776                 }
22777                 innerHTML  += toadd;
22778                 
22779                 i++;
22780                 currentElementChild = currentElement.childNodes.item(i);
22781                 lastNode = '';
22782                 continue;
22783             }
22784             allText = false;
22785             
22786             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22787                 
22788             // Recursively traverse the tree structure of the child node
22789             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22790             lastnode = currentElementChild.nodeName;
22791             i++;
22792             currentElementChild=currentElement.childNodes.item(i);
22793         }
22794         
22795         ret += innerHTML;
22796         
22797         if (!allText) {
22798                 // The remaining code is mostly for formatting the tree
22799             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22800         }
22801         
22802         
22803         if (tagName) {
22804             ret+= "</"+tagName+">";
22805         }
22806         return ret;
22807         
22808     },
22809         
22810     applyBlacklists : function()
22811     {
22812         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22813         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22814         
22815         this.white = [];
22816         this.black = [];
22817         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22818             if (b.indexOf(tag) > -1) {
22819                 return;
22820             }
22821             this.white.push(tag);
22822             
22823         }, this);
22824         
22825         Roo.each(w, function(tag) {
22826             if (b.indexOf(tag) > -1) {
22827                 return;
22828             }
22829             if (this.white.indexOf(tag) > -1) {
22830                 return;
22831             }
22832             this.white.push(tag);
22833             
22834         }, this);
22835         
22836         
22837         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22838             if (w.indexOf(tag) > -1) {
22839                 return;
22840             }
22841             this.black.push(tag);
22842             
22843         }, this);
22844         
22845         Roo.each(b, function(tag) {
22846             if (w.indexOf(tag) > -1) {
22847                 return;
22848             }
22849             if (this.black.indexOf(tag) > -1) {
22850                 return;
22851             }
22852             this.black.push(tag);
22853             
22854         }, this);
22855         
22856         
22857         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22858         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22859         
22860         this.cwhite = [];
22861         this.cblack = [];
22862         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22863             if (b.indexOf(tag) > -1) {
22864                 return;
22865             }
22866             this.cwhite.push(tag);
22867             
22868         }, this);
22869         
22870         Roo.each(w, function(tag) {
22871             if (b.indexOf(tag) > -1) {
22872                 return;
22873             }
22874             if (this.cwhite.indexOf(tag) > -1) {
22875                 return;
22876             }
22877             this.cwhite.push(tag);
22878             
22879         }, this);
22880         
22881         
22882         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22883             if (w.indexOf(tag) > -1) {
22884                 return;
22885             }
22886             this.cblack.push(tag);
22887             
22888         }, this);
22889         
22890         Roo.each(b, function(tag) {
22891             if (w.indexOf(tag) > -1) {
22892                 return;
22893             }
22894             if (this.cblack.indexOf(tag) > -1) {
22895                 return;
22896             }
22897             this.cblack.push(tag);
22898             
22899         }, this);
22900     },
22901     
22902     setStylesheets : function(stylesheets)
22903     {
22904         if(typeof(stylesheets) == 'string'){
22905             Roo.get(this.iframe.contentDocument.head).createChild({
22906                 tag : 'link',
22907                 rel : 'stylesheet',
22908                 type : 'text/css',
22909                 href : stylesheets
22910             });
22911             
22912             return;
22913         }
22914         var _this = this;
22915      
22916         Roo.each(stylesheets, function(s) {
22917             if(!s.length){
22918                 return;
22919             }
22920             
22921             Roo.get(_this.iframe.contentDocument.head).createChild({
22922                 tag : 'link',
22923                 rel : 'stylesheet',
22924                 type : 'text/css',
22925                 href : s
22926             });
22927         });
22928
22929         
22930     },
22931     
22932     removeStylesheets : function()
22933     {
22934         var _this = this;
22935         
22936         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22937             s.remove();
22938         });
22939     },
22940     
22941     setStyle : function(style)
22942     {
22943         Roo.get(this.iframe.contentDocument.head).createChild({
22944             tag : 'style',
22945             type : 'text/css',
22946             html : style
22947         });
22948
22949         return;
22950     }
22951     
22952     // hide stuff that is not compatible
22953     /**
22954      * @event blur
22955      * @hide
22956      */
22957     /**
22958      * @event change
22959      * @hide
22960      */
22961     /**
22962      * @event focus
22963      * @hide
22964      */
22965     /**
22966      * @event specialkey
22967      * @hide
22968      */
22969     /**
22970      * @cfg {String} fieldClass @hide
22971      */
22972     /**
22973      * @cfg {String} focusClass @hide
22974      */
22975     /**
22976      * @cfg {String} autoCreate @hide
22977      */
22978     /**
22979      * @cfg {String} inputType @hide
22980      */
22981     /**
22982      * @cfg {String} invalidClass @hide
22983      */
22984     /**
22985      * @cfg {String} invalidText @hide
22986      */
22987     /**
22988      * @cfg {String} msgFx @hide
22989      */
22990     /**
22991      * @cfg {String} validateOnBlur @hide
22992      */
22993 });
22994
22995 Roo.HtmlEditorCore.white = [
22996         'area', 'br', 'img', 'input', 'hr', 'wbr',
22997         
22998        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22999        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23000        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23001        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23002        'table',   'ul',         'xmp', 
23003        
23004        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23005       'thead',   'tr', 
23006      
23007       'dir', 'menu', 'ol', 'ul', 'dl',
23008        
23009       'embed',  'object'
23010 ];
23011
23012
23013 Roo.HtmlEditorCore.black = [
23014     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23015         'applet', // 
23016         'base',   'basefont', 'bgsound', 'blink',  'body', 
23017         'frame',  'frameset', 'head',    'html',   'ilayer', 
23018         'iframe', 'layer',  'link',     'meta',    'object',   
23019         'script', 'style' ,'title',  'xml' // clean later..
23020 ];
23021 Roo.HtmlEditorCore.clean = [
23022     'script', 'style', 'title', 'xml'
23023 ];
23024 Roo.HtmlEditorCore.remove = [
23025     'font'
23026 ];
23027 // attributes..
23028
23029 Roo.HtmlEditorCore.ablack = [
23030     'on'
23031 ];
23032     
23033 Roo.HtmlEditorCore.aclean = [ 
23034     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23035 ];
23036
23037 // protocols..
23038 Roo.HtmlEditorCore.pwhite= [
23039         'http',  'https',  'mailto'
23040 ];
23041
23042 // white listed style attributes.
23043 Roo.HtmlEditorCore.cwhite= [
23044       //  'text-align', /// default is to allow most things..
23045       
23046          
23047 //        'font-size'//??
23048 ];
23049
23050 // black listed style attributes.
23051 Roo.HtmlEditorCore.cblack= [
23052       //  'font-size' -- this can be set by the project 
23053 ];
23054
23055
23056 Roo.HtmlEditorCore.swapCodes   =[ 
23057     [    8211, "--" ], 
23058     [    8212, "--" ], 
23059     [    8216,  "'" ],  
23060     [    8217, "'" ],  
23061     [    8220, '"' ],  
23062     [    8221, '"' ],  
23063     [    8226, "*" ],  
23064     [    8230, "..." ]
23065 ]; 
23066
23067     /*
23068  * - LGPL
23069  *
23070  * HtmlEditor
23071  * 
23072  */
23073
23074 /**
23075  * @class Roo.bootstrap.HtmlEditor
23076  * @extends Roo.bootstrap.TextArea
23077  * Bootstrap HtmlEditor class
23078
23079  * @constructor
23080  * Create a new HtmlEditor
23081  * @param {Object} config The config object
23082  */
23083
23084 Roo.bootstrap.HtmlEditor = function(config){
23085     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23086     if (!this.toolbars) {
23087         this.toolbars = [];
23088     }
23089     
23090     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23091     this.addEvents({
23092             /**
23093              * @event initialize
23094              * Fires when the editor is fully initialized (including the iframe)
23095              * @param {HtmlEditor} this
23096              */
23097             initialize: true,
23098             /**
23099              * @event activate
23100              * Fires when the editor is first receives the focus. Any insertion must wait
23101              * until after this event.
23102              * @param {HtmlEditor} this
23103              */
23104             activate: true,
23105              /**
23106              * @event beforesync
23107              * Fires before the textarea is updated with content from the editor iframe. Return false
23108              * to cancel the sync.
23109              * @param {HtmlEditor} this
23110              * @param {String} html
23111              */
23112             beforesync: true,
23113              /**
23114              * @event beforepush
23115              * Fires before the iframe editor is updated with content from the textarea. Return false
23116              * to cancel the push.
23117              * @param {HtmlEditor} this
23118              * @param {String} html
23119              */
23120             beforepush: true,
23121              /**
23122              * @event sync
23123              * Fires when the textarea is updated with content from the editor iframe.
23124              * @param {HtmlEditor} this
23125              * @param {String} html
23126              */
23127             sync: true,
23128              /**
23129              * @event push
23130              * Fires when the iframe editor is updated with content from the textarea.
23131              * @param {HtmlEditor} this
23132              * @param {String} html
23133              */
23134             push: true,
23135              /**
23136              * @event editmodechange
23137              * Fires when the editor switches edit modes
23138              * @param {HtmlEditor} this
23139              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23140              */
23141             editmodechange: true,
23142             /**
23143              * @event editorevent
23144              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23145              * @param {HtmlEditor} this
23146              */
23147             editorevent: true,
23148             /**
23149              * @event firstfocus
23150              * Fires when on first focus - needed by toolbars..
23151              * @param {HtmlEditor} this
23152              */
23153             firstfocus: true,
23154             /**
23155              * @event autosave
23156              * Auto save the htmlEditor value as a file into Events
23157              * @param {HtmlEditor} this
23158              */
23159             autosave: true,
23160             /**
23161              * @event savedpreview
23162              * preview the saved version of htmlEditor
23163              * @param {HtmlEditor} this
23164              */
23165             savedpreview: true
23166         });
23167 };
23168
23169
23170 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23171     
23172     
23173       /**
23174      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23175      */
23176     toolbars : false,
23177     
23178      /**
23179     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23180     */
23181     btns : [],
23182    
23183      /**
23184      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23185      *                        Roo.resizable.
23186      */
23187     resizable : false,
23188      /**
23189      * @cfg {Number} height (in pixels)
23190      */   
23191     height: 300,
23192    /**
23193      * @cfg {Number} width (in pixels)
23194      */   
23195     width: false,
23196     
23197     /**
23198      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23199      * 
23200      */
23201     stylesheets: false,
23202     
23203     // id of frame..
23204     frameId: false,
23205     
23206     // private properties
23207     validationEvent : false,
23208     deferHeight: true,
23209     initialized : false,
23210     activated : false,
23211     
23212     onFocus : Roo.emptyFn,
23213     iframePad:3,
23214     hideMode:'offsets',
23215     
23216     tbContainer : false,
23217     
23218     bodyCls : '',
23219     
23220     toolbarContainer :function() {
23221         return this.wrap.select('.x-html-editor-tb',true).first();
23222     },
23223
23224     /**
23225      * Protected method that will not generally be called directly. It
23226      * is called when the editor creates its toolbar. Override this method if you need to
23227      * add custom toolbar buttons.
23228      * @param {HtmlEditor} editor
23229      */
23230     createToolbar : function(){
23231         Roo.log('renewing');
23232         Roo.log("create toolbars");
23233         
23234         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23235         this.toolbars[0].render(this.toolbarContainer());
23236         
23237         return;
23238         
23239 //        if (!editor.toolbars || !editor.toolbars.length) {
23240 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23241 //        }
23242 //        
23243 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23244 //            editor.toolbars[i] = Roo.factory(
23245 //                    typeof(editor.toolbars[i]) == 'string' ?
23246 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23247 //                Roo.bootstrap.HtmlEditor);
23248 //            editor.toolbars[i].init(editor);
23249 //        }
23250     },
23251
23252      
23253     // private
23254     onRender : function(ct, position)
23255     {
23256        // Roo.log("Call onRender: " + this.xtype);
23257         var _t = this;
23258         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23259       
23260         this.wrap = this.inputEl().wrap({
23261             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23262         });
23263         
23264         this.editorcore.onRender(ct, position);
23265          
23266         if (this.resizable) {
23267             this.resizeEl = new Roo.Resizable(this.wrap, {
23268                 pinned : true,
23269                 wrap: true,
23270                 dynamic : true,
23271                 minHeight : this.height,
23272                 height: this.height,
23273                 handles : this.resizable,
23274                 width: this.width,
23275                 listeners : {
23276                     resize : function(r, w, h) {
23277                         _t.onResize(w,h); // -something
23278                     }
23279                 }
23280             });
23281             
23282         }
23283         this.createToolbar(this);
23284        
23285         
23286         if(!this.width && this.resizable){
23287             this.setSize(this.wrap.getSize());
23288         }
23289         if (this.resizeEl) {
23290             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23291             // should trigger onReize..
23292         }
23293         
23294     },
23295
23296     // private
23297     onResize : function(w, h)
23298     {
23299         Roo.log('resize: ' +w + ',' + h );
23300         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23301         var ew = false;
23302         var eh = false;
23303         
23304         if(this.inputEl() ){
23305             if(typeof w == 'number'){
23306                 var aw = w - this.wrap.getFrameWidth('lr');
23307                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23308                 ew = aw;
23309             }
23310             if(typeof h == 'number'){
23311                  var tbh = -11;  // fixme it needs to tool bar size!
23312                 for (var i =0; i < this.toolbars.length;i++) {
23313                     // fixme - ask toolbars for heights?
23314                     tbh += this.toolbars[i].el.getHeight();
23315                     //if (this.toolbars[i].footer) {
23316                     //    tbh += this.toolbars[i].footer.el.getHeight();
23317                     //}
23318                 }
23319               
23320                 
23321                 
23322                 
23323                 
23324                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23325                 ah -= 5; // knock a few pixes off for look..
23326                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23327                 var eh = ah;
23328             }
23329         }
23330         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23331         this.editorcore.onResize(ew,eh);
23332         
23333     },
23334
23335     /**
23336      * Toggles the editor between standard and source edit mode.
23337      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23338      */
23339     toggleSourceEdit : function(sourceEditMode)
23340     {
23341         this.editorcore.toggleSourceEdit(sourceEditMode);
23342         
23343         if(this.editorcore.sourceEditMode){
23344             Roo.log('editor - showing textarea');
23345             
23346 //            Roo.log('in');
23347 //            Roo.log(this.syncValue());
23348             this.syncValue();
23349             this.inputEl().removeClass(['hide', 'x-hidden']);
23350             this.inputEl().dom.removeAttribute('tabIndex');
23351             this.inputEl().focus();
23352         }else{
23353             Roo.log('editor - hiding textarea');
23354 //            Roo.log('out')
23355 //            Roo.log(this.pushValue()); 
23356             this.pushValue();
23357             
23358             this.inputEl().addClass(['hide', 'x-hidden']);
23359             this.inputEl().dom.setAttribute('tabIndex', -1);
23360             //this.deferFocus();
23361         }
23362          
23363         if(this.resizable){
23364             this.setSize(this.wrap.getSize());
23365         }
23366         
23367         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23368     },
23369  
23370     // private (for BoxComponent)
23371     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23372
23373     // private (for BoxComponent)
23374     getResizeEl : function(){
23375         return this.wrap;
23376     },
23377
23378     // private (for BoxComponent)
23379     getPositionEl : function(){
23380         return this.wrap;
23381     },
23382
23383     // private
23384     initEvents : function(){
23385         this.originalValue = this.getValue();
23386     },
23387
23388 //    /**
23389 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23390 //     * @method
23391 //     */
23392 //    markInvalid : Roo.emptyFn,
23393 //    /**
23394 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23395 //     * @method
23396 //     */
23397 //    clearInvalid : Roo.emptyFn,
23398
23399     setValue : function(v){
23400         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23401         this.editorcore.pushValue();
23402     },
23403
23404      
23405     // private
23406     deferFocus : function(){
23407         this.focus.defer(10, this);
23408     },
23409
23410     // doc'ed in Field
23411     focus : function(){
23412         this.editorcore.focus();
23413         
23414     },
23415       
23416
23417     // private
23418     onDestroy : function(){
23419         
23420         
23421         
23422         if(this.rendered){
23423             
23424             for (var i =0; i < this.toolbars.length;i++) {
23425                 // fixme - ask toolbars for heights?
23426                 this.toolbars[i].onDestroy();
23427             }
23428             
23429             this.wrap.dom.innerHTML = '';
23430             this.wrap.remove();
23431         }
23432     },
23433
23434     // private
23435     onFirstFocus : function(){
23436         //Roo.log("onFirstFocus");
23437         this.editorcore.onFirstFocus();
23438          for (var i =0; i < this.toolbars.length;i++) {
23439             this.toolbars[i].onFirstFocus();
23440         }
23441         
23442     },
23443     
23444     // private
23445     syncValue : function()
23446     {   
23447         this.editorcore.syncValue();
23448     },
23449     
23450     pushValue : function()
23451     {   
23452         this.editorcore.pushValue();
23453     }
23454      
23455     
23456     // hide stuff that is not compatible
23457     /**
23458      * @event blur
23459      * @hide
23460      */
23461     /**
23462      * @event change
23463      * @hide
23464      */
23465     /**
23466      * @event focus
23467      * @hide
23468      */
23469     /**
23470      * @event specialkey
23471      * @hide
23472      */
23473     /**
23474      * @cfg {String} fieldClass @hide
23475      */
23476     /**
23477      * @cfg {String} focusClass @hide
23478      */
23479     /**
23480      * @cfg {String} autoCreate @hide
23481      */
23482     /**
23483      * @cfg {String} inputType @hide
23484      */
23485     /**
23486      * @cfg {String} invalidClass @hide
23487      */
23488     /**
23489      * @cfg {String} invalidText @hide
23490      */
23491     /**
23492      * @cfg {String} msgFx @hide
23493      */
23494     /**
23495      * @cfg {String} validateOnBlur @hide
23496      */
23497 });
23498  
23499     
23500    
23501    
23502    
23503       
23504 Roo.namespace('Roo.bootstrap.htmleditor');
23505 /**
23506  * @class Roo.bootstrap.HtmlEditorToolbar1
23507  * Basic Toolbar
23508  * 
23509  * Usage:
23510  *
23511  new Roo.bootstrap.HtmlEditor({
23512     ....
23513     toolbars : [
23514         new Roo.bootstrap.HtmlEditorToolbar1({
23515             disable : { fonts: 1 , format: 1, ..., ... , ...],
23516             btns : [ .... ]
23517         })
23518     }
23519      
23520  * 
23521  * @cfg {Object} disable List of elements to disable..
23522  * @cfg {Array} btns List of additional buttons.
23523  * 
23524  * 
23525  * NEEDS Extra CSS? 
23526  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23527  */
23528  
23529 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23530 {
23531     
23532     Roo.apply(this, config);
23533     
23534     // default disabled, based on 'good practice'..
23535     this.disable = this.disable || {};
23536     Roo.applyIf(this.disable, {
23537         fontSize : true,
23538         colors : true,
23539         specialElements : true
23540     });
23541     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23542     
23543     this.editor = config.editor;
23544     this.editorcore = config.editor.editorcore;
23545     
23546     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23547     
23548     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23549     // dont call parent... till later.
23550 }
23551 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23552      
23553     bar : true,
23554     
23555     editor : false,
23556     editorcore : false,
23557     
23558     
23559     formats : [
23560         "p" ,  
23561         "h1","h2","h3","h4","h5","h6", 
23562         "pre", "code", 
23563         "abbr", "acronym", "address", "cite", "samp", "var",
23564         'div','span'
23565     ],
23566     
23567     onRender : function(ct, position)
23568     {
23569        // Roo.log("Call onRender: " + this.xtype);
23570         
23571        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23572        Roo.log(this.el);
23573        this.el.dom.style.marginBottom = '0';
23574        var _this = this;
23575        var editorcore = this.editorcore;
23576        var editor= this.editor;
23577        
23578        var children = [];
23579        var btn = function(id,cmd , toggle, handler, html){
23580        
23581             var  event = toggle ? 'toggle' : 'click';
23582        
23583             var a = {
23584                 size : 'sm',
23585                 xtype: 'Button',
23586                 xns: Roo.bootstrap,
23587                 glyphicon : id,
23588                 cmd : id || cmd,
23589                 enableToggle:toggle !== false,
23590                 html : html || '',
23591                 pressed : toggle ? false : null,
23592                 listeners : {}
23593             };
23594             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23595                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23596             };
23597             children.push(a);
23598             return a;
23599        }
23600        
23601     //    var cb_box = function...
23602         
23603         var style = {
23604                 xtype: 'Button',
23605                 size : 'sm',
23606                 xns: Roo.bootstrap,
23607                 glyphicon : 'font',
23608                 //html : 'submit'
23609                 menu : {
23610                     xtype: 'Menu',
23611                     xns: Roo.bootstrap,
23612                     items:  []
23613                 }
23614         };
23615         Roo.each(this.formats, function(f) {
23616             style.menu.items.push({
23617                 xtype :'MenuItem',
23618                 xns: Roo.bootstrap,
23619                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23620                 tagname : f,
23621                 listeners : {
23622                     click : function()
23623                     {
23624                         editorcore.insertTag(this.tagname);
23625                         editor.focus();
23626                     }
23627                 }
23628                 
23629             });
23630         });
23631         children.push(style);   
23632         
23633         btn('bold',false,true);
23634         btn('italic',false,true);
23635         btn('align-left', 'justifyleft',true);
23636         btn('align-center', 'justifycenter',true);
23637         btn('align-right' , 'justifyright',true);
23638         btn('link', false, false, function(btn) {
23639             //Roo.log("create link?");
23640             var url = prompt(this.createLinkText, this.defaultLinkValue);
23641             if(url && url != 'http:/'+'/'){
23642                 this.editorcore.relayCmd('createlink', url);
23643             }
23644         }),
23645         btn('list','insertunorderedlist',true);
23646         btn('pencil', false,true, function(btn){
23647                 Roo.log(this);
23648                 this.toggleSourceEdit(btn.pressed);
23649         });
23650         
23651         if (this.editor.btns.length > 0) {
23652             for (var i = 0; i<this.editor.btns.length; i++) {
23653                 children.push(this.editor.btns[i]);
23654             }
23655         }
23656         
23657         /*
23658         var cog = {
23659                 xtype: 'Button',
23660                 size : 'sm',
23661                 xns: Roo.bootstrap,
23662                 glyphicon : 'cog',
23663                 //html : 'submit'
23664                 menu : {
23665                     xtype: 'Menu',
23666                     xns: Roo.bootstrap,
23667                     items:  []
23668                 }
23669         };
23670         
23671         cog.menu.items.push({
23672             xtype :'MenuItem',
23673             xns: Roo.bootstrap,
23674             html : Clean styles,
23675             tagname : f,
23676             listeners : {
23677                 click : function()
23678                 {
23679                     editorcore.insertTag(this.tagname);
23680                     editor.focus();
23681                 }
23682             }
23683             
23684         });
23685        */
23686         
23687          
23688        this.xtype = 'NavSimplebar';
23689         
23690         for(var i=0;i< children.length;i++) {
23691             
23692             this.buttons.add(this.addxtypeChild(children[i]));
23693             
23694         }
23695         
23696         editor.on('editorevent', this.updateToolbar, this);
23697     },
23698     onBtnClick : function(id)
23699     {
23700        this.editorcore.relayCmd(id);
23701        this.editorcore.focus();
23702     },
23703     
23704     /**
23705      * Protected method that will not generally be called directly. It triggers
23706      * a toolbar update by reading the markup state of the current selection in the editor.
23707      */
23708     updateToolbar: function(){
23709
23710         if(!this.editorcore.activated){
23711             this.editor.onFirstFocus(); // is this neeed?
23712             return;
23713         }
23714
23715         var btns = this.buttons; 
23716         var doc = this.editorcore.doc;
23717         btns.get('bold').setActive(doc.queryCommandState('bold'));
23718         btns.get('italic').setActive(doc.queryCommandState('italic'));
23719         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23720         
23721         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23722         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23723         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23724         
23725         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23726         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23727          /*
23728         
23729         var ans = this.editorcore.getAllAncestors();
23730         if (this.formatCombo) {
23731             
23732             
23733             var store = this.formatCombo.store;
23734             this.formatCombo.setValue("");
23735             for (var i =0; i < ans.length;i++) {
23736                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23737                     // select it..
23738                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23739                     break;
23740                 }
23741             }
23742         }
23743         
23744         
23745         
23746         // hides menus... - so this cant be on a menu...
23747         Roo.bootstrap.MenuMgr.hideAll();
23748         */
23749         Roo.bootstrap.MenuMgr.hideAll();
23750         //this.editorsyncValue();
23751     },
23752     onFirstFocus: function() {
23753         this.buttons.each(function(item){
23754            item.enable();
23755         });
23756     },
23757     toggleSourceEdit : function(sourceEditMode){
23758         
23759           
23760         if(sourceEditMode){
23761             Roo.log("disabling buttons");
23762            this.buttons.each( function(item){
23763                 if(item.cmd != 'pencil'){
23764                     item.disable();
23765                 }
23766             });
23767           
23768         }else{
23769             Roo.log("enabling buttons");
23770             if(this.editorcore.initialized){
23771                 this.buttons.each( function(item){
23772                     item.enable();
23773                 });
23774             }
23775             
23776         }
23777         Roo.log("calling toggole on editor");
23778         // tell the editor that it's been pressed..
23779         this.editor.toggleSourceEdit(sourceEditMode);
23780        
23781     }
23782 });
23783
23784
23785
23786
23787
23788 /**
23789  * @class Roo.bootstrap.Table.AbstractSelectionModel
23790  * @extends Roo.util.Observable
23791  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23792  * implemented by descendant classes.  This class should not be directly instantiated.
23793  * @constructor
23794  */
23795 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23796     this.locked = false;
23797     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23798 };
23799
23800
23801 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23802     /** @ignore Called by the grid automatically. Do not call directly. */
23803     init : function(grid){
23804         this.grid = grid;
23805         this.initEvents();
23806     },
23807
23808     /**
23809      * Locks the selections.
23810      */
23811     lock : function(){
23812         this.locked = true;
23813     },
23814
23815     /**
23816      * Unlocks the selections.
23817      */
23818     unlock : function(){
23819         this.locked = false;
23820     },
23821
23822     /**
23823      * Returns true if the selections are locked.
23824      * @return {Boolean}
23825      */
23826     isLocked : function(){
23827         return this.locked;
23828     }
23829 });
23830 /**
23831  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23832  * @class Roo.bootstrap.Table.RowSelectionModel
23833  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23834  * It supports multiple selections and keyboard selection/navigation. 
23835  * @constructor
23836  * @param {Object} config
23837  */
23838
23839 Roo.bootstrap.Table.RowSelectionModel = function(config){
23840     Roo.apply(this, config);
23841     this.selections = new Roo.util.MixedCollection(false, function(o){
23842         return o.id;
23843     });
23844
23845     this.last = false;
23846     this.lastActive = false;
23847
23848     this.addEvents({
23849         /**
23850              * @event selectionchange
23851              * Fires when the selection changes
23852              * @param {SelectionModel} this
23853              */
23854             "selectionchange" : true,
23855         /**
23856              * @event afterselectionchange
23857              * Fires after the selection changes (eg. by key press or clicking)
23858              * @param {SelectionModel} this
23859              */
23860             "afterselectionchange" : true,
23861         /**
23862              * @event beforerowselect
23863              * Fires when a row is selected being selected, return false to cancel.
23864              * @param {SelectionModel} this
23865              * @param {Number} rowIndex The selected index
23866              * @param {Boolean} keepExisting False if other selections will be cleared
23867              */
23868             "beforerowselect" : true,
23869         /**
23870              * @event rowselect
23871              * Fires when a row is selected.
23872              * @param {SelectionModel} this
23873              * @param {Number} rowIndex The selected index
23874              * @param {Roo.data.Record} r The record
23875              */
23876             "rowselect" : true,
23877         /**
23878              * @event rowdeselect
23879              * Fires when a row is deselected.
23880              * @param {SelectionModel} this
23881              * @param {Number} rowIndex The selected index
23882              */
23883         "rowdeselect" : true
23884     });
23885     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23886     this.locked = false;
23887  };
23888
23889 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23890     /**
23891      * @cfg {Boolean} singleSelect
23892      * True to allow selection of only one row at a time (defaults to false)
23893      */
23894     singleSelect : false,
23895
23896     // private
23897     initEvents : function()
23898     {
23899
23900         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23901         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23902         //}else{ // allow click to work like normal
23903          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23904         //}
23905         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23906         this.grid.on("rowclick", this.handleMouseDown, this);
23907         
23908         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23909             "up" : function(e){
23910                 if(!e.shiftKey){
23911                     this.selectPrevious(e.shiftKey);
23912                 }else if(this.last !== false && this.lastActive !== false){
23913                     var last = this.last;
23914                     this.selectRange(this.last,  this.lastActive-1);
23915                     this.grid.getView().focusRow(this.lastActive);
23916                     if(last !== false){
23917                         this.last = last;
23918                     }
23919                 }else{
23920                     this.selectFirstRow();
23921                 }
23922                 this.fireEvent("afterselectionchange", this);
23923             },
23924             "down" : function(e){
23925                 if(!e.shiftKey){
23926                     this.selectNext(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             scope: this
23940         });
23941         this.grid.store.on('load', function(){
23942             this.selections.clear();
23943         },this);
23944         /*
23945         var view = this.grid.view;
23946         view.on("refresh", this.onRefresh, this);
23947         view.on("rowupdated", this.onRowUpdated, this);
23948         view.on("rowremoved", this.onRemove, this);
23949         */
23950     },
23951
23952     // private
23953     onRefresh : function()
23954     {
23955         var ds = this.grid.store, i, v = this.grid.view;
23956         var s = this.selections;
23957         s.each(function(r){
23958             if((i = ds.indexOfId(r.id)) != -1){
23959                 v.onRowSelect(i);
23960             }else{
23961                 s.remove(r);
23962             }
23963         });
23964     },
23965
23966     // private
23967     onRemove : function(v, index, r){
23968         this.selections.remove(r);
23969     },
23970
23971     // private
23972     onRowUpdated : function(v, index, r){
23973         if(this.isSelected(r)){
23974             v.onRowSelect(index);
23975         }
23976     },
23977
23978     /**
23979      * Select records.
23980      * @param {Array} records The records to select
23981      * @param {Boolean} keepExisting (optional) True to keep existing selections
23982      */
23983     selectRecords : function(records, keepExisting)
23984     {
23985         if(!keepExisting){
23986             this.clearSelections();
23987         }
23988             var ds = this.grid.store;
23989         for(var i = 0, len = records.length; i < len; i++){
23990             this.selectRow(ds.indexOf(records[i]), true);
23991         }
23992     },
23993
23994     /**
23995      * Gets the number of selected rows.
23996      * @return {Number}
23997      */
23998     getCount : function(){
23999         return this.selections.length;
24000     },
24001
24002     /**
24003      * Selects the first row in the grid.
24004      */
24005     selectFirstRow : function(){
24006         this.selectRow(0);
24007     },
24008
24009     /**
24010      * Select the last row.
24011      * @param {Boolean} keepExisting (optional) True to keep existing selections
24012      */
24013     selectLastRow : function(keepExisting){
24014         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24015         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24016     },
24017
24018     /**
24019      * Selects the row immediately following the last selected row.
24020      * @param {Boolean} keepExisting (optional) True to keep existing selections
24021      */
24022     selectNext : function(keepExisting)
24023     {
24024             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24025             this.selectRow(this.last+1, keepExisting);
24026             this.grid.getView().focusRow(this.last);
24027         }
24028     },
24029
24030     /**
24031      * Selects the row that precedes the last selected row.
24032      * @param {Boolean} keepExisting (optional) True to keep existing selections
24033      */
24034     selectPrevious : function(keepExisting){
24035         if(this.last){
24036             this.selectRow(this.last-1, keepExisting);
24037             this.grid.getView().focusRow(this.last);
24038         }
24039     },
24040
24041     /**
24042      * Returns the selected records
24043      * @return {Array} Array of selected records
24044      */
24045     getSelections : function(){
24046         return [].concat(this.selections.items);
24047     },
24048
24049     /**
24050      * Returns the first selected record.
24051      * @return {Record}
24052      */
24053     getSelected : function(){
24054         return this.selections.itemAt(0);
24055     },
24056
24057
24058     /**
24059      * Clears all selections.
24060      */
24061     clearSelections : function(fast)
24062     {
24063         if(this.locked) {
24064             return;
24065         }
24066         if(fast !== true){
24067                 var ds = this.grid.store;
24068             var s = this.selections;
24069             s.each(function(r){
24070                 this.deselectRow(ds.indexOfId(r.id));
24071             }, this);
24072             s.clear();
24073         }else{
24074             this.selections.clear();
24075         }
24076         this.last = false;
24077     },
24078
24079
24080     /**
24081      * Selects all rows.
24082      */
24083     selectAll : function(){
24084         if(this.locked) {
24085             return;
24086         }
24087         this.selections.clear();
24088         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24089             this.selectRow(i, true);
24090         }
24091     },
24092
24093     /**
24094      * Returns True if there is a selection.
24095      * @return {Boolean}
24096      */
24097     hasSelection : function(){
24098         return this.selections.length > 0;
24099     },
24100
24101     /**
24102      * Returns True if the specified row is selected.
24103      * @param {Number/Record} record The record or index of the record to check
24104      * @return {Boolean}
24105      */
24106     isSelected : function(index){
24107             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24108         return (r && this.selections.key(r.id) ? true : false);
24109     },
24110
24111     /**
24112      * Returns True if the specified record id is selected.
24113      * @param {String} id The id of record to check
24114      * @return {Boolean}
24115      */
24116     isIdSelected : function(id){
24117         return (this.selections.key(id) ? true : false);
24118     },
24119
24120
24121     // private
24122     handleMouseDBClick : function(e, t){
24123         
24124     },
24125     // private
24126     handleMouseDown : function(e, t)
24127     {
24128             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24129         if(this.isLocked() || rowIndex < 0 ){
24130             return;
24131         };
24132         if(e.shiftKey && this.last !== false){
24133             var last = this.last;
24134             this.selectRange(last, rowIndex, e.ctrlKey);
24135             this.last = last; // reset the last
24136             t.focus();
24137     
24138         }else{
24139             var isSelected = this.isSelected(rowIndex);
24140             //Roo.log("select row:" + rowIndex);
24141             if(isSelected){
24142                 this.deselectRow(rowIndex);
24143             } else {
24144                         this.selectRow(rowIndex, true);
24145             }
24146     
24147             /*
24148                 if(e.button !== 0 && isSelected){
24149                 alert('rowIndex 2: ' + rowIndex);
24150                     view.focusRow(rowIndex);
24151                 }else if(e.ctrlKey && isSelected){
24152                     this.deselectRow(rowIndex);
24153                 }else if(!isSelected){
24154                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24155                     view.focusRow(rowIndex);
24156                 }
24157             */
24158         }
24159         this.fireEvent("afterselectionchange", this);
24160     },
24161     // private
24162     handleDragableRowClick :  function(grid, rowIndex, e) 
24163     {
24164         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24165             this.selectRow(rowIndex, false);
24166             grid.view.focusRow(rowIndex);
24167              this.fireEvent("afterselectionchange", this);
24168         }
24169     },
24170     
24171     /**
24172      * Selects multiple rows.
24173      * @param {Array} rows Array of the indexes of the row to select
24174      * @param {Boolean} keepExisting (optional) True to keep existing selections
24175      */
24176     selectRows : function(rows, keepExisting){
24177         if(!keepExisting){
24178             this.clearSelections();
24179         }
24180         for(var i = 0, len = rows.length; i < len; i++){
24181             this.selectRow(rows[i], true);
24182         }
24183     },
24184
24185     /**
24186      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24187      * @param {Number} startRow The index of the first row in the range
24188      * @param {Number} endRow The index of the last row in the range
24189      * @param {Boolean} keepExisting (optional) True to retain existing selections
24190      */
24191     selectRange : function(startRow, endRow, keepExisting){
24192         if(this.locked) {
24193             return;
24194         }
24195         if(!keepExisting){
24196             this.clearSelections();
24197         }
24198         if(startRow <= endRow){
24199             for(var i = startRow; i <= endRow; i++){
24200                 this.selectRow(i, true);
24201             }
24202         }else{
24203             for(var i = startRow; i >= endRow; i--){
24204                 this.selectRow(i, true);
24205             }
24206         }
24207     },
24208
24209     /**
24210      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24211      * @param {Number} startRow The index of the first row in the range
24212      * @param {Number} endRow The index of the last row in the range
24213      */
24214     deselectRange : function(startRow, endRow, preventViewNotify){
24215         if(this.locked) {
24216             return;
24217         }
24218         for(var i = startRow; i <= endRow; i++){
24219             this.deselectRow(i, preventViewNotify);
24220         }
24221     },
24222
24223     /**
24224      * Selects a row.
24225      * @param {Number} row The index of the row to select
24226      * @param {Boolean} keepExisting (optional) True to keep existing selections
24227      */
24228     selectRow : function(index, keepExisting, preventViewNotify)
24229     {
24230             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24231             return;
24232         }
24233         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24234             if(!keepExisting || this.singleSelect){
24235                 this.clearSelections();
24236             }
24237             
24238             var r = this.grid.store.getAt(index);
24239             //console.log('selectRow - record id :' + r.id);
24240             
24241             this.selections.add(r);
24242             this.last = this.lastActive = index;
24243             if(!preventViewNotify){
24244                 var proxy = new Roo.Element(
24245                                 this.grid.getRowDom(index)
24246                 );
24247                 proxy.addClass('bg-info info');
24248             }
24249             this.fireEvent("rowselect", this, index, r);
24250             this.fireEvent("selectionchange", this);
24251         }
24252     },
24253
24254     /**
24255      * Deselects a row.
24256      * @param {Number} row The index of the row to deselect
24257      */
24258     deselectRow : function(index, preventViewNotify)
24259     {
24260         if(this.locked) {
24261             return;
24262         }
24263         if(this.last == index){
24264             this.last = false;
24265         }
24266         if(this.lastActive == index){
24267             this.lastActive = false;
24268         }
24269         
24270         var r = this.grid.store.getAt(index);
24271         if (!r) {
24272             return;
24273         }
24274         
24275         this.selections.remove(r);
24276         //.console.log('deselectRow - record id :' + r.id);
24277         if(!preventViewNotify){
24278         
24279             var proxy = new Roo.Element(
24280                 this.grid.getRowDom(index)
24281             );
24282             proxy.removeClass('bg-info info');
24283         }
24284         this.fireEvent("rowdeselect", this, index);
24285         this.fireEvent("selectionchange", this);
24286     },
24287
24288     // private
24289     restoreLast : function(){
24290         if(this._last){
24291             this.last = this._last;
24292         }
24293     },
24294
24295     // private
24296     acceptsNav : function(row, col, cm){
24297         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24298     },
24299
24300     // private
24301     onEditorKey : function(field, e){
24302         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24303         if(k == e.TAB){
24304             e.stopEvent();
24305             ed.completeEdit();
24306             if(e.shiftKey){
24307                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24308             }else{
24309                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24310             }
24311         }else if(k == e.ENTER && !e.ctrlKey){
24312             e.stopEvent();
24313             ed.completeEdit();
24314             if(e.shiftKey){
24315                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24316             }else{
24317                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24318             }
24319         }else if(k == e.ESC){
24320             ed.cancelEdit();
24321         }
24322         if(newCell){
24323             g.startEditing(newCell[0], newCell[1]);
24324         }
24325     }
24326 });
24327 /*
24328  * Based on:
24329  * Ext JS Library 1.1.1
24330  * Copyright(c) 2006-2007, Ext JS, LLC.
24331  *
24332  * Originally Released Under LGPL - original licence link has changed is not relivant.
24333  *
24334  * Fork - LGPL
24335  * <script type="text/javascript">
24336  */
24337  
24338 /**
24339  * @class Roo.bootstrap.PagingToolbar
24340  * @extends Roo.bootstrap.NavSimplebar
24341  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24342  * @constructor
24343  * Create a new PagingToolbar
24344  * @param {Object} config The config object
24345  * @param {Roo.data.Store} store
24346  */
24347 Roo.bootstrap.PagingToolbar = function(config)
24348 {
24349     // old args format still supported... - xtype is prefered..
24350         // created from xtype...
24351     
24352     this.ds = config.dataSource;
24353     
24354     if (config.store && !this.ds) {
24355         this.store= Roo.factory(config.store, Roo.data);
24356         this.ds = this.store;
24357         this.ds.xmodule = this.xmodule || false;
24358     }
24359     
24360     this.toolbarItems = [];
24361     if (config.items) {
24362         this.toolbarItems = config.items;
24363     }
24364     
24365     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24366     
24367     this.cursor = 0;
24368     
24369     if (this.ds) { 
24370         this.bind(this.ds);
24371     }
24372     
24373     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24374     
24375 };
24376
24377 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24378     /**
24379      * @cfg {Roo.data.Store} dataSource
24380      * The underlying data store providing the paged data
24381      */
24382     /**
24383      * @cfg {String/HTMLElement/Element} container
24384      * container The id or element that will contain the toolbar
24385      */
24386     /**
24387      * @cfg {Boolean} displayInfo
24388      * True to display the displayMsg (defaults to false)
24389      */
24390     /**
24391      * @cfg {Number} pageSize
24392      * The number of records to display per page (defaults to 20)
24393      */
24394     pageSize: 20,
24395     /**
24396      * @cfg {String} displayMsg
24397      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24398      */
24399     displayMsg : 'Displaying {0} - {1} of {2}',
24400     /**
24401      * @cfg {String} emptyMsg
24402      * The message to display when no records are found (defaults to "No data to display")
24403      */
24404     emptyMsg : 'No data to display',
24405     /**
24406      * Customizable piece of the default paging text (defaults to "Page")
24407      * @type String
24408      */
24409     beforePageText : "Page",
24410     /**
24411      * Customizable piece of the default paging text (defaults to "of %0")
24412      * @type String
24413      */
24414     afterPageText : "of {0}",
24415     /**
24416      * Customizable piece of the default paging text (defaults to "First Page")
24417      * @type String
24418      */
24419     firstText : "First Page",
24420     /**
24421      * Customizable piece of the default paging text (defaults to "Previous Page")
24422      * @type String
24423      */
24424     prevText : "Previous Page",
24425     /**
24426      * Customizable piece of the default paging text (defaults to "Next Page")
24427      * @type String
24428      */
24429     nextText : "Next Page",
24430     /**
24431      * Customizable piece of the default paging text (defaults to "Last Page")
24432      * @type String
24433      */
24434     lastText : "Last Page",
24435     /**
24436      * Customizable piece of the default paging text (defaults to "Refresh")
24437      * @type String
24438      */
24439     refreshText : "Refresh",
24440
24441     buttons : false,
24442     // private
24443     onRender : function(ct, position) 
24444     {
24445         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24446         this.navgroup.parentId = this.id;
24447         this.navgroup.onRender(this.el, null);
24448         // add the buttons to the navgroup
24449         
24450         if(this.displayInfo){
24451             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24452             this.displayEl = this.el.select('.x-paging-info', true).first();
24453 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24454 //            this.displayEl = navel.el.select('span',true).first();
24455         }
24456         
24457         var _this = this;
24458         
24459         if(this.buttons){
24460             Roo.each(_this.buttons, function(e){ // this might need to use render????
24461                Roo.factory(e).onRender(_this.el, null);
24462             });
24463         }
24464             
24465         Roo.each(_this.toolbarItems, function(e) {
24466             _this.navgroup.addItem(e);
24467         });
24468         
24469         
24470         this.first = this.navgroup.addItem({
24471             tooltip: this.firstText,
24472             cls: "prev",
24473             icon : 'fa fa-backward',
24474             disabled: true,
24475             preventDefault: true,
24476             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24477         });
24478         
24479         this.prev =  this.navgroup.addItem({
24480             tooltip: this.prevText,
24481             cls: "prev",
24482             icon : 'fa fa-step-backward',
24483             disabled: true,
24484             preventDefault: true,
24485             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24486         });
24487     //this.addSeparator();
24488         
24489         
24490         var field = this.navgroup.addItem( {
24491             tagtype : 'span',
24492             cls : 'x-paging-position',
24493             
24494             html : this.beforePageText  +
24495                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24496                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24497          } ); //?? escaped?
24498         
24499         this.field = field.el.select('input', true).first();
24500         this.field.on("keydown", this.onPagingKeydown, this);
24501         this.field.on("focus", function(){this.dom.select();});
24502     
24503     
24504         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24505         //this.field.setHeight(18);
24506         //this.addSeparator();
24507         this.next = this.navgroup.addItem({
24508             tooltip: this.nextText,
24509             cls: "next",
24510             html : ' <i class="fa fa-step-forward">',
24511             disabled: true,
24512             preventDefault: true,
24513             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24514         });
24515         this.last = this.navgroup.addItem({
24516             tooltip: this.lastText,
24517             icon : 'fa fa-forward',
24518             cls: "next",
24519             disabled: true,
24520             preventDefault: true,
24521             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24522         });
24523     //this.addSeparator();
24524         this.loading = this.navgroup.addItem({
24525             tooltip: this.refreshText,
24526             icon: 'fa fa-refresh',
24527             preventDefault: true,
24528             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24529         });
24530         
24531     },
24532
24533     // private
24534     updateInfo : function(){
24535         if(this.displayEl){
24536             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24537             var msg = count == 0 ?
24538                 this.emptyMsg :
24539                 String.format(
24540                     this.displayMsg,
24541                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24542                 );
24543             this.displayEl.update(msg);
24544         }
24545     },
24546
24547     // private
24548     onLoad : function(ds, r, o)
24549     {
24550         this.cursor = o.params.start ? o.params.start : 0;
24551         
24552         var d = this.getPageData(),
24553             ap = d.activePage,
24554             ps = d.pages;
24555         
24556         
24557         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24558         this.field.dom.value = ap;
24559         this.first.setDisabled(ap == 1);
24560         this.prev.setDisabled(ap == 1);
24561         this.next.setDisabled(ap == ps);
24562         this.last.setDisabled(ap == ps);
24563         this.loading.enable();
24564         this.updateInfo();
24565     },
24566
24567     // private
24568     getPageData : function(){
24569         var total = this.ds.getTotalCount();
24570         return {
24571             total : total,
24572             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24573             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24574         };
24575     },
24576
24577     // private
24578     onLoadError : function(){
24579         this.loading.enable();
24580     },
24581
24582     // private
24583     onPagingKeydown : function(e){
24584         var k = e.getKey();
24585         var d = this.getPageData();
24586         if(k == e.RETURN){
24587             var v = this.field.dom.value, pageNum;
24588             if(!v || isNaN(pageNum = parseInt(v, 10))){
24589                 this.field.dom.value = d.activePage;
24590                 return;
24591             }
24592             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24593             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24594             e.stopEvent();
24595         }
24596         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))
24597         {
24598           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24599           this.field.dom.value = pageNum;
24600           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24601           e.stopEvent();
24602         }
24603         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24604         {
24605           var v = this.field.dom.value, pageNum; 
24606           var increment = (e.shiftKey) ? 10 : 1;
24607           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24608                 increment *= -1;
24609           }
24610           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24611             this.field.dom.value = d.activePage;
24612             return;
24613           }
24614           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24615           {
24616             this.field.dom.value = parseInt(v, 10) + increment;
24617             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24618             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24619           }
24620           e.stopEvent();
24621         }
24622     },
24623
24624     // private
24625     beforeLoad : function(){
24626         if(this.loading){
24627             this.loading.disable();
24628         }
24629     },
24630
24631     // private
24632     onClick : function(which){
24633         
24634         var ds = this.ds;
24635         if (!ds) {
24636             return;
24637         }
24638         
24639         switch(which){
24640             case "first":
24641                 ds.load({params:{start: 0, limit: this.pageSize}});
24642             break;
24643             case "prev":
24644                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24645             break;
24646             case "next":
24647                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24648             break;
24649             case "last":
24650                 var total = ds.getTotalCount();
24651                 var extra = total % this.pageSize;
24652                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24653                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24654             break;
24655             case "refresh":
24656                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24657             break;
24658         }
24659     },
24660
24661     /**
24662      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24663      * @param {Roo.data.Store} store The data store to unbind
24664      */
24665     unbind : function(ds){
24666         ds.un("beforeload", this.beforeLoad, this);
24667         ds.un("load", this.onLoad, this);
24668         ds.un("loadexception", this.onLoadError, this);
24669         ds.un("remove", this.updateInfo, this);
24670         ds.un("add", this.updateInfo, this);
24671         this.ds = undefined;
24672     },
24673
24674     /**
24675      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24676      * @param {Roo.data.Store} store The data store to bind
24677      */
24678     bind : function(ds){
24679         ds.on("beforeload", this.beforeLoad, this);
24680         ds.on("load", this.onLoad, this);
24681         ds.on("loadexception", this.onLoadError, this);
24682         ds.on("remove", this.updateInfo, this);
24683         ds.on("add", this.updateInfo, this);
24684         this.ds = ds;
24685     }
24686 });/*
24687  * - LGPL
24688  *
24689  * element
24690  * 
24691  */
24692
24693 /**
24694  * @class Roo.bootstrap.MessageBar
24695  * @extends Roo.bootstrap.Component
24696  * Bootstrap MessageBar class
24697  * @cfg {String} html contents of the MessageBar
24698  * @cfg {String} weight (info | success | warning | danger) default info
24699  * @cfg {String} beforeClass insert the bar before the given class
24700  * @cfg {Boolean} closable (true | false) default false
24701  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24702  * 
24703  * @constructor
24704  * Create a new Element
24705  * @param {Object} config The config object
24706  */
24707
24708 Roo.bootstrap.MessageBar = function(config){
24709     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24710 };
24711
24712 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24713     
24714     html: '',
24715     weight: 'info',
24716     closable: false,
24717     fixed: false,
24718     beforeClass: 'bootstrap-sticky-wrap',
24719     
24720     getAutoCreate : function(){
24721         
24722         var cfg = {
24723             tag: 'div',
24724             cls: 'alert alert-dismissable alert-' + this.weight,
24725             cn: [
24726                 {
24727                     tag: 'span',
24728                     cls: 'message',
24729                     html: this.html || ''
24730                 }
24731             ]
24732         };
24733         
24734         if(this.fixed){
24735             cfg.cls += ' alert-messages-fixed';
24736         }
24737         
24738         if(this.closable){
24739             cfg.cn.push({
24740                 tag: 'button',
24741                 cls: 'close',
24742                 html: 'x'
24743             });
24744         }
24745         
24746         return cfg;
24747     },
24748     
24749     onRender : function(ct, position)
24750     {
24751         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24752         
24753         if(!this.el){
24754             var cfg = Roo.apply({},  this.getAutoCreate());
24755             cfg.id = Roo.id();
24756             
24757             if (this.cls) {
24758                 cfg.cls += ' ' + this.cls;
24759             }
24760             if (this.style) {
24761                 cfg.style = this.style;
24762             }
24763             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24764             
24765             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24766         }
24767         
24768         this.el.select('>button.close').on('click', this.hide, this);
24769         
24770     },
24771     
24772     show : function()
24773     {
24774         if (!this.rendered) {
24775             this.render();
24776         }
24777         
24778         this.el.show();
24779         
24780         this.fireEvent('show', this);
24781         
24782     },
24783     
24784     hide : function()
24785     {
24786         if (!this.rendered) {
24787             this.render();
24788         }
24789         
24790         this.el.hide();
24791         
24792         this.fireEvent('hide', this);
24793     },
24794     
24795     update : function()
24796     {
24797 //        var e = this.el.dom.firstChild;
24798 //        
24799 //        if(this.closable){
24800 //            e = e.nextSibling;
24801 //        }
24802 //        
24803 //        e.data = this.html || '';
24804
24805         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24806     }
24807    
24808 });
24809
24810  
24811
24812      /*
24813  * - LGPL
24814  *
24815  * Graph
24816  * 
24817  */
24818
24819
24820 /**
24821  * @class Roo.bootstrap.Graph
24822  * @extends Roo.bootstrap.Component
24823  * Bootstrap Graph class
24824 > Prameters
24825  -sm {number} sm 4
24826  -md {number} md 5
24827  @cfg {String} graphtype  bar | vbar | pie
24828  @cfg {number} g_x coodinator | centre x (pie)
24829  @cfg {number} g_y coodinator | centre y (pie)
24830  @cfg {number} g_r radius (pie)
24831  @cfg {number} g_height height of the chart (respected by all elements in the set)
24832  @cfg {number} g_width width of the chart (respected by all elements in the set)
24833  @cfg {Object} title The title of the chart
24834     
24835  -{Array}  values
24836  -opts (object) options for the chart 
24837      o {
24838      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24839      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24840      o vgutter (number)
24841      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.
24842      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24843      o to
24844      o stretch (boolean)
24845      o }
24846  -opts (object) options for the pie
24847      o{
24848      o cut
24849      o startAngle (number)
24850      o endAngle (number)
24851      } 
24852  *
24853  * @constructor
24854  * Create a new Input
24855  * @param {Object} config The config object
24856  */
24857
24858 Roo.bootstrap.Graph = function(config){
24859     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24860     
24861     this.addEvents({
24862         // img events
24863         /**
24864          * @event click
24865          * The img click event for the img.
24866          * @param {Roo.EventObject} e
24867          */
24868         "click" : true
24869     });
24870 };
24871
24872 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24873     
24874     sm: 4,
24875     md: 5,
24876     graphtype: 'bar',
24877     g_height: 250,
24878     g_width: 400,
24879     g_x: 50,
24880     g_y: 50,
24881     g_r: 30,
24882     opts:{
24883         //g_colors: this.colors,
24884         g_type: 'soft',
24885         g_gutter: '20%'
24886
24887     },
24888     title : false,
24889
24890     getAutoCreate : function(){
24891         
24892         var cfg = {
24893             tag: 'div',
24894             html : null
24895         };
24896         
24897         
24898         return  cfg;
24899     },
24900
24901     onRender : function(ct,position){
24902         
24903         
24904         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24905         
24906         if (typeof(Raphael) == 'undefined') {
24907             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24908             return;
24909         }
24910         
24911         this.raphael = Raphael(this.el.dom);
24912         
24913                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24914                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24915                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24916                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24917                 /*
24918                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24919                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24920                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24921                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24922                 
24923                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24924                 r.barchart(330, 10, 300, 220, data1);
24925                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24926                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24927                 */
24928                 
24929                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24930                 // r.barchart(30, 30, 560, 250,  xdata, {
24931                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24932                 //     axis : "0 0 1 1",
24933                 //     axisxlabels :  xdata
24934                 //     //yvalues : cols,
24935                    
24936                 // });
24937 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24938 //        
24939 //        this.load(null,xdata,{
24940 //                axis : "0 0 1 1",
24941 //                axisxlabels :  xdata
24942 //                });
24943
24944     },
24945
24946     load : function(graphtype,xdata,opts)
24947     {
24948         this.raphael.clear();
24949         if(!graphtype) {
24950             graphtype = this.graphtype;
24951         }
24952         if(!opts){
24953             opts = this.opts;
24954         }
24955         var r = this.raphael,
24956             fin = function () {
24957                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24958             },
24959             fout = function () {
24960                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24961             },
24962             pfin = function() {
24963                 this.sector.stop();
24964                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24965
24966                 if (this.label) {
24967                     this.label[0].stop();
24968                     this.label[0].attr({ r: 7.5 });
24969                     this.label[1].attr({ "font-weight": 800 });
24970                 }
24971             },
24972             pfout = function() {
24973                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24974
24975                 if (this.label) {
24976                     this.label[0].animate({ r: 5 }, 500, "bounce");
24977                     this.label[1].attr({ "font-weight": 400 });
24978                 }
24979             };
24980
24981         switch(graphtype){
24982             case 'bar':
24983                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24984                 break;
24985             case 'hbar':
24986                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24987                 break;
24988             case 'pie':
24989 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24990 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24991 //            
24992                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24993                 
24994                 break;
24995
24996         }
24997         
24998         if(this.title){
24999             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25000         }
25001         
25002     },
25003     
25004     setTitle: function(o)
25005     {
25006         this.title = o;
25007     },
25008     
25009     initEvents: function() {
25010         
25011         if(!this.href){
25012             this.el.on('click', this.onClick, this);
25013         }
25014     },
25015     
25016     onClick : function(e)
25017     {
25018         Roo.log('img onclick');
25019         this.fireEvent('click', this, e);
25020     }
25021    
25022 });
25023
25024  
25025 /*
25026  * - LGPL
25027  *
25028  * numberBox
25029  * 
25030  */
25031 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25032
25033 /**
25034  * @class Roo.bootstrap.dash.NumberBox
25035  * @extends Roo.bootstrap.Component
25036  * Bootstrap NumberBox class
25037  * @cfg {String} headline Box headline
25038  * @cfg {String} content Box content
25039  * @cfg {String} icon Box icon
25040  * @cfg {String} footer Footer text
25041  * @cfg {String} fhref Footer href
25042  * 
25043  * @constructor
25044  * Create a new NumberBox
25045  * @param {Object} config The config object
25046  */
25047
25048
25049 Roo.bootstrap.dash.NumberBox = function(config){
25050     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25051     
25052 };
25053
25054 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25055     
25056     headline : '',
25057     content : '',
25058     icon : '',
25059     footer : '',
25060     fhref : '',
25061     ficon : '',
25062     
25063     getAutoCreate : function(){
25064         
25065         var cfg = {
25066             tag : 'div',
25067             cls : 'small-box ',
25068             cn : [
25069                 {
25070                     tag : 'div',
25071                     cls : 'inner',
25072                     cn :[
25073                         {
25074                             tag : 'h3',
25075                             cls : 'roo-headline',
25076                             html : this.headline
25077                         },
25078                         {
25079                             tag : 'p',
25080                             cls : 'roo-content',
25081                             html : this.content
25082                         }
25083                     ]
25084                 }
25085             ]
25086         };
25087         
25088         if(this.icon){
25089             cfg.cn.push({
25090                 tag : 'div',
25091                 cls : 'icon',
25092                 cn :[
25093                     {
25094                         tag : 'i',
25095                         cls : 'ion ' + this.icon
25096                     }
25097                 ]
25098             });
25099         }
25100         
25101         if(this.footer){
25102             var footer = {
25103                 tag : 'a',
25104                 cls : 'small-box-footer',
25105                 href : this.fhref || '#',
25106                 html : this.footer
25107             };
25108             
25109             cfg.cn.push(footer);
25110             
25111         }
25112         
25113         return  cfg;
25114     },
25115
25116     onRender : function(ct,position){
25117         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25118
25119
25120        
25121                 
25122     },
25123
25124     setHeadline: function (value)
25125     {
25126         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25127     },
25128     
25129     setFooter: function (value, href)
25130     {
25131         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25132         
25133         if(href){
25134             this.el.select('a.small-box-footer',true).first().attr('href', href);
25135         }
25136         
25137     },
25138
25139     setContent: function (value)
25140     {
25141         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25142     },
25143
25144     initEvents: function() 
25145     {   
25146         
25147     }
25148     
25149 });
25150
25151  
25152 /*
25153  * - LGPL
25154  *
25155  * TabBox
25156  * 
25157  */
25158 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25159
25160 /**
25161  * @class Roo.bootstrap.dash.TabBox
25162  * @extends Roo.bootstrap.Component
25163  * Bootstrap TabBox class
25164  * @cfg {String} title Title of the TabBox
25165  * @cfg {String} icon Icon of the TabBox
25166  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25167  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25168  * 
25169  * @constructor
25170  * Create a new TabBox
25171  * @param {Object} config The config object
25172  */
25173
25174
25175 Roo.bootstrap.dash.TabBox = function(config){
25176     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25177     this.addEvents({
25178         // raw events
25179         /**
25180          * @event addpane
25181          * When a pane is added
25182          * @param {Roo.bootstrap.dash.TabPane} pane
25183          */
25184         "addpane" : true,
25185         /**
25186          * @event activatepane
25187          * When a pane is activated
25188          * @param {Roo.bootstrap.dash.TabPane} pane
25189          */
25190         "activatepane" : true
25191         
25192          
25193     });
25194     
25195     this.panes = [];
25196 };
25197
25198 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25199
25200     title : '',
25201     icon : false,
25202     showtabs : true,
25203     tabScrollable : false,
25204     
25205     getChildContainer : function()
25206     {
25207         return this.el.select('.tab-content', true).first();
25208     },
25209     
25210     getAutoCreate : function(){
25211         
25212         var header = {
25213             tag: 'li',
25214             cls: 'pull-left header',
25215             html: this.title,
25216             cn : []
25217         };
25218         
25219         if(this.icon){
25220             header.cn.push({
25221                 tag: 'i',
25222                 cls: 'fa ' + this.icon
25223             });
25224         }
25225         
25226         var h = {
25227             tag: 'ul',
25228             cls: 'nav nav-tabs pull-right',
25229             cn: [
25230                 header
25231             ]
25232         };
25233         
25234         if(this.tabScrollable){
25235             h = {
25236                 tag: 'div',
25237                 cls: 'tab-header',
25238                 cn: [
25239                     {
25240                         tag: 'ul',
25241                         cls: 'nav nav-tabs pull-right',
25242                         cn: [
25243                             header
25244                         ]
25245                     }
25246                 ]
25247             };
25248         }
25249         
25250         var cfg = {
25251             tag: 'div',
25252             cls: 'nav-tabs-custom',
25253             cn: [
25254                 h,
25255                 {
25256                     tag: 'div',
25257                     cls: 'tab-content no-padding',
25258                     cn: []
25259                 }
25260             ]
25261         };
25262
25263         return  cfg;
25264     },
25265     initEvents : function()
25266     {
25267         //Roo.log('add add pane handler');
25268         this.on('addpane', this.onAddPane, this);
25269     },
25270      /**
25271      * Updates the box title
25272      * @param {String} html to set the title to.
25273      */
25274     setTitle : function(value)
25275     {
25276         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25277     },
25278     onAddPane : function(pane)
25279     {
25280         this.panes.push(pane);
25281         //Roo.log('addpane');
25282         //Roo.log(pane);
25283         // tabs are rendere left to right..
25284         if(!this.showtabs){
25285             return;
25286         }
25287         
25288         var ctr = this.el.select('.nav-tabs', true).first();
25289          
25290          
25291         var existing = ctr.select('.nav-tab',true);
25292         var qty = existing.getCount();;
25293         
25294         
25295         var tab = ctr.createChild({
25296             tag : 'li',
25297             cls : 'nav-tab' + (qty ? '' : ' active'),
25298             cn : [
25299                 {
25300                     tag : 'a',
25301                     href:'#',
25302                     html : pane.title
25303                 }
25304             ]
25305         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25306         pane.tab = tab;
25307         
25308         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25309         if (!qty) {
25310             pane.el.addClass('active');
25311         }
25312         
25313                 
25314     },
25315     onTabClick : function(ev,un,ob,pane)
25316     {
25317         //Roo.log('tab - prev default');
25318         ev.preventDefault();
25319         
25320         
25321         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25322         pane.tab.addClass('active');
25323         //Roo.log(pane.title);
25324         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25325         // technically we should have a deactivate event.. but maybe add later.
25326         // and it should not de-activate the selected tab...
25327         this.fireEvent('activatepane', pane);
25328         pane.el.addClass('active');
25329         pane.fireEvent('activate');
25330         
25331         
25332     },
25333     
25334     getActivePane : function()
25335     {
25336         var r = false;
25337         Roo.each(this.panes, function(p) {
25338             if(p.el.hasClass('active')){
25339                 r = p;
25340                 return false;
25341             }
25342             
25343             return;
25344         });
25345         
25346         return r;
25347     }
25348     
25349     
25350 });
25351
25352  
25353 /*
25354  * - LGPL
25355  *
25356  * Tab pane
25357  * 
25358  */
25359 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25360 /**
25361  * @class Roo.bootstrap.TabPane
25362  * @extends Roo.bootstrap.Component
25363  * Bootstrap TabPane class
25364  * @cfg {Boolean} active (false | true) Default false
25365  * @cfg {String} title title of panel
25366
25367  * 
25368  * @constructor
25369  * Create a new TabPane
25370  * @param {Object} config The config object
25371  */
25372
25373 Roo.bootstrap.dash.TabPane = function(config){
25374     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25375     
25376     this.addEvents({
25377         // raw events
25378         /**
25379          * @event activate
25380          * When a pane is activated
25381          * @param {Roo.bootstrap.dash.TabPane} pane
25382          */
25383         "activate" : true
25384          
25385     });
25386 };
25387
25388 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25389     
25390     active : false,
25391     title : '',
25392     
25393     // the tabBox that this is attached to.
25394     tab : false,
25395      
25396     getAutoCreate : function() 
25397     {
25398         var cfg = {
25399             tag: 'div',
25400             cls: 'tab-pane'
25401         };
25402         
25403         if(this.active){
25404             cfg.cls += ' active';
25405         }
25406         
25407         return cfg;
25408     },
25409     initEvents  : function()
25410     {
25411         //Roo.log('trigger add pane handler');
25412         this.parent().fireEvent('addpane', this)
25413     },
25414     
25415      /**
25416      * Updates the tab title 
25417      * @param {String} html to set the title to.
25418      */
25419     setTitle: function(str)
25420     {
25421         if (!this.tab) {
25422             return;
25423         }
25424         this.title = str;
25425         this.tab.select('a', true).first().dom.innerHTML = str;
25426         
25427     }
25428     
25429     
25430     
25431 });
25432
25433  
25434
25435
25436  /*
25437  * - LGPL
25438  *
25439  * menu
25440  * 
25441  */
25442 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25443
25444 /**
25445  * @class Roo.bootstrap.menu.Menu
25446  * @extends Roo.bootstrap.Component
25447  * Bootstrap Menu class - container for Menu
25448  * @cfg {String} html Text of the menu
25449  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25450  * @cfg {String} icon Font awesome icon
25451  * @cfg {String} pos Menu align to (top | bottom) default bottom
25452  * 
25453  * 
25454  * @constructor
25455  * Create a new Menu
25456  * @param {Object} config The config object
25457  */
25458
25459
25460 Roo.bootstrap.menu.Menu = function(config){
25461     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25462     
25463     this.addEvents({
25464         /**
25465          * @event beforeshow
25466          * Fires before this menu is displayed
25467          * @param {Roo.bootstrap.menu.Menu} this
25468          */
25469         beforeshow : true,
25470         /**
25471          * @event beforehide
25472          * Fires before this menu is hidden
25473          * @param {Roo.bootstrap.menu.Menu} this
25474          */
25475         beforehide : true,
25476         /**
25477          * @event show
25478          * Fires after this menu is displayed
25479          * @param {Roo.bootstrap.menu.Menu} this
25480          */
25481         show : true,
25482         /**
25483          * @event hide
25484          * Fires after this menu is hidden
25485          * @param {Roo.bootstrap.menu.Menu} this
25486          */
25487         hide : true,
25488         /**
25489          * @event click
25490          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25491          * @param {Roo.bootstrap.menu.Menu} this
25492          * @param {Roo.EventObject} e
25493          */
25494         click : true
25495     });
25496     
25497 };
25498
25499 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25500     
25501     submenu : false,
25502     html : '',
25503     weight : 'default',
25504     icon : false,
25505     pos : 'bottom',
25506     
25507     
25508     getChildContainer : function() {
25509         if(this.isSubMenu){
25510             return this.el;
25511         }
25512         
25513         return this.el.select('ul.dropdown-menu', true).first();  
25514     },
25515     
25516     getAutoCreate : function()
25517     {
25518         var text = [
25519             {
25520                 tag : 'span',
25521                 cls : 'roo-menu-text',
25522                 html : this.html
25523             }
25524         ];
25525         
25526         if(this.icon){
25527             text.unshift({
25528                 tag : 'i',
25529                 cls : 'fa ' + this.icon
25530             })
25531         }
25532         
25533         
25534         var cfg = {
25535             tag : 'div',
25536             cls : 'btn-group',
25537             cn : [
25538                 {
25539                     tag : 'button',
25540                     cls : 'dropdown-button btn btn-' + this.weight,
25541                     cn : text
25542                 },
25543                 {
25544                     tag : 'button',
25545                     cls : 'dropdown-toggle btn btn-' + this.weight,
25546                     cn : [
25547                         {
25548                             tag : 'span',
25549                             cls : 'caret'
25550                         }
25551                     ]
25552                 },
25553                 {
25554                     tag : 'ul',
25555                     cls : 'dropdown-menu'
25556                 }
25557             ]
25558             
25559         };
25560         
25561         if(this.pos == 'top'){
25562             cfg.cls += ' dropup';
25563         }
25564         
25565         if(this.isSubMenu){
25566             cfg = {
25567                 tag : 'ul',
25568                 cls : 'dropdown-menu'
25569             }
25570         }
25571         
25572         return cfg;
25573     },
25574     
25575     onRender : function(ct, position)
25576     {
25577         this.isSubMenu = ct.hasClass('dropdown-submenu');
25578         
25579         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25580     },
25581     
25582     initEvents : function() 
25583     {
25584         if(this.isSubMenu){
25585             return;
25586         }
25587         
25588         this.hidden = true;
25589         
25590         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25591         this.triggerEl.on('click', this.onTriggerPress, this);
25592         
25593         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25594         this.buttonEl.on('click', this.onClick, this);
25595         
25596     },
25597     
25598     list : function()
25599     {
25600         if(this.isSubMenu){
25601             return this.el;
25602         }
25603         
25604         return this.el.select('ul.dropdown-menu', true).first();
25605     },
25606     
25607     onClick : function(e)
25608     {
25609         this.fireEvent("click", this, e);
25610     },
25611     
25612     onTriggerPress  : function(e)
25613     {   
25614         if (this.isVisible()) {
25615             this.hide();
25616         } else {
25617             this.show();
25618         }
25619     },
25620     
25621     isVisible : function(){
25622         return !this.hidden;
25623     },
25624     
25625     show : function()
25626     {
25627         this.fireEvent("beforeshow", this);
25628         
25629         this.hidden = false;
25630         this.el.addClass('open');
25631         
25632         Roo.get(document).on("mouseup", this.onMouseUp, this);
25633         
25634         this.fireEvent("show", this);
25635         
25636         
25637     },
25638     
25639     hide : function()
25640     {
25641         this.fireEvent("beforehide", this);
25642         
25643         this.hidden = true;
25644         this.el.removeClass('open');
25645         
25646         Roo.get(document).un("mouseup", this.onMouseUp);
25647         
25648         this.fireEvent("hide", this);
25649     },
25650     
25651     onMouseUp : function()
25652     {
25653         this.hide();
25654     }
25655     
25656 });
25657
25658  
25659  /*
25660  * - LGPL
25661  *
25662  * menu item
25663  * 
25664  */
25665 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25666
25667 /**
25668  * @class Roo.bootstrap.menu.Item
25669  * @extends Roo.bootstrap.Component
25670  * Bootstrap MenuItem class
25671  * @cfg {Boolean} submenu (true | false) default false
25672  * @cfg {String} html text of the item
25673  * @cfg {String} href the link
25674  * @cfg {Boolean} disable (true | false) default false
25675  * @cfg {Boolean} preventDefault (true | false) default true
25676  * @cfg {String} icon Font awesome icon
25677  * @cfg {String} pos Submenu align to (left | right) default right 
25678  * 
25679  * 
25680  * @constructor
25681  * Create a new Item
25682  * @param {Object} config The config object
25683  */
25684
25685
25686 Roo.bootstrap.menu.Item = function(config){
25687     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25688     this.addEvents({
25689         /**
25690          * @event mouseover
25691          * Fires when the mouse is hovering over this menu
25692          * @param {Roo.bootstrap.menu.Item} this
25693          * @param {Roo.EventObject} e
25694          */
25695         mouseover : true,
25696         /**
25697          * @event mouseout
25698          * Fires when the mouse exits this menu
25699          * @param {Roo.bootstrap.menu.Item} this
25700          * @param {Roo.EventObject} e
25701          */
25702         mouseout : true,
25703         // raw events
25704         /**
25705          * @event click
25706          * The raw click event for the entire grid.
25707          * @param {Roo.EventObject} e
25708          */
25709         click : true
25710     });
25711 };
25712
25713 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25714     
25715     submenu : false,
25716     href : '',
25717     html : '',
25718     preventDefault: true,
25719     disable : false,
25720     icon : false,
25721     pos : 'right',
25722     
25723     getAutoCreate : function()
25724     {
25725         var text = [
25726             {
25727                 tag : 'span',
25728                 cls : 'roo-menu-item-text',
25729                 html : this.html
25730             }
25731         ];
25732         
25733         if(this.icon){
25734             text.unshift({
25735                 tag : 'i',
25736                 cls : 'fa ' + this.icon
25737             })
25738         }
25739         
25740         var cfg = {
25741             tag : 'li',
25742             cn : [
25743                 {
25744                     tag : 'a',
25745                     href : this.href || '#',
25746                     cn : text
25747                 }
25748             ]
25749         };
25750         
25751         if(this.disable){
25752             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25753         }
25754         
25755         if(this.submenu){
25756             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25757             
25758             if(this.pos == 'left'){
25759                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25760             }
25761         }
25762         
25763         return cfg;
25764     },
25765     
25766     initEvents : function() 
25767     {
25768         this.el.on('mouseover', this.onMouseOver, this);
25769         this.el.on('mouseout', this.onMouseOut, this);
25770         
25771         this.el.select('a', true).first().on('click', this.onClick, this);
25772         
25773     },
25774     
25775     onClick : function(e)
25776     {
25777         if(this.preventDefault){
25778             e.preventDefault();
25779         }
25780         
25781         this.fireEvent("click", this, e);
25782     },
25783     
25784     onMouseOver : function(e)
25785     {
25786         if(this.submenu && this.pos == 'left'){
25787             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25788         }
25789         
25790         this.fireEvent("mouseover", this, e);
25791     },
25792     
25793     onMouseOut : function(e)
25794     {
25795         this.fireEvent("mouseout", this, e);
25796     }
25797 });
25798
25799  
25800
25801  /*
25802  * - LGPL
25803  *
25804  * menu separator
25805  * 
25806  */
25807 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25808
25809 /**
25810  * @class Roo.bootstrap.menu.Separator
25811  * @extends Roo.bootstrap.Component
25812  * Bootstrap Separator class
25813  * 
25814  * @constructor
25815  * Create a new Separator
25816  * @param {Object} config The config object
25817  */
25818
25819
25820 Roo.bootstrap.menu.Separator = function(config){
25821     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25822 };
25823
25824 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25825     
25826     getAutoCreate : function(){
25827         var cfg = {
25828             tag : 'li',
25829             cls: 'divider'
25830         };
25831         
25832         return cfg;
25833     }
25834    
25835 });
25836
25837  
25838
25839  /*
25840  * - LGPL
25841  *
25842  * Tooltip
25843  * 
25844  */
25845
25846 /**
25847  * @class Roo.bootstrap.Tooltip
25848  * Bootstrap Tooltip class
25849  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25850  * to determine which dom element triggers the tooltip.
25851  * 
25852  * It needs to add support for additional attributes like tooltip-position
25853  * 
25854  * @constructor
25855  * Create a new Toolti
25856  * @param {Object} config The config object
25857  */
25858
25859 Roo.bootstrap.Tooltip = function(config){
25860     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25861     
25862     this.alignment = Roo.bootstrap.Tooltip.alignment;
25863     
25864     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25865         this.alignment = config.alignment;
25866     }
25867     
25868 };
25869
25870 Roo.apply(Roo.bootstrap.Tooltip, {
25871     /**
25872      * @function init initialize tooltip monitoring.
25873      * @static
25874      */
25875     currentEl : false,
25876     currentTip : false,
25877     currentRegion : false,
25878     
25879     //  init : delay?
25880     
25881     init : function()
25882     {
25883         Roo.get(document).on('mouseover', this.enter ,this);
25884         Roo.get(document).on('mouseout', this.leave, this);
25885          
25886         
25887         this.currentTip = new Roo.bootstrap.Tooltip();
25888     },
25889     
25890     enter : function(ev)
25891     {
25892         var dom = ev.getTarget();
25893         
25894         //Roo.log(['enter',dom]);
25895         var el = Roo.fly(dom);
25896         if (this.currentEl) {
25897             //Roo.log(dom);
25898             //Roo.log(this.currentEl);
25899             //Roo.log(this.currentEl.contains(dom));
25900             if (this.currentEl == el) {
25901                 return;
25902             }
25903             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25904                 return;
25905             }
25906
25907         }
25908         
25909         if (this.currentTip.el) {
25910             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25911         }    
25912         //Roo.log(ev);
25913         
25914         if(!el || el.dom == document){
25915             return;
25916         }
25917         
25918         var bindEl = el;
25919         
25920         // you can not look for children, as if el is the body.. then everythign is the child..
25921         if (!el.attr('tooltip')) { //
25922             if (!el.select("[tooltip]").elements.length) {
25923                 return;
25924             }
25925             // is the mouse over this child...?
25926             bindEl = el.select("[tooltip]").first();
25927             var xy = ev.getXY();
25928             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25929                 //Roo.log("not in region.");
25930                 return;
25931             }
25932             //Roo.log("child element over..");
25933             
25934         }
25935         this.currentEl = bindEl;
25936         this.currentTip.bind(bindEl);
25937         this.currentRegion = Roo.lib.Region.getRegion(dom);
25938         this.currentTip.enter();
25939         
25940     },
25941     leave : function(ev)
25942     {
25943         var dom = ev.getTarget();
25944         //Roo.log(['leave',dom]);
25945         if (!this.currentEl) {
25946             return;
25947         }
25948         
25949         
25950         if (dom != this.currentEl.dom) {
25951             return;
25952         }
25953         var xy = ev.getXY();
25954         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25955             return;
25956         }
25957         // only activate leave if mouse cursor is outside... bounding box..
25958         
25959         
25960         
25961         
25962         if (this.currentTip) {
25963             this.currentTip.leave();
25964         }
25965         //Roo.log('clear currentEl');
25966         this.currentEl = false;
25967         
25968         
25969     },
25970     alignment : {
25971         'left' : ['r-l', [-2,0], 'right'],
25972         'right' : ['l-r', [2,0], 'left'],
25973         'bottom' : ['t-b', [0,2], 'top'],
25974         'top' : [ 'b-t', [0,-2], 'bottom']
25975     }
25976     
25977 });
25978
25979
25980 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25981     
25982     
25983     bindEl : false,
25984     
25985     delay : null, // can be { show : 300 , hide: 500}
25986     
25987     timeout : null,
25988     
25989     hoverState : null, //???
25990     
25991     placement : 'bottom', 
25992     
25993     alignment : false,
25994     
25995     getAutoCreate : function(){
25996     
25997         var cfg = {
25998            cls : 'tooltip',
25999            role : 'tooltip',
26000            cn : [
26001                 {
26002                     cls : 'tooltip-arrow'
26003                 },
26004                 {
26005                     cls : 'tooltip-inner'
26006                 }
26007            ]
26008         };
26009         
26010         return cfg;
26011     },
26012     bind : function(el)
26013     {
26014         this.bindEl = el;
26015     },
26016       
26017     
26018     enter : function () {
26019        
26020         if (this.timeout != null) {
26021             clearTimeout(this.timeout);
26022         }
26023         
26024         this.hoverState = 'in';
26025          //Roo.log("enter - show");
26026         if (!this.delay || !this.delay.show) {
26027             this.show();
26028             return;
26029         }
26030         var _t = this;
26031         this.timeout = setTimeout(function () {
26032             if (_t.hoverState == 'in') {
26033                 _t.show();
26034             }
26035         }, this.delay.show);
26036     },
26037     leave : function()
26038     {
26039         clearTimeout(this.timeout);
26040     
26041         this.hoverState = 'out';
26042          if (!this.delay || !this.delay.hide) {
26043             this.hide();
26044             return;
26045         }
26046        
26047         var _t = this;
26048         this.timeout = setTimeout(function () {
26049             //Roo.log("leave - timeout");
26050             
26051             if (_t.hoverState == 'out') {
26052                 _t.hide();
26053                 Roo.bootstrap.Tooltip.currentEl = false;
26054             }
26055         }, delay);
26056     },
26057     
26058     show : function (msg)
26059     {
26060         if (!this.el) {
26061             this.render(document.body);
26062         }
26063         // set content.
26064         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26065         
26066         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26067         
26068         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26069         
26070         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26071         
26072         var placement = typeof this.placement == 'function' ?
26073             this.placement.call(this, this.el, on_el) :
26074             this.placement;
26075             
26076         var autoToken = /\s?auto?\s?/i;
26077         var autoPlace = autoToken.test(placement);
26078         if (autoPlace) {
26079             placement = placement.replace(autoToken, '') || 'top';
26080         }
26081         
26082         //this.el.detach()
26083         //this.el.setXY([0,0]);
26084         this.el.show();
26085         //this.el.dom.style.display='block';
26086         
26087         //this.el.appendTo(on_el);
26088         
26089         var p = this.getPosition();
26090         var box = this.el.getBox();
26091         
26092         if (autoPlace) {
26093             // fixme..
26094         }
26095         
26096         var align = this.alignment[placement];
26097         
26098         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26099         
26100         if(placement == 'top' || placement == 'bottom'){
26101             if(xy[0] < 0){
26102                 placement = 'right';
26103             }
26104             
26105             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26106                 placement = 'left';
26107             }
26108             
26109             var scroll = Roo.select('body', true).first().getScroll();
26110             
26111             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26112                 placement = 'top';
26113             }
26114             
26115         }
26116         
26117         this.el.alignTo(this.bindEl, align[0],align[1]);
26118         //var arrow = this.el.select('.arrow',true).first();
26119         //arrow.set(align[2], 
26120         
26121         this.el.addClass(placement);
26122         
26123         this.el.addClass('in fade');
26124         
26125         this.hoverState = null;
26126         
26127         if (this.el.hasClass('fade')) {
26128             // fade it?
26129         }
26130         
26131     },
26132     hide : function()
26133     {
26134          
26135         if (!this.el) {
26136             return;
26137         }
26138         //this.el.setXY([0,0]);
26139         this.el.removeClass('in');
26140         //this.el.hide();
26141         
26142     }
26143     
26144 });
26145  
26146
26147  /*
26148  * - LGPL
26149  *
26150  * Location Picker
26151  * 
26152  */
26153
26154 /**
26155  * @class Roo.bootstrap.LocationPicker
26156  * @extends Roo.bootstrap.Component
26157  * Bootstrap LocationPicker class
26158  * @cfg {Number} latitude Position when init default 0
26159  * @cfg {Number} longitude Position when init default 0
26160  * @cfg {Number} zoom default 15
26161  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26162  * @cfg {Boolean} mapTypeControl default false
26163  * @cfg {Boolean} disableDoubleClickZoom default false
26164  * @cfg {Boolean} scrollwheel default true
26165  * @cfg {Boolean} streetViewControl default false
26166  * @cfg {Number} radius default 0
26167  * @cfg {String} locationName
26168  * @cfg {Boolean} draggable default true
26169  * @cfg {Boolean} enableAutocomplete default false
26170  * @cfg {Boolean} enableReverseGeocode default true
26171  * @cfg {String} markerTitle
26172  * 
26173  * @constructor
26174  * Create a new LocationPicker
26175  * @param {Object} config The config object
26176  */
26177
26178
26179 Roo.bootstrap.LocationPicker = function(config){
26180     
26181     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26182     
26183     this.addEvents({
26184         /**
26185          * @event initial
26186          * Fires when the picker initialized.
26187          * @param {Roo.bootstrap.LocationPicker} this
26188          * @param {Google Location} location
26189          */
26190         initial : true,
26191         /**
26192          * @event positionchanged
26193          * Fires when the picker position changed.
26194          * @param {Roo.bootstrap.LocationPicker} this
26195          * @param {Google Location} location
26196          */
26197         positionchanged : true,
26198         /**
26199          * @event resize
26200          * Fires when the map resize.
26201          * @param {Roo.bootstrap.LocationPicker} this
26202          */
26203         resize : true,
26204         /**
26205          * @event show
26206          * Fires when the map show.
26207          * @param {Roo.bootstrap.LocationPicker} this
26208          */
26209         show : true,
26210         /**
26211          * @event hide
26212          * Fires when the map hide.
26213          * @param {Roo.bootstrap.LocationPicker} this
26214          */
26215         hide : true,
26216         /**
26217          * @event mapClick
26218          * Fires when click the map.
26219          * @param {Roo.bootstrap.LocationPicker} this
26220          * @param {Map event} e
26221          */
26222         mapClick : true,
26223         /**
26224          * @event mapRightClick
26225          * Fires when right click the map.
26226          * @param {Roo.bootstrap.LocationPicker} this
26227          * @param {Map event} e
26228          */
26229         mapRightClick : true,
26230         /**
26231          * @event markerClick
26232          * Fires when click the marker.
26233          * @param {Roo.bootstrap.LocationPicker} this
26234          * @param {Map event} e
26235          */
26236         markerClick : true,
26237         /**
26238          * @event markerRightClick
26239          * Fires when right click the marker.
26240          * @param {Roo.bootstrap.LocationPicker} this
26241          * @param {Map event} e
26242          */
26243         markerRightClick : true,
26244         /**
26245          * @event OverlayViewDraw
26246          * Fires when OverlayView Draw
26247          * @param {Roo.bootstrap.LocationPicker} this
26248          */
26249         OverlayViewDraw : true,
26250         /**
26251          * @event OverlayViewOnAdd
26252          * Fires when OverlayView Draw
26253          * @param {Roo.bootstrap.LocationPicker} this
26254          */
26255         OverlayViewOnAdd : true,
26256         /**
26257          * @event OverlayViewOnRemove
26258          * Fires when OverlayView Draw
26259          * @param {Roo.bootstrap.LocationPicker} this
26260          */
26261         OverlayViewOnRemove : true,
26262         /**
26263          * @event OverlayViewShow
26264          * Fires when OverlayView Draw
26265          * @param {Roo.bootstrap.LocationPicker} this
26266          * @param {Pixel} cpx
26267          */
26268         OverlayViewShow : true,
26269         /**
26270          * @event OverlayViewHide
26271          * Fires when OverlayView Draw
26272          * @param {Roo.bootstrap.LocationPicker} this
26273          */
26274         OverlayViewHide : true,
26275         /**
26276          * @event loadexception
26277          * Fires when load google lib failed.
26278          * @param {Roo.bootstrap.LocationPicker} this
26279          */
26280         loadexception : true
26281     });
26282         
26283 };
26284
26285 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26286     
26287     gMapContext: false,
26288     
26289     latitude: 0,
26290     longitude: 0,
26291     zoom: 15,
26292     mapTypeId: false,
26293     mapTypeControl: false,
26294     disableDoubleClickZoom: false,
26295     scrollwheel: true,
26296     streetViewControl: false,
26297     radius: 0,
26298     locationName: '',
26299     draggable: true,
26300     enableAutocomplete: false,
26301     enableReverseGeocode: true,
26302     markerTitle: '',
26303     
26304     getAutoCreate: function()
26305     {
26306
26307         var cfg = {
26308             tag: 'div',
26309             cls: 'roo-location-picker'
26310         };
26311         
26312         return cfg
26313     },
26314     
26315     initEvents: function(ct, position)
26316     {       
26317         if(!this.el.getWidth() || this.isApplied()){
26318             return;
26319         }
26320         
26321         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26322         
26323         this.initial();
26324     },
26325     
26326     initial: function()
26327     {
26328         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26329             this.fireEvent('loadexception', this);
26330             return;
26331         }
26332         
26333         if(!this.mapTypeId){
26334             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26335         }
26336         
26337         this.gMapContext = this.GMapContext();
26338         
26339         this.initOverlayView();
26340         
26341         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26342         
26343         var _this = this;
26344                 
26345         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26346             _this.setPosition(_this.gMapContext.marker.position);
26347         });
26348         
26349         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26350             _this.fireEvent('mapClick', this, event);
26351             
26352         });
26353
26354         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26355             _this.fireEvent('mapRightClick', this, event);
26356             
26357         });
26358         
26359         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26360             _this.fireEvent('markerClick', this, event);
26361             
26362         });
26363
26364         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26365             _this.fireEvent('markerRightClick', this, event);
26366             
26367         });
26368         
26369         this.setPosition(this.gMapContext.location);
26370         
26371         this.fireEvent('initial', this, this.gMapContext.location);
26372     },
26373     
26374     initOverlayView: function()
26375     {
26376         var _this = this;
26377         
26378         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26379             
26380             draw: function()
26381             {
26382                 _this.fireEvent('OverlayViewDraw', _this);
26383             },
26384             
26385             onAdd: function()
26386             {
26387                 _this.fireEvent('OverlayViewOnAdd', _this);
26388             },
26389             
26390             onRemove: function()
26391             {
26392                 _this.fireEvent('OverlayViewOnRemove', _this);
26393             },
26394             
26395             show: function(cpx)
26396             {
26397                 _this.fireEvent('OverlayViewShow', _this, cpx);
26398             },
26399             
26400             hide: function()
26401             {
26402                 _this.fireEvent('OverlayViewHide', _this);
26403             }
26404             
26405         });
26406     },
26407     
26408     fromLatLngToContainerPixel: function(event)
26409     {
26410         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26411     },
26412     
26413     isApplied: function() 
26414     {
26415         return this.getGmapContext() == false ? false : true;
26416     },
26417     
26418     getGmapContext: function() 
26419     {
26420         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26421     },
26422     
26423     GMapContext: function() 
26424     {
26425         var position = new google.maps.LatLng(this.latitude, this.longitude);
26426         
26427         var _map = new google.maps.Map(this.el.dom, {
26428             center: position,
26429             zoom: this.zoom,
26430             mapTypeId: this.mapTypeId,
26431             mapTypeControl: this.mapTypeControl,
26432             disableDoubleClickZoom: this.disableDoubleClickZoom,
26433             scrollwheel: this.scrollwheel,
26434             streetViewControl: this.streetViewControl,
26435             locationName: this.locationName,
26436             draggable: this.draggable,
26437             enableAutocomplete: this.enableAutocomplete,
26438             enableReverseGeocode: this.enableReverseGeocode
26439         });
26440         
26441         var _marker = new google.maps.Marker({
26442             position: position,
26443             map: _map,
26444             title: this.markerTitle,
26445             draggable: this.draggable
26446         });
26447         
26448         return {
26449             map: _map,
26450             marker: _marker,
26451             circle: null,
26452             location: position,
26453             radius: this.radius,
26454             locationName: this.locationName,
26455             addressComponents: {
26456                 formatted_address: null,
26457                 addressLine1: null,
26458                 addressLine2: null,
26459                 streetName: null,
26460                 streetNumber: null,
26461                 city: null,
26462                 district: null,
26463                 state: null,
26464                 stateOrProvince: null
26465             },
26466             settings: this,
26467             domContainer: this.el.dom,
26468             geodecoder: new google.maps.Geocoder()
26469         };
26470     },
26471     
26472     drawCircle: function(center, radius, options) 
26473     {
26474         if (this.gMapContext.circle != null) {
26475             this.gMapContext.circle.setMap(null);
26476         }
26477         if (radius > 0) {
26478             radius *= 1;
26479             options = Roo.apply({}, options, {
26480                 strokeColor: "#0000FF",
26481                 strokeOpacity: .35,
26482                 strokeWeight: 2,
26483                 fillColor: "#0000FF",
26484                 fillOpacity: .2
26485             });
26486             
26487             options.map = this.gMapContext.map;
26488             options.radius = radius;
26489             options.center = center;
26490             this.gMapContext.circle = new google.maps.Circle(options);
26491             return this.gMapContext.circle;
26492         }
26493         
26494         return null;
26495     },
26496     
26497     setPosition: function(location) 
26498     {
26499         this.gMapContext.location = location;
26500         this.gMapContext.marker.setPosition(location);
26501         this.gMapContext.map.panTo(location);
26502         this.drawCircle(location, this.gMapContext.radius, {});
26503         
26504         var _this = this;
26505         
26506         if (this.gMapContext.settings.enableReverseGeocode) {
26507             this.gMapContext.geodecoder.geocode({
26508                 latLng: this.gMapContext.location
26509             }, function(results, status) {
26510                 
26511                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26512                     _this.gMapContext.locationName = results[0].formatted_address;
26513                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26514                     
26515                     _this.fireEvent('positionchanged', this, location);
26516                 }
26517             });
26518             
26519             return;
26520         }
26521         
26522         this.fireEvent('positionchanged', this, location);
26523     },
26524     
26525     resize: function()
26526     {
26527         google.maps.event.trigger(this.gMapContext.map, "resize");
26528         
26529         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26530         
26531         this.fireEvent('resize', this);
26532     },
26533     
26534     setPositionByLatLng: function(latitude, longitude)
26535     {
26536         this.setPosition(new google.maps.LatLng(latitude, longitude));
26537     },
26538     
26539     getCurrentPosition: function() 
26540     {
26541         return {
26542             latitude: this.gMapContext.location.lat(),
26543             longitude: this.gMapContext.location.lng()
26544         };
26545     },
26546     
26547     getAddressName: function() 
26548     {
26549         return this.gMapContext.locationName;
26550     },
26551     
26552     getAddressComponents: function() 
26553     {
26554         return this.gMapContext.addressComponents;
26555     },
26556     
26557     address_component_from_google_geocode: function(address_components) 
26558     {
26559         var result = {};
26560         
26561         for (var i = 0; i < address_components.length; i++) {
26562             var component = address_components[i];
26563             if (component.types.indexOf("postal_code") >= 0) {
26564                 result.postalCode = component.short_name;
26565             } else if (component.types.indexOf("street_number") >= 0) {
26566                 result.streetNumber = component.short_name;
26567             } else if (component.types.indexOf("route") >= 0) {
26568                 result.streetName = component.short_name;
26569             } else if (component.types.indexOf("neighborhood") >= 0) {
26570                 result.city = component.short_name;
26571             } else if (component.types.indexOf("locality") >= 0) {
26572                 result.city = component.short_name;
26573             } else if (component.types.indexOf("sublocality") >= 0) {
26574                 result.district = component.short_name;
26575             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26576                 result.stateOrProvince = component.short_name;
26577             } else if (component.types.indexOf("country") >= 0) {
26578                 result.country = component.short_name;
26579             }
26580         }
26581         
26582         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26583         result.addressLine2 = "";
26584         return result;
26585     },
26586     
26587     setZoomLevel: function(zoom)
26588     {
26589         this.gMapContext.map.setZoom(zoom);
26590     },
26591     
26592     show: function()
26593     {
26594         if(!this.el){
26595             return;
26596         }
26597         
26598         this.el.show();
26599         
26600         this.resize();
26601         
26602         this.fireEvent('show', this);
26603     },
26604     
26605     hide: function()
26606     {
26607         if(!this.el){
26608             return;
26609         }
26610         
26611         this.el.hide();
26612         
26613         this.fireEvent('hide', this);
26614     }
26615     
26616 });
26617
26618 Roo.apply(Roo.bootstrap.LocationPicker, {
26619     
26620     OverlayView : function(map, options)
26621     {
26622         options = options || {};
26623         
26624         this.setMap(map);
26625     }
26626     
26627     
26628 });/*
26629  * - LGPL
26630  *
26631  * Alert
26632  * 
26633  */
26634
26635 /**
26636  * @class Roo.bootstrap.Alert
26637  * @extends Roo.bootstrap.Component
26638  * Bootstrap Alert class
26639  * @cfg {String} title The title of alert
26640  * @cfg {String} html The content of alert
26641  * @cfg {String} weight (  success | info | warning | danger )
26642  * @cfg {String} faicon font-awesomeicon
26643  * 
26644  * @constructor
26645  * Create a new alert
26646  * @param {Object} config The config object
26647  */
26648
26649
26650 Roo.bootstrap.Alert = function(config){
26651     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26652     
26653 };
26654
26655 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26656     
26657     title: '',
26658     html: '',
26659     weight: false,
26660     faicon: false,
26661     
26662     getAutoCreate : function()
26663     {
26664         
26665         var cfg = {
26666             tag : 'div',
26667             cls : 'alert',
26668             cn : [
26669                 {
26670                     tag : 'i',
26671                     cls : 'roo-alert-icon'
26672                     
26673                 },
26674                 {
26675                     tag : 'b',
26676                     cls : 'roo-alert-title',
26677                     html : this.title
26678                 },
26679                 {
26680                     tag : 'span',
26681                     cls : 'roo-alert-text',
26682                     html : this.html
26683                 }
26684             ]
26685         };
26686         
26687         if(this.faicon){
26688             cfg.cn[0].cls += ' fa ' + this.faicon;
26689         }
26690         
26691         if(this.weight){
26692             cfg.cls += ' alert-' + this.weight;
26693         }
26694         
26695         return cfg;
26696     },
26697     
26698     initEvents: function() 
26699     {
26700         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26701     },
26702     
26703     setTitle : function(str)
26704     {
26705         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26706     },
26707     
26708     setText : function(str)
26709     {
26710         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26711     },
26712     
26713     setWeight : function(weight)
26714     {
26715         if(this.weight){
26716             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26717         }
26718         
26719         this.weight = weight;
26720         
26721         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26722     },
26723     
26724     setIcon : function(icon)
26725     {
26726         if(this.faicon){
26727             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26728         }
26729         
26730         this.faicon = icon;
26731         
26732         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26733     },
26734     
26735     hide: function() 
26736     {
26737         this.el.hide();   
26738     },
26739     
26740     show: function() 
26741     {  
26742         this.el.show();   
26743     }
26744     
26745 });
26746
26747  
26748 /*
26749 * Licence: LGPL
26750 */
26751
26752 /**
26753  * @class Roo.bootstrap.UploadCropbox
26754  * @extends Roo.bootstrap.Component
26755  * Bootstrap UploadCropbox class
26756  * @cfg {String} emptyText show when image has been loaded
26757  * @cfg {String} rotateNotify show when image too small to rotate
26758  * @cfg {Number} errorTimeout default 3000
26759  * @cfg {Number} minWidth default 300
26760  * @cfg {Number} minHeight default 300
26761  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26762  * @cfg {Boolean} isDocument (true|false) default false
26763  * @cfg {String} url action url
26764  * @cfg {String} paramName default 'imageUpload'
26765  * @cfg {String} method default POST
26766  * @cfg {Boolean} loadMask (true|false) default true
26767  * @cfg {Boolean} loadingText default 'Loading...'
26768  * 
26769  * @constructor
26770  * Create a new UploadCropbox
26771  * @param {Object} config The config object
26772  */
26773
26774 Roo.bootstrap.UploadCropbox = function(config){
26775     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26776     
26777     this.addEvents({
26778         /**
26779          * @event beforeselectfile
26780          * Fire before select file
26781          * @param {Roo.bootstrap.UploadCropbox} this
26782          */
26783         "beforeselectfile" : true,
26784         /**
26785          * @event initial
26786          * Fire after initEvent
26787          * @param {Roo.bootstrap.UploadCropbox} this
26788          */
26789         "initial" : true,
26790         /**
26791          * @event crop
26792          * Fire after initEvent
26793          * @param {Roo.bootstrap.UploadCropbox} this
26794          * @param {String} data
26795          */
26796         "crop" : true,
26797         /**
26798          * @event prepare
26799          * Fire when preparing the file data
26800          * @param {Roo.bootstrap.UploadCropbox} this
26801          * @param {Object} file
26802          */
26803         "prepare" : true,
26804         /**
26805          * @event exception
26806          * Fire when get exception
26807          * @param {Roo.bootstrap.UploadCropbox} this
26808          * @param {XMLHttpRequest} xhr
26809          */
26810         "exception" : true,
26811         /**
26812          * @event beforeloadcanvas
26813          * Fire before load the canvas
26814          * @param {Roo.bootstrap.UploadCropbox} this
26815          * @param {String} src
26816          */
26817         "beforeloadcanvas" : true,
26818         /**
26819          * @event trash
26820          * Fire when trash image
26821          * @param {Roo.bootstrap.UploadCropbox} this
26822          */
26823         "trash" : true,
26824         /**
26825          * @event download
26826          * Fire when download the image
26827          * @param {Roo.bootstrap.UploadCropbox} this
26828          */
26829         "download" : true,
26830         /**
26831          * @event footerbuttonclick
26832          * Fire when footerbuttonclick
26833          * @param {Roo.bootstrap.UploadCropbox} this
26834          * @param {String} type
26835          */
26836         "footerbuttonclick" : true,
26837         /**
26838          * @event resize
26839          * Fire when resize
26840          * @param {Roo.bootstrap.UploadCropbox} this
26841          */
26842         "resize" : true,
26843         /**
26844          * @event rotate
26845          * Fire when rotate the image
26846          * @param {Roo.bootstrap.UploadCropbox} this
26847          * @param {String} pos
26848          */
26849         "rotate" : true,
26850         /**
26851          * @event inspect
26852          * Fire when inspect the file
26853          * @param {Roo.bootstrap.UploadCropbox} this
26854          * @param {Object} file
26855          */
26856         "inspect" : true,
26857         /**
26858          * @event upload
26859          * Fire when xhr upload the file
26860          * @param {Roo.bootstrap.UploadCropbox} this
26861          * @param {Object} data
26862          */
26863         "upload" : true,
26864         /**
26865          * @event arrange
26866          * Fire when arrange the file data
26867          * @param {Roo.bootstrap.UploadCropbox} this
26868          * @param {Object} formData
26869          */
26870         "arrange" : true
26871     });
26872     
26873     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26874 };
26875
26876 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26877     
26878     emptyText : 'Click to upload image',
26879     rotateNotify : 'Image is too small to rotate',
26880     errorTimeout : 3000,
26881     scale : 0,
26882     baseScale : 1,
26883     rotate : 0,
26884     dragable : false,
26885     pinching : false,
26886     mouseX : 0,
26887     mouseY : 0,
26888     cropData : false,
26889     minWidth : 300,
26890     minHeight : 300,
26891     file : false,
26892     exif : {},
26893     baseRotate : 1,
26894     cropType : 'image/jpeg',
26895     buttons : false,
26896     canvasLoaded : false,
26897     isDocument : false,
26898     method : 'POST',
26899     paramName : 'imageUpload',
26900     loadMask : true,
26901     loadingText : 'Loading...',
26902     maskEl : false,
26903     
26904     getAutoCreate : function()
26905     {
26906         var cfg = {
26907             tag : 'div',
26908             cls : 'roo-upload-cropbox',
26909             cn : [
26910                 {
26911                     tag : 'input',
26912                     cls : 'roo-upload-cropbox-selector',
26913                     type : 'file'
26914                 },
26915                 {
26916                     tag : 'div',
26917                     cls : 'roo-upload-cropbox-body',
26918                     style : 'cursor:pointer',
26919                     cn : [
26920                         {
26921                             tag : 'div',
26922                             cls : 'roo-upload-cropbox-preview'
26923                         },
26924                         {
26925                             tag : 'div',
26926                             cls : 'roo-upload-cropbox-thumb'
26927                         },
26928                         {
26929                             tag : 'div',
26930                             cls : 'roo-upload-cropbox-empty-notify',
26931                             html : this.emptyText
26932                         },
26933                         {
26934                             tag : 'div',
26935                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26936                             html : this.rotateNotify
26937                         }
26938                     ]
26939                 },
26940                 {
26941                     tag : 'div',
26942                     cls : 'roo-upload-cropbox-footer',
26943                     cn : {
26944                         tag : 'div',
26945                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26946                         cn : []
26947                     }
26948                 }
26949             ]
26950         };
26951         
26952         return cfg;
26953     },
26954     
26955     onRender : function(ct, position)
26956     {
26957         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26958         
26959         if (this.buttons.length) {
26960             
26961             Roo.each(this.buttons, function(bb) {
26962                 
26963                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26964                 
26965                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26966                 
26967             }, this);
26968         }
26969         
26970         if(this.loadMask){
26971             this.maskEl = this.el;
26972         }
26973     },
26974     
26975     initEvents : function()
26976     {
26977         this.urlAPI = (window.createObjectURL && window) || 
26978                                 (window.URL && URL.revokeObjectURL && URL) || 
26979                                 (window.webkitURL && webkitURL);
26980                         
26981         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26982         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26983         
26984         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26985         this.selectorEl.hide();
26986         
26987         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26988         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26989         
26990         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26991         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26992         this.thumbEl.hide();
26993         
26994         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26995         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26996         
26997         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26998         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26999         this.errorEl.hide();
27000         
27001         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27002         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27003         this.footerEl.hide();
27004         
27005         this.setThumbBoxSize();
27006         
27007         this.bind();
27008         
27009         this.resize();
27010         
27011         this.fireEvent('initial', this);
27012     },
27013
27014     bind : function()
27015     {
27016         var _this = this;
27017         
27018         window.addEventListener("resize", function() { _this.resize(); } );
27019         
27020         this.bodyEl.on('click', this.beforeSelectFile, this);
27021         
27022         if(Roo.isTouch){
27023             this.bodyEl.on('touchstart', this.onTouchStart, this);
27024             this.bodyEl.on('touchmove', this.onTouchMove, this);
27025             this.bodyEl.on('touchend', this.onTouchEnd, this);
27026         }
27027         
27028         if(!Roo.isTouch){
27029             this.bodyEl.on('mousedown', this.onMouseDown, this);
27030             this.bodyEl.on('mousemove', this.onMouseMove, this);
27031             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27032             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27033             Roo.get(document).on('mouseup', this.onMouseUp, this);
27034         }
27035         
27036         this.selectorEl.on('change', this.onFileSelected, this);
27037     },
27038     
27039     reset : function()
27040     {    
27041         this.scale = 0;
27042         this.baseScale = 1;
27043         this.rotate = 0;
27044         this.baseRotate = 1;
27045         this.dragable = false;
27046         this.pinching = false;
27047         this.mouseX = 0;
27048         this.mouseY = 0;
27049         this.cropData = false;
27050         this.notifyEl.dom.innerHTML = this.emptyText;
27051         
27052         this.selectorEl.dom.value = '';
27053         
27054     },
27055     
27056     resize : function()
27057     {
27058         if(this.fireEvent('resize', this) != false){
27059             this.setThumbBoxPosition();
27060             this.setCanvasPosition();
27061         }
27062     },
27063     
27064     onFooterButtonClick : function(e, el, o, type)
27065     {
27066         switch (type) {
27067             case 'rotate-left' :
27068                 this.onRotateLeft(e);
27069                 break;
27070             case 'rotate-right' :
27071                 this.onRotateRight(e);
27072                 break;
27073             case 'picture' :
27074                 this.beforeSelectFile(e);
27075                 break;
27076             case 'trash' :
27077                 this.trash(e);
27078                 break;
27079             case 'crop' :
27080                 this.crop(e);
27081                 break;
27082             case 'download' :
27083                 this.download(e);
27084                 break;
27085             default :
27086                 break;
27087         }
27088         
27089         this.fireEvent('footerbuttonclick', this, type);
27090     },
27091     
27092     beforeSelectFile : function(e)
27093     {
27094         e.preventDefault();
27095         
27096         if(this.fireEvent('beforeselectfile', this) != false){
27097             this.selectorEl.dom.click();
27098         }
27099     },
27100     
27101     onFileSelected : function(e)
27102     {
27103         e.preventDefault();
27104         
27105         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27106             return;
27107         }
27108         
27109         var file = this.selectorEl.dom.files[0];
27110         
27111         if(this.fireEvent('inspect', this, file) != false){
27112             this.prepare(file);
27113         }
27114         
27115     },
27116     
27117     trash : function(e)
27118     {
27119         this.fireEvent('trash', this);
27120     },
27121     
27122     download : function(e)
27123     {
27124         this.fireEvent('download', this);
27125     },
27126     
27127     loadCanvas : function(src)
27128     {   
27129         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27130             
27131             this.reset();
27132             
27133             this.imageEl = document.createElement('img');
27134             
27135             var _this = this;
27136             
27137             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27138             
27139             this.imageEl.src = src;
27140         }
27141     },
27142     
27143     onLoadCanvas : function()
27144     {   
27145         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27146         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27147         
27148         this.bodyEl.un('click', this.beforeSelectFile, this);
27149         
27150         this.notifyEl.hide();
27151         this.thumbEl.show();
27152         this.footerEl.show();
27153         
27154         this.baseRotateLevel();
27155         
27156         if(this.isDocument){
27157             this.setThumbBoxSize();
27158         }
27159         
27160         this.setThumbBoxPosition();
27161         
27162         this.baseScaleLevel();
27163         
27164         this.draw();
27165         
27166         this.resize();
27167         
27168         this.canvasLoaded = true;
27169         
27170         if(this.loadMask){
27171             this.maskEl.unmask();
27172         }
27173         
27174     },
27175     
27176     setCanvasPosition : function()
27177     {   
27178         if(!this.canvasEl){
27179             return;
27180         }
27181         
27182         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27183         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27184         
27185         this.previewEl.setLeft(pw);
27186         this.previewEl.setTop(ph);
27187         
27188     },
27189     
27190     onMouseDown : function(e)
27191     {   
27192         e.stopEvent();
27193         
27194         this.dragable = true;
27195         this.pinching = false;
27196         
27197         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27198             this.dragable = false;
27199             return;
27200         }
27201         
27202         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27203         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27204         
27205     },
27206     
27207     onMouseMove : function(e)
27208     {   
27209         e.stopEvent();
27210         
27211         if(!this.canvasLoaded){
27212             return;
27213         }
27214         
27215         if (!this.dragable){
27216             return;
27217         }
27218         
27219         var minX = Math.ceil(this.thumbEl.getLeft(true));
27220         var minY = Math.ceil(this.thumbEl.getTop(true));
27221         
27222         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27223         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27224         
27225         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27226         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27227         
27228         x = x - this.mouseX;
27229         y = y - this.mouseY;
27230         
27231         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27232         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27233         
27234         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27235         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27236         
27237         this.previewEl.setLeft(bgX);
27238         this.previewEl.setTop(bgY);
27239         
27240         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27241         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27242     },
27243     
27244     onMouseUp : function(e)
27245     {   
27246         e.stopEvent();
27247         
27248         this.dragable = false;
27249     },
27250     
27251     onMouseWheel : function(e)
27252     {   
27253         e.stopEvent();
27254         
27255         this.startScale = this.scale;
27256         
27257         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27258         
27259         if(!this.zoomable()){
27260             this.scale = this.startScale;
27261             return;
27262         }
27263         
27264         this.draw();
27265         
27266         return;
27267     },
27268     
27269     zoomable : function()
27270     {
27271         var minScale = this.thumbEl.getWidth() / this.minWidth;
27272         
27273         if(this.minWidth < this.minHeight){
27274             minScale = this.thumbEl.getHeight() / this.minHeight;
27275         }
27276         
27277         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27278         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27279         
27280         if(
27281                 this.isDocument &&
27282                 (this.rotate == 0 || this.rotate == 180) && 
27283                 (
27284                     width > this.imageEl.OriginWidth || 
27285                     height > this.imageEl.OriginHeight ||
27286                     (width < this.minWidth && height < this.minHeight)
27287                 )
27288         ){
27289             return false;
27290         }
27291         
27292         if(
27293                 this.isDocument &&
27294                 (this.rotate == 90 || this.rotate == 270) && 
27295                 (
27296                     width > this.imageEl.OriginWidth || 
27297                     height > this.imageEl.OriginHeight ||
27298                     (width < this.minHeight && height < this.minWidth)
27299                 )
27300         ){
27301             return false;
27302         }
27303         
27304         if(
27305                 !this.isDocument &&
27306                 (this.rotate == 0 || this.rotate == 180) && 
27307                 (
27308                     width < this.minWidth || 
27309                     width > this.imageEl.OriginWidth || 
27310                     height < this.minHeight || 
27311                     height > this.imageEl.OriginHeight
27312                 )
27313         ){
27314             return false;
27315         }
27316         
27317         if(
27318                 !this.isDocument &&
27319                 (this.rotate == 90 || this.rotate == 270) && 
27320                 (
27321                     width < this.minHeight || 
27322                     width > this.imageEl.OriginWidth || 
27323                     height < this.minWidth || 
27324                     height > this.imageEl.OriginHeight
27325                 )
27326         ){
27327             return false;
27328         }
27329         
27330         return true;
27331         
27332     },
27333     
27334     onRotateLeft : function(e)
27335     {   
27336         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27337             
27338             var minScale = this.thumbEl.getWidth() / this.minWidth;
27339             
27340             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27341             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27342             
27343             this.startScale = this.scale;
27344             
27345             while (this.getScaleLevel() < minScale){
27346             
27347                 this.scale = this.scale + 1;
27348                 
27349                 if(!this.zoomable()){
27350                     break;
27351                 }
27352                 
27353                 if(
27354                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27355                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27356                 ){
27357                     continue;
27358                 }
27359                 
27360                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27361
27362                 this.draw();
27363                 
27364                 return;
27365             }
27366             
27367             this.scale = this.startScale;
27368             
27369             this.onRotateFail();
27370             
27371             return false;
27372         }
27373         
27374         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27375
27376         if(this.isDocument){
27377             this.setThumbBoxSize();
27378             this.setThumbBoxPosition();
27379             this.setCanvasPosition();
27380         }
27381         
27382         this.draw();
27383         
27384         this.fireEvent('rotate', this, 'left');
27385         
27386     },
27387     
27388     onRotateRight : function(e)
27389     {
27390         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27391             
27392             var minScale = this.thumbEl.getWidth() / this.minWidth;
27393         
27394             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27395             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27396             
27397             this.startScale = this.scale;
27398             
27399             while (this.getScaleLevel() < minScale){
27400             
27401                 this.scale = this.scale + 1;
27402                 
27403                 if(!this.zoomable()){
27404                     break;
27405                 }
27406                 
27407                 if(
27408                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27409                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27410                 ){
27411                     continue;
27412                 }
27413                 
27414                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27415
27416                 this.draw();
27417                 
27418                 return;
27419             }
27420             
27421             this.scale = this.startScale;
27422             
27423             this.onRotateFail();
27424             
27425             return false;
27426         }
27427         
27428         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27429
27430         if(this.isDocument){
27431             this.setThumbBoxSize();
27432             this.setThumbBoxPosition();
27433             this.setCanvasPosition();
27434         }
27435         
27436         this.draw();
27437         
27438         this.fireEvent('rotate', this, 'right');
27439     },
27440     
27441     onRotateFail : function()
27442     {
27443         this.errorEl.show(true);
27444         
27445         var _this = this;
27446         
27447         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27448     },
27449     
27450     draw : function()
27451     {
27452         this.previewEl.dom.innerHTML = '';
27453         
27454         var canvasEl = document.createElement("canvas");
27455         
27456         var contextEl = canvasEl.getContext("2d");
27457         
27458         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27459         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27460         var center = this.imageEl.OriginWidth / 2;
27461         
27462         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27463             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27464             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27465             center = this.imageEl.OriginHeight / 2;
27466         }
27467         
27468         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27469         
27470         contextEl.translate(center, center);
27471         contextEl.rotate(this.rotate * Math.PI / 180);
27472
27473         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27474         
27475         this.canvasEl = document.createElement("canvas");
27476         
27477         this.contextEl = this.canvasEl.getContext("2d");
27478         
27479         switch (this.rotate) {
27480             case 0 :
27481                 
27482                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27483                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27484                 
27485                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27486                 
27487                 break;
27488             case 90 : 
27489                 
27490                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27491                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27492                 
27493                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27494                     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);
27495                     break;
27496                 }
27497                 
27498                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27499                 
27500                 break;
27501             case 180 :
27502                 
27503                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27504                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27505                 
27506                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27507                     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);
27508                     break;
27509                 }
27510                 
27511                 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);
27512                 
27513                 break;
27514             case 270 :
27515                 
27516                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27517                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27518         
27519                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27520                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27521                     break;
27522                 }
27523                 
27524                 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);
27525                 
27526                 break;
27527             default : 
27528                 break;
27529         }
27530         
27531         this.previewEl.appendChild(this.canvasEl);
27532         
27533         this.setCanvasPosition();
27534     },
27535     
27536     crop : function()
27537     {
27538         if(!this.canvasLoaded){
27539             return;
27540         }
27541         
27542         var imageCanvas = document.createElement("canvas");
27543         
27544         var imageContext = imageCanvas.getContext("2d");
27545         
27546         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27547         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27548         
27549         var center = imageCanvas.width / 2;
27550         
27551         imageContext.translate(center, center);
27552         
27553         imageContext.rotate(this.rotate * Math.PI / 180);
27554         
27555         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27556         
27557         var canvas = document.createElement("canvas");
27558         
27559         var context = canvas.getContext("2d");
27560                 
27561         canvas.width = this.minWidth;
27562         canvas.height = this.minHeight;
27563
27564         switch (this.rotate) {
27565             case 0 :
27566                 
27567                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27568                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27569                 
27570                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27571                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27572                 
27573                 var targetWidth = this.minWidth - 2 * x;
27574                 var targetHeight = this.minHeight - 2 * y;
27575                 
27576                 var scale = 1;
27577                 
27578                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27579                     scale = targetWidth / width;
27580                 }
27581                 
27582                 if(x > 0 && y == 0){
27583                     scale = targetHeight / height;
27584                 }
27585                 
27586                 if(x > 0 && y > 0){
27587                     scale = targetWidth / width;
27588                     
27589                     if(width < height){
27590                         scale = targetHeight / height;
27591                     }
27592                 }
27593                 
27594                 context.scale(scale, scale);
27595                 
27596                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27597                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27598
27599                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27600                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27601
27602                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27603                 
27604                 break;
27605             case 90 : 
27606                 
27607                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27608                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27609                 
27610                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27611                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27612                 
27613                 var targetWidth = this.minWidth - 2 * x;
27614                 var targetHeight = this.minHeight - 2 * y;
27615                 
27616                 var scale = 1;
27617                 
27618                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27619                     scale = targetWidth / width;
27620                 }
27621                 
27622                 if(x > 0 && y == 0){
27623                     scale = targetHeight / height;
27624                 }
27625                 
27626                 if(x > 0 && y > 0){
27627                     scale = targetWidth / width;
27628                     
27629                     if(width < height){
27630                         scale = targetHeight / height;
27631                     }
27632                 }
27633                 
27634                 context.scale(scale, scale);
27635                 
27636                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27637                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27638
27639                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27640                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27641                 
27642                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27643                 
27644                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27645                 
27646                 break;
27647             case 180 :
27648                 
27649                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27650                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27651                 
27652                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27653                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27654                 
27655                 var targetWidth = this.minWidth - 2 * x;
27656                 var targetHeight = this.minHeight - 2 * y;
27657                 
27658                 var scale = 1;
27659                 
27660                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27661                     scale = targetWidth / width;
27662                 }
27663                 
27664                 if(x > 0 && y == 0){
27665                     scale = targetHeight / height;
27666                 }
27667                 
27668                 if(x > 0 && y > 0){
27669                     scale = targetWidth / width;
27670                     
27671                     if(width < height){
27672                         scale = targetHeight / height;
27673                     }
27674                 }
27675                 
27676                 context.scale(scale, scale);
27677                 
27678                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27679                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27680
27681                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27682                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27683
27684                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27685                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27686                 
27687                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27688                 
27689                 break;
27690             case 270 :
27691                 
27692                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27693                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27694                 
27695                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27696                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27697                 
27698                 var targetWidth = this.minWidth - 2 * x;
27699                 var targetHeight = this.minHeight - 2 * y;
27700                 
27701                 var scale = 1;
27702                 
27703                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27704                     scale = targetWidth / width;
27705                 }
27706                 
27707                 if(x > 0 && y == 0){
27708                     scale = targetHeight / height;
27709                 }
27710                 
27711                 if(x > 0 && y > 0){
27712                     scale = targetWidth / width;
27713                     
27714                     if(width < height){
27715                         scale = targetHeight / height;
27716                     }
27717                 }
27718                 
27719                 context.scale(scale, scale);
27720                 
27721                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27722                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27723
27724                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27725                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27726                 
27727                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27728                 
27729                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27730                 
27731                 break;
27732             default : 
27733                 break;
27734         }
27735         
27736         this.cropData = canvas.toDataURL(this.cropType);
27737         
27738         if(this.fireEvent('crop', this, this.cropData) !== false){
27739             this.process(this.file, this.cropData);
27740         }
27741         
27742         return;
27743         
27744     },
27745     
27746     setThumbBoxSize : function()
27747     {
27748         var width, height;
27749         
27750         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27751             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27752             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27753             
27754             this.minWidth = width;
27755             this.minHeight = height;
27756             
27757             if(this.rotate == 90 || this.rotate == 270){
27758                 this.minWidth = height;
27759                 this.minHeight = width;
27760             }
27761         }
27762         
27763         height = 300;
27764         width = Math.ceil(this.minWidth * height / this.minHeight);
27765         
27766         if(this.minWidth > this.minHeight){
27767             width = 300;
27768             height = Math.ceil(this.minHeight * width / this.minWidth);
27769         }
27770         
27771         this.thumbEl.setStyle({
27772             width : width + 'px',
27773             height : height + 'px'
27774         });
27775
27776         return;
27777             
27778     },
27779     
27780     setThumbBoxPosition : function()
27781     {
27782         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27783         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27784         
27785         this.thumbEl.setLeft(x);
27786         this.thumbEl.setTop(y);
27787         
27788     },
27789     
27790     baseRotateLevel : function()
27791     {
27792         this.baseRotate = 1;
27793         
27794         if(
27795                 typeof(this.exif) != 'undefined' &&
27796                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27797                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27798         ){
27799             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27800         }
27801         
27802         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27803         
27804     },
27805     
27806     baseScaleLevel : function()
27807     {
27808         var width, height;
27809         
27810         if(this.isDocument){
27811             
27812             if(this.baseRotate == 6 || this.baseRotate == 8){
27813             
27814                 height = this.thumbEl.getHeight();
27815                 this.baseScale = height / this.imageEl.OriginWidth;
27816
27817                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27818                     width = this.thumbEl.getWidth();
27819                     this.baseScale = width / this.imageEl.OriginHeight;
27820                 }
27821
27822                 return;
27823             }
27824
27825             height = this.thumbEl.getHeight();
27826             this.baseScale = height / this.imageEl.OriginHeight;
27827
27828             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27829                 width = this.thumbEl.getWidth();
27830                 this.baseScale = width / this.imageEl.OriginWidth;
27831             }
27832
27833             return;
27834         }
27835         
27836         if(this.baseRotate == 6 || this.baseRotate == 8){
27837             
27838             width = this.thumbEl.getHeight();
27839             this.baseScale = width / this.imageEl.OriginHeight;
27840             
27841             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27842                 height = this.thumbEl.getWidth();
27843                 this.baseScale = height / this.imageEl.OriginHeight;
27844             }
27845             
27846             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27847                 height = this.thumbEl.getWidth();
27848                 this.baseScale = height / this.imageEl.OriginHeight;
27849                 
27850                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27851                     width = this.thumbEl.getHeight();
27852                     this.baseScale = width / this.imageEl.OriginWidth;
27853                 }
27854             }
27855             
27856             return;
27857         }
27858         
27859         width = this.thumbEl.getWidth();
27860         this.baseScale = width / this.imageEl.OriginWidth;
27861         
27862         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27863             height = this.thumbEl.getHeight();
27864             this.baseScale = height / this.imageEl.OriginHeight;
27865         }
27866         
27867         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27868             
27869             height = this.thumbEl.getHeight();
27870             this.baseScale = height / this.imageEl.OriginHeight;
27871             
27872             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27873                 width = this.thumbEl.getWidth();
27874                 this.baseScale = width / this.imageEl.OriginWidth;
27875             }
27876             
27877         }
27878         
27879         return;
27880     },
27881     
27882     getScaleLevel : function()
27883     {
27884         return this.baseScale * Math.pow(1.1, this.scale);
27885     },
27886     
27887     onTouchStart : function(e)
27888     {
27889         if(!this.canvasLoaded){
27890             this.beforeSelectFile(e);
27891             return;
27892         }
27893         
27894         var touches = e.browserEvent.touches;
27895         
27896         if(!touches){
27897             return;
27898         }
27899         
27900         if(touches.length == 1){
27901             this.onMouseDown(e);
27902             return;
27903         }
27904         
27905         if(touches.length != 2){
27906             return;
27907         }
27908         
27909         var coords = [];
27910         
27911         for(var i = 0, finger; finger = touches[i]; i++){
27912             coords.push(finger.pageX, finger.pageY);
27913         }
27914         
27915         var x = Math.pow(coords[0] - coords[2], 2);
27916         var y = Math.pow(coords[1] - coords[3], 2);
27917         
27918         this.startDistance = Math.sqrt(x + y);
27919         
27920         this.startScale = this.scale;
27921         
27922         this.pinching = true;
27923         this.dragable = false;
27924         
27925     },
27926     
27927     onTouchMove : function(e)
27928     {
27929         if(!this.pinching && !this.dragable){
27930             return;
27931         }
27932         
27933         var touches = e.browserEvent.touches;
27934         
27935         if(!touches){
27936             return;
27937         }
27938         
27939         if(this.dragable){
27940             this.onMouseMove(e);
27941             return;
27942         }
27943         
27944         var coords = [];
27945         
27946         for(var i = 0, finger; finger = touches[i]; i++){
27947             coords.push(finger.pageX, finger.pageY);
27948         }
27949         
27950         var x = Math.pow(coords[0] - coords[2], 2);
27951         var y = Math.pow(coords[1] - coords[3], 2);
27952         
27953         this.endDistance = Math.sqrt(x + y);
27954         
27955         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27956         
27957         if(!this.zoomable()){
27958             this.scale = this.startScale;
27959             return;
27960         }
27961         
27962         this.draw();
27963         
27964     },
27965     
27966     onTouchEnd : function(e)
27967     {
27968         this.pinching = false;
27969         this.dragable = false;
27970         
27971     },
27972     
27973     process : function(file, crop)
27974     {
27975         if(this.loadMask){
27976             this.maskEl.mask(this.loadingText);
27977         }
27978         
27979         this.xhr = new XMLHttpRequest();
27980         
27981         file.xhr = this.xhr;
27982
27983         this.xhr.open(this.method, this.url, true);
27984         
27985         var headers = {
27986             "Accept": "application/json",
27987             "Cache-Control": "no-cache",
27988             "X-Requested-With": "XMLHttpRequest"
27989         };
27990         
27991         for (var headerName in headers) {
27992             var headerValue = headers[headerName];
27993             if (headerValue) {
27994                 this.xhr.setRequestHeader(headerName, headerValue);
27995             }
27996         }
27997         
27998         var _this = this;
27999         
28000         this.xhr.onload = function()
28001         {
28002             _this.xhrOnLoad(_this.xhr);
28003         }
28004         
28005         this.xhr.onerror = function()
28006         {
28007             _this.xhrOnError(_this.xhr);
28008         }
28009         
28010         var formData = new FormData();
28011
28012         formData.append('returnHTML', 'NO');
28013         
28014         if(crop){
28015             formData.append('crop', crop);
28016         }
28017         
28018         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28019             formData.append(this.paramName, file, file.name);
28020         }
28021         
28022         if(typeof(file.filename) != 'undefined'){
28023             formData.append('filename', file.filename);
28024         }
28025         
28026         if(typeof(file.mimetype) != 'undefined'){
28027             formData.append('mimetype', file.mimetype);
28028         }
28029         
28030         if(this.fireEvent('arrange', this, formData) != false){
28031             this.xhr.send(formData);
28032         };
28033     },
28034     
28035     xhrOnLoad : function(xhr)
28036     {
28037         if(this.loadMask){
28038             this.maskEl.unmask();
28039         }
28040         
28041         if (xhr.readyState !== 4) {
28042             this.fireEvent('exception', this, xhr);
28043             return;
28044         }
28045
28046         var response = Roo.decode(xhr.responseText);
28047         
28048         if(!response.success){
28049             this.fireEvent('exception', this, xhr);
28050             return;
28051         }
28052         
28053         var response = Roo.decode(xhr.responseText);
28054         
28055         this.fireEvent('upload', this, response);
28056         
28057     },
28058     
28059     xhrOnError : function()
28060     {
28061         if(this.loadMask){
28062             this.maskEl.unmask();
28063         }
28064         
28065         Roo.log('xhr on error');
28066         
28067         var response = Roo.decode(xhr.responseText);
28068           
28069         Roo.log(response);
28070         
28071     },
28072     
28073     prepare : function(file)
28074     {   
28075         if(this.loadMask){
28076             this.maskEl.mask(this.loadingText);
28077         }
28078         
28079         this.file = false;
28080         this.exif = {};
28081         
28082         if(typeof(file) === 'string'){
28083             this.loadCanvas(file);
28084             return;
28085         }
28086         
28087         if(!file || !this.urlAPI){
28088             return;
28089         }
28090         
28091         this.file = file;
28092         this.cropType = file.type;
28093         
28094         var _this = this;
28095         
28096         if(this.fireEvent('prepare', this, this.file) != false){
28097             
28098             var reader = new FileReader();
28099             
28100             reader.onload = function (e) {
28101                 if (e.target.error) {
28102                     Roo.log(e.target.error);
28103                     return;
28104                 }
28105                 
28106                 var buffer = e.target.result,
28107                     dataView = new DataView(buffer),
28108                     offset = 2,
28109                     maxOffset = dataView.byteLength - 4,
28110                     markerBytes,
28111                     markerLength;
28112                 
28113                 if (dataView.getUint16(0) === 0xffd8) {
28114                     while (offset < maxOffset) {
28115                         markerBytes = dataView.getUint16(offset);
28116                         
28117                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28118                             markerLength = dataView.getUint16(offset + 2) + 2;
28119                             if (offset + markerLength > dataView.byteLength) {
28120                                 Roo.log('Invalid meta data: Invalid segment size.');
28121                                 break;
28122                             }
28123                             
28124                             if(markerBytes == 0xffe1){
28125                                 _this.parseExifData(
28126                                     dataView,
28127                                     offset,
28128                                     markerLength
28129                                 );
28130                             }
28131                             
28132                             offset += markerLength;
28133                             
28134                             continue;
28135                         }
28136                         
28137                         break;
28138                     }
28139                     
28140                 }
28141                 
28142                 var url = _this.urlAPI.createObjectURL(_this.file);
28143                 
28144                 _this.loadCanvas(url);
28145                 
28146                 return;
28147             }
28148             
28149             reader.readAsArrayBuffer(this.file);
28150             
28151         }
28152         
28153     },
28154     
28155     parseExifData : function(dataView, offset, length)
28156     {
28157         var tiffOffset = offset + 10,
28158             littleEndian,
28159             dirOffset;
28160     
28161         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28162             // No Exif data, might be XMP data instead
28163             return;
28164         }
28165         
28166         // Check for the ASCII code for "Exif" (0x45786966):
28167         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28168             // No Exif data, might be XMP data instead
28169             return;
28170         }
28171         if (tiffOffset + 8 > dataView.byteLength) {
28172             Roo.log('Invalid Exif data: Invalid segment size.');
28173             return;
28174         }
28175         // Check for the two null bytes:
28176         if (dataView.getUint16(offset + 8) !== 0x0000) {
28177             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28178             return;
28179         }
28180         // Check the byte alignment:
28181         switch (dataView.getUint16(tiffOffset)) {
28182         case 0x4949:
28183             littleEndian = true;
28184             break;
28185         case 0x4D4D:
28186             littleEndian = false;
28187             break;
28188         default:
28189             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28190             return;
28191         }
28192         // Check for the TIFF tag marker (0x002A):
28193         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28194             Roo.log('Invalid Exif data: Missing TIFF marker.');
28195             return;
28196         }
28197         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28198         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28199         
28200         this.parseExifTags(
28201             dataView,
28202             tiffOffset,
28203             tiffOffset + dirOffset,
28204             littleEndian
28205         );
28206     },
28207     
28208     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28209     {
28210         var tagsNumber,
28211             dirEndOffset,
28212             i;
28213         if (dirOffset + 6 > dataView.byteLength) {
28214             Roo.log('Invalid Exif data: Invalid directory offset.');
28215             return;
28216         }
28217         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28218         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28219         if (dirEndOffset + 4 > dataView.byteLength) {
28220             Roo.log('Invalid Exif data: Invalid directory size.');
28221             return;
28222         }
28223         for (i = 0; i < tagsNumber; i += 1) {
28224             this.parseExifTag(
28225                 dataView,
28226                 tiffOffset,
28227                 dirOffset + 2 + 12 * i, // tag offset
28228                 littleEndian
28229             );
28230         }
28231         // Return the offset to the next directory:
28232         return dataView.getUint32(dirEndOffset, littleEndian);
28233     },
28234     
28235     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28236     {
28237         var tag = dataView.getUint16(offset, littleEndian);
28238         
28239         this.exif[tag] = this.getExifValue(
28240             dataView,
28241             tiffOffset,
28242             offset,
28243             dataView.getUint16(offset + 2, littleEndian), // tag type
28244             dataView.getUint32(offset + 4, littleEndian), // tag length
28245             littleEndian
28246         );
28247     },
28248     
28249     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28250     {
28251         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28252             tagSize,
28253             dataOffset,
28254             values,
28255             i,
28256             str,
28257             c;
28258     
28259         if (!tagType) {
28260             Roo.log('Invalid Exif data: Invalid tag type.');
28261             return;
28262         }
28263         
28264         tagSize = tagType.size * length;
28265         // Determine if the value is contained in the dataOffset bytes,
28266         // or if the value at the dataOffset is a pointer to the actual data:
28267         dataOffset = tagSize > 4 ?
28268                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28269         if (dataOffset + tagSize > dataView.byteLength) {
28270             Roo.log('Invalid Exif data: Invalid data offset.');
28271             return;
28272         }
28273         if (length === 1) {
28274             return tagType.getValue(dataView, dataOffset, littleEndian);
28275         }
28276         values = [];
28277         for (i = 0; i < length; i += 1) {
28278             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28279         }
28280         
28281         if (tagType.ascii) {
28282             str = '';
28283             // Concatenate the chars:
28284             for (i = 0; i < values.length; i += 1) {
28285                 c = values[i];
28286                 // Ignore the terminating NULL byte(s):
28287                 if (c === '\u0000') {
28288                     break;
28289                 }
28290                 str += c;
28291             }
28292             return str;
28293         }
28294         return values;
28295     }
28296     
28297 });
28298
28299 Roo.apply(Roo.bootstrap.UploadCropbox, {
28300     tags : {
28301         'Orientation': 0x0112
28302     },
28303     
28304     Orientation: {
28305             1: 0, //'top-left',
28306 //            2: 'top-right',
28307             3: 180, //'bottom-right',
28308 //            4: 'bottom-left',
28309 //            5: 'left-top',
28310             6: 90, //'right-top',
28311 //            7: 'right-bottom',
28312             8: 270 //'left-bottom'
28313     },
28314     
28315     exifTagTypes : {
28316         // byte, 8-bit unsigned int:
28317         1: {
28318             getValue: function (dataView, dataOffset) {
28319                 return dataView.getUint8(dataOffset);
28320             },
28321             size: 1
28322         },
28323         // ascii, 8-bit byte:
28324         2: {
28325             getValue: function (dataView, dataOffset) {
28326                 return String.fromCharCode(dataView.getUint8(dataOffset));
28327             },
28328             size: 1,
28329             ascii: true
28330         },
28331         // short, 16 bit int:
28332         3: {
28333             getValue: function (dataView, dataOffset, littleEndian) {
28334                 return dataView.getUint16(dataOffset, littleEndian);
28335             },
28336             size: 2
28337         },
28338         // long, 32 bit int:
28339         4: {
28340             getValue: function (dataView, dataOffset, littleEndian) {
28341                 return dataView.getUint32(dataOffset, littleEndian);
28342             },
28343             size: 4
28344         },
28345         // rational = two long values, first is numerator, second is denominator:
28346         5: {
28347             getValue: function (dataView, dataOffset, littleEndian) {
28348                 return dataView.getUint32(dataOffset, littleEndian) /
28349                     dataView.getUint32(dataOffset + 4, littleEndian);
28350             },
28351             size: 8
28352         },
28353         // slong, 32 bit signed int:
28354         9: {
28355             getValue: function (dataView, dataOffset, littleEndian) {
28356                 return dataView.getInt32(dataOffset, littleEndian);
28357             },
28358             size: 4
28359         },
28360         // srational, two slongs, first is numerator, second is denominator:
28361         10: {
28362             getValue: function (dataView, dataOffset, littleEndian) {
28363                 return dataView.getInt32(dataOffset, littleEndian) /
28364                     dataView.getInt32(dataOffset + 4, littleEndian);
28365             },
28366             size: 8
28367         }
28368     },
28369     
28370     footer : {
28371         STANDARD : [
28372             {
28373                 tag : 'div',
28374                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28375                 action : 'rotate-left',
28376                 cn : [
28377                     {
28378                         tag : 'button',
28379                         cls : 'btn btn-default',
28380                         html : '<i class="fa fa-undo"></i>'
28381                     }
28382                 ]
28383             },
28384             {
28385                 tag : 'div',
28386                 cls : 'btn-group roo-upload-cropbox-picture',
28387                 action : 'picture',
28388                 cn : [
28389                     {
28390                         tag : 'button',
28391                         cls : 'btn btn-default',
28392                         html : '<i class="fa fa-picture-o"></i>'
28393                     }
28394                 ]
28395             },
28396             {
28397                 tag : 'div',
28398                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28399                 action : 'rotate-right',
28400                 cn : [
28401                     {
28402                         tag : 'button',
28403                         cls : 'btn btn-default',
28404                         html : '<i class="fa fa-repeat"></i>'
28405                     }
28406                 ]
28407             }
28408         ],
28409         DOCUMENT : [
28410             {
28411                 tag : 'div',
28412                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28413                 action : 'rotate-left',
28414                 cn : [
28415                     {
28416                         tag : 'button',
28417                         cls : 'btn btn-default',
28418                         html : '<i class="fa fa-undo"></i>'
28419                     }
28420                 ]
28421             },
28422             {
28423                 tag : 'div',
28424                 cls : 'btn-group roo-upload-cropbox-download',
28425                 action : 'download',
28426                 cn : [
28427                     {
28428                         tag : 'button',
28429                         cls : 'btn btn-default',
28430                         html : '<i class="fa fa-download"></i>'
28431                     }
28432                 ]
28433             },
28434             {
28435                 tag : 'div',
28436                 cls : 'btn-group roo-upload-cropbox-crop',
28437                 action : 'crop',
28438                 cn : [
28439                     {
28440                         tag : 'button',
28441                         cls : 'btn btn-default',
28442                         html : '<i class="fa fa-crop"></i>'
28443                     }
28444                 ]
28445             },
28446             {
28447                 tag : 'div',
28448                 cls : 'btn-group roo-upload-cropbox-trash',
28449                 action : 'trash',
28450                 cn : [
28451                     {
28452                         tag : 'button',
28453                         cls : 'btn btn-default',
28454                         html : '<i class="fa fa-trash"></i>'
28455                     }
28456                 ]
28457             },
28458             {
28459                 tag : 'div',
28460                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28461                 action : 'rotate-right',
28462                 cn : [
28463                     {
28464                         tag : 'button',
28465                         cls : 'btn btn-default',
28466                         html : '<i class="fa fa-repeat"></i>'
28467                     }
28468                 ]
28469             }
28470         ],
28471         ROTATOR : [
28472             {
28473                 tag : 'div',
28474                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28475                 action : 'rotate-left',
28476                 cn : [
28477                     {
28478                         tag : 'button',
28479                         cls : 'btn btn-default',
28480                         html : '<i class="fa fa-undo"></i>'
28481                     }
28482                 ]
28483             },
28484             {
28485                 tag : 'div',
28486                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28487                 action : 'rotate-right',
28488                 cn : [
28489                     {
28490                         tag : 'button',
28491                         cls : 'btn btn-default',
28492                         html : '<i class="fa fa-repeat"></i>'
28493                     }
28494                 ]
28495             }
28496         ]
28497     }
28498 });
28499
28500 /*
28501 * Licence: LGPL
28502 */
28503
28504 /**
28505  * @class Roo.bootstrap.DocumentManager
28506  * @extends Roo.bootstrap.Component
28507  * Bootstrap DocumentManager class
28508  * @cfg {String} paramName default 'imageUpload'
28509  * @cfg {String} toolTipName default 'filename'
28510  * @cfg {String} method default POST
28511  * @cfg {String} url action url
28512  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28513  * @cfg {Boolean} multiple multiple upload default true
28514  * @cfg {Number} thumbSize default 300
28515  * @cfg {String} fieldLabel
28516  * @cfg {Number} labelWidth default 4
28517  * @cfg {String} labelAlign (left|top) default left
28518  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28519 * @cfg {Number} labellg set the width of label (1-12)
28520  * @cfg {Number} labelmd set the width of label (1-12)
28521  * @cfg {Number} labelsm set the width of label (1-12)
28522  * @cfg {Number} labelxs set the width of label (1-12)
28523  * 
28524  * @constructor
28525  * Create a new DocumentManager
28526  * @param {Object} config The config object
28527  */
28528
28529 Roo.bootstrap.DocumentManager = function(config){
28530     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28531     
28532     this.files = [];
28533     this.delegates = [];
28534     
28535     this.addEvents({
28536         /**
28537          * @event initial
28538          * Fire when initial the DocumentManager
28539          * @param {Roo.bootstrap.DocumentManager} this
28540          */
28541         "initial" : true,
28542         /**
28543          * @event inspect
28544          * inspect selected file
28545          * @param {Roo.bootstrap.DocumentManager} this
28546          * @param {File} file
28547          */
28548         "inspect" : true,
28549         /**
28550          * @event exception
28551          * Fire when xhr load exception
28552          * @param {Roo.bootstrap.DocumentManager} this
28553          * @param {XMLHttpRequest} xhr
28554          */
28555         "exception" : true,
28556         /**
28557          * @event afterupload
28558          * Fire when xhr load exception
28559          * @param {Roo.bootstrap.DocumentManager} this
28560          * @param {XMLHttpRequest} xhr
28561          */
28562         "afterupload" : true,
28563         /**
28564          * @event prepare
28565          * prepare the form data
28566          * @param {Roo.bootstrap.DocumentManager} this
28567          * @param {Object} formData
28568          */
28569         "prepare" : true,
28570         /**
28571          * @event remove
28572          * Fire when remove the file
28573          * @param {Roo.bootstrap.DocumentManager} this
28574          * @param {Object} file
28575          */
28576         "remove" : true,
28577         /**
28578          * @event refresh
28579          * Fire after refresh the file
28580          * @param {Roo.bootstrap.DocumentManager} this
28581          */
28582         "refresh" : true,
28583         /**
28584          * @event click
28585          * Fire after click the image
28586          * @param {Roo.bootstrap.DocumentManager} this
28587          * @param {Object} file
28588          */
28589         "click" : true,
28590         /**
28591          * @event edit
28592          * Fire when upload a image and editable set to true
28593          * @param {Roo.bootstrap.DocumentManager} this
28594          * @param {Object} file
28595          */
28596         "edit" : true,
28597         /**
28598          * @event beforeselectfile
28599          * Fire before select file
28600          * @param {Roo.bootstrap.DocumentManager} this
28601          */
28602         "beforeselectfile" : true,
28603         /**
28604          * @event process
28605          * Fire before process file
28606          * @param {Roo.bootstrap.DocumentManager} this
28607          * @param {Object} file
28608          */
28609         "process" : true,
28610         /**
28611          * @event previewrendered
28612          * Fire when preview rendered
28613          * @param {Roo.bootstrap.DocumentManager} this
28614          * @param {Object} file
28615          */
28616         "previewrendered" : true
28617         
28618     });
28619 };
28620
28621 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28622     
28623     boxes : 0,
28624     inputName : '',
28625     thumbSize : 300,
28626     multiple : true,
28627     files : false,
28628     method : 'POST',
28629     url : '',
28630     paramName : 'imageUpload',
28631     toolTipName : 'filename',
28632     fieldLabel : '',
28633     labelWidth : 4,
28634     labelAlign : 'left',
28635     editable : true,
28636     delegates : false,
28637     xhr : false, 
28638     
28639     labellg : 0,
28640     labelmd : 0,
28641     labelsm : 0,
28642     labelxs : 0,
28643     
28644     getAutoCreate : function()
28645     {   
28646         var managerWidget = {
28647             tag : 'div',
28648             cls : 'roo-document-manager',
28649             cn : [
28650                 {
28651                     tag : 'input',
28652                     cls : 'roo-document-manager-selector',
28653                     type : 'file'
28654                 },
28655                 {
28656                     tag : 'div',
28657                     cls : 'roo-document-manager-uploader',
28658                     cn : [
28659                         {
28660                             tag : 'div',
28661                             cls : 'roo-document-manager-upload-btn',
28662                             html : '<i class="fa fa-plus"></i>'
28663                         }
28664                     ]
28665                     
28666                 }
28667             ]
28668         };
28669         
28670         var content = [
28671             {
28672                 tag : 'div',
28673                 cls : 'column col-md-12',
28674                 cn : managerWidget
28675             }
28676         ];
28677         
28678         if(this.fieldLabel.length){
28679             
28680             content = [
28681                 {
28682                     tag : 'div',
28683                     cls : 'column col-md-12',
28684                     html : this.fieldLabel
28685                 },
28686                 {
28687                     tag : 'div',
28688                     cls : 'column col-md-12',
28689                     cn : managerWidget
28690                 }
28691             ];
28692
28693             if(this.labelAlign == 'left'){
28694                 content = [
28695                     {
28696                         tag : 'div',
28697                         cls : 'column',
28698                         html : this.fieldLabel
28699                     },
28700                     {
28701                         tag : 'div',
28702                         cls : 'column',
28703                         cn : managerWidget
28704                     }
28705                 ];
28706                 
28707                 if(this.labelWidth > 12){
28708                     content[0].style = "width: " + this.labelWidth + 'px';
28709                 }
28710
28711                 if(this.labelWidth < 13 && this.labelmd == 0){
28712                     this.labelmd = this.labelWidth;
28713                 }
28714
28715                 if(this.labellg > 0){
28716                     content[0].cls += ' col-lg-' + this.labellg;
28717                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28718                 }
28719
28720                 if(this.labelmd > 0){
28721                     content[0].cls += ' col-md-' + this.labelmd;
28722                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28723                 }
28724
28725                 if(this.labelsm > 0){
28726                     content[0].cls += ' col-sm-' + this.labelsm;
28727                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28728                 }
28729
28730                 if(this.labelxs > 0){
28731                     content[0].cls += ' col-xs-' + this.labelxs;
28732                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28733                 }
28734                 
28735             }
28736         }
28737         
28738         var cfg = {
28739             tag : 'div',
28740             cls : 'row clearfix',
28741             cn : content
28742         };
28743         
28744         return cfg;
28745         
28746     },
28747     
28748     initEvents : function()
28749     {
28750         this.managerEl = this.el.select('.roo-document-manager', true).first();
28751         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28752         
28753         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28754         this.selectorEl.hide();
28755         
28756         if(this.multiple){
28757             this.selectorEl.attr('multiple', 'multiple');
28758         }
28759         
28760         this.selectorEl.on('change', this.onFileSelected, this);
28761         
28762         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28763         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28764         
28765         this.uploader.on('click', this.onUploaderClick, this);
28766         
28767         this.renderProgressDialog();
28768         
28769         var _this = this;
28770         
28771         window.addEventListener("resize", function() { _this.refresh(); } );
28772         
28773         this.fireEvent('initial', this);
28774     },
28775     
28776     renderProgressDialog : function()
28777     {
28778         var _this = this;
28779         
28780         this.progressDialog = new Roo.bootstrap.Modal({
28781             cls : 'roo-document-manager-progress-dialog',
28782             allow_close : false,
28783             title : '',
28784             buttons : [
28785                 {
28786                     name  :'cancel',
28787                     weight : 'danger',
28788                     html : 'Cancel'
28789                 }
28790             ], 
28791             listeners : { 
28792                 btnclick : function() {
28793                     _this.uploadCancel();
28794                     this.hide();
28795                 }
28796             }
28797         });
28798          
28799         this.progressDialog.render(Roo.get(document.body));
28800          
28801         this.progress = new Roo.bootstrap.Progress({
28802             cls : 'roo-document-manager-progress',
28803             active : true,
28804             striped : true
28805         });
28806         
28807         this.progress.render(this.progressDialog.getChildContainer());
28808         
28809         this.progressBar = new Roo.bootstrap.ProgressBar({
28810             cls : 'roo-document-manager-progress-bar',
28811             aria_valuenow : 0,
28812             aria_valuemin : 0,
28813             aria_valuemax : 12,
28814             panel : 'success'
28815         });
28816         
28817         this.progressBar.render(this.progress.getChildContainer());
28818     },
28819     
28820     onUploaderClick : function(e)
28821     {
28822         e.preventDefault();
28823      
28824         if(this.fireEvent('beforeselectfile', this) != false){
28825             this.selectorEl.dom.click();
28826         }
28827         
28828     },
28829     
28830     onFileSelected : function(e)
28831     {
28832         e.preventDefault();
28833         
28834         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28835             return;
28836         }
28837         
28838         Roo.each(this.selectorEl.dom.files, function(file){
28839             if(this.fireEvent('inspect', this, file) != false){
28840                 this.files.push(file);
28841             }
28842         }, this);
28843         
28844         this.queue();
28845         
28846     },
28847     
28848     queue : function()
28849     {
28850         this.selectorEl.dom.value = '';
28851         
28852         if(!this.files || !this.files.length){
28853             return;
28854         }
28855         
28856         if(this.boxes > 0 && this.files.length > this.boxes){
28857             this.files = this.files.slice(0, this.boxes);
28858         }
28859         
28860         this.uploader.show();
28861         
28862         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28863             this.uploader.hide();
28864         }
28865         
28866         var _this = this;
28867         
28868         var files = [];
28869         
28870         var docs = [];
28871         
28872         Roo.each(this.files, function(file){
28873             
28874             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28875                 var f = this.renderPreview(file);
28876                 files.push(f);
28877                 return;
28878             }
28879             
28880             if(file.type.indexOf('image') != -1){
28881                 this.delegates.push(
28882                     (function(){
28883                         _this.process(file);
28884                     }).createDelegate(this)
28885                 );
28886         
28887                 return;
28888             }
28889             
28890             docs.push(
28891                 (function(){
28892                     _this.process(file);
28893                 }).createDelegate(this)
28894             );
28895             
28896         }, this);
28897         
28898         this.files = files;
28899         
28900         this.delegates = this.delegates.concat(docs);
28901         
28902         if(!this.delegates.length){
28903             this.refresh();
28904             return;
28905         }
28906         
28907         this.progressBar.aria_valuemax = this.delegates.length;
28908         
28909         this.arrange();
28910         
28911         return;
28912     },
28913     
28914     arrange : function()
28915     {
28916         if(!this.delegates.length){
28917             this.progressDialog.hide();
28918             this.refresh();
28919             return;
28920         }
28921         
28922         var delegate = this.delegates.shift();
28923         
28924         this.progressDialog.show();
28925         
28926         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28927         
28928         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28929         
28930         delegate();
28931     },
28932     
28933     refresh : function()
28934     {
28935         this.uploader.show();
28936         
28937         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28938             this.uploader.hide();
28939         }
28940         
28941         Roo.isTouch ? this.closable(false) : this.closable(true);
28942         
28943         this.fireEvent('refresh', this);
28944     },
28945     
28946     onRemove : function(e, el, o)
28947     {
28948         e.preventDefault();
28949         
28950         this.fireEvent('remove', this, o);
28951         
28952     },
28953     
28954     remove : function(o)
28955     {
28956         var files = [];
28957         
28958         Roo.each(this.files, function(file){
28959             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28960                 files.push(file);
28961                 return;
28962             }
28963
28964             o.target.remove();
28965
28966         }, this);
28967         
28968         this.files = files;
28969         
28970         this.refresh();
28971     },
28972     
28973     clear : function()
28974     {
28975         Roo.each(this.files, function(file){
28976             if(!file.target){
28977                 return;
28978             }
28979             
28980             file.target.remove();
28981
28982         }, this);
28983         
28984         this.files = [];
28985         
28986         this.refresh();
28987     },
28988     
28989     onClick : function(e, el, o)
28990     {
28991         e.preventDefault();
28992         
28993         this.fireEvent('click', this, o);
28994         
28995     },
28996     
28997     closable : function(closable)
28998     {
28999         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29000             
29001             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29002             
29003             if(closable){
29004                 el.show();
29005                 return;
29006             }
29007             
29008             el.hide();
29009             
29010         }, this);
29011     },
29012     
29013     xhrOnLoad : function(xhr)
29014     {
29015         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29016             el.remove();
29017         }, this);
29018         
29019         if (xhr.readyState !== 4) {
29020             this.arrange();
29021             this.fireEvent('exception', this, xhr);
29022             return;
29023         }
29024
29025         var response = Roo.decode(xhr.responseText);
29026         
29027         if(!response.success){
29028             this.arrange();
29029             this.fireEvent('exception', this, xhr);
29030             return;
29031         }
29032         
29033         var file = this.renderPreview(response.data);
29034         
29035         this.files.push(file);
29036         
29037         this.arrange();
29038         
29039         this.fireEvent('afterupload', this, xhr);
29040         
29041     },
29042     
29043     xhrOnError : function(xhr)
29044     {
29045         Roo.log('xhr on error');
29046         
29047         var response = Roo.decode(xhr.responseText);
29048           
29049         Roo.log(response);
29050         
29051         this.arrange();
29052     },
29053     
29054     process : function(file)
29055     {
29056         if(this.fireEvent('process', this, file) !== false){
29057             if(this.editable && file.type.indexOf('image') != -1){
29058                 this.fireEvent('edit', this, file);
29059                 return;
29060             }
29061
29062             this.uploadStart(file, false);
29063
29064             return;
29065         }
29066         
29067     },
29068     
29069     uploadStart : function(file, crop)
29070     {
29071         this.xhr = new XMLHttpRequest();
29072         
29073         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29074             this.arrange();
29075             return;
29076         }
29077         
29078         file.xhr = this.xhr;
29079             
29080         this.managerEl.createChild({
29081             tag : 'div',
29082             cls : 'roo-document-manager-loading',
29083             cn : [
29084                 {
29085                     tag : 'div',
29086                     tooltip : file.name,
29087                     cls : 'roo-document-manager-thumb',
29088                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29089                 }
29090             ]
29091
29092         });
29093
29094         this.xhr.open(this.method, this.url, true);
29095         
29096         var headers = {
29097             "Accept": "application/json",
29098             "Cache-Control": "no-cache",
29099             "X-Requested-With": "XMLHttpRequest"
29100         };
29101         
29102         for (var headerName in headers) {
29103             var headerValue = headers[headerName];
29104             if (headerValue) {
29105                 this.xhr.setRequestHeader(headerName, headerValue);
29106             }
29107         }
29108         
29109         var _this = this;
29110         
29111         this.xhr.onload = function()
29112         {
29113             _this.xhrOnLoad(_this.xhr);
29114         }
29115         
29116         this.xhr.onerror = function()
29117         {
29118             _this.xhrOnError(_this.xhr);
29119         }
29120         
29121         var formData = new FormData();
29122
29123         formData.append('returnHTML', 'NO');
29124         
29125         if(crop){
29126             formData.append('crop', crop);
29127         }
29128         
29129         formData.append(this.paramName, file, file.name);
29130         
29131         var options = {
29132             file : file, 
29133             manually : false
29134         };
29135         
29136         if(this.fireEvent('prepare', this, formData, options) != false){
29137             
29138             if(options.manually){
29139                 return;
29140             }
29141             
29142             this.xhr.send(formData);
29143             return;
29144         };
29145         
29146         this.uploadCancel();
29147     },
29148     
29149     uploadCancel : function()
29150     {
29151         if (this.xhr) {
29152             this.xhr.abort();
29153         }
29154         
29155         this.delegates = [];
29156         
29157         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29158             el.remove();
29159         }, this);
29160         
29161         this.arrange();
29162     },
29163     
29164     renderPreview : function(file)
29165     {
29166         if(typeof(file.target) != 'undefined' && file.target){
29167             return file;
29168         }
29169         
29170         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29171         
29172         var previewEl = this.managerEl.createChild({
29173             tag : 'div',
29174             cls : 'roo-document-manager-preview',
29175             cn : [
29176                 {
29177                     tag : 'div',
29178                     tooltip : file[this.toolTipName],
29179                     cls : 'roo-document-manager-thumb',
29180                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29181                 },
29182                 {
29183                     tag : 'button',
29184                     cls : 'close',
29185                     html : '<i class="fa fa-times-circle"></i>'
29186                 }
29187             ]
29188         });
29189
29190         var close = previewEl.select('button.close', true).first();
29191
29192         close.on('click', this.onRemove, this, file);
29193
29194         file.target = previewEl;
29195
29196         var image = previewEl.select('img', true).first();
29197         
29198         var _this = this;
29199         
29200         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29201         
29202         image.on('click', this.onClick, this, file);
29203         
29204         this.fireEvent('previewrendered', this, file);
29205         
29206         return file;
29207         
29208     },
29209     
29210     onPreviewLoad : function(file, image)
29211     {
29212         if(typeof(file.target) == 'undefined' || !file.target){
29213             return;
29214         }
29215         
29216         var width = image.dom.naturalWidth || image.dom.width;
29217         var height = image.dom.naturalHeight || image.dom.height;
29218         
29219         if(width > height){
29220             file.target.addClass('wide');
29221             return;
29222         }
29223         
29224         file.target.addClass('tall');
29225         return;
29226         
29227     },
29228     
29229     uploadFromSource : function(file, crop)
29230     {
29231         this.xhr = new XMLHttpRequest();
29232         
29233         this.managerEl.createChild({
29234             tag : 'div',
29235             cls : 'roo-document-manager-loading',
29236             cn : [
29237                 {
29238                     tag : 'div',
29239                     tooltip : file.name,
29240                     cls : 'roo-document-manager-thumb',
29241                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29242                 }
29243             ]
29244
29245         });
29246
29247         this.xhr.open(this.method, this.url, true);
29248         
29249         var headers = {
29250             "Accept": "application/json",
29251             "Cache-Control": "no-cache",
29252             "X-Requested-With": "XMLHttpRequest"
29253         };
29254         
29255         for (var headerName in headers) {
29256             var headerValue = headers[headerName];
29257             if (headerValue) {
29258                 this.xhr.setRequestHeader(headerName, headerValue);
29259             }
29260         }
29261         
29262         var _this = this;
29263         
29264         this.xhr.onload = function()
29265         {
29266             _this.xhrOnLoad(_this.xhr);
29267         }
29268         
29269         this.xhr.onerror = function()
29270         {
29271             _this.xhrOnError(_this.xhr);
29272         }
29273         
29274         var formData = new FormData();
29275
29276         formData.append('returnHTML', 'NO');
29277         
29278         formData.append('crop', crop);
29279         
29280         if(typeof(file.filename) != 'undefined'){
29281             formData.append('filename', file.filename);
29282         }
29283         
29284         if(typeof(file.mimetype) != 'undefined'){
29285             formData.append('mimetype', file.mimetype);
29286         }
29287         
29288         Roo.log(formData);
29289         
29290         if(this.fireEvent('prepare', this, formData) != false){
29291             this.xhr.send(formData);
29292         };
29293     }
29294 });
29295
29296 /*
29297 * Licence: LGPL
29298 */
29299
29300 /**
29301  * @class Roo.bootstrap.DocumentViewer
29302  * @extends Roo.bootstrap.Component
29303  * Bootstrap DocumentViewer class
29304  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29305  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29306  * 
29307  * @constructor
29308  * Create a new DocumentViewer
29309  * @param {Object} config The config object
29310  */
29311
29312 Roo.bootstrap.DocumentViewer = function(config){
29313     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29314     
29315     this.addEvents({
29316         /**
29317          * @event initial
29318          * Fire after initEvent
29319          * @param {Roo.bootstrap.DocumentViewer} this
29320          */
29321         "initial" : true,
29322         /**
29323          * @event click
29324          * Fire after click
29325          * @param {Roo.bootstrap.DocumentViewer} this
29326          */
29327         "click" : true,
29328         /**
29329          * @event download
29330          * Fire after download button
29331          * @param {Roo.bootstrap.DocumentViewer} this
29332          */
29333         "download" : true,
29334         /**
29335          * @event trash
29336          * Fire after trash button
29337          * @param {Roo.bootstrap.DocumentViewer} this
29338          */
29339         "trash" : true
29340         
29341     });
29342 };
29343
29344 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29345     
29346     showDownload : true,
29347     
29348     showTrash : true,
29349     
29350     getAutoCreate : function()
29351     {
29352         var cfg = {
29353             tag : 'div',
29354             cls : 'roo-document-viewer',
29355             cn : [
29356                 {
29357                     tag : 'div',
29358                     cls : 'roo-document-viewer-body',
29359                     cn : [
29360                         {
29361                             tag : 'div',
29362                             cls : 'roo-document-viewer-thumb',
29363                             cn : [
29364                                 {
29365                                     tag : 'img',
29366                                     cls : 'roo-document-viewer-image'
29367                                 }
29368                             ]
29369                         }
29370                     ]
29371                 },
29372                 {
29373                     tag : 'div',
29374                     cls : 'roo-document-viewer-footer',
29375                     cn : {
29376                         tag : 'div',
29377                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29378                         cn : [
29379                             {
29380                                 tag : 'div',
29381                                 cls : 'btn-group roo-document-viewer-download',
29382                                 cn : [
29383                                     {
29384                                         tag : 'button',
29385                                         cls : 'btn btn-default',
29386                                         html : '<i class="fa fa-download"></i>'
29387                                     }
29388                                 ]
29389                             },
29390                             {
29391                                 tag : 'div',
29392                                 cls : 'btn-group roo-document-viewer-trash',
29393                                 cn : [
29394                                     {
29395                                         tag : 'button',
29396                                         cls : 'btn btn-default',
29397                                         html : '<i class="fa fa-trash"></i>'
29398                                     }
29399                                 ]
29400                             }
29401                         ]
29402                     }
29403                 }
29404             ]
29405         };
29406         
29407         return cfg;
29408     },
29409     
29410     initEvents : function()
29411     {
29412         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29413         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29414         
29415         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29416         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29417         
29418         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29419         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29420         
29421         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29422         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29423         
29424         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29425         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29426         
29427         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29428         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29429         
29430         this.bodyEl.on('click', this.onClick, this);
29431         this.downloadBtn.on('click', this.onDownload, this);
29432         this.trashBtn.on('click', this.onTrash, this);
29433         
29434         this.downloadBtn.hide();
29435         this.trashBtn.hide();
29436         
29437         if(this.showDownload){
29438             this.downloadBtn.show();
29439         }
29440         
29441         if(this.showTrash){
29442             this.trashBtn.show();
29443         }
29444         
29445         if(!this.showDownload && !this.showTrash) {
29446             this.footerEl.hide();
29447         }
29448         
29449     },
29450     
29451     initial : function()
29452     {
29453         this.fireEvent('initial', this);
29454         
29455     },
29456     
29457     onClick : function(e)
29458     {
29459         e.preventDefault();
29460         
29461         this.fireEvent('click', this);
29462     },
29463     
29464     onDownload : function(e)
29465     {
29466         e.preventDefault();
29467         
29468         this.fireEvent('download', this);
29469     },
29470     
29471     onTrash : function(e)
29472     {
29473         e.preventDefault();
29474         
29475         this.fireEvent('trash', this);
29476     }
29477     
29478 });
29479 /*
29480  * - LGPL
29481  *
29482  * nav progress bar
29483  * 
29484  */
29485
29486 /**
29487  * @class Roo.bootstrap.NavProgressBar
29488  * @extends Roo.bootstrap.Component
29489  * Bootstrap NavProgressBar class
29490  * 
29491  * @constructor
29492  * Create a new nav progress bar
29493  * @param {Object} config The config object
29494  */
29495
29496 Roo.bootstrap.NavProgressBar = function(config){
29497     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29498
29499     this.bullets = this.bullets || [];
29500    
29501 //    Roo.bootstrap.NavProgressBar.register(this);
29502      this.addEvents({
29503         /**
29504              * @event changed
29505              * Fires when the active item changes
29506              * @param {Roo.bootstrap.NavProgressBar} this
29507              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29508              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29509          */
29510         'changed': true
29511      });
29512     
29513 };
29514
29515 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29516     
29517     bullets : [],
29518     barItems : [],
29519     
29520     getAutoCreate : function()
29521     {
29522         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29523         
29524         cfg = {
29525             tag : 'div',
29526             cls : 'roo-navigation-bar-group',
29527             cn : [
29528                 {
29529                     tag : 'div',
29530                     cls : 'roo-navigation-top-bar'
29531                 },
29532                 {
29533                     tag : 'div',
29534                     cls : 'roo-navigation-bullets-bar',
29535                     cn : [
29536                         {
29537                             tag : 'ul',
29538                             cls : 'roo-navigation-bar'
29539                         }
29540                     ]
29541                 },
29542                 
29543                 {
29544                     tag : 'div',
29545                     cls : 'roo-navigation-bottom-bar'
29546                 }
29547             ]
29548             
29549         };
29550         
29551         return cfg;
29552         
29553     },
29554     
29555     initEvents: function() 
29556     {
29557         
29558     },
29559     
29560     onRender : function(ct, position) 
29561     {
29562         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29563         
29564         if(this.bullets.length){
29565             Roo.each(this.bullets, function(b){
29566                this.addItem(b);
29567             }, this);
29568         }
29569         
29570         this.format();
29571         
29572     },
29573     
29574     addItem : function(cfg)
29575     {
29576         var item = new Roo.bootstrap.NavProgressItem(cfg);
29577         
29578         item.parentId = this.id;
29579         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29580         
29581         if(cfg.html){
29582             var top = new Roo.bootstrap.Element({
29583                 tag : 'div',
29584                 cls : 'roo-navigation-bar-text'
29585             });
29586             
29587             var bottom = new Roo.bootstrap.Element({
29588                 tag : 'div',
29589                 cls : 'roo-navigation-bar-text'
29590             });
29591             
29592             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29593             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29594             
29595             var topText = new Roo.bootstrap.Element({
29596                 tag : 'span',
29597                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29598             });
29599             
29600             var bottomText = new Roo.bootstrap.Element({
29601                 tag : 'span',
29602                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29603             });
29604             
29605             topText.onRender(top.el, null);
29606             bottomText.onRender(bottom.el, null);
29607             
29608             item.topEl = top;
29609             item.bottomEl = bottom;
29610         }
29611         
29612         this.barItems.push(item);
29613         
29614         return item;
29615     },
29616     
29617     getActive : function()
29618     {
29619         var active = false;
29620         
29621         Roo.each(this.barItems, function(v){
29622             
29623             if (!v.isActive()) {
29624                 return;
29625             }
29626             
29627             active = v;
29628             return false;
29629             
29630         });
29631         
29632         return active;
29633     },
29634     
29635     setActiveItem : function(item)
29636     {
29637         var prev = false;
29638         
29639         Roo.each(this.barItems, function(v){
29640             if (v.rid == item.rid) {
29641                 return ;
29642             }
29643             
29644             if (v.isActive()) {
29645                 v.setActive(false);
29646                 prev = v;
29647             }
29648         });
29649
29650         item.setActive(true);
29651         
29652         this.fireEvent('changed', this, item, prev);
29653     },
29654     
29655     getBarItem: function(rid)
29656     {
29657         var ret = false;
29658         
29659         Roo.each(this.barItems, function(e) {
29660             if (e.rid != rid) {
29661                 return;
29662             }
29663             
29664             ret =  e;
29665             return false;
29666         });
29667         
29668         return ret;
29669     },
29670     
29671     indexOfItem : function(item)
29672     {
29673         var index = false;
29674         
29675         Roo.each(this.barItems, function(v, i){
29676             
29677             if (v.rid != item.rid) {
29678                 return;
29679             }
29680             
29681             index = i;
29682             return false
29683         });
29684         
29685         return index;
29686     },
29687     
29688     setActiveNext : function()
29689     {
29690         var i = this.indexOfItem(this.getActive());
29691         
29692         if (i > this.barItems.length) {
29693             return;
29694         }
29695         
29696         this.setActiveItem(this.barItems[i+1]);
29697     },
29698     
29699     setActivePrev : function()
29700     {
29701         var i = this.indexOfItem(this.getActive());
29702         
29703         if (i  < 1) {
29704             return;
29705         }
29706         
29707         this.setActiveItem(this.barItems[i-1]);
29708     },
29709     
29710     format : function()
29711     {
29712         if(!this.barItems.length){
29713             return;
29714         }
29715      
29716         var width = 100 / this.barItems.length;
29717         
29718         Roo.each(this.barItems, function(i){
29719             i.el.setStyle('width', width + '%');
29720             i.topEl.el.setStyle('width', width + '%');
29721             i.bottomEl.el.setStyle('width', width + '%');
29722         }, this);
29723         
29724     }
29725     
29726 });
29727 /*
29728  * - LGPL
29729  *
29730  * Nav Progress Item
29731  * 
29732  */
29733
29734 /**
29735  * @class Roo.bootstrap.NavProgressItem
29736  * @extends Roo.bootstrap.Component
29737  * Bootstrap NavProgressItem class
29738  * @cfg {String} rid the reference id
29739  * @cfg {Boolean} active (true|false) Is item active default false
29740  * @cfg {Boolean} disabled (true|false) Is item active default false
29741  * @cfg {String} html
29742  * @cfg {String} position (top|bottom) text position default bottom
29743  * @cfg {String} icon show icon instead of number
29744  * 
29745  * @constructor
29746  * Create a new NavProgressItem
29747  * @param {Object} config The config object
29748  */
29749 Roo.bootstrap.NavProgressItem = function(config){
29750     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29751     this.addEvents({
29752         // raw events
29753         /**
29754          * @event click
29755          * The raw click event for the entire grid.
29756          * @param {Roo.bootstrap.NavProgressItem} this
29757          * @param {Roo.EventObject} e
29758          */
29759         "click" : true
29760     });
29761    
29762 };
29763
29764 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29765     
29766     rid : '',
29767     active : false,
29768     disabled : false,
29769     html : '',
29770     position : 'bottom',
29771     icon : false,
29772     
29773     getAutoCreate : function()
29774     {
29775         var iconCls = 'roo-navigation-bar-item-icon';
29776         
29777         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29778         
29779         var cfg = {
29780             tag: 'li',
29781             cls: 'roo-navigation-bar-item',
29782             cn : [
29783                 {
29784                     tag : 'i',
29785                     cls : iconCls
29786                 }
29787             ]
29788         };
29789         
29790         if(this.active){
29791             cfg.cls += ' active';
29792         }
29793         if(this.disabled){
29794             cfg.cls += ' disabled';
29795         }
29796         
29797         return cfg;
29798     },
29799     
29800     disable : function()
29801     {
29802         this.setDisabled(true);
29803     },
29804     
29805     enable : function()
29806     {
29807         this.setDisabled(false);
29808     },
29809     
29810     initEvents: function() 
29811     {
29812         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29813         
29814         this.iconEl.on('click', this.onClick, this);
29815     },
29816     
29817     onClick : function(e)
29818     {
29819         e.preventDefault();
29820         
29821         if(this.disabled){
29822             return;
29823         }
29824         
29825         if(this.fireEvent('click', this, e) === false){
29826             return;
29827         };
29828         
29829         this.parent().setActiveItem(this);
29830     },
29831     
29832     isActive: function () 
29833     {
29834         return this.active;
29835     },
29836     
29837     setActive : function(state)
29838     {
29839         if(this.active == state){
29840             return;
29841         }
29842         
29843         this.active = state;
29844         
29845         if (state) {
29846             this.el.addClass('active');
29847             return;
29848         }
29849         
29850         this.el.removeClass('active');
29851         
29852         return;
29853     },
29854     
29855     setDisabled : function(state)
29856     {
29857         if(this.disabled == state){
29858             return;
29859         }
29860         
29861         this.disabled = state;
29862         
29863         if (state) {
29864             this.el.addClass('disabled');
29865             return;
29866         }
29867         
29868         this.el.removeClass('disabled');
29869     },
29870     
29871     tooltipEl : function()
29872     {
29873         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29874     }
29875 });
29876  
29877
29878  /*
29879  * - LGPL
29880  *
29881  * FieldLabel
29882  * 
29883  */
29884
29885 /**
29886  * @class Roo.bootstrap.FieldLabel
29887  * @extends Roo.bootstrap.Component
29888  * Bootstrap FieldLabel class
29889  * @cfg {String} html contents of the element
29890  * @cfg {String} tag tag of the element default label
29891  * @cfg {String} cls class of the element
29892  * @cfg {String} target label target 
29893  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29894  * @cfg {String} invalidClass default "text-warning"
29895  * @cfg {String} validClass default "text-success"
29896  * @cfg {String} iconTooltip default "This field is required"
29897  * @cfg {String} indicatorpos (left|right) default left
29898  * 
29899  * @constructor
29900  * Create a new FieldLabel
29901  * @param {Object} config The config object
29902  */
29903
29904 Roo.bootstrap.FieldLabel = function(config){
29905     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29906     
29907     this.addEvents({
29908             /**
29909              * @event invalid
29910              * Fires after the field has been marked as invalid.
29911              * @param {Roo.form.FieldLabel} this
29912              * @param {String} msg The validation message
29913              */
29914             invalid : true,
29915             /**
29916              * @event valid
29917              * Fires after the field has been validated with no errors.
29918              * @param {Roo.form.FieldLabel} this
29919              */
29920             valid : true
29921         });
29922 };
29923
29924 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29925     
29926     tag: 'label',
29927     cls: '',
29928     html: '',
29929     target: '',
29930     allowBlank : true,
29931     invalidClass : 'has-warning',
29932     validClass : 'has-success',
29933     iconTooltip : 'This field is required',
29934     indicatorpos : 'left',
29935     
29936     getAutoCreate : function(){
29937         
29938         var cfg = {
29939             tag : this.tag,
29940             cls : 'roo-bootstrap-field-label ' + this.cls,
29941             for : this.target,
29942             cn : [
29943                 {
29944                     tag : 'i',
29945                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29946                     tooltip : this.iconTooltip
29947                 },
29948                 {
29949                     tag : 'span',
29950                     html : this.html
29951                 }
29952             ] 
29953         };
29954         
29955         if(this.indicatorpos == 'right'){
29956             var cfg = {
29957                 tag : this.tag,
29958                 cls : 'roo-bootstrap-field-label ' + this.cls,
29959                 for : this.target,
29960                 cn : [
29961                     {
29962                         tag : 'span',
29963                         html : this.html
29964                     },
29965                     {
29966                         tag : 'i',
29967                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29968                         tooltip : this.iconTooltip
29969                     }
29970                 ] 
29971             };
29972         }
29973         
29974         return cfg;
29975     },
29976     
29977     initEvents: function() 
29978     {
29979         Roo.bootstrap.Element.superclass.initEvents.call(this);
29980         
29981         this.indicator = this.indicatorEl();
29982         
29983         if(this.indicator){
29984             this.indicator.removeClass('visible');
29985             this.indicator.addClass('invisible');
29986         }
29987         
29988         Roo.bootstrap.FieldLabel.register(this);
29989     },
29990     
29991     indicatorEl : function()
29992     {
29993         var indicator = this.el.select('i.roo-required-indicator',true).first();
29994         
29995         if(!indicator){
29996             return false;
29997         }
29998         
29999         return indicator;
30000         
30001     },
30002     
30003     /**
30004      * Mark this field as valid
30005      */
30006     markValid : function()
30007     {
30008         if(this.indicator){
30009             this.indicator.removeClass('visible');
30010             this.indicator.addClass('invisible');
30011         }
30012         
30013         this.el.removeClass(this.invalidClass);
30014         
30015         this.el.addClass(this.validClass);
30016         
30017         this.fireEvent('valid', this);
30018     },
30019     
30020     /**
30021      * Mark this field as invalid
30022      * @param {String} msg The validation message
30023      */
30024     markInvalid : function(msg)
30025     {
30026         if(this.indicator){
30027             this.indicator.removeClass('invisible');
30028             this.indicator.addClass('visible');
30029         }
30030         
30031         this.el.removeClass(this.validClass);
30032         
30033         this.el.addClass(this.invalidClass);
30034         
30035         this.fireEvent('invalid', this, msg);
30036     }
30037     
30038    
30039 });
30040
30041 Roo.apply(Roo.bootstrap.FieldLabel, {
30042     
30043     groups: {},
30044     
30045      /**
30046     * register a FieldLabel Group
30047     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30048     */
30049     register : function(label)
30050     {
30051         if(this.groups.hasOwnProperty(label.target)){
30052             return;
30053         }
30054      
30055         this.groups[label.target] = label;
30056         
30057     },
30058     /**
30059     * fetch a FieldLabel Group based on the target
30060     * @param {string} target
30061     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30062     */
30063     get: function(target) {
30064         if (typeof(this.groups[target]) == 'undefined') {
30065             return false;
30066         }
30067         
30068         return this.groups[target] ;
30069     }
30070 });
30071
30072  
30073
30074  /*
30075  * - LGPL
30076  *
30077  * page DateSplitField.
30078  * 
30079  */
30080
30081
30082 /**
30083  * @class Roo.bootstrap.DateSplitField
30084  * @extends Roo.bootstrap.Component
30085  * Bootstrap DateSplitField class
30086  * @cfg {string} fieldLabel - the label associated
30087  * @cfg {Number} labelWidth set the width of label (0-12)
30088  * @cfg {String} labelAlign (top|left)
30089  * @cfg {Boolean} dayAllowBlank (true|false) default false
30090  * @cfg {Boolean} monthAllowBlank (true|false) default false
30091  * @cfg {Boolean} yearAllowBlank (true|false) default false
30092  * @cfg {string} dayPlaceholder 
30093  * @cfg {string} monthPlaceholder
30094  * @cfg {string} yearPlaceholder
30095  * @cfg {string} dayFormat default 'd'
30096  * @cfg {string} monthFormat default 'm'
30097  * @cfg {string} yearFormat default 'Y'
30098  * @cfg {Number} labellg set the width of label (1-12)
30099  * @cfg {Number} labelmd set the width of label (1-12)
30100  * @cfg {Number} labelsm set the width of label (1-12)
30101  * @cfg {Number} labelxs set the width of label (1-12)
30102
30103  *     
30104  * @constructor
30105  * Create a new DateSplitField
30106  * @param {Object} config The config object
30107  */
30108
30109 Roo.bootstrap.DateSplitField = function(config){
30110     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30111     
30112     this.addEvents({
30113         // raw events
30114          /**
30115          * @event years
30116          * getting the data of years
30117          * @param {Roo.bootstrap.DateSplitField} this
30118          * @param {Object} years
30119          */
30120         "years" : true,
30121         /**
30122          * @event days
30123          * getting the data of days
30124          * @param {Roo.bootstrap.DateSplitField} this
30125          * @param {Object} days
30126          */
30127         "days" : true,
30128         /**
30129          * @event invalid
30130          * Fires after the field has been marked as invalid.
30131          * @param {Roo.form.Field} this
30132          * @param {String} msg The validation message
30133          */
30134         invalid : true,
30135        /**
30136          * @event valid
30137          * Fires after the field has been validated with no errors.
30138          * @param {Roo.form.Field} this
30139          */
30140         valid : true
30141     });
30142 };
30143
30144 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30145     
30146     fieldLabel : '',
30147     labelAlign : 'top',
30148     labelWidth : 3,
30149     dayAllowBlank : false,
30150     monthAllowBlank : false,
30151     yearAllowBlank : false,
30152     dayPlaceholder : '',
30153     monthPlaceholder : '',
30154     yearPlaceholder : '',
30155     dayFormat : 'd',
30156     monthFormat : 'm',
30157     yearFormat : 'Y',
30158     isFormField : true,
30159     labellg : 0,
30160     labelmd : 0,
30161     labelsm : 0,
30162     labelxs : 0,
30163     
30164     getAutoCreate : function()
30165     {
30166         var cfg = {
30167             tag : 'div',
30168             cls : 'row roo-date-split-field-group',
30169             cn : [
30170                 {
30171                     tag : 'input',
30172                     type : 'hidden',
30173                     cls : 'form-hidden-field roo-date-split-field-group-value',
30174                     name : this.name
30175                 }
30176             ]
30177         };
30178         
30179         var labelCls = 'col-md-12';
30180         var contentCls = 'col-md-4';
30181         
30182         if(this.fieldLabel){
30183             
30184             var label = {
30185                 tag : 'div',
30186                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30187                 cn : [
30188                     {
30189                         tag : 'label',
30190                         html : this.fieldLabel
30191                     }
30192                 ]
30193             };
30194             
30195             if(this.labelAlign == 'left'){
30196             
30197                 if(this.labelWidth > 12){
30198                     label.style = "width: " + this.labelWidth + 'px';
30199                 }
30200
30201                 if(this.labelWidth < 13 && this.labelmd == 0){
30202                     this.labelmd = this.labelWidth;
30203                 }
30204
30205                 if(this.labellg > 0){
30206                     labelCls = ' col-lg-' + this.labellg;
30207                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30208                 }
30209
30210                 if(this.labelmd > 0){
30211                     labelCls = ' col-md-' + this.labelmd;
30212                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30213                 }
30214
30215                 if(this.labelsm > 0){
30216                     labelCls = ' col-sm-' + this.labelsm;
30217                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30218                 }
30219
30220                 if(this.labelxs > 0){
30221                     labelCls = ' col-xs-' + this.labelxs;
30222                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30223                 }
30224             }
30225             
30226             label.cls += ' ' + labelCls;
30227             
30228             cfg.cn.push(label);
30229         }
30230         
30231         Roo.each(['day', 'month', 'year'], function(t){
30232             cfg.cn.push({
30233                 tag : 'div',
30234                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30235             });
30236         }, this);
30237         
30238         return cfg;
30239     },
30240     
30241     inputEl: function ()
30242     {
30243         return this.el.select('.roo-date-split-field-group-value', true).first();
30244     },
30245     
30246     onRender : function(ct, position) 
30247     {
30248         var _this = this;
30249         
30250         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30251         
30252         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30253         
30254         this.dayField = new Roo.bootstrap.ComboBox({
30255             allowBlank : this.dayAllowBlank,
30256             alwaysQuery : true,
30257             displayField : 'value',
30258             editable : false,
30259             fieldLabel : '',
30260             forceSelection : true,
30261             mode : 'local',
30262             placeholder : this.dayPlaceholder,
30263             selectOnFocus : true,
30264             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30265             triggerAction : 'all',
30266             typeAhead : true,
30267             valueField : 'value',
30268             store : new Roo.data.SimpleStore({
30269                 data : (function() {    
30270                     var days = [];
30271                     _this.fireEvent('days', _this, days);
30272                     return days;
30273                 })(),
30274                 fields : [ 'value' ]
30275             }),
30276             listeners : {
30277                 select : function (_self, record, index)
30278                 {
30279                     _this.setValue(_this.getValue());
30280                 }
30281             }
30282         });
30283
30284         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30285         
30286         this.monthField = new Roo.bootstrap.MonthField({
30287             after : '<i class=\"fa fa-calendar\"></i>',
30288             allowBlank : this.monthAllowBlank,
30289             placeholder : this.monthPlaceholder,
30290             readOnly : true,
30291             listeners : {
30292                 render : function (_self)
30293                 {
30294                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30295                         e.preventDefault();
30296                         _self.focus();
30297                     });
30298                 },
30299                 select : function (_self, oldvalue, newvalue)
30300                 {
30301                     _this.setValue(_this.getValue());
30302                 }
30303             }
30304         });
30305         
30306         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30307         
30308         this.yearField = new Roo.bootstrap.ComboBox({
30309             allowBlank : this.yearAllowBlank,
30310             alwaysQuery : true,
30311             displayField : 'value',
30312             editable : false,
30313             fieldLabel : '',
30314             forceSelection : true,
30315             mode : 'local',
30316             placeholder : this.yearPlaceholder,
30317             selectOnFocus : true,
30318             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30319             triggerAction : 'all',
30320             typeAhead : true,
30321             valueField : 'value',
30322             store : new Roo.data.SimpleStore({
30323                 data : (function() {
30324                     var years = [];
30325                     _this.fireEvent('years', _this, years);
30326                     return years;
30327                 })(),
30328                 fields : [ 'value' ]
30329             }),
30330             listeners : {
30331                 select : function (_self, record, index)
30332                 {
30333                     _this.setValue(_this.getValue());
30334                 }
30335             }
30336         });
30337
30338         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30339     },
30340     
30341     setValue : function(v, format)
30342     {
30343         this.inputEl.dom.value = v;
30344         
30345         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30346         
30347         var d = Date.parseDate(v, f);
30348         
30349         if(!d){
30350             this.validate();
30351             return;
30352         }
30353         
30354         this.setDay(d.format(this.dayFormat));
30355         this.setMonth(d.format(this.monthFormat));
30356         this.setYear(d.format(this.yearFormat));
30357         
30358         this.validate();
30359         
30360         return;
30361     },
30362     
30363     setDay : function(v)
30364     {
30365         this.dayField.setValue(v);
30366         this.inputEl.dom.value = this.getValue();
30367         this.validate();
30368         return;
30369     },
30370     
30371     setMonth : function(v)
30372     {
30373         this.monthField.setValue(v, true);
30374         this.inputEl.dom.value = this.getValue();
30375         this.validate();
30376         return;
30377     },
30378     
30379     setYear : function(v)
30380     {
30381         this.yearField.setValue(v);
30382         this.inputEl.dom.value = this.getValue();
30383         this.validate();
30384         return;
30385     },
30386     
30387     getDay : function()
30388     {
30389         return this.dayField.getValue();
30390     },
30391     
30392     getMonth : function()
30393     {
30394         return this.monthField.getValue();
30395     },
30396     
30397     getYear : function()
30398     {
30399         return this.yearField.getValue();
30400     },
30401     
30402     getValue : function()
30403     {
30404         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30405         
30406         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30407         
30408         return date;
30409     },
30410     
30411     reset : function()
30412     {
30413         this.setDay('');
30414         this.setMonth('');
30415         this.setYear('');
30416         this.inputEl.dom.value = '';
30417         this.validate();
30418         return;
30419     },
30420     
30421     validate : function()
30422     {
30423         var d = this.dayField.validate();
30424         var m = this.monthField.validate();
30425         var y = this.yearField.validate();
30426         
30427         var valid = true;
30428         
30429         if(
30430                 (!this.dayAllowBlank && !d) ||
30431                 (!this.monthAllowBlank && !m) ||
30432                 (!this.yearAllowBlank && !y)
30433         ){
30434             valid = false;
30435         }
30436         
30437         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30438             return valid;
30439         }
30440         
30441         if(valid){
30442             this.markValid();
30443             return valid;
30444         }
30445         
30446         this.markInvalid();
30447         
30448         return valid;
30449     },
30450     
30451     markValid : function()
30452     {
30453         
30454         var label = this.el.select('label', true).first();
30455         var icon = this.el.select('i.fa-star', true).first();
30456
30457         if(label && icon){
30458             icon.remove();
30459         }
30460         
30461         this.fireEvent('valid', this);
30462     },
30463     
30464      /**
30465      * Mark this field as invalid
30466      * @param {String} msg The validation message
30467      */
30468     markInvalid : function(msg)
30469     {
30470         
30471         var label = this.el.select('label', true).first();
30472         var icon = this.el.select('i.fa-star', true).first();
30473
30474         if(label && !icon){
30475             this.el.select('.roo-date-split-field-label', true).createChild({
30476                 tag : 'i',
30477                 cls : 'text-danger fa fa-lg fa-star',
30478                 tooltip : 'This field is required',
30479                 style : 'margin-right:5px;'
30480             }, label, true);
30481         }
30482         
30483         this.fireEvent('invalid', this, msg);
30484     },
30485     
30486     clearInvalid : function()
30487     {
30488         var label = this.el.select('label', true).first();
30489         var icon = this.el.select('i.fa-star', true).first();
30490
30491         if(label && icon){
30492             icon.remove();
30493         }
30494         
30495         this.fireEvent('valid', this);
30496     },
30497     
30498     getName: function()
30499     {
30500         return this.name;
30501     }
30502     
30503 });
30504
30505  /**
30506  *
30507  * This is based on 
30508  * http://masonry.desandro.com
30509  *
30510  * The idea is to render all the bricks based on vertical width...
30511  *
30512  * The original code extends 'outlayer' - we might need to use that....
30513  * 
30514  */
30515
30516
30517 /**
30518  * @class Roo.bootstrap.LayoutMasonry
30519  * @extends Roo.bootstrap.Component
30520  * Bootstrap Layout Masonry class
30521  * 
30522  * @constructor
30523  * Create a new Element
30524  * @param {Object} config The config object
30525  */
30526
30527 Roo.bootstrap.LayoutMasonry = function(config){
30528     
30529     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30530     
30531     this.bricks = [];
30532     
30533     Roo.bootstrap.LayoutMasonry.register(this);
30534     
30535     this.addEvents({
30536         // raw events
30537         /**
30538          * @event layout
30539          * Fire after layout the items
30540          * @param {Roo.bootstrap.LayoutMasonry} this
30541          * @param {Roo.EventObject} e
30542          */
30543         "layout" : true
30544     });
30545     
30546 };
30547
30548 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30549     
30550     /**
30551      * @cfg {Boolean} isLayoutInstant = no animation?
30552      */   
30553     isLayoutInstant : false, // needed?
30554    
30555     /**
30556      * @cfg {Number} boxWidth  width of the columns
30557      */   
30558     boxWidth : 450,
30559     
30560       /**
30561      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30562      */   
30563     boxHeight : 0,
30564     
30565     /**
30566      * @cfg {Number} padWidth padding below box..
30567      */   
30568     padWidth : 10, 
30569     
30570     /**
30571      * @cfg {Number} gutter gutter width..
30572      */   
30573     gutter : 10,
30574     
30575      /**
30576      * @cfg {Number} maxCols maximum number of columns
30577      */   
30578     
30579     maxCols: 0,
30580     
30581     /**
30582      * @cfg {Boolean} isAutoInitial defalut true
30583      */   
30584     isAutoInitial : true, 
30585     
30586     containerWidth: 0,
30587     
30588     /**
30589      * @cfg {Boolean} isHorizontal defalut false
30590      */   
30591     isHorizontal : false, 
30592
30593     currentSize : null,
30594     
30595     tag: 'div',
30596     
30597     cls: '',
30598     
30599     bricks: null, //CompositeElement
30600     
30601     cols : 1,
30602     
30603     _isLayoutInited : false,
30604     
30605 //    isAlternative : false, // only use for vertical layout...
30606     
30607     /**
30608      * @cfg {Number} alternativePadWidth padding below box..
30609      */   
30610     alternativePadWidth : 50,
30611     
30612     selectedBrick : [],
30613     
30614     getAutoCreate : function(){
30615         
30616         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30617         
30618         var cfg = {
30619             tag: this.tag,
30620             cls: 'blog-masonary-wrapper ' + this.cls,
30621             cn : {
30622                 cls : 'mas-boxes masonary'
30623             }
30624         };
30625         
30626         return cfg;
30627     },
30628     
30629     getChildContainer: function( )
30630     {
30631         if (this.boxesEl) {
30632             return this.boxesEl;
30633         }
30634         
30635         this.boxesEl = this.el.select('.mas-boxes').first();
30636         
30637         return this.boxesEl;
30638     },
30639     
30640     
30641     initEvents : function()
30642     {
30643         var _this = this;
30644         
30645         if(this.isAutoInitial){
30646             Roo.log('hook children rendered');
30647             this.on('childrenrendered', function() {
30648                 Roo.log('children rendered');
30649                 _this.initial();
30650             } ,this);
30651         }
30652     },
30653     
30654     initial : function()
30655     {
30656         this.selectedBrick = [];
30657         
30658         this.currentSize = this.el.getBox(true);
30659         
30660         Roo.EventManager.onWindowResize(this.resize, this); 
30661
30662         if(!this.isAutoInitial){
30663             this.layout();
30664             return;
30665         }
30666         
30667         this.layout();
30668         
30669         return;
30670         //this.layout.defer(500,this);
30671         
30672     },
30673     
30674     resize : function()
30675     {
30676         var cs = this.el.getBox(true);
30677         
30678         if (
30679                 this.currentSize.width == cs.width && 
30680                 this.currentSize.x == cs.x && 
30681                 this.currentSize.height == cs.height && 
30682                 this.currentSize.y == cs.y 
30683         ) {
30684             Roo.log("no change in with or X or Y");
30685             return;
30686         }
30687         
30688         this.currentSize = cs;
30689         
30690         this.layout();
30691         
30692     },
30693     
30694     layout : function()
30695     {   
30696         this._resetLayout();
30697         
30698         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30699         
30700         this.layoutItems( isInstant );
30701       
30702         this._isLayoutInited = true;
30703         
30704         this.fireEvent('layout', this);
30705         
30706     },
30707     
30708     _resetLayout : function()
30709     {
30710         if(this.isHorizontal){
30711             this.horizontalMeasureColumns();
30712             return;
30713         }
30714         
30715         this.verticalMeasureColumns();
30716         
30717     },
30718     
30719     verticalMeasureColumns : function()
30720     {
30721         this.getContainerWidth();
30722         
30723 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30724 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30725 //            return;
30726 //        }
30727         
30728         var boxWidth = this.boxWidth + this.padWidth;
30729         
30730         if(this.containerWidth < this.boxWidth){
30731             boxWidth = this.containerWidth
30732         }
30733         
30734         var containerWidth = this.containerWidth;
30735         
30736         var cols = Math.floor(containerWidth / boxWidth);
30737         
30738         this.cols = Math.max( cols, 1 );
30739         
30740         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30741         
30742         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30743         
30744         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30745         
30746         this.colWidth = boxWidth + avail - this.padWidth;
30747         
30748         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30749         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30750     },
30751     
30752     horizontalMeasureColumns : function()
30753     {
30754         this.getContainerWidth();
30755         
30756         var boxWidth = this.boxWidth;
30757         
30758         if(this.containerWidth < boxWidth){
30759             boxWidth = this.containerWidth;
30760         }
30761         
30762         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30763         
30764         this.el.setHeight(boxWidth);
30765         
30766     },
30767     
30768     getContainerWidth : function()
30769     {
30770         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30771     },
30772     
30773     layoutItems : function( isInstant )
30774     {
30775         Roo.log(this.bricks);
30776         
30777         var items = Roo.apply([], this.bricks);
30778         
30779         if(this.isHorizontal){
30780             this._horizontalLayoutItems( items , isInstant );
30781             return;
30782         }
30783         
30784 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30785 //            this._verticalAlternativeLayoutItems( items , isInstant );
30786 //            return;
30787 //        }
30788         
30789         this._verticalLayoutItems( items , isInstant );
30790         
30791     },
30792     
30793     _verticalLayoutItems : function ( items , isInstant)
30794     {
30795         if ( !items || !items.length ) {
30796             return;
30797         }
30798         
30799         var standard = [
30800             ['xs', 'xs', 'xs', 'tall'],
30801             ['xs', 'xs', 'tall'],
30802             ['xs', 'xs', 'sm'],
30803             ['xs', 'xs', 'xs'],
30804             ['xs', 'tall'],
30805             ['xs', 'sm'],
30806             ['xs', 'xs'],
30807             ['xs'],
30808             
30809             ['sm', 'xs', 'xs'],
30810             ['sm', 'xs'],
30811             ['sm'],
30812             
30813             ['tall', 'xs', 'xs', 'xs'],
30814             ['tall', 'xs', 'xs'],
30815             ['tall', 'xs'],
30816             ['tall']
30817             
30818         ];
30819         
30820         var queue = [];
30821         
30822         var boxes = [];
30823         
30824         var box = [];
30825         
30826         Roo.each(items, function(item, k){
30827             
30828             switch (item.size) {
30829                 // these layouts take up a full box,
30830                 case 'md' :
30831                 case 'md-left' :
30832                 case 'md-right' :
30833                 case 'wide' :
30834                     
30835                     if(box.length){
30836                         boxes.push(box);
30837                         box = [];
30838                     }
30839                     
30840                     boxes.push([item]);
30841                     
30842                     break;
30843                     
30844                 case 'xs' :
30845                 case 'sm' :
30846                 case 'tall' :
30847                     
30848                     box.push(item);
30849                     
30850                     break;
30851                 default :
30852                     break;
30853                     
30854             }
30855             
30856         }, this);
30857         
30858         if(box.length){
30859             boxes.push(box);
30860             box = [];
30861         }
30862         
30863         var filterPattern = function(box, length)
30864         {
30865             if(!box.length){
30866                 return;
30867             }
30868             
30869             var match = false;
30870             
30871             var pattern = box.slice(0, length);
30872             
30873             var format = [];
30874             
30875             Roo.each(pattern, function(i){
30876                 format.push(i.size);
30877             }, this);
30878             
30879             Roo.each(standard, function(s){
30880                 
30881                 if(String(s) != String(format)){
30882                     return;
30883                 }
30884                 
30885                 match = true;
30886                 return false;
30887                 
30888             }, this);
30889             
30890             if(!match && length == 1){
30891                 return;
30892             }
30893             
30894             if(!match){
30895                 filterPattern(box, length - 1);
30896                 return;
30897             }
30898                 
30899             queue.push(pattern);
30900
30901             box = box.slice(length, box.length);
30902
30903             filterPattern(box, 4);
30904
30905             return;
30906             
30907         }
30908         
30909         Roo.each(boxes, function(box, k){
30910             
30911             if(!box.length){
30912                 return;
30913             }
30914             
30915             if(box.length == 1){
30916                 queue.push(box);
30917                 return;
30918             }
30919             
30920             filterPattern(box, 4);
30921             
30922         }, this);
30923         
30924         this._processVerticalLayoutQueue( queue, isInstant );
30925         
30926     },
30927     
30928 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30929 //    {
30930 //        if ( !items || !items.length ) {
30931 //            return;
30932 //        }
30933 //
30934 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30935 //        
30936 //    },
30937     
30938     _horizontalLayoutItems : function ( items , isInstant)
30939     {
30940         if ( !items || !items.length || items.length < 3) {
30941             return;
30942         }
30943         
30944         items.reverse();
30945         
30946         var eItems = items.slice(0, 3);
30947         
30948         items = items.slice(3, items.length);
30949         
30950         var standard = [
30951             ['xs', 'xs', 'xs', 'wide'],
30952             ['xs', 'xs', 'wide'],
30953             ['xs', 'xs', 'sm'],
30954             ['xs', 'xs', 'xs'],
30955             ['xs', 'wide'],
30956             ['xs', 'sm'],
30957             ['xs', 'xs'],
30958             ['xs'],
30959             
30960             ['sm', 'xs', 'xs'],
30961             ['sm', 'xs'],
30962             ['sm'],
30963             
30964             ['wide', 'xs', 'xs', 'xs'],
30965             ['wide', 'xs', 'xs'],
30966             ['wide', 'xs'],
30967             ['wide'],
30968             
30969             ['wide-thin']
30970         ];
30971         
30972         var queue = [];
30973         
30974         var boxes = [];
30975         
30976         var box = [];
30977         
30978         Roo.each(items, function(item, k){
30979             
30980             switch (item.size) {
30981                 case 'md' :
30982                 case 'md-left' :
30983                 case 'md-right' :
30984                 case 'tall' :
30985                     
30986                     if(box.length){
30987                         boxes.push(box);
30988                         box = [];
30989                     }
30990                     
30991                     boxes.push([item]);
30992                     
30993                     break;
30994                     
30995                 case 'xs' :
30996                 case 'sm' :
30997                 case 'wide' :
30998                 case 'wide-thin' :
30999                     
31000                     box.push(item);
31001                     
31002                     break;
31003                 default :
31004                     break;
31005                     
31006             }
31007             
31008         }, this);
31009         
31010         if(box.length){
31011             boxes.push(box);
31012             box = [];
31013         }
31014         
31015         var filterPattern = function(box, length)
31016         {
31017             if(!box.length){
31018                 return;
31019             }
31020             
31021             var match = false;
31022             
31023             var pattern = box.slice(0, length);
31024             
31025             var format = [];
31026             
31027             Roo.each(pattern, function(i){
31028                 format.push(i.size);
31029             }, this);
31030             
31031             Roo.each(standard, function(s){
31032                 
31033                 if(String(s) != String(format)){
31034                     return;
31035                 }
31036                 
31037                 match = true;
31038                 return false;
31039                 
31040             }, this);
31041             
31042             if(!match && length == 1){
31043                 return;
31044             }
31045             
31046             if(!match){
31047                 filterPattern(box, length - 1);
31048                 return;
31049             }
31050                 
31051             queue.push(pattern);
31052
31053             box = box.slice(length, box.length);
31054
31055             filterPattern(box, 4);
31056
31057             return;
31058             
31059         }
31060         
31061         Roo.each(boxes, function(box, k){
31062             
31063             if(!box.length){
31064                 return;
31065             }
31066             
31067             if(box.length == 1){
31068                 queue.push(box);
31069                 return;
31070             }
31071             
31072             filterPattern(box, 4);
31073             
31074         }, this);
31075         
31076         
31077         var prune = [];
31078         
31079         var pos = this.el.getBox(true);
31080         
31081         var minX = pos.x;
31082         
31083         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31084         
31085         var hit_end = false;
31086         
31087         Roo.each(queue, function(box){
31088             
31089             if(hit_end){
31090                 
31091                 Roo.each(box, function(b){
31092                 
31093                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31094                     b.el.hide();
31095
31096                 }, this);
31097
31098                 return;
31099             }
31100             
31101             var mx = 0;
31102             
31103             Roo.each(box, function(b){
31104                 
31105                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31106                 b.el.show();
31107
31108                 mx = Math.max(mx, b.x);
31109                 
31110             }, this);
31111             
31112             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31113             
31114             if(maxX < minX){
31115                 
31116                 Roo.each(box, function(b){
31117                 
31118                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31119                     b.el.hide();
31120                     
31121                 }, this);
31122                 
31123                 hit_end = true;
31124                 
31125                 return;
31126             }
31127             
31128             prune.push(box);
31129             
31130         }, this);
31131         
31132         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31133     },
31134     
31135     /** Sets position of item in DOM
31136     * @param {Element} item
31137     * @param {Number} x - horizontal position
31138     * @param {Number} y - vertical position
31139     * @param {Boolean} isInstant - disables transitions
31140     */
31141     _processVerticalLayoutQueue : function( queue, isInstant )
31142     {
31143         var pos = this.el.getBox(true);
31144         var x = pos.x;
31145         var y = pos.y;
31146         var maxY = [];
31147         
31148         for (var i = 0; i < this.cols; i++){
31149             maxY[i] = pos.y;
31150         }
31151         
31152         Roo.each(queue, function(box, k){
31153             
31154             var col = k % this.cols;
31155             
31156             Roo.each(box, function(b,kk){
31157                 
31158                 b.el.position('absolute');
31159                 
31160                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31161                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31162                 
31163                 if(b.size == 'md-left' || b.size == 'md-right'){
31164                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31165                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31166                 }
31167                 
31168                 b.el.setWidth(width);
31169                 b.el.setHeight(height);
31170                 // iframe?
31171                 b.el.select('iframe',true).setSize(width,height);
31172                 
31173             }, this);
31174             
31175             for (var i = 0; i < this.cols; i++){
31176                 
31177                 if(maxY[i] < maxY[col]){
31178                     col = i;
31179                     continue;
31180                 }
31181                 
31182                 col = Math.min(col, i);
31183                 
31184             }
31185             
31186             x = pos.x + col * (this.colWidth + this.padWidth);
31187             
31188             y = maxY[col];
31189             
31190             var positions = [];
31191             
31192             switch (box.length){
31193                 case 1 :
31194                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31195                     break;
31196                 case 2 :
31197                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31198                     break;
31199                 case 3 :
31200                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31201                     break;
31202                 case 4 :
31203                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31204                     break;
31205                 default :
31206                     break;
31207             }
31208             
31209             Roo.each(box, function(b,kk){
31210                 
31211                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31212                 
31213                 var sz = b.el.getSize();
31214                 
31215                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31216                 
31217             }, this);
31218             
31219         }, this);
31220         
31221         var mY = 0;
31222         
31223         for (var i = 0; i < this.cols; i++){
31224             mY = Math.max(mY, maxY[i]);
31225         }
31226         
31227         this.el.setHeight(mY - pos.y);
31228         
31229     },
31230     
31231 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31232 //    {
31233 //        var pos = this.el.getBox(true);
31234 //        var x = pos.x;
31235 //        var y = pos.y;
31236 //        var maxX = pos.right;
31237 //        
31238 //        var maxHeight = 0;
31239 //        
31240 //        Roo.each(items, function(item, k){
31241 //            
31242 //            var c = k % 2;
31243 //            
31244 //            item.el.position('absolute');
31245 //                
31246 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31247 //
31248 //            item.el.setWidth(width);
31249 //
31250 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31251 //
31252 //            item.el.setHeight(height);
31253 //            
31254 //            if(c == 0){
31255 //                item.el.setXY([x, y], isInstant ? false : true);
31256 //            } else {
31257 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31258 //            }
31259 //            
31260 //            y = y + height + this.alternativePadWidth;
31261 //            
31262 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31263 //            
31264 //        }, this);
31265 //        
31266 //        this.el.setHeight(maxHeight);
31267 //        
31268 //    },
31269     
31270     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31271     {
31272         var pos = this.el.getBox(true);
31273         
31274         var minX = pos.x;
31275         var minY = pos.y;
31276         
31277         var maxX = pos.right;
31278         
31279         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31280         
31281         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31282         
31283         Roo.each(queue, function(box, k){
31284             
31285             Roo.each(box, function(b, kk){
31286                 
31287                 b.el.position('absolute');
31288                 
31289                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31290                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31291                 
31292                 if(b.size == 'md-left' || b.size == 'md-right'){
31293                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31294                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31295                 }
31296                 
31297                 b.el.setWidth(width);
31298                 b.el.setHeight(height);
31299                 
31300             }, this);
31301             
31302             if(!box.length){
31303                 return;
31304             }
31305             
31306             var positions = [];
31307             
31308             switch (box.length){
31309                 case 1 :
31310                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31311                     break;
31312                 case 2 :
31313                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31314                     break;
31315                 case 3 :
31316                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31317                     break;
31318                 case 4 :
31319                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31320                     break;
31321                 default :
31322                     break;
31323             }
31324             
31325             Roo.each(box, function(b,kk){
31326                 
31327                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31328                 
31329                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31330                 
31331             }, this);
31332             
31333         }, this);
31334         
31335     },
31336     
31337     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31338     {
31339         Roo.each(eItems, function(b,k){
31340             
31341             b.size = (k == 0) ? 'sm' : 'xs';
31342             b.x = (k == 0) ? 2 : 1;
31343             b.y = (k == 0) ? 2 : 1;
31344             
31345             b.el.position('absolute');
31346             
31347             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31348                 
31349             b.el.setWidth(width);
31350             
31351             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31352             
31353             b.el.setHeight(height);
31354             
31355         }, this);
31356
31357         var positions = [];
31358         
31359         positions.push({
31360             x : maxX - this.unitWidth * 2 - this.gutter,
31361             y : minY
31362         });
31363         
31364         positions.push({
31365             x : maxX - this.unitWidth,
31366             y : minY + (this.unitWidth + this.gutter) * 2
31367         });
31368         
31369         positions.push({
31370             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31371             y : minY
31372         });
31373         
31374         Roo.each(eItems, function(b,k){
31375             
31376             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31377
31378         }, this);
31379         
31380     },
31381     
31382     getVerticalOneBoxColPositions : function(x, y, box)
31383     {
31384         var pos = [];
31385         
31386         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31387         
31388         if(box[0].size == 'md-left'){
31389             rand = 0;
31390         }
31391         
31392         if(box[0].size == 'md-right'){
31393             rand = 1;
31394         }
31395         
31396         pos.push({
31397             x : x + (this.unitWidth + this.gutter) * rand,
31398             y : y
31399         });
31400         
31401         return pos;
31402     },
31403     
31404     getVerticalTwoBoxColPositions : function(x, y, box)
31405     {
31406         var pos = [];
31407         
31408         if(box[0].size == 'xs'){
31409             
31410             pos.push({
31411                 x : x,
31412                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31413             });
31414
31415             pos.push({
31416                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31417                 y : y
31418             });
31419             
31420             return pos;
31421             
31422         }
31423         
31424         pos.push({
31425             x : x,
31426             y : y
31427         });
31428
31429         pos.push({
31430             x : x + (this.unitWidth + this.gutter) * 2,
31431             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31432         });
31433         
31434         return pos;
31435         
31436     },
31437     
31438     getVerticalThreeBoxColPositions : function(x, y, box)
31439     {
31440         var pos = [];
31441         
31442         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31443             
31444             pos.push({
31445                 x : x,
31446                 y : y
31447             });
31448
31449             pos.push({
31450                 x : x + (this.unitWidth + this.gutter) * 1,
31451                 y : y
31452             });
31453             
31454             pos.push({
31455                 x : x + (this.unitWidth + this.gutter) * 2,
31456                 y : y
31457             });
31458             
31459             return pos;
31460             
31461         }
31462         
31463         if(box[0].size == 'xs' && box[1].size == 'xs'){
31464             
31465             pos.push({
31466                 x : x,
31467                 y : y
31468             });
31469
31470             pos.push({
31471                 x : x,
31472                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31473             });
31474             
31475             pos.push({
31476                 x : x + (this.unitWidth + this.gutter) * 1,
31477                 y : y
31478             });
31479             
31480             return pos;
31481             
31482         }
31483         
31484         pos.push({
31485             x : x,
31486             y : y
31487         });
31488
31489         pos.push({
31490             x : x + (this.unitWidth + this.gutter) * 2,
31491             y : y
31492         });
31493
31494         pos.push({
31495             x : x + (this.unitWidth + this.gutter) * 2,
31496             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31497         });
31498             
31499         return pos;
31500         
31501     },
31502     
31503     getVerticalFourBoxColPositions : function(x, y, box)
31504     {
31505         var pos = [];
31506         
31507         if(box[0].size == 'xs'){
31508             
31509             pos.push({
31510                 x : x,
31511                 y : y
31512             });
31513
31514             pos.push({
31515                 x : x,
31516                 y : y + (this.unitHeight + this.gutter) * 1
31517             });
31518             
31519             pos.push({
31520                 x : x,
31521                 y : y + (this.unitHeight + this.gutter) * 2
31522             });
31523             
31524             pos.push({
31525                 x : x + (this.unitWidth + this.gutter) * 1,
31526                 y : y
31527             });
31528             
31529             return pos;
31530             
31531         }
31532         
31533         pos.push({
31534             x : x,
31535             y : y
31536         });
31537
31538         pos.push({
31539             x : x + (this.unitWidth + this.gutter) * 2,
31540             y : y
31541         });
31542
31543         pos.push({
31544             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31545             y : y + (this.unitHeight + this.gutter) * 1
31546         });
31547
31548         pos.push({
31549             x : x + (this.unitWidth + this.gutter) * 2,
31550             y : y + (this.unitWidth + this.gutter) * 2
31551         });
31552
31553         return pos;
31554         
31555     },
31556     
31557     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31558     {
31559         var pos = [];
31560         
31561         if(box[0].size == 'md-left'){
31562             pos.push({
31563                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31564                 y : minY
31565             });
31566             
31567             return pos;
31568         }
31569         
31570         if(box[0].size == 'md-right'){
31571             pos.push({
31572                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31573                 y : minY + (this.unitWidth + this.gutter) * 1
31574             });
31575             
31576             return pos;
31577         }
31578         
31579         var rand = Math.floor(Math.random() * (4 - box[0].y));
31580         
31581         pos.push({
31582             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31583             y : minY + (this.unitWidth + this.gutter) * rand
31584         });
31585         
31586         return pos;
31587         
31588     },
31589     
31590     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31591     {
31592         var pos = [];
31593         
31594         if(box[0].size == 'xs'){
31595             
31596             pos.push({
31597                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31598                 y : minY
31599             });
31600
31601             pos.push({
31602                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31603                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31604             });
31605             
31606             return pos;
31607             
31608         }
31609         
31610         pos.push({
31611             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31612             y : minY
31613         });
31614
31615         pos.push({
31616             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31617             y : minY + (this.unitWidth + this.gutter) * 2
31618         });
31619         
31620         return pos;
31621         
31622     },
31623     
31624     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31625     {
31626         var pos = [];
31627         
31628         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31629             
31630             pos.push({
31631                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31632                 y : minY
31633             });
31634
31635             pos.push({
31636                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31637                 y : minY + (this.unitWidth + this.gutter) * 1
31638             });
31639             
31640             pos.push({
31641                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31642                 y : minY + (this.unitWidth + this.gutter) * 2
31643             });
31644             
31645             return pos;
31646             
31647         }
31648         
31649         if(box[0].size == 'xs' && box[1].size == 'xs'){
31650             
31651             pos.push({
31652                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31653                 y : minY
31654             });
31655
31656             pos.push({
31657                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31658                 y : minY
31659             });
31660             
31661             pos.push({
31662                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31663                 y : minY + (this.unitWidth + this.gutter) * 1
31664             });
31665             
31666             return pos;
31667             
31668         }
31669         
31670         pos.push({
31671             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31672             y : minY
31673         });
31674
31675         pos.push({
31676             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31677             y : minY + (this.unitWidth + this.gutter) * 2
31678         });
31679
31680         pos.push({
31681             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31682             y : minY + (this.unitWidth + this.gutter) * 2
31683         });
31684             
31685         return pos;
31686         
31687     },
31688     
31689     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31690     {
31691         var pos = [];
31692         
31693         if(box[0].size == 'xs'){
31694             
31695             pos.push({
31696                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31697                 y : minY
31698             });
31699
31700             pos.push({
31701                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31702                 y : minY
31703             });
31704             
31705             pos.push({
31706                 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),
31707                 y : minY
31708             });
31709             
31710             pos.push({
31711                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31712                 y : minY + (this.unitWidth + this.gutter) * 1
31713             });
31714             
31715             return pos;
31716             
31717         }
31718         
31719         pos.push({
31720             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31721             y : minY
31722         });
31723         
31724         pos.push({
31725             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31726             y : minY + (this.unitWidth + this.gutter) * 2
31727         });
31728         
31729         pos.push({
31730             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31731             y : minY + (this.unitWidth + this.gutter) * 2
31732         });
31733         
31734         pos.push({
31735             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),
31736             y : minY + (this.unitWidth + this.gutter) * 2
31737         });
31738
31739         return pos;
31740         
31741     },
31742     
31743     /**
31744     * remove a Masonry Brick
31745     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31746     */
31747     removeBrick : function(brick_id)
31748     {
31749         if (!brick_id) {
31750             return;
31751         }
31752         
31753         for (var i = 0; i<this.bricks.length; i++) {
31754             if (this.bricks[i].id == brick_id) {
31755                 this.bricks.splice(i,1);
31756                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31757                 this.initial();
31758             }
31759         }
31760     },
31761     
31762     /**
31763     * adds a Masonry Brick
31764     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31765     */
31766     addBrick : function(cfg)
31767     {
31768         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31769         //this.register(cn);
31770         cn.parentId = this.id;
31771         cn.onRender(this.el, null);
31772         return cn;
31773     },
31774     
31775     /**
31776     * register a Masonry Brick
31777     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31778     */
31779     
31780     register : function(brick)
31781     {
31782         this.bricks.push(brick);
31783         brick.masonryId = this.id;
31784     },
31785     
31786     /**
31787     * clear all the Masonry Brick
31788     */
31789     clearAll : function()
31790     {
31791         this.bricks = [];
31792         //this.getChildContainer().dom.innerHTML = "";
31793         this.el.dom.innerHTML = '';
31794     },
31795     
31796     getSelected : function()
31797     {
31798         if (!this.selectedBrick) {
31799             return false;
31800         }
31801         
31802         return this.selectedBrick;
31803     }
31804 });
31805
31806 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31807     
31808     groups: {},
31809      /**
31810     * register a Masonry Layout
31811     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31812     */
31813     
31814     register : function(layout)
31815     {
31816         this.groups[layout.id] = layout;
31817     },
31818     /**
31819     * fetch a  Masonry Layout based on the masonry layout ID
31820     * @param {string} the masonry layout to add
31821     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31822     */
31823     
31824     get: function(layout_id) {
31825         if (typeof(this.groups[layout_id]) == 'undefined') {
31826             return false;
31827         }
31828         return this.groups[layout_id] ;
31829     }
31830     
31831     
31832     
31833 });
31834
31835  
31836
31837  /**
31838  *
31839  * This is based on 
31840  * http://masonry.desandro.com
31841  *
31842  * The idea is to render all the bricks based on vertical width...
31843  *
31844  * The original code extends 'outlayer' - we might need to use that....
31845  * 
31846  */
31847
31848
31849 /**
31850  * @class Roo.bootstrap.LayoutMasonryAuto
31851  * @extends Roo.bootstrap.Component
31852  * Bootstrap Layout Masonry class
31853  * 
31854  * @constructor
31855  * Create a new Element
31856  * @param {Object} config The config object
31857  */
31858
31859 Roo.bootstrap.LayoutMasonryAuto = function(config){
31860     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31861 };
31862
31863 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31864     
31865       /**
31866      * @cfg {Boolean} isFitWidth  - resize the width..
31867      */   
31868     isFitWidth : false,  // options..
31869     /**
31870      * @cfg {Boolean} isOriginLeft = left align?
31871      */   
31872     isOriginLeft : true,
31873     /**
31874      * @cfg {Boolean} isOriginTop = top align?
31875      */   
31876     isOriginTop : false,
31877     /**
31878      * @cfg {Boolean} isLayoutInstant = no animation?
31879      */   
31880     isLayoutInstant : false, // needed?
31881     /**
31882      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31883      */   
31884     isResizingContainer : true,
31885     /**
31886      * @cfg {Number} columnWidth  width of the columns 
31887      */   
31888     
31889     columnWidth : 0,
31890     
31891     /**
31892      * @cfg {Number} maxCols maximum number of columns
31893      */   
31894     
31895     maxCols: 0,
31896     /**
31897      * @cfg {Number} padHeight padding below box..
31898      */   
31899     
31900     padHeight : 10, 
31901     
31902     /**
31903      * @cfg {Boolean} isAutoInitial defalut true
31904      */   
31905     
31906     isAutoInitial : true, 
31907     
31908     // private?
31909     gutter : 0,
31910     
31911     containerWidth: 0,
31912     initialColumnWidth : 0,
31913     currentSize : null,
31914     
31915     colYs : null, // array.
31916     maxY : 0,
31917     padWidth: 10,
31918     
31919     
31920     tag: 'div',
31921     cls: '',
31922     bricks: null, //CompositeElement
31923     cols : 0, // array?
31924     // element : null, // wrapped now this.el
31925     _isLayoutInited : null, 
31926     
31927     
31928     getAutoCreate : function(){
31929         
31930         var cfg = {
31931             tag: this.tag,
31932             cls: 'blog-masonary-wrapper ' + this.cls,
31933             cn : {
31934                 cls : 'mas-boxes masonary'
31935             }
31936         };
31937         
31938         return cfg;
31939     },
31940     
31941     getChildContainer: function( )
31942     {
31943         if (this.boxesEl) {
31944             return this.boxesEl;
31945         }
31946         
31947         this.boxesEl = this.el.select('.mas-boxes').first();
31948         
31949         return this.boxesEl;
31950     },
31951     
31952     
31953     initEvents : function()
31954     {
31955         var _this = this;
31956         
31957         if(this.isAutoInitial){
31958             Roo.log('hook children rendered');
31959             this.on('childrenrendered', function() {
31960                 Roo.log('children rendered');
31961                 _this.initial();
31962             } ,this);
31963         }
31964         
31965     },
31966     
31967     initial : function()
31968     {
31969         this.reloadItems();
31970
31971         this.currentSize = this.el.getBox(true);
31972
31973         /// was window resize... - let's see if this works..
31974         Roo.EventManager.onWindowResize(this.resize, this); 
31975
31976         if(!this.isAutoInitial){
31977             this.layout();
31978             return;
31979         }
31980         
31981         this.layout.defer(500,this);
31982     },
31983     
31984     reloadItems: function()
31985     {
31986         this.bricks = this.el.select('.masonry-brick', true);
31987         
31988         this.bricks.each(function(b) {
31989             //Roo.log(b.getSize());
31990             if (!b.attr('originalwidth')) {
31991                 b.attr('originalwidth',  b.getSize().width);
31992             }
31993             
31994         });
31995         
31996         Roo.log(this.bricks.elements.length);
31997     },
31998     
31999     resize : function()
32000     {
32001         Roo.log('resize');
32002         var cs = this.el.getBox(true);
32003         
32004         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32005             Roo.log("no change in with or X");
32006             return;
32007         }
32008         this.currentSize = cs;
32009         this.layout();
32010     },
32011     
32012     layout : function()
32013     {
32014          Roo.log('layout');
32015         this._resetLayout();
32016         //this._manageStamps();
32017       
32018         // don't animate first layout
32019         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32020         this.layoutItems( isInstant );
32021       
32022         // flag for initalized
32023         this._isLayoutInited = true;
32024     },
32025     
32026     layoutItems : function( isInstant )
32027     {
32028         //var items = this._getItemsForLayout( this.items );
32029         // original code supports filtering layout items.. we just ignore it..
32030         
32031         this._layoutItems( this.bricks , isInstant );
32032       
32033         this._postLayout();
32034     },
32035     _layoutItems : function ( items , isInstant)
32036     {
32037        //this.fireEvent( 'layout', this, items );
32038     
32039
32040         if ( !items || !items.elements.length ) {
32041           // no items, emit event with empty array
32042             return;
32043         }
32044
32045         var queue = [];
32046         items.each(function(item) {
32047             Roo.log("layout item");
32048             Roo.log(item);
32049             // get x/y object from method
32050             var position = this._getItemLayoutPosition( item );
32051             // enqueue
32052             position.item = item;
32053             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32054             queue.push( position );
32055         }, this);
32056       
32057         this._processLayoutQueue( queue );
32058     },
32059     /** Sets position of item in DOM
32060     * @param {Element} item
32061     * @param {Number} x - horizontal position
32062     * @param {Number} y - vertical position
32063     * @param {Boolean} isInstant - disables transitions
32064     */
32065     _processLayoutQueue : function( queue )
32066     {
32067         for ( var i=0, len = queue.length; i < len; i++ ) {
32068             var obj = queue[i];
32069             obj.item.position('absolute');
32070             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32071         }
32072     },
32073       
32074     
32075     /**
32076     * Any logic you want to do after each layout,
32077     * i.e. size the container
32078     */
32079     _postLayout : function()
32080     {
32081         this.resizeContainer();
32082     },
32083     
32084     resizeContainer : function()
32085     {
32086         if ( !this.isResizingContainer ) {
32087             return;
32088         }
32089         var size = this._getContainerSize();
32090         if ( size ) {
32091             this.el.setSize(size.width,size.height);
32092             this.boxesEl.setSize(size.width,size.height);
32093         }
32094     },
32095     
32096     
32097     
32098     _resetLayout : function()
32099     {
32100         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32101         this.colWidth = this.el.getWidth();
32102         //this.gutter = this.el.getWidth(); 
32103         
32104         this.measureColumns();
32105
32106         // reset column Y
32107         var i = this.cols;
32108         this.colYs = [];
32109         while (i--) {
32110             this.colYs.push( 0 );
32111         }
32112     
32113         this.maxY = 0;
32114     },
32115
32116     measureColumns : function()
32117     {
32118         this.getContainerWidth();
32119       // if columnWidth is 0, default to outerWidth of first item
32120         if ( !this.columnWidth ) {
32121             var firstItem = this.bricks.first();
32122             Roo.log(firstItem);
32123             this.columnWidth  = this.containerWidth;
32124             if (firstItem && firstItem.attr('originalwidth') ) {
32125                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32126             }
32127             // columnWidth fall back to item of first element
32128             Roo.log("set column width?");
32129                         this.initialColumnWidth = this.columnWidth  ;
32130
32131             // if first elem has no width, default to size of container
32132             
32133         }
32134         
32135         
32136         if (this.initialColumnWidth) {
32137             this.columnWidth = this.initialColumnWidth;
32138         }
32139         
32140         
32141             
32142         // column width is fixed at the top - however if container width get's smaller we should
32143         // reduce it...
32144         
32145         // this bit calcs how man columns..
32146             
32147         var columnWidth = this.columnWidth += this.gutter;
32148       
32149         // calculate columns
32150         var containerWidth = this.containerWidth + this.gutter;
32151         
32152         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32153         // fix rounding errors, typically with gutters
32154         var excess = columnWidth - containerWidth % columnWidth;
32155         
32156         
32157         // if overshoot is less than a pixel, round up, otherwise floor it
32158         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32159         cols = Math[ mathMethod ]( cols );
32160         this.cols = Math.max( cols, 1 );
32161         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32162         
32163          // padding positioning..
32164         var totalColWidth = this.cols * this.columnWidth;
32165         var padavail = this.containerWidth - totalColWidth;
32166         // so for 2 columns - we need 3 'pads'
32167         
32168         var padNeeded = (1+this.cols) * this.padWidth;
32169         
32170         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32171         
32172         this.columnWidth += padExtra
32173         //this.padWidth = Math.floor(padavail /  ( this.cols));
32174         
32175         // adjust colum width so that padding is fixed??
32176         
32177         // we have 3 columns ... total = width * 3
32178         // we have X left over... that should be used by 
32179         
32180         //if (this.expandC) {
32181             
32182         //}
32183         
32184         
32185         
32186     },
32187     
32188     getContainerWidth : function()
32189     {
32190        /* // container is parent if fit width
32191         var container = this.isFitWidth ? this.element.parentNode : this.element;
32192         // check that this.size and size are there
32193         // IE8 triggers resize on body size change, so they might not be
32194         
32195         var size = getSize( container );  //FIXME
32196         this.containerWidth = size && size.innerWidth; //FIXME
32197         */
32198          
32199         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32200         
32201     },
32202     
32203     _getItemLayoutPosition : function( item )  // what is item?
32204     {
32205         // we resize the item to our columnWidth..
32206       
32207         item.setWidth(this.columnWidth);
32208         item.autoBoxAdjust  = false;
32209         
32210         var sz = item.getSize();
32211  
32212         // how many columns does this brick span
32213         var remainder = this.containerWidth % this.columnWidth;
32214         
32215         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32216         // round if off by 1 pixel, otherwise use ceil
32217         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32218         colSpan = Math.min( colSpan, this.cols );
32219         
32220         // normally this should be '1' as we dont' currently allow multi width columns..
32221         
32222         var colGroup = this._getColGroup( colSpan );
32223         // get the minimum Y value from the columns
32224         var minimumY = Math.min.apply( Math, colGroup );
32225         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32226         
32227         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32228          
32229         // position the brick
32230         var position = {
32231             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32232             y: this.currentSize.y + minimumY + this.padHeight
32233         };
32234         
32235         Roo.log(position);
32236         // apply setHeight to necessary columns
32237         var setHeight = minimumY + sz.height + this.padHeight;
32238         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32239         
32240         var setSpan = this.cols + 1 - colGroup.length;
32241         for ( var i = 0; i < setSpan; i++ ) {
32242           this.colYs[ shortColIndex + i ] = setHeight ;
32243         }
32244       
32245         return position;
32246     },
32247     
32248     /**
32249      * @param {Number} colSpan - number of columns the element spans
32250      * @returns {Array} colGroup
32251      */
32252     _getColGroup : function( colSpan )
32253     {
32254         if ( colSpan < 2 ) {
32255           // if brick spans only one column, use all the column Ys
32256           return this.colYs;
32257         }
32258       
32259         var colGroup = [];
32260         // how many different places could this brick fit horizontally
32261         var groupCount = this.cols + 1 - colSpan;
32262         // for each group potential horizontal position
32263         for ( var i = 0; i < groupCount; i++ ) {
32264           // make an array of colY values for that one group
32265           var groupColYs = this.colYs.slice( i, i + colSpan );
32266           // and get the max value of the array
32267           colGroup[i] = Math.max.apply( Math, groupColYs );
32268         }
32269         return colGroup;
32270     },
32271     /*
32272     _manageStamp : function( stamp )
32273     {
32274         var stampSize =  stamp.getSize();
32275         var offset = stamp.getBox();
32276         // get the columns that this stamp affects
32277         var firstX = this.isOriginLeft ? offset.x : offset.right;
32278         var lastX = firstX + stampSize.width;
32279         var firstCol = Math.floor( firstX / this.columnWidth );
32280         firstCol = Math.max( 0, firstCol );
32281         
32282         var lastCol = Math.floor( lastX / this.columnWidth );
32283         // lastCol should not go over if multiple of columnWidth #425
32284         lastCol -= lastX % this.columnWidth ? 0 : 1;
32285         lastCol = Math.min( this.cols - 1, lastCol );
32286         
32287         // set colYs to bottom of the stamp
32288         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32289             stampSize.height;
32290             
32291         for ( var i = firstCol; i <= lastCol; i++ ) {
32292           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32293         }
32294     },
32295     */
32296     
32297     _getContainerSize : function()
32298     {
32299         this.maxY = Math.max.apply( Math, this.colYs );
32300         var size = {
32301             height: this.maxY
32302         };
32303       
32304         if ( this.isFitWidth ) {
32305             size.width = this._getContainerFitWidth();
32306         }
32307       
32308         return size;
32309     },
32310     
32311     _getContainerFitWidth : function()
32312     {
32313         var unusedCols = 0;
32314         // count unused columns
32315         var i = this.cols;
32316         while ( --i ) {
32317           if ( this.colYs[i] !== 0 ) {
32318             break;
32319           }
32320           unusedCols++;
32321         }
32322         // fit container to columns that have been used
32323         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32324     },
32325     
32326     needsResizeLayout : function()
32327     {
32328         var previousWidth = this.containerWidth;
32329         this.getContainerWidth();
32330         return previousWidth !== this.containerWidth;
32331     }
32332  
32333 });
32334
32335  
32336
32337  /*
32338  * - LGPL
32339  *
32340  * element
32341  * 
32342  */
32343
32344 /**
32345  * @class Roo.bootstrap.MasonryBrick
32346  * @extends Roo.bootstrap.Component
32347  * Bootstrap MasonryBrick class
32348  * 
32349  * @constructor
32350  * Create a new MasonryBrick
32351  * @param {Object} config The config object
32352  */
32353
32354 Roo.bootstrap.MasonryBrick = function(config){
32355     
32356     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32357     
32358     Roo.bootstrap.MasonryBrick.register(this);
32359     
32360     this.addEvents({
32361         // raw events
32362         /**
32363          * @event click
32364          * When a MasonryBrick is clcik
32365          * @param {Roo.bootstrap.MasonryBrick} this
32366          * @param {Roo.EventObject} e
32367          */
32368         "click" : true
32369     });
32370 };
32371
32372 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32373     
32374     /**
32375      * @cfg {String} title
32376      */   
32377     title : '',
32378     /**
32379      * @cfg {String} html
32380      */   
32381     html : '',
32382     /**
32383      * @cfg {String} bgimage
32384      */   
32385     bgimage : '',
32386     /**
32387      * @cfg {String} videourl
32388      */   
32389     videourl : '',
32390     /**
32391      * @cfg {String} cls
32392      */   
32393     cls : '',
32394     /**
32395      * @cfg {String} href
32396      */   
32397     href : '',
32398     /**
32399      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32400      */   
32401     size : 'xs',
32402     
32403     /**
32404      * @cfg {String} placetitle (center|bottom)
32405      */   
32406     placetitle : '',
32407     
32408     /**
32409      * @cfg {Boolean} isFitContainer defalut true
32410      */   
32411     isFitContainer : true, 
32412     
32413     /**
32414      * @cfg {Boolean} preventDefault defalut false
32415      */   
32416     preventDefault : false, 
32417     
32418     /**
32419      * @cfg {Boolean} inverse defalut false
32420      */   
32421     maskInverse : false, 
32422     
32423     getAutoCreate : function()
32424     {
32425         if(!this.isFitContainer){
32426             return this.getSplitAutoCreate();
32427         }
32428         
32429         var cls = 'masonry-brick masonry-brick-full';
32430         
32431         if(this.href.length){
32432             cls += ' masonry-brick-link';
32433         }
32434         
32435         if(this.bgimage.length){
32436             cls += ' masonry-brick-image';
32437         }
32438         
32439         if(this.maskInverse){
32440             cls += ' mask-inverse';
32441         }
32442         
32443         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32444             cls += ' enable-mask';
32445         }
32446         
32447         if(this.size){
32448             cls += ' masonry-' + this.size + '-brick';
32449         }
32450         
32451         if(this.placetitle.length){
32452             
32453             switch (this.placetitle) {
32454                 case 'center' :
32455                     cls += ' masonry-center-title';
32456                     break;
32457                 case 'bottom' :
32458                     cls += ' masonry-bottom-title';
32459                     break;
32460                 default:
32461                     break;
32462             }
32463             
32464         } else {
32465             if(!this.html.length && !this.bgimage.length){
32466                 cls += ' masonry-center-title';
32467             }
32468
32469             if(!this.html.length && this.bgimage.length){
32470                 cls += ' masonry-bottom-title';
32471             }
32472         }
32473         
32474         if(this.cls){
32475             cls += ' ' + this.cls;
32476         }
32477         
32478         var cfg = {
32479             tag: (this.href.length) ? 'a' : 'div',
32480             cls: cls,
32481             cn: [
32482                 {
32483                     tag: 'div',
32484                     cls: 'masonry-brick-mask'
32485                 },
32486                 {
32487                     tag: 'div',
32488                     cls: 'masonry-brick-paragraph',
32489                     cn: []
32490                 }
32491             ]
32492         };
32493         
32494         if(this.href.length){
32495             cfg.href = this.href;
32496         }
32497         
32498         var cn = cfg.cn[1].cn;
32499         
32500         if(this.title.length){
32501             cn.push({
32502                 tag: 'h4',
32503                 cls: 'masonry-brick-title',
32504                 html: this.title
32505             });
32506         }
32507         
32508         if(this.html.length){
32509             cn.push({
32510                 tag: 'p',
32511                 cls: 'masonry-brick-text',
32512                 html: this.html
32513             });
32514         }
32515         
32516         if (!this.title.length && !this.html.length) {
32517             cfg.cn[1].cls += ' hide';
32518         }
32519         
32520         if(this.bgimage.length){
32521             cfg.cn.push({
32522                 tag: 'img',
32523                 cls: 'masonry-brick-image-view',
32524                 src: this.bgimage
32525             });
32526         }
32527         
32528         if(this.videourl.length){
32529             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32530             // youtube support only?
32531             cfg.cn.push({
32532                 tag: 'iframe',
32533                 cls: 'masonry-brick-image-view',
32534                 src: vurl,
32535                 frameborder : 0,
32536                 allowfullscreen : true
32537             });
32538         }
32539         
32540         return cfg;
32541         
32542     },
32543     
32544     getSplitAutoCreate : function()
32545     {
32546         var cls = 'masonry-brick masonry-brick-split';
32547         
32548         if(this.href.length){
32549             cls += ' masonry-brick-link';
32550         }
32551         
32552         if(this.bgimage.length){
32553             cls += ' masonry-brick-image';
32554         }
32555         
32556         if(this.size){
32557             cls += ' masonry-' + this.size + '-brick';
32558         }
32559         
32560         switch (this.placetitle) {
32561             case 'center' :
32562                 cls += ' masonry-center-title';
32563                 break;
32564             case 'bottom' :
32565                 cls += ' masonry-bottom-title';
32566                 break;
32567             default:
32568                 if(!this.bgimage.length){
32569                     cls += ' masonry-center-title';
32570                 }
32571
32572                 if(this.bgimage.length){
32573                     cls += ' masonry-bottom-title';
32574                 }
32575                 break;
32576         }
32577         
32578         if(this.cls){
32579             cls += ' ' + this.cls;
32580         }
32581         
32582         var cfg = {
32583             tag: (this.href.length) ? 'a' : 'div',
32584             cls: cls,
32585             cn: [
32586                 {
32587                     tag: 'div',
32588                     cls: 'masonry-brick-split-head',
32589                     cn: [
32590                         {
32591                             tag: 'div',
32592                             cls: 'masonry-brick-paragraph',
32593                             cn: []
32594                         }
32595                     ]
32596                 },
32597                 {
32598                     tag: 'div',
32599                     cls: 'masonry-brick-split-body',
32600                     cn: []
32601                 }
32602             ]
32603         };
32604         
32605         if(this.href.length){
32606             cfg.href = this.href;
32607         }
32608         
32609         if(this.title.length){
32610             cfg.cn[0].cn[0].cn.push({
32611                 tag: 'h4',
32612                 cls: 'masonry-brick-title',
32613                 html: this.title
32614             });
32615         }
32616         
32617         if(this.html.length){
32618             cfg.cn[1].cn.push({
32619                 tag: 'p',
32620                 cls: 'masonry-brick-text',
32621                 html: this.html
32622             });
32623         }
32624
32625         if(this.bgimage.length){
32626             cfg.cn[0].cn.push({
32627                 tag: 'img',
32628                 cls: 'masonry-brick-image-view',
32629                 src: this.bgimage
32630             });
32631         }
32632         
32633         if(this.videourl.length){
32634             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32635             // youtube support only?
32636             cfg.cn[0].cn.cn.push({
32637                 tag: 'iframe',
32638                 cls: 'masonry-brick-image-view',
32639                 src: vurl,
32640                 frameborder : 0,
32641                 allowfullscreen : true
32642             });
32643         }
32644         
32645         return cfg;
32646     },
32647     
32648     initEvents: function() 
32649     {
32650         switch (this.size) {
32651             case 'xs' :
32652                 this.x = 1;
32653                 this.y = 1;
32654                 break;
32655             case 'sm' :
32656                 this.x = 2;
32657                 this.y = 2;
32658                 break;
32659             case 'md' :
32660             case 'md-left' :
32661             case 'md-right' :
32662                 this.x = 3;
32663                 this.y = 3;
32664                 break;
32665             case 'tall' :
32666                 this.x = 2;
32667                 this.y = 3;
32668                 break;
32669             case 'wide' :
32670                 this.x = 3;
32671                 this.y = 2;
32672                 break;
32673             case 'wide-thin' :
32674                 this.x = 3;
32675                 this.y = 1;
32676                 break;
32677                         
32678             default :
32679                 break;
32680         }
32681         
32682         if(Roo.isTouch){
32683             this.el.on('touchstart', this.onTouchStart, this);
32684             this.el.on('touchmove', this.onTouchMove, this);
32685             this.el.on('touchend', this.onTouchEnd, this);
32686             this.el.on('contextmenu', this.onContextMenu, this);
32687         } else {
32688             this.el.on('mouseenter'  ,this.enter, this);
32689             this.el.on('mouseleave', this.leave, this);
32690             this.el.on('click', this.onClick, this);
32691         }
32692         
32693         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32694             this.parent().bricks.push(this);   
32695         }
32696         
32697     },
32698     
32699     onClick: function(e, el)
32700     {
32701         var time = this.endTimer - this.startTimer;
32702         // Roo.log(e.preventDefault());
32703         if(Roo.isTouch){
32704             if(time > 1000){
32705                 e.preventDefault();
32706                 return;
32707             }
32708         }
32709         
32710         if(!this.preventDefault){
32711             return;
32712         }
32713         
32714         e.preventDefault();
32715         
32716         if (this.activcClass != '') {
32717             this.selectBrick();
32718         }
32719         
32720         this.fireEvent('click', this);
32721     },
32722     
32723     enter: function(e, el)
32724     {
32725         e.preventDefault();
32726         
32727         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32728             return;
32729         }
32730         
32731         if(this.bgimage.length && this.html.length){
32732             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32733         }
32734     },
32735     
32736     leave: function(e, el)
32737     {
32738         e.preventDefault();
32739         
32740         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32741             return;
32742         }
32743         
32744         if(this.bgimage.length && this.html.length){
32745             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32746         }
32747     },
32748     
32749     onTouchStart: function(e, el)
32750     {
32751 //        e.preventDefault();
32752         
32753         this.touchmoved = false;
32754         
32755         if(!this.isFitContainer){
32756             return;
32757         }
32758         
32759         if(!this.bgimage.length || !this.html.length){
32760             return;
32761         }
32762         
32763         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32764         
32765         this.timer = new Date().getTime();
32766         
32767     },
32768     
32769     onTouchMove: function(e, el)
32770     {
32771         this.touchmoved = true;
32772     },
32773     
32774     onContextMenu : function(e,el)
32775     {
32776         e.preventDefault();
32777         e.stopPropagation();
32778         return false;
32779     },
32780     
32781     onTouchEnd: function(e, el)
32782     {
32783 //        e.preventDefault();
32784         
32785         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32786         
32787             this.leave(e,el);
32788             
32789             return;
32790         }
32791         
32792         if(!this.bgimage.length || !this.html.length){
32793             
32794             if(this.href.length){
32795                 window.location.href = this.href;
32796             }
32797             
32798             return;
32799         }
32800         
32801         if(!this.isFitContainer){
32802             return;
32803         }
32804         
32805         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32806         
32807         window.location.href = this.href;
32808     },
32809     
32810     //selection on single brick only
32811     selectBrick : function() {
32812         
32813         if (!this.parentId) {
32814             return;
32815         }
32816         
32817         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32818         var index = m.selectedBrick.indexOf(this.id);
32819         
32820         if ( index > -1) {
32821             m.selectedBrick.splice(index,1);
32822             this.el.removeClass(this.activeClass);
32823             return;
32824         }
32825         
32826         for(var i = 0; i < m.selectedBrick.length; i++) {
32827             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32828             b.el.removeClass(b.activeClass);
32829         }
32830         
32831         m.selectedBrick = [];
32832         
32833         m.selectedBrick.push(this.id);
32834         this.el.addClass(this.activeClass);
32835         return;
32836     }
32837     
32838 });
32839
32840 Roo.apply(Roo.bootstrap.MasonryBrick, {
32841     
32842     //groups: {},
32843     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32844      /**
32845     * register a Masonry Brick
32846     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32847     */
32848     
32849     register : function(brick)
32850     {
32851         //this.groups[brick.id] = brick;
32852         this.groups.add(brick.id, brick);
32853     },
32854     /**
32855     * fetch a  masonry brick based on the masonry brick ID
32856     * @param {string} the masonry brick to add
32857     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32858     */
32859     
32860     get: function(brick_id) 
32861     {
32862         // if (typeof(this.groups[brick_id]) == 'undefined') {
32863         //     return false;
32864         // }
32865         // return this.groups[brick_id] ;
32866         
32867         if(this.groups.key(brick_id)) {
32868             return this.groups.key(brick_id);
32869         }
32870         
32871         return false;
32872     }
32873     
32874     
32875     
32876 });
32877
32878  /*
32879  * - LGPL
32880  *
32881  * element
32882  * 
32883  */
32884
32885 /**
32886  * @class Roo.bootstrap.Brick
32887  * @extends Roo.bootstrap.Component
32888  * Bootstrap Brick class
32889  * 
32890  * @constructor
32891  * Create a new Brick
32892  * @param {Object} config The config object
32893  */
32894
32895 Roo.bootstrap.Brick = function(config){
32896     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32897     
32898     this.addEvents({
32899         // raw events
32900         /**
32901          * @event click
32902          * When a Brick is click
32903          * @param {Roo.bootstrap.Brick} this
32904          * @param {Roo.EventObject} e
32905          */
32906         "click" : true
32907     });
32908 };
32909
32910 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32911     
32912     /**
32913      * @cfg {String} title
32914      */   
32915     title : '',
32916     /**
32917      * @cfg {String} html
32918      */   
32919     html : '',
32920     /**
32921      * @cfg {String} bgimage
32922      */   
32923     bgimage : '',
32924     /**
32925      * @cfg {String} cls
32926      */   
32927     cls : '',
32928     /**
32929      * @cfg {String} href
32930      */   
32931     href : '',
32932     /**
32933      * @cfg {String} video
32934      */   
32935     video : '',
32936     /**
32937      * @cfg {Boolean} square
32938      */   
32939     square : true,
32940     
32941     getAutoCreate : function()
32942     {
32943         var cls = 'roo-brick';
32944         
32945         if(this.href.length){
32946             cls += ' roo-brick-link';
32947         }
32948         
32949         if(this.bgimage.length){
32950             cls += ' roo-brick-image';
32951         }
32952         
32953         if(!this.html.length && !this.bgimage.length){
32954             cls += ' roo-brick-center-title';
32955         }
32956         
32957         if(!this.html.length && this.bgimage.length){
32958             cls += ' roo-brick-bottom-title';
32959         }
32960         
32961         if(this.cls){
32962             cls += ' ' + this.cls;
32963         }
32964         
32965         var cfg = {
32966             tag: (this.href.length) ? 'a' : 'div',
32967             cls: cls,
32968             cn: [
32969                 {
32970                     tag: 'div',
32971                     cls: 'roo-brick-paragraph',
32972                     cn: []
32973                 }
32974             ]
32975         };
32976         
32977         if(this.href.length){
32978             cfg.href = this.href;
32979         }
32980         
32981         var cn = cfg.cn[0].cn;
32982         
32983         if(this.title.length){
32984             cn.push({
32985                 tag: 'h4',
32986                 cls: 'roo-brick-title',
32987                 html: this.title
32988             });
32989         }
32990         
32991         if(this.html.length){
32992             cn.push({
32993                 tag: 'p',
32994                 cls: 'roo-brick-text',
32995                 html: this.html
32996             });
32997         } else {
32998             cn.cls += ' hide';
32999         }
33000         
33001         if(this.bgimage.length){
33002             cfg.cn.push({
33003                 tag: 'img',
33004                 cls: 'roo-brick-image-view',
33005                 src: this.bgimage
33006             });
33007         }
33008         
33009         return cfg;
33010     },
33011     
33012     initEvents: function() 
33013     {
33014         if(this.title.length || this.html.length){
33015             this.el.on('mouseenter'  ,this.enter, this);
33016             this.el.on('mouseleave', this.leave, this);
33017         }
33018         
33019         Roo.EventManager.onWindowResize(this.resize, this); 
33020         
33021         if(this.bgimage.length){
33022             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33023             this.imageEl.on('load', this.onImageLoad, this);
33024             return;
33025         }
33026         
33027         this.resize();
33028     },
33029     
33030     onImageLoad : function()
33031     {
33032         this.resize();
33033     },
33034     
33035     resize : function()
33036     {
33037         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33038         
33039         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33040         
33041         if(this.bgimage.length){
33042             var image = this.el.select('.roo-brick-image-view', true).first();
33043             
33044             image.setWidth(paragraph.getWidth());
33045             
33046             if(this.square){
33047                 image.setHeight(paragraph.getWidth());
33048             }
33049             
33050             this.el.setHeight(image.getHeight());
33051             paragraph.setHeight(image.getHeight());
33052             
33053         }
33054         
33055     },
33056     
33057     enter: function(e, el)
33058     {
33059         e.preventDefault();
33060         
33061         if(this.bgimage.length){
33062             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33063             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33064         }
33065     },
33066     
33067     leave: function(e, el)
33068     {
33069         e.preventDefault();
33070         
33071         if(this.bgimage.length){
33072             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33073             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33074         }
33075     }
33076     
33077 });
33078
33079  
33080
33081  /*
33082  * - LGPL
33083  *
33084  * Number field 
33085  */
33086
33087 /**
33088  * @class Roo.bootstrap.NumberField
33089  * @extends Roo.bootstrap.Input
33090  * Bootstrap NumberField class
33091  * 
33092  * 
33093  * 
33094  * 
33095  * @constructor
33096  * Create a new NumberField
33097  * @param {Object} config The config object
33098  */
33099
33100 Roo.bootstrap.NumberField = function(config){
33101     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33102 };
33103
33104 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33105     
33106     /**
33107      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33108      */
33109     allowDecimals : true,
33110     /**
33111      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33112      */
33113     decimalSeparator : ".",
33114     /**
33115      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33116      */
33117     decimalPrecision : 2,
33118     /**
33119      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33120      */
33121     allowNegative : true,
33122     
33123     /**
33124      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33125      */
33126     allowZero: true,
33127     /**
33128      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33129      */
33130     minValue : Number.NEGATIVE_INFINITY,
33131     /**
33132      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33133      */
33134     maxValue : Number.MAX_VALUE,
33135     /**
33136      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33137      */
33138     minText : "The minimum value for this field is {0}",
33139     /**
33140      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33141      */
33142     maxText : "The maximum value for this field is {0}",
33143     /**
33144      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33145      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33146      */
33147     nanText : "{0} is not a valid number",
33148     /**
33149      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33150      */
33151     castInt : true,
33152     /**
33153      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33154      */
33155     thousandsDelimiter : false,
33156     /**
33157      * @cfg {String} valueAlign alignment of value
33158      */
33159     valueAlign : "left",
33160
33161     getAutoCreate : function()
33162     {
33163         var hiddenInput = {
33164             tag: 'input',
33165             type: 'hidden',
33166             id: Roo.id(),
33167             cls: 'hidden-number-input'
33168         };
33169         
33170         if (this.name) {
33171             hiddenInput.name = this.name;
33172         }
33173         
33174         this.name = '';
33175         
33176         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33177         
33178         this.name = hiddenInput.name;
33179         
33180         if(cfg.cn.length > 0) {
33181             cfg.cn.push(hiddenInput);
33182         }
33183         
33184         return cfg;
33185     },
33186
33187     // private
33188     initEvents : function()
33189     {   
33190         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33191         
33192         var allowed = "0123456789";
33193         
33194         if(this.allowDecimals){
33195             allowed += this.decimalSeparator;
33196         }
33197         
33198         if(this.allowNegative){
33199             allowed += "-";
33200         }
33201         
33202         if(this.thousandsDelimiter) {
33203             allowed += ",";
33204         }
33205         
33206         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33207         
33208         var keyPress = function(e){
33209             
33210             var k = e.getKey();
33211             
33212             var c = e.getCharCode();
33213             
33214             if(
33215                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33216                     allowed.indexOf(String.fromCharCode(c)) === -1
33217             ){
33218                 e.stopEvent();
33219                 return;
33220             }
33221             
33222             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33223                 return;
33224             }
33225             
33226             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33227                 e.stopEvent();
33228             }
33229         };
33230         
33231         this.el.on("keypress", keyPress, this);
33232     },
33233     
33234     validateValue : function(value)
33235     {
33236         
33237         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33238             return false;
33239         }
33240         
33241         var num = this.parseValue(value);
33242         
33243         if(isNaN(num)){
33244             this.markInvalid(String.format(this.nanText, value));
33245             return false;
33246         }
33247         
33248         if(num < this.minValue){
33249             this.markInvalid(String.format(this.minText, this.minValue));
33250             return false;
33251         }
33252         
33253         if(num > this.maxValue){
33254             this.markInvalid(String.format(this.maxText, this.maxValue));
33255             return false;
33256         }
33257         
33258         return true;
33259     },
33260
33261     getValue : function()
33262     {
33263         var v = this.hiddenEl().getValue();
33264         
33265         return this.fixPrecision(this.parseValue(v));
33266     },
33267
33268     parseValue : function(value)
33269     {
33270         if(this.thousandsDelimiter) {
33271             value += "";
33272             r = new RegExp(",", "g");
33273             value = value.replace(r, "");
33274         }
33275         
33276         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33277         return isNaN(value) ? '' : value;
33278     },
33279
33280     fixPrecision : function(value)
33281     {
33282         if(this.thousandsDelimiter) {
33283             value += "";
33284             r = new RegExp(",", "g");
33285             value = value.replace(r, "");
33286         }
33287         
33288         var nan = isNaN(value);
33289         
33290         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33291             return nan ? '' : value;
33292         }
33293         return parseFloat(value).toFixed(this.decimalPrecision);
33294     },
33295
33296     setValue : function(v)
33297     {
33298         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33299         
33300         this.value = v;
33301         
33302         if(this.rendered){
33303             
33304             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33305             
33306             this.inputEl().dom.value = (v == '') ? '' :
33307                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33308             
33309             if(!this.allowZero && v === '0') {
33310                 this.hiddenEl().dom.value = '';
33311                 this.inputEl().dom.value = '';
33312             }
33313             
33314             this.validate();
33315         }
33316     },
33317
33318     decimalPrecisionFcn : function(v)
33319     {
33320         return Math.floor(v);
33321     },
33322
33323     beforeBlur : function()
33324     {
33325         if(!this.castInt){
33326             return;
33327         }
33328         
33329         var v = this.parseValue(this.getRawValue());
33330         
33331         if(v || v === 0){
33332             this.setValue(v);
33333         }
33334     },
33335     
33336     hiddenEl : function()
33337     {
33338         return this.el.select('input.hidden-number-input',true).first();
33339     }
33340     
33341 });
33342
33343  
33344
33345 /*
33346 * Licence: LGPL
33347 */
33348
33349 /**
33350  * @class Roo.bootstrap.DocumentSlider
33351  * @extends Roo.bootstrap.Component
33352  * Bootstrap DocumentSlider class
33353  * 
33354  * @constructor
33355  * Create a new DocumentViewer
33356  * @param {Object} config The config object
33357  */
33358
33359 Roo.bootstrap.DocumentSlider = function(config){
33360     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33361     
33362     this.files = [];
33363     
33364     this.addEvents({
33365         /**
33366          * @event initial
33367          * Fire after initEvent
33368          * @param {Roo.bootstrap.DocumentSlider} this
33369          */
33370         "initial" : true,
33371         /**
33372          * @event update
33373          * Fire after update
33374          * @param {Roo.bootstrap.DocumentSlider} this
33375          */
33376         "update" : true,
33377         /**
33378          * @event click
33379          * Fire after click
33380          * @param {Roo.bootstrap.DocumentSlider} this
33381          */
33382         "click" : true
33383     });
33384 };
33385
33386 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33387     
33388     files : false,
33389     
33390     indicator : 0,
33391     
33392     getAutoCreate : function()
33393     {
33394         var cfg = {
33395             tag : 'div',
33396             cls : 'roo-document-slider',
33397             cn : [
33398                 {
33399                     tag : 'div',
33400                     cls : 'roo-document-slider-header',
33401                     cn : [
33402                         {
33403                             tag : 'div',
33404                             cls : 'roo-document-slider-header-title'
33405                         }
33406                     ]
33407                 },
33408                 {
33409                     tag : 'div',
33410                     cls : 'roo-document-slider-body',
33411                     cn : [
33412                         {
33413                             tag : 'div',
33414                             cls : 'roo-document-slider-prev',
33415                             cn : [
33416                                 {
33417                                     tag : 'i',
33418                                     cls : 'fa fa-chevron-left'
33419                                 }
33420                             ]
33421                         },
33422                         {
33423                             tag : 'div',
33424                             cls : 'roo-document-slider-thumb',
33425                             cn : [
33426                                 {
33427                                     tag : 'img',
33428                                     cls : 'roo-document-slider-image'
33429                                 }
33430                             ]
33431                         },
33432                         {
33433                             tag : 'div',
33434                             cls : 'roo-document-slider-next',
33435                             cn : [
33436                                 {
33437                                     tag : 'i',
33438                                     cls : 'fa fa-chevron-right'
33439                                 }
33440                             ]
33441                         }
33442                     ]
33443                 }
33444             ]
33445         };
33446         
33447         return cfg;
33448     },
33449     
33450     initEvents : function()
33451     {
33452         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33453         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33454         
33455         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33456         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33457         
33458         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33459         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33460         
33461         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33462         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33463         
33464         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33465         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33466         
33467         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33468         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33469         
33470         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33471         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33472         
33473         this.thumbEl.on('click', this.onClick, this);
33474         
33475         this.prevIndicator.on('click', this.prev, this);
33476         
33477         this.nextIndicator.on('click', this.next, this);
33478         
33479     },
33480     
33481     initial : function()
33482     {
33483         if(this.files.length){
33484             this.indicator = 1;
33485             this.update()
33486         }
33487         
33488         this.fireEvent('initial', this);
33489     },
33490     
33491     update : function()
33492     {
33493         this.imageEl.attr('src', this.files[this.indicator - 1]);
33494         
33495         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33496         
33497         this.prevIndicator.show();
33498         
33499         if(this.indicator == 1){
33500             this.prevIndicator.hide();
33501         }
33502         
33503         this.nextIndicator.show();
33504         
33505         if(this.indicator == this.files.length){
33506             this.nextIndicator.hide();
33507         }
33508         
33509         this.thumbEl.scrollTo('top');
33510         
33511         this.fireEvent('update', this);
33512     },
33513     
33514     onClick : function(e)
33515     {
33516         e.preventDefault();
33517         
33518         this.fireEvent('click', this);
33519     },
33520     
33521     prev : function(e)
33522     {
33523         e.preventDefault();
33524         
33525         this.indicator = Math.max(1, this.indicator - 1);
33526         
33527         this.update();
33528     },
33529     
33530     next : function(e)
33531     {
33532         e.preventDefault();
33533         
33534         this.indicator = Math.min(this.files.length, this.indicator + 1);
33535         
33536         this.update();
33537     }
33538 });
33539 /*
33540  * - LGPL
33541  *
33542  * RadioSet
33543  *
33544  *
33545  */
33546
33547 /**
33548  * @class Roo.bootstrap.RadioSet
33549  * @extends Roo.bootstrap.Input
33550  * Bootstrap RadioSet class
33551  * @cfg {String} indicatorpos (left|right) default left
33552  * @cfg {Boolean} inline (true|false) inline the element (default true)
33553  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33554  * @constructor
33555  * Create a new RadioSet
33556  * @param {Object} config The config object
33557  */
33558
33559 Roo.bootstrap.RadioSet = function(config){
33560     
33561     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33562     
33563     this.radioes = [];
33564     
33565     Roo.bootstrap.RadioSet.register(this);
33566     
33567     this.addEvents({
33568         /**
33569         * @event check
33570         * Fires when the element is checked or unchecked.
33571         * @param {Roo.bootstrap.RadioSet} this This radio
33572         * @param {Roo.bootstrap.Radio} item The checked item
33573         */
33574        check : true,
33575        /**
33576         * @event click
33577         * Fires when the element is click.
33578         * @param {Roo.bootstrap.RadioSet} this This radio set
33579         * @param {Roo.bootstrap.Radio} item The checked item
33580         * @param {Roo.EventObject} e The event object
33581         */
33582        click : true
33583     });
33584     
33585 };
33586
33587 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33588
33589     radioes : false,
33590     
33591     inline : true,
33592     
33593     weight : '',
33594     
33595     indicatorpos : 'left',
33596     
33597     getAutoCreate : function()
33598     {
33599         var label = {
33600             tag : 'label',
33601             cls : 'roo-radio-set-label',
33602             cn : [
33603                 {
33604                     tag : 'span',
33605                     html : this.fieldLabel
33606                 }
33607             ]
33608         };
33609         
33610         if(this.indicatorpos == 'left'){
33611             label.cn.unshift({
33612                 tag : 'i',
33613                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33614                 tooltip : 'This field is required'
33615             });
33616         } else {
33617             label.cn.push({
33618                 tag : 'i',
33619                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33620                 tooltip : 'This field is required'
33621             });
33622         }
33623         
33624         var items = {
33625             tag : 'div',
33626             cls : 'roo-radio-set-items'
33627         };
33628         
33629         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33630         
33631         if (align === 'left' && this.fieldLabel.length) {
33632             
33633             items = {
33634                 cls : "roo-radio-set-right", 
33635                 cn: [
33636                     items
33637                 ]
33638             };
33639             
33640             if(this.labelWidth > 12){
33641                 label.style = "width: " + this.labelWidth + 'px';
33642             }
33643             
33644             if(this.labelWidth < 13 && this.labelmd == 0){
33645                 this.labelmd = this.labelWidth;
33646             }
33647             
33648             if(this.labellg > 0){
33649                 label.cls += ' col-lg-' + this.labellg;
33650                 items.cls += ' col-lg-' + (12 - this.labellg);
33651             }
33652             
33653             if(this.labelmd > 0){
33654                 label.cls += ' col-md-' + this.labelmd;
33655                 items.cls += ' col-md-' + (12 - this.labelmd);
33656             }
33657             
33658             if(this.labelsm > 0){
33659                 label.cls += ' col-sm-' + this.labelsm;
33660                 items.cls += ' col-sm-' + (12 - this.labelsm);
33661             }
33662             
33663             if(this.labelxs > 0){
33664                 label.cls += ' col-xs-' + this.labelxs;
33665                 items.cls += ' col-xs-' + (12 - this.labelxs);
33666             }
33667         }
33668         
33669         var cfg = {
33670             tag : 'div',
33671             cls : 'roo-radio-set',
33672             cn : [
33673                 {
33674                     tag : 'input',
33675                     cls : 'roo-radio-set-input',
33676                     type : 'hidden',
33677                     name : this.name,
33678                     value : this.value ? this.value :  ''
33679                 },
33680                 label,
33681                 items
33682             ]
33683         };
33684         
33685         if(this.weight.length){
33686             cfg.cls += ' roo-radio-' + this.weight;
33687         }
33688         
33689         if(this.inline) {
33690             cfg.cls += ' roo-radio-set-inline';
33691         }
33692         
33693         var settings=this;
33694         ['xs','sm','md','lg'].map(function(size){
33695             if (settings[size]) {
33696                 cfg.cls += ' col-' + size + '-' + settings[size];
33697             }
33698         });
33699         
33700         return cfg;
33701         
33702     },
33703
33704     initEvents : function()
33705     {
33706         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33707         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33708         
33709         if(!this.fieldLabel.length){
33710             this.labelEl.hide();
33711         }
33712         
33713         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33714         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33715         
33716         this.indicatorEl().addClass('invisible');
33717         
33718         this.originalValue = this.getValue();
33719         
33720     },
33721     
33722     inputEl: function ()
33723     {
33724         return this.el.select('.roo-radio-set-input', true).first();
33725     },
33726     
33727     getChildContainer : function()
33728     {
33729         return this.itemsEl;
33730     },
33731     
33732     register : function(item)
33733     {
33734         this.radioes.push(item);
33735         
33736     },
33737     
33738     validate : function()
33739     {   
33740         if(this.getVisibilityEl().hasClass('hidden')){
33741             return true;
33742         }
33743         
33744         var valid = false;
33745         
33746         Roo.each(this.radioes, function(i){
33747             if(!i.checked){
33748                 return;
33749             }
33750             
33751             valid = true;
33752             return false;
33753         });
33754         
33755         if(this.allowBlank) {
33756             return true;
33757         }
33758         
33759         if(this.disabled || valid){
33760             this.markValid();
33761             return true;
33762         }
33763         
33764         this.markInvalid();
33765         return false;
33766         
33767     },
33768     
33769     markValid : function()
33770     {
33771         if(this.labelEl.isVisible(true)){
33772             this.indicatorEl().removeClass('visible');
33773             this.indicatorEl().addClass('invisible');
33774         }
33775         
33776         this.el.removeClass([this.invalidClass, this.validClass]);
33777         this.el.addClass(this.validClass);
33778         
33779         this.fireEvent('valid', this);
33780     },
33781     
33782     markInvalid : function(msg)
33783     {
33784         if(this.allowBlank || this.disabled){
33785             return;
33786         }
33787         
33788         if(this.labelEl.isVisible(true)){
33789             this.indicatorEl().removeClass('invisible');
33790             this.indicatorEl().addClass('visible');
33791         }
33792         
33793         this.el.removeClass([this.invalidClass, this.validClass]);
33794         this.el.addClass(this.invalidClass);
33795         
33796         this.fireEvent('invalid', this, msg);
33797         
33798     },
33799     
33800     setValue : function(v, suppressEvent)
33801     {   
33802         if(this.value === v){
33803             return;
33804         }
33805         
33806         this.value = v;
33807         
33808         if(this.rendered){
33809             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33810         }
33811         
33812         Roo.each(this.radioes, function(i){
33813             i.checked = false;
33814             i.el.removeClass('checked');
33815         });
33816         
33817         Roo.each(this.radioes, function(i){
33818             
33819             if(i.value === v || i.value.toString() === v.toString()){
33820                 i.checked = true;
33821                 i.el.addClass('checked');
33822                 
33823                 if(suppressEvent !== true){
33824                     this.fireEvent('check', this, i);
33825                 }
33826                 
33827                 return false;
33828             }
33829             
33830         }, this);
33831         
33832         this.validate();
33833     },
33834     
33835     clearInvalid : function(){
33836         
33837         if(!this.el || this.preventMark){
33838             return;
33839         }
33840         
33841         this.el.removeClass([this.invalidClass]);
33842         
33843         this.fireEvent('valid', this);
33844     }
33845     
33846 });
33847
33848 Roo.apply(Roo.bootstrap.RadioSet, {
33849     
33850     groups: {},
33851     
33852     register : function(set)
33853     {
33854         this.groups[set.name] = set;
33855     },
33856     
33857     get: function(name) 
33858     {
33859         if (typeof(this.groups[name]) == 'undefined') {
33860             return false;
33861         }
33862         
33863         return this.groups[name] ;
33864     }
33865     
33866 });
33867 /*
33868  * Based on:
33869  * Ext JS Library 1.1.1
33870  * Copyright(c) 2006-2007, Ext JS, LLC.
33871  *
33872  * Originally Released Under LGPL - original licence link has changed is not relivant.
33873  *
33874  * Fork - LGPL
33875  * <script type="text/javascript">
33876  */
33877
33878
33879 /**
33880  * @class Roo.bootstrap.SplitBar
33881  * @extends Roo.util.Observable
33882  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33883  * <br><br>
33884  * Usage:
33885  * <pre><code>
33886 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33887                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33888 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33889 split.minSize = 100;
33890 split.maxSize = 600;
33891 split.animate = true;
33892 split.on('moved', splitterMoved);
33893 </code></pre>
33894  * @constructor
33895  * Create a new SplitBar
33896  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33897  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33898  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33899  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33900                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33901                         position of the SplitBar).
33902  */
33903 Roo.bootstrap.SplitBar = function(cfg){
33904     
33905     /** @private */
33906     
33907     //{
33908     //  dragElement : elm
33909     //  resizingElement: el,
33910         // optional..
33911     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33912     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33913         // existingProxy ???
33914     //}
33915     
33916     this.el = Roo.get(cfg.dragElement, true);
33917     this.el.dom.unselectable = "on";
33918     /** @private */
33919     this.resizingEl = Roo.get(cfg.resizingElement, true);
33920
33921     /**
33922      * @private
33923      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33924      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33925      * @type Number
33926      */
33927     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33928     
33929     /**
33930      * The minimum size of the resizing element. (Defaults to 0)
33931      * @type Number
33932      */
33933     this.minSize = 0;
33934     
33935     /**
33936      * The maximum size of the resizing element. (Defaults to 2000)
33937      * @type Number
33938      */
33939     this.maxSize = 2000;
33940     
33941     /**
33942      * Whether to animate the transition to the new size
33943      * @type Boolean
33944      */
33945     this.animate = false;
33946     
33947     /**
33948      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33949      * @type Boolean
33950      */
33951     this.useShim = false;
33952     
33953     /** @private */
33954     this.shim = null;
33955     
33956     if(!cfg.existingProxy){
33957         /** @private */
33958         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33959     }else{
33960         this.proxy = Roo.get(cfg.existingProxy).dom;
33961     }
33962     /** @private */
33963     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33964     
33965     /** @private */
33966     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33967     
33968     /** @private */
33969     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33970     
33971     /** @private */
33972     this.dragSpecs = {};
33973     
33974     /**
33975      * @private The adapter to use to positon and resize elements
33976      */
33977     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33978     this.adapter.init(this);
33979     
33980     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33981         /** @private */
33982         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33983         this.el.addClass("roo-splitbar-h");
33984     }else{
33985         /** @private */
33986         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33987         this.el.addClass("roo-splitbar-v");
33988     }
33989     
33990     this.addEvents({
33991         /**
33992          * @event resize
33993          * Fires when the splitter is moved (alias for {@link #event-moved})
33994          * @param {Roo.bootstrap.SplitBar} this
33995          * @param {Number} newSize the new width or height
33996          */
33997         "resize" : true,
33998         /**
33999          * @event moved
34000          * Fires when the splitter is moved
34001          * @param {Roo.bootstrap.SplitBar} this
34002          * @param {Number} newSize the new width or height
34003          */
34004         "moved" : true,
34005         /**
34006          * @event beforeresize
34007          * Fires before the splitter is dragged
34008          * @param {Roo.bootstrap.SplitBar} this
34009          */
34010         "beforeresize" : true,
34011
34012         "beforeapply" : true
34013     });
34014
34015     Roo.util.Observable.call(this);
34016 };
34017
34018 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34019     onStartProxyDrag : function(x, y){
34020         this.fireEvent("beforeresize", this);
34021         if(!this.overlay){
34022             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34023             o.unselectable();
34024             o.enableDisplayMode("block");
34025             // all splitbars share the same overlay
34026             Roo.bootstrap.SplitBar.prototype.overlay = o;
34027         }
34028         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34029         this.overlay.show();
34030         Roo.get(this.proxy).setDisplayed("block");
34031         var size = this.adapter.getElementSize(this);
34032         this.activeMinSize = this.getMinimumSize();;
34033         this.activeMaxSize = this.getMaximumSize();;
34034         var c1 = size - this.activeMinSize;
34035         var c2 = Math.max(this.activeMaxSize - size, 0);
34036         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34037             this.dd.resetConstraints();
34038             this.dd.setXConstraint(
34039                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34040                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34041             );
34042             this.dd.setYConstraint(0, 0);
34043         }else{
34044             this.dd.resetConstraints();
34045             this.dd.setXConstraint(0, 0);
34046             this.dd.setYConstraint(
34047                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34048                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34049             );
34050          }
34051         this.dragSpecs.startSize = size;
34052         this.dragSpecs.startPoint = [x, y];
34053         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34054     },
34055     
34056     /** 
34057      * @private Called after the drag operation by the DDProxy
34058      */
34059     onEndProxyDrag : function(e){
34060         Roo.get(this.proxy).setDisplayed(false);
34061         var endPoint = Roo.lib.Event.getXY(e);
34062         if(this.overlay){
34063             this.overlay.hide();
34064         }
34065         var newSize;
34066         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34067             newSize = this.dragSpecs.startSize + 
34068                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34069                     endPoint[0] - this.dragSpecs.startPoint[0] :
34070                     this.dragSpecs.startPoint[0] - endPoint[0]
34071                 );
34072         }else{
34073             newSize = this.dragSpecs.startSize + 
34074                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34075                     endPoint[1] - this.dragSpecs.startPoint[1] :
34076                     this.dragSpecs.startPoint[1] - endPoint[1]
34077                 );
34078         }
34079         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34080         if(newSize != this.dragSpecs.startSize){
34081             if(this.fireEvent('beforeapply', this, newSize) !== false){
34082                 this.adapter.setElementSize(this, newSize);
34083                 this.fireEvent("moved", this, newSize);
34084                 this.fireEvent("resize", this, newSize);
34085             }
34086         }
34087     },
34088     
34089     /**
34090      * Get the adapter this SplitBar uses
34091      * @return The adapter object
34092      */
34093     getAdapter : function(){
34094         return this.adapter;
34095     },
34096     
34097     /**
34098      * Set the adapter this SplitBar uses
34099      * @param {Object} adapter A SplitBar adapter object
34100      */
34101     setAdapter : function(adapter){
34102         this.adapter = adapter;
34103         this.adapter.init(this);
34104     },
34105     
34106     /**
34107      * Gets the minimum size for the resizing element
34108      * @return {Number} The minimum size
34109      */
34110     getMinimumSize : function(){
34111         return this.minSize;
34112     },
34113     
34114     /**
34115      * Sets the minimum size for the resizing element
34116      * @param {Number} minSize The minimum size
34117      */
34118     setMinimumSize : function(minSize){
34119         this.minSize = minSize;
34120     },
34121     
34122     /**
34123      * Gets the maximum size for the resizing element
34124      * @return {Number} The maximum size
34125      */
34126     getMaximumSize : function(){
34127         return this.maxSize;
34128     },
34129     
34130     /**
34131      * Sets the maximum size for the resizing element
34132      * @param {Number} maxSize The maximum size
34133      */
34134     setMaximumSize : function(maxSize){
34135         this.maxSize = maxSize;
34136     },
34137     
34138     /**
34139      * Sets the initialize size for the resizing element
34140      * @param {Number} size The initial size
34141      */
34142     setCurrentSize : function(size){
34143         var oldAnimate = this.animate;
34144         this.animate = false;
34145         this.adapter.setElementSize(this, size);
34146         this.animate = oldAnimate;
34147     },
34148     
34149     /**
34150      * Destroy this splitbar. 
34151      * @param {Boolean} removeEl True to remove the element
34152      */
34153     destroy : function(removeEl){
34154         if(this.shim){
34155             this.shim.remove();
34156         }
34157         this.dd.unreg();
34158         this.proxy.parentNode.removeChild(this.proxy);
34159         if(removeEl){
34160             this.el.remove();
34161         }
34162     }
34163 });
34164
34165 /**
34166  * @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.
34167  */
34168 Roo.bootstrap.SplitBar.createProxy = function(dir){
34169     var proxy = new Roo.Element(document.createElement("div"));
34170     proxy.unselectable();
34171     var cls = 'roo-splitbar-proxy';
34172     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34173     document.body.appendChild(proxy.dom);
34174     return proxy.dom;
34175 };
34176
34177 /** 
34178  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34179  * Default Adapter. It assumes the splitter and resizing element are not positioned
34180  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34181  */
34182 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34183 };
34184
34185 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34186     // do nothing for now
34187     init : function(s){
34188     
34189     },
34190     /**
34191      * Called before drag operations to get the current size of the resizing element. 
34192      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34193      */
34194      getElementSize : function(s){
34195         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34196             return s.resizingEl.getWidth();
34197         }else{
34198             return s.resizingEl.getHeight();
34199         }
34200     },
34201     
34202     /**
34203      * Called after drag operations to set the size of the resizing element.
34204      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34205      * @param {Number} newSize The new size to set
34206      * @param {Function} onComplete A function to be invoked when resizing is complete
34207      */
34208     setElementSize : function(s, newSize, onComplete){
34209         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34210             if(!s.animate){
34211                 s.resizingEl.setWidth(newSize);
34212                 if(onComplete){
34213                     onComplete(s, newSize);
34214                 }
34215             }else{
34216                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34217             }
34218         }else{
34219             
34220             if(!s.animate){
34221                 s.resizingEl.setHeight(newSize);
34222                 if(onComplete){
34223                     onComplete(s, newSize);
34224                 }
34225             }else{
34226                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34227             }
34228         }
34229     }
34230 };
34231
34232 /** 
34233  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34234  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34235  * Adapter that  moves the splitter element to align with the resized sizing element. 
34236  * Used with an absolute positioned SplitBar.
34237  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34238  * document.body, make sure you assign an id to the body element.
34239  */
34240 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34241     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34242     this.container = Roo.get(container);
34243 };
34244
34245 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34246     init : function(s){
34247         this.basic.init(s);
34248     },
34249     
34250     getElementSize : function(s){
34251         return this.basic.getElementSize(s);
34252     },
34253     
34254     setElementSize : function(s, newSize, onComplete){
34255         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34256     },
34257     
34258     moveSplitter : function(s){
34259         var yes = Roo.bootstrap.SplitBar;
34260         switch(s.placement){
34261             case yes.LEFT:
34262                 s.el.setX(s.resizingEl.getRight());
34263                 break;
34264             case yes.RIGHT:
34265                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34266                 break;
34267             case yes.TOP:
34268                 s.el.setY(s.resizingEl.getBottom());
34269                 break;
34270             case yes.BOTTOM:
34271                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34272                 break;
34273         }
34274     }
34275 };
34276
34277 /**
34278  * Orientation constant - Create a vertical SplitBar
34279  * @static
34280  * @type Number
34281  */
34282 Roo.bootstrap.SplitBar.VERTICAL = 1;
34283
34284 /**
34285  * Orientation constant - Create a horizontal SplitBar
34286  * @static
34287  * @type Number
34288  */
34289 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34290
34291 /**
34292  * Placement constant - The resizing element is to the left of the splitter element
34293  * @static
34294  * @type Number
34295  */
34296 Roo.bootstrap.SplitBar.LEFT = 1;
34297
34298 /**
34299  * Placement constant - The resizing element is to the right of the splitter element
34300  * @static
34301  * @type Number
34302  */
34303 Roo.bootstrap.SplitBar.RIGHT = 2;
34304
34305 /**
34306  * Placement constant - The resizing element is positioned above the splitter element
34307  * @static
34308  * @type Number
34309  */
34310 Roo.bootstrap.SplitBar.TOP = 3;
34311
34312 /**
34313  * Placement constant - The resizing element is positioned under splitter element
34314  * @static
34315  * @type Number
34316  */
34317 Roo.bootstrap.SplitBar.BOTTOM = 4;
34318 Roo.namespace("Roo.bootstrap.layout");/*
34319  * Based on:
34320  * Ext JS Library 1.1.1
34321  * Copyright(c) 2006-2007, Ext JS, LLC.
34322  *
34323  * Originally Released Under LGPL - original licence link has changed is not relivant.
34324  *
34325  * Fork - LGPL
34326  * <script type="text/javascript">
34327  */
34328
34329 /**
34330  * @class Roo.bootstrap.layout.Manager
34331  * @extends Roo.bootstrap.Component
34332  * Base class for layout managers.
34333  */
34334 Roo.bootstrap.layout.Manager = function(config)
34335 {
34336     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34337
34338
34339
34340
34341
34342     /** false to disable window resize monitoring @type Boolean */
34343     this.monitorWindowResize = true;
34344     this.regions = {};
34345     this.addEvents({
34346         /**
34347          * @event layout
34348          * Fires when a layout is performed.
34349          * @param {Roo.LayoutManager} this
34350          */
34351         "layout" : true,
34352         /**
34353          * @event regionresized
34354          * Fires when the user resizes a region.
34355          * @param {Roo.LayoutRegion} region The resized region
34356          * @param {Number} newSize The new size (width for east/west, height for north/south)
34357          */
34358         "regionresized" : true,
34359         /**
34360          * @event regioncollapsed
34361          * Fires when a region is collapsed.
34362          * @param {Roo.LayoutRegion} region The collapsed region
34363          */
34364         "regioncollapsed" : true,
34365         /**
34366          * @event regionexpanded
34367          * Fires when a region is expanded.
34368          * @param {Roo.LayoutRegion} region The expanded region
34369          */
34370         "regionexpanded" : true
34371     });
34372     this.updating = false;
34373
34374     if (config.el) {
34375         this.el = Roo.get(config.el);
34376         this.initEvents();
34377     }
34378
34379 };
34380
34381 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34382
34383
34384     regions : null,
34385
34386     monitorWindowResize : true,
34387
34388
34389     updating : false,
34390
34391
34392     onRender : function(ct, position)
34393     {
34394         if(!this.el){
34395             this.el = Roo.get(ct);
34396             this.initEvents();
34397         }
34398         //this.fireEvent('render',this);
34399     },
34400
34401
34402     initEvents: function()
34403     {
34404
34405
34406         // ie scrollbar fix
34407         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34408             document.body.scroll = "no";
34409         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34410             this.el.position('relative');
34411         }
34412         this.id = this.el.id;
34413         this.el.addClass("roo-layout-container");
34414         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34415         if(this.el.dom != document.body ) {
34416             this.el.on('resize', this.layout,this);
34417             this.el.on('show', this.layout,this);
34418         }
34419
34420     },
34421
34422     /**
34423      * Returns true if this layout is currently being updated
34424      * @return {Boolean}
34425      */
34426     isUpdating : function(){
34427         return this.updating;
34428     },
34429
34430     /**
34431      * Suspend the LayoutManager from doing auto-layouts while
34432      * making multiple add or remove calls
34433      */
34434     beginUpdate : function(){
34435         this.updating = true;
34436     },
34437
34438     /**
34439      * Restore auto-layouts and optionally disable the manager from performing a layout
34440      * @param {Boolean} noLayout true to disable a layout update
34441      */
34442     endUpdate : function(noLayout){
34443         this.updating = false;
34444         if(!noLayout){
34445             this.layout();
34446         }
34447     },
34448
34449     layout: function(){
34450         // abstract...
34451     },
34452
34453     onRegionResized : function(region, newSize){
34454         this.fireEvent("regionresized", region, newSize);
34455         this.layout();
34456     },
34457
34458     onRegionCollapsed : function(region){
34459         this.fireEvent("regioncollapsed", region);
34460     },
34461
34462     onRegionExpanded : function(region){
34463         this.fireEvent("regionexpanded", region);
34464     },
34465
34466     /**
34467      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34468      * performs box-model adjustments.
34469      * @return {Object} The size as an object {width: (the width), height: (the height)}
34470      */
34471     getViewSize : function()
34472     {
34473         var size;
34474         if(this.el.dom != document.body){
34475             size = this.el.getSize();
34476         }else{
34477             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34478         }
34479         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34480         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34481         return size;
34482     },
34483
34484     /**
34485      * Returns the Element this layout is bound to.
34486      * @return {Roo.Element}
34487      */
34488     getEl : function(){
34489         return this.el;
34490     },
34491
34492     /**
34493      * Returns the specified region.
34494      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34495      * @return {Roo.LayoutRegion}
34496      */
34497     getRegion : function(target){
34498         return this.regions[target.toLowerCase()];
34499     },
34500
34501     onWindowResize : function(){
34502         if(this.monitorWindowResize){
34503             this.layout();
34504         }
34505     }
34506 });
34507 /*
34508  * Based on:
34509  * Ext JS Library 1.1.1
34510  * Copyright(c) 2006-2007, Ext JS, LLC.
34511  *
34512  * Originally Released Under LGPL - original licence link has changed is not relivant.
34513  *
34514  * Fork - LGPL
34515  * <script type="text/javascript">
34516  */
34517 /**
34518  * @class Roo.bootstrap.layout.Border
34519  * @extends Roo.bootstrap.layout.Manager
34520  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34521  * please see: examples/bootstrap/nested.html<br><br>
34522  
34523 <b>The container the layout is rendered into can be either the body element or any other element.
34524 If it is not the body element, the container needs to either be an absolute positioned element,
34525 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34526 the container size if it is not the body element.</b>
34527
34528 * @constructor
34529 * Create a new Border
34530 * @param {Object} config Configuration options
34531  */
34532 Roo.bootstrap.layout.Border = function(config){
34533     config = config || {};
34534     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34535     
34536     
34537     
34538     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34539         if(config[region]){
34540             config[region].region = region;
34541             this.addRegion(config[region]);
34542         }
34543     },this);
34544     
34545 };
34546
34547 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34548
34549 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34550     /**
34551      * Creates and adds a new region if it doesn't already exist.
34552      * @param {String} target The target region key (north, south, east, west or center).
34553      * @param {Object} config The regions config object
34554      * @return {BorderLayoutRegion} The new region
34555      */
34556     addRegion : function(config)
34557     {
34558         if(!this.regions[config.region]){
34559             var r = this.factory(config);
34560             this.bindRegion(r);
34561         }
34562         return this.regions[config.region];
34563     },
34564
34565     // private (kinda)
34566     bindRegion : function(r){
34567         this.regions[r.config.region] = r;
34568         
34569         r.on("visibilitychange",    this.layout, this);
34570         r.on("paneladded",          this.layout, this);
34571         r.on("panelremoved",        this.layout, this);
34572         r.on("invalidated",         this.layout, this);
34573         r.on("resized",             this.onRegionResized, this);
34574         r.on("collapsed",           this.onRegionCollapsed, this);
34575         r.on("expanded",            this.onRegionExpanded, this);
34576     },
34577
34578     /**
34579      * Performs a layout update.
34580      */
34581     layout : function()
34582     {
34583         if(this.updating) {
34584             return;
34585         }
34586         
34587         // render all the rebions if they have not been done alreayd?
34588         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34589             if(this.regions[region] && !this.regions[region].bodyEl){
34590                 this.regions[region].onRender(this.el)
34591             }
34592         },this);
34593         
34594         var size = this.getViewSize();
34595         var w = size.width;
34596         var h = size.height;
34597         var centerW = w;
34598         var centerH = h;
34599         var centerY = 0;
34600         var centerX = 0;
34601         //var x = 0, y = 0;
34602
34603         var rs = this.regions;
34604         var north = rs["north"];
34605         var south = rs["south"]; 
34606         var west = rs["west"];
34607         var east = rs["east"];
34608         var center = rs["center"];
34609         //if(this.hideOnLayout){ // not supported anymore
34610             //c.el.setStyle("display", "none");
34611         //}
34612         if(north && north.isVisible()){
34613             var b = north.getBox();
34614             var m = north.getMargins();
34615             b.width = w - (m.left+m.right);
34616             b.x = m.left;
34617             b.y = m.top;
34618             centerY = b.height + b.y + m.bottom;
34619             centerH -= centerY;
34620             north.updateBox(this.safeBox(b));
34621         }
34622         if(south && south.isVisible()){
34623             var b = south.getBox();
34624             var m = south.getMargins();
34625             b.width = w - (m.left+m.right);
34626             b.x = m.left;
34627             var totalHeight = (b.height + m.top + m.bottom);
34628             b.y = h - totalHeight + m.top;
34629             centerH -= totalHeight;
34630             south.updateBox(this.safeBox(b));
34631         }
34632         if(west && west.isVisible()){
34633             var b = west.getBox();
34634             var m = west.getMargins();
34635             b.height = centerH - (m.top+m.bottom);
34636             b.x = m.left;
34637             b.y = centerY + m.top;
34638             var totalWidth = (b.width + m.left + m.right);
34639             centerX += totalWidth;
34640             centerW -= totalWidth;
34641             west.updateBox(this.safeBox(b));
34642         }
34643         if(east && east.isVisible()){
34644             var b = east.getBox();
34645             var m = east.getMargins();
34646             b.height = centerH - (m.top+m.bottom);
34647             var totalWidth = (b.width + m.left + m.right);
34648             b.x = w - totalWidth + m.left;
34649             b.y = centerY + m.top;
34650             centerW -= totalWidth;
34651             east.updateBox(this.safeBox(b));
34652         }
34653         if(center){
34654             var m = center.getMargins();
34655             var centerBox = {
34656                 x: centerX + m.left,
34657                 y: centerY + m.top,
34658                 width: centerW - (m.left+m.right),
34659                 height: centerH - (m.top+m.bottom)
34660             };
34661             //if(this.hideOnLayout){
34662                 //center.el.setStyle("display", "block");
34663             //}
34664             center.updateBox(this.safeBox(centerBox));
34665         }
34666         this.el.repaint();
34667         this.fireEvent("layout", this);
34668     },
34669
34670     // private
34671     safeBox : function(box){
34672         box.width = Math.max(0, box.width);
34673         box.height = Math.max(0, box.height);
34674         return box;
34675     },
34676
34677     /**
34678      * Adds a ContentPanel (or subclass) to this layout.
34679      * @param {String} target The target region key (north, south, east, west or center).
34680      * @param {Roo.ContentPanel} panel The panel to add
34681      * @return {Roo.ContentPanel} The added panel
34682      */
34683     add : function(target, panel){
34684          
34685         target = target.toLowerCase();
34686         return this.regions[target].add(panel);
34687     },
34688
34689     /**
34690      * Remove a ContentPanel (or subclass) to this layout.
34691      * @param {String} target The target region key (north, south, east, west or center).
34692      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34693      * @return {Roo.ContentPanel} The removed panel
34694      */
34695     remove : function(target, panel){
34696         target = target.toLowerCase();
34697         return this.regions[target].remove(panel);
34698     },
34699
34700     /**
34701      * Searches all regions for a panel with the specified id
34702      * @param {String} panelId
34703      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34704      */
34705     findPanel : function(panelId){
34706         var rs = this.regions;
34707         for(var target in rs){
34708             if(typeof rs[target] != "function"){
34709                 var p = rs[target].getPanel(panelId);
34710                 if(p){
34711                     return p;
34712                 }
34713             }
34714         }
34715         return null;
34716     },
34717
34718     /**
34719      * Searches all regions for a panel with the specified id and activates (shows) it.
34720      * @param {String/ContentPanel} panelId The panels id or the panel itself
34721      * @return {Roo.ContentPanel} The shown panel or null
34722      */
34723     showPanel : function(panelId) {
34724       var rs = this.regions;
34725       for(var target in rs){
34726          var r = rs[target];
34727          if(typeof r != "function"){
34728             if(r.hasPanel(panelId)){
34729                return r.showPanel(panelId);
34730             }
34731          }
34732       }
34733       return null;
34734    },
34735
34736    /**
34737      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34738      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34739      */
34740    /*
34741     restoreState : function(provider){
34742         if(!provider){
34743             provider = Roo.state.Manager;
34744         }
34745         var sm = new Roo.LayoutStateManager();
34746         sm.init(this, provider);
34747     },
34748 */
34749  
34750  
34751     /**
34752      * Adds a xtype elements to the layout.
34753      * <pre><code>
34754
34755 layout.addxtype({
34756        xtype : 'ContentPanel',
34757        region: 'west',
34758        items: [ .... ]
34759    }
34760 );
34761
34762 layout.addxtype({
34763         xtype : 'NestedLayoutPanel',
34764         region: 'west',
34765         layout: {
34766            center: { },
34767            west: { }   
34768         },
34769         items : [ ... list of content panels or nested layout panels.. ]
34770    }
34771 );
34772 </code></pre>
34773      * @param {Object} cfg Xtype definition of item to add.
34774      */
34775     addxtype : function(cfg)
34776     {
34777         // basically accepts a pannel...
34778         // can accept a layout region..!?!?
34779         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34780         
34781         
34782         // theory?  children can only be panels??
34783         
34784         //if (!cfg.xtype.match(/Panel$/)) {
34785         //    return false;
34786         //}
34787         var ret = false;
34788         
34789         if (typeof(cfg.region) == 'undefined') {
34790             Roo.log("Failed to add Panel, region was not set");
34791             Roo.log(cfg);
34792             return false;
34793         }
34794         var region = cfg.region;
34795         delete cfg.region;
34796         
34797           
34798         var xitems = [];
34799         if (cfg.items) {
34800             xitems = cfg.items;
34801             delete cfg.items;
34802         }
34803         var nb = false;
34804         
34805         switch(cfg.xtype) 
34806         {
34807             case 'Content':  // ContentPanel (el, cfg)
34808             case 'Scroll':  // ContentPanel (el, cfg)
34809             case 'View': 
34810                 cfg.autoCreate = true;
34811                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34812                 //} else {
34813                 //    var el = this.el.createChild();
34814                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34815                 //}
34816                 
34817                 this.add(region, ret);
34818                 break;
34819             
34820             /*
34821             case 'TreePanel': // our new panel!
34822                 cfg.el = this.el.createChild();
34823                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34824                 this.add(region, ret);
34825                 break;
34826             */
34827             
34828             case 'Nest': 
34829                 // create a new Layout (which is  a Border Layout...
34830                 
34831                 var clayout = cfg.layout;
34832                 clayout.el  = this.el.createChild();
34833                 clayout.items   = clayout.items  || [];
34834                 
34835                 delete cfg.layout;
34836                 
34837                 // replace this exitems with the clayout ones..
34838                 xitems = clayout.items;
34839                  
34840                 // force background off if it's in center...
34841                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34842                     cfg.background = false;
34843                 }
34844                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34845                 
34846                 
34847                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34848                 //console.log('adding nested layout panel '  + cfg.toSource());
34849                 this.add(region, ret);
34850                 nb = {}; /// find first...
34851                 break;
34852             
34853             case 'Grid':
34854                 
34855                 // needs grid and region
34856                 
34857                 //var el = this.getRegion(region).el.createChild();
34858                 /*
34859                  *var el = this.el.createChild();
34860                 // create the grid first...
34861                 cfg.grid.container = el;
34862                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34863                 */
34864                 
34865                 if (region == 'center' && this.active ) {
34866                     cfg.background = false;
34867                 }
34868                 
34869                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34870                 
34871                 this.add(region, ret);
34872                 /*
34873                 if (cfg.background) {
34874                     // render grid on panel activation (if panel background)
34875                     ret.on('activate', function(gp) {
34876                         if (!gp.grid.rendered) {
34877                     //        gp.grid.render(el);
34878                         }
34879                     });
34880                 } else {
34881                   //  cfg.grid.render(el);
34882                 }
34883                 */
34884                 break;
34885            
34886            
34887             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34888                 // it was the old xcomponent building that caused this before.
34889                 // espeically if border is the top element in the tree.
34890                 ret = this;
34891                 break; 
34892                 
34893                     
34894                 
34895                 
34896                 
34897             default:
34898                 /*
34899                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34900                     
34901                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34902                     this.add(region, ret);
34903                 } else {
34904                 */
34905                     Roo.log(cfg);
34906                     throw "Can not add '" + cfg.xtype + "' to Border";
34907                     return null;
34908              
34909                                 
34910              
34911         }
34912         this.beginUpdate();
34913         // add children..
34914         var region = '';
34915         var abn = {};
34916         Roo.each(xitems, function(i)  {
34917             region = nb && i.region ? i.region : false;
34918             
34919             var add = ret.addxtype(i);
34920            
34921             if (region) {
34922                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34923                 if (!i.background) {
34924                     abn[region] = nb[region] ;
34925                 }
34926             }
34927             
34928         });
34929         this.endUpdate();
34930
34931         // make the last non-background panel active..
34932         //if (nb) { Roo.log(abn); }
34933         if (nb) {
34934             
34935             for(var r in abn) {
34936                 region = this.getRegion(r);
34937                 if (region) {
34938                     // tried using nb[r], but it does not work..
34939                      
34940                     region.showPanel(abn[r]);
34941                    
34942                 }
34943             }
34944         }
34945         return ret;
34946         
34947     },
34948     
34949     
34950 // private
34951     factory : function(cfg)
34952     {
34953         
34954         var validRegions = Roo.bootstrap.layout.Border.regions;
34955
34956         var target = cfg.region;
34957         cfg.mgr = this;
34958         
34959         var r = Roo.bootstrap.layout;
34960         Roo.log(target);
34961         switch(target){
34962             case "north":
34963                 return new r.North(cfg);
34964             case "south":
34965                 return new r.South(cfg);
34966             case "east":
34967                 return new r.East(cfg);
34968             case "west":
34969                 return new r.West(cfg);
34970             case "center":
34971                 return new r.Center(cfg);
34972         }
34973         throw 'Layout region "'+target+'" not supported.';
34974     }
34975     
34976     
34977 });
34978  /*
34979  * Based on:
34980  * Ext JS Library 1.1.1
34981  * Copyright(c) 2006-2007, Ext JS, LLC.
34982  *
34983  * Originally Released Under LGPL - original licence link has changed is not relivant.
34984  *
34985  * Fork - LGPL
34986  * <script type="text/javascript">
34987  */
34988  
34989 /**
34990  * @class Roo.bootstrap.layout.Basic
34991  * @extends Roo.util.Observable
34992  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34993  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34994  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34995  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34996  * @cfg {string}   region  the region that it inhabits..
34997  * @cfg {bool}   skipConfig skip config?
34998  * 
34999
35000  */
35001 Roo.bootstrap.layout.Basic = function(config){
35002     
35003     this.mgr = config.mgr;
35004     
35005     this.position = config.region;
35006     
35007     var skipConfig = config.skipConfig;
35008     
35009     this.events = {
35010         /**
35011          * @scope Roo.BasicLayoutRegion
35012          */
35013         
35014         /**
35015          * @event beforeremove
35016          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35017          * @param {Roo.LayoutRegion} this
35018          * @param {Roo.ContentPanel} panel The panel
35019          * @param {Object} e The cancel event object
35020          */
35021         "beforeremove" : true,
35022         /**
35023          * @event invalidated
35024          * Fires when the layout for this region is changed.
35025          * @param {Roo.LayoutRegion} this
35026          */
35027         "invalidated" : true,
35028         /**
35029          * @event visibilitychange
35030          * Fires when this region is shown or hidden 
35031          * @param {Roo.LayoutRegion} this
35032          * @param {Boolean} visibility true or false
35033          */
35034         "visibilitychange" : true,
35035         /**
35036          * @event paneladded
35037          * Fires when a panel is added. 
35038          * @param {Roo.LayoutRegion} this
35039          * @param {Roo.ContentPanel} panel The panel
35040          */
35041         "paneladded" : true,
35042         /**
35043          * @event panelremoved
35044          * Fires when a panel is removed. 
35045          * @param {Roo.LayoutRegion} this
35046          * @param {Roo.ContentPanel} panel The panel
35047          */
35048         "panelremoved" : true,
35049         /**
35050          * @event beforecollapse
35051          * Fires when this region before collapse.
35052          * @param {Roo.LayoutRegion} this
35053          */
35054         "beforecollapse" : true,
35055         /**
35056          * @event collapsed
35057          * Fires when this region is collapsed.
35058          * @param {Roo.LayoutRegion} this
35059          */
35060         "collapsed" : true,
35061         /**
35062          * @event expanded
35063          * Fires when this region is expanded.
35064          * @param {Roo.LayoutRegion} this
35065          */
35066         "expanded" : true,
35067         /**
35068          * @event slideshow
35069          * Fires when this region is slid into view.
35070          * @param {Roo.LayoutRegion} this
35071          */
35072         "slideshow" : true,
35073         /**
35074          * @event slidehide
35075          * Fires when this region slides out of view. 
35076          * @param {Roo.LayoutRegion} this
35077          */
35078         "slidehide" : true,
35079         /**
35080          * @event panelactivated
35081          * Fires when a panel is activated. 
35082          * @param {Roo.LayoutRegion} this
35083          * @param {Roo.ContentPanel} panel The activated panel
35084          */
35085         "panelactivated" : true,
35086         /**
35087          * @event resized
35088          * Fires when the user resizes this region. 
35089          * @param {Roo.LayoutRegion} this
35090          * @param {Number} newSize The new size (width for east/west, height for north/south)
35091          */
35092         "resized" : true
35093     };
35094     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35095     this.panels = new Roo.util.MixedCollection();
35096     this.panels.getKey = this.getPanelId.createDelegate(this);
35097     this.box = null;
35098     this.activePanel = null;
35099     // ensure listeners are added...
35100     
35101     if (config.listeners || config.events) {
35102         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35103             listeners : config.listeners || {},
35104             events : config.events || {}
35105         });
35106     }
35107     
35108     if(skipConfig !== true){
35109         this.applyConfig(config);
35110     }
35111 };
35112
35113 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35114 {
35115     getPanelId : function(p){
35116         return p.getId();
35117     },
35118     
35119     applyConfig : function(config){
35120         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35121         this.config = config;
35122         
35123     },
35124     
35125     /**
35126      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35127      * the width, for horizontal (north, south) the height.
35128      * @param {Number} newSize The new width or height
35129      */
35130     resizeTo : function(newSize){
35131         var el = this.el ? this.el :
35132                  (this.activePanel ? this.activePanel.getEl() : null);
35133         if(el){
35134             switch(this.position){
35135                 case "east":
35136                 case "west":
35137                     el.setWidth(newSize);
35138                     this.fireEvent("resized", this, newSize);
35139                 break;
35140                 case "north":
35141                 case "south":
35142                     el.setHeight(newSize);
35143                     this.fireEvent("resized", this, newSize);
35144                 break;                
35145             }
35146         }
35147     },
35148     
35149     getBox : function(){
35150         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35151     },
35152     
35153     getMargins : function(){
35154         return this.margins;
35155     },
35156     
35157     updateBox : function(box){
35158         this.box = box;
35159         var el = this.activePanel.getEl();
35160         el.dom.style.left = box.x + "px";
35161         el.dom.style.top = box.y + "px";
35162         this.activePanel.setSize(box.width, box.height);
35163     },
35164     
35165     /**
35166      * Returns the container element for this region.
35167      * @return {Roo.Element}
35168      */
35169     getEl : function(){
35170         return this.activePanel;
35171     },
35172     
35173     /**
35174      * Returns true if this region is currently visible.
35175      * @return {Boolean}
35176      */
35177     isVisible : function(){
35178         return this.activePanel ? true : false;
35179     },
35180     
35181     setActivePanel : function(panel){
35182         panel = this.getPanel(panel);
35183         if(this.activePanel && this.activePanel != panel){
35184             this.activePanel.setActiveState(false);
35185             this.activePanel.getEl().setLeftTop(-10000,-10000);
35186         }
35187         this.activePanel = panel;
35188         panel.setActiveState(true);
35189         if(this.box){
35190             panel.setSize(this.box.width, this.box.height);
35191         }
35192         this.fireEvent("panelactivated", this, panel);
35193         this.fireEvent("invalidated");
35194     },
35195     
35196     /**
35197      * Show the specified panel.
35198      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35199      * @return {Roo.ContentPanel} The shown panel or null
35200      */
35201     showPanel : function(panel){
35202         panel = this.getPanel(panel);
35203         if(panel){
35204             this.setActivePanel(panel);
35205         }
35206         return panel;
35207     },
35208     
35209     /**
35210      * Get the active panel for this region.
35211      * @return {Roo.ContentPanel} The active panel or null
35212      */
35213     getActivePanel : function(){
35214         return this.activePanel;
35215     },
35216     
35217     /**
35218      * Add the passed ContentPanel(s)
35219      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35220      * @return {Roo.ContentPanel} The panel added (if only one was added)
35221      */
35222     add : function(panel){
35223         if(arguments.length > 1){
35224             for(var i = 0, len = arguments.length; i < len; i++) {
35225                 this.add(arguments[i]);
35226             }
35227             return null;
35228         }
35229         if(this.hasPanel(panel)){
35230             this.showPanel(panel);
35231             return panel;
35232         }
35233         var el = panel.getEl();
35234         if(el.dom.parentNode != this.mgr.el.dom){
35235             this.mgr.el.dom.appendChild(el.dom);
35236         }
35237         if(panel.setRegion){
35238             panel.setRegion(this);
35239         }
35240         this.panels.add(panel);
35241         el.setStyle("position", "absolute");
35242         if(!panel.background){
35243             this.setActivePanel(panel);
35244             if(this.config.initialSize && this.panels.getCount()==1){
35245                 this.resizeTo(this.config.initialSize);
35246             }
35247         }
35248         this.fireEvent("paneladded", this, panel);
35249         return panel;
35250     },
35251     
35252     /**
35253      * Returns true if the panel is in this region.
35254      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35255      * @return {Boolean}
35256      */
35257     hasPanel : function(panel){
35258         if(typeof panel == "object"){ // must be panel obj
35259             panel = panel.getId();
35260         }
35261         return this.getPanel(panel) ? true : false;
35262     },
35263     
35264     /**
35265      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35266      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35267      * @param {Boolean} preservePanel Overrides the config preservePanel option
35268      * @return {Roo.ContentPanel} The panel that was removed
35269      */
35270     remove : function(panel, preservePanel){
35271         panel = this.getPanel(panel);
35272         if(!panel){
35273             return null;
35274         }
35275         var e = {};
35276         this.fireEvent("beforeremove", this, panel, e);
35277         if(e.cancel === true){
35278             return null;
35279         }
35280         var panelId = panel.getId();
35281         this.panels.removeKey(panelId);
35282         return panel;
35283     },
35284     
35285     /**
35286      * Returns the panel specified or null if it's not in this region.
35287      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35288      * @return {Roo.ContentPanel}
35289      */
35290     getPanel : function(id){
35291         if(typeof id == "object"){ // must be panel obj
35292             return id;
35293         }
35294         return this.panels.get(id);
35295     },
35296     
35297     /**
35298      * Returns this regions position (north/south/east/west/center).
35299      * @return {String} 
35300      */
35301     getPosition: function(){
35302         return this.position;    
35303     }
35304 });/*
35305  * Based on:
35306  * Ext JS Library 1.1.1
35307  * Copyright(c) 2006-2007, Ext JS, LLC.
35308  *
35309  * Originally Released Under LGPL - original licence link has changed is not relivant.
35310  *
35311  * Fork - LGPL
35312  * <script type="text/javascript">
35313  */
35314  
35315 /**
35316  * @class Roo.bootstrap.layout.Region
35317  * @extends Roo.bootstrap.layout.Basic
35318  * This class represents a region in a layout manager.
35319  
35320  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35321  * @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})
35322  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35323  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35324  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35325  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35326  * @cfg {String}    title           The title for the region (overrides panel titles)
35327  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35328  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35329  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35330  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35331  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35332  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35333  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35334  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35335  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35336  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35337
35338  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35339  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35340  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35341  * @cfg {Number}    width           For East/West panels
35342  * @cfg {Number}    height          For North/South panels
35343  * @cfg {Boolean}   split           To show the splitter
35344  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35345  * 
35346  * @cfg {string}   cls             Extra CSS classes to add to region
35347  * 
35348  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35349  * @cfg {string}   region  the region that it inhabits..
35350  *
35351
35352  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35353  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35354
35355  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35356  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35357  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35358  */
35359 Roo.bootstrap.layout.Region = function(config)
35360 {
35361     this.applyConfig(config);
35362
35363     var mgr = config.mgr;
35364     var pos = config.region;
35365     config.skipConfig = true;
35366     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35367     
35368     if (mgr.el) {
35369         this.onRender(mgr.el);   
35370     }
35371      
35372     this.visible = true;
35373     this.collapsed = false;
35374     this.unrendered_panels = [];
35375 };
35376
35377 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35378
35379     position: '', // set by wrapper (eg. north/south etc..)
35380     unrendered_panels : null,  // unrendered panels.
35381     createBody : function(){
35382         /** This region's body element 
35383         * @type Roo.Element */
35384         this.bodyEl = this.el.createChild({
35385                 tag: "div",
35386                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35387         });
35388     },
35389
35390     onRender: function(ctr, pos)
35391     {
35392         var dh = Roo.DomHelper;
35393         /** This region's container element 
35394         * @type Roo.Element */
35395         this.el = dh.append(ctr.dom, {
35396                 tag: "div",
35397                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35398             }, true);
35399         /** This region's title element 
35400         * @type Roo.Element */
35401     
35402         this.titleEl = dh.append(this.el.dom,
35403             {
35404                     tag: "div",
35405                     unselectable: "on",
35406                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35407                     children:[
35408                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35409                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35410                     ]}, true);
35411         
35412         this.titleEl.enableDisplayMode();
35413         /** This region's title text element 
35414         * @type HTMLElement */
35415         this.titleTextEl = this.titleEl.dom.firstChild;
35416         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35417         /*
35418         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35419         this.closeBtn.enableDisplayMode();
35420         this.closeBtn.on("click", this.closeClicked, this);
35421         this.closeBtn.hide();
35422     */
35423         this.createBody(this.config);
35424         if(this.config.hideWhenEmpty){
35425             this.hide();
35426             this.on("paneladded", this.validateVisibility, this);
35427             this.on("panelremoved", this.validateVisibility, this);
35428         }
35429         if(this.autoScroll){
35430             this.bodyEl.setStyle("overflow", "auto");
35431         }else{
35432             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35433         }
35434         //if(c.titlebar !== false){
35435             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35436                 this.titleEl.hide();
35437             }else{
35438                 this.titleEl.show();
35439                 if(this.config.title){
35440                     this.titleTextEl.innerHTML = this.config.title;
35441                 }
35442             }
35443         //}
35444         if(this.config.collapsed){
35445             this.collapse(true);
35446         }
35447         if(this.config.hidden){
35448             this.hide();
35449         }
35450         
35451         if (this.unrendered_panels && this.unrendered_panels.length) {
35452             for (var i =0;i< this.unrendered_panels.length; i++) {
35453                 this.add(this.unrendered_panels[i]);
35454             }
35455             this.unrendered_panels = null;
35456             
35457         }
35458         
35459     },
35460     
35461     applyConfig : function(c)
35462     {
35463         /*
35464          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35465             var dh = Roo.DomHelper;
35466             if(c.titlebar !== false){
35467                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35468                 this.collapseBtn.on("click", this.collapse, this);
35469                 this.collapseBtn.enableDisplayMode();
35470                 /*
35471                 if(c.showPin === true || this.showPin){
35472                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35473                     this.stickBtn.enableDisplayMode();
35474                     this.stickBtn.on("click", this.expand, this);
35475                     this.stickBtn.hide();
35476                 }
35477                 
35478             }
35479             */
35480             /** This region's collapsed element
35481             * @type Roo.Element */
35482             /*
35483              *
35484             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35485                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35486             ]}, true);
35487             
35488             if(c.floatable !== false){
35489                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35490                this.collapsedEl.on("click", this.collapseClick, this);
35491             }
35492
35493             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35494                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35495                    id: "message", unselectable: "on", style:{"float":"left"}});
35496                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35497              }
35498             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35499             this.expandBtn.on("click", this.expand, this);
35500             
35501         }
35502         
35503         if(this.collapseBtn){
35504             this.collapseBtn.setVisible(c.collapsible == true);
35505         }
35506         
35507         this.cmargins = c.cmargins || this.cmargins ||
35508                          (this.position == "west" || this.position == "east" ?
35509                              {top: 0, left: 2, right:2, bottom: 0} :
35510                              {top: 2, left: 0, right:0, bottom: 2});
35511         */
35512         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35513         
35514         
35515         this.bottomTabs = c.tabPosition != "top";
35516         
35517         this.autoScroll = c.autoScroll || false;
35518         
35519         
35520        
35521         
35522         this.duration = c.duration || .30;
35523         this.slideDuration = c.slideDuration || .45;
35524         this.config = c;
35525        
35526     },
35527     /**
35528      * Returns true if this region is currently visible.
35529      * @return {Boolean}
35530      */
35531     isVisible : function(){
35532         return this.visible;
35533     },
35534
35535     /**
35536      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35537      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35538      */
35539     //setCollapsedTitle : function(title){
35540     //    title = title || "&#160;";
35541      //   if(this.collapsedTitleTextEl){
35542       //      this.collapsedTitleTextEl.innerHTML = title;
35543        // }
35544     //},
35545
35546     getBox : function(){
35547         var b;
35548       //  if(!this.collapsed){
35549             b = this.el.getBox(false, true);
35550        // }else{
35551           //  b = this.collapsedEl.getBox(false, true);
35552         //}
35553         return b;
35554     },
35555
35556     getMargins : function(){
35557         return this.margins;
35558         //return this.collapsed ? this.cmargins : this.margins;
35559     },
35560 /*
35561     highlight : function(){
35562         this.el.addClass("x-layout-panel-dragover");
35563     },
35564
35565     unhighlight : function(){
35566         this.el.removeClass("x-layout-panel-dragover");
35567     },
35568 */
35569     updateBox : function(box)
35570     {
35571         if (!this.bodyEl) {
35572             return; // not rendered yet..
35573         }
35574         
35575         this.box = box;
35576         if(!this.collapsed){
35577             this.el.dom.style.left = box.x + "px";
35578             this.el.dom.style.top = box.y + "px";
35579             this.updateBody(box.width, box.height);
35580         }else{
35581             this.collapsedEl.dom.style.left = box.x + "px";
35582             this.collapsedEl.dom.style.top = box.y + "px";
35583             this.collapsedEl.setSize(box.width, box.height);
35584         }
35585         if(this.tabs){
35586             this.tabs.autoSizeTabs();
35587         }
35588     },
35589
35590     updateBody : function(w, h)
35591     {
35592         if(w !== null){
35593             this.el.setWidth(w);
35594             w -= this.el.getBorderWidth("rl");
35595             if(this.config.adjustments){
35596                 w += this.config.adjustments[0];
35597             }
35598         }
35599         if(h !== null && h > 0){
35600             this.el.setHeight(h);
35601             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35602             h -= this.el.getBorderWidth("tb");
35603             if(this.config.adjustments){
35604                 h += this.config.adjustments[1];
35605             }
35606             this.bodyEl.setHeight(h);
35607             if(this.tabs){
35608                 h = this.tabs.syncHeight(h);
35609             }
35610         }
35611         if(this.panelSize){
35612             w = w !== null ? w : this.panelSize.width;
35613             h = h !== null ? h : this.panelSize.height;
35614         }
35615         if(this.activePanel){
35616             var el = this.activePanel.getEl();
35617             w = w !== null ? w : el.getWidth();
35618             h = h !== null ? h : el.getHeight();
35619             this.panelSize = {width: w, height: h};
35620             this.activePanel.setSize(w, h);
35621         }
35622         if(Roo.isIE && this.tabs){
35623             this.tabs.el.repaint();
35624         }
35625     },
35626
35627     /**
35628      * Returns the container element for this region.
35629      * @return {Roo.Element}
35630      */
35631     getEl : function(){
35632         return this.el;
35633     },
35634
35635     /**
35636      * Hides this region.
35637      */
35638     hide : function(){
35639         //if(!this.collapsed){
35640             this.el.dom.style.left = "-2000px";
35641             this.el.hide();
35642         //}else{
35643          //   this.collapsedEl.dom.style.left = "-2000px";
35644          //   this.collapsedEl.hide();
35645        // }
35646         this.visible = false;
35647         this.fireEvent("visibilitychange", this, false);
35648     },
35649
35650     /**
35651      * Shows this region if it was previously hidden.
35652      */
35653     show : function(){
35654         //if(!this.collapsed){
35655             this.el.show();
35656         //}else{
35657         //    this.collapsedEl.show();
35658        // }
35659         this.visible = true;
35660         this.fireEvent("visibilitychange", this, true);
35661     },
35662 /*
35663     closeClicked : function(){
35664         if(this.activePanel){
35665             this.remove(this.activePanel);
35666         }
35667     },
35668
35669     collapseClick : function(e){
35670         if(this.isSlid){
35671            e.stopPropagation();
35672            this.slideIn();
35673         }else{
35674            e.stopPropagation();
35675            this.slideOut();
35676         }
35677     },
35678 */
35679     /**
35680      * Collapses this region.
35681      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35682      */
35683     /*
35684     collapse : function(skipAnim, skipCheck = false){
35685         if(this.collapsed) {
35686             return;
35687         }
35688         
35689         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35690             
35691             this.collapsed = true;
35692             if(this.split){
35693                 this.split.el.hide();
35694             }
35695             if(this.config.animate && skipAnim !== true){
35696                 this.fireEvent("invalidated", this);
35697                 this.animateCollapse();
35698             }else{
35699                 this.el.setLocation(-20000,-20000);
35700                 this.el.hide();
35701                 this.collapsedEl.show();
35702                 this.fireEvent("collapsed", this);
35703                 this.fireEvent("invalidated", this);
35704             }
35705         }
35706         
35707     },
35708 */
35709     animateCollapse : function(){
35710         // overridden
35711     },
35712
35713     /**
35714      * Expands this region if it was previously collapsed.
35715      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35716      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35717      */
35718     /*
35719     expand : function(e, skipAnim){
35720         if(e) {
35721             e.stopPropagation();
35722         }
35723         if(!this.collapsed || this.el.hasActiveFx()) {
35724             return;
35725         }
35726         if(this.isSlid){
35727             this.afterSlideIn();
35728             skipAnim = true;
35729         }
35730         this.collapsed = false;
35731         if(this.config.animate && skipAnim !== true){
35732             this.animateExpand();
35733         }else{
35734             this.el.show();
35735             if(this.split){
35736                 this.split.el.show();
35737             }
35738             this.collapsedEl.setLocation(-2000,-2000);
35739             this.collapsedEl.hide();
35740             this.fireEvent("invalidated", this);
35741             this.fireEvent("expanded", this);
35742         }
35743     },
35744 */
35745     animateExpand : function(){
35746         // overridden
35747     },
35748
35749     initTabs : function()
35750     {
35751         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35752         
35753         var ts = new Roo.bootstrap.panel.Tabs({
35754                 el: this.bodyEl.dom,
35755                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35756                 disableTooltips: this.config.disableTabTips,
35757                 toolbar : this.config.toolbar
35758             });
35759         
35760         if(this.config.hideTabs){
35761             ts.stripWrap.setDisplayed(false);
35762         }
35763         this.tabs = ts;
35764         ts.resizeTabs = this.config.resizeTabs === true;
35765         ts.minTabWidth = this.config.minTabWidth || 40;
35766         ts.maxTabWidth = this.config.maxTabWidth || 250;
35767         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35768         ts.monitorResize = false;
35769         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35770         ts.bodyEl.addClass('roo-layout-tabs-body');
35771         this.panels.each(this.initPanelAsTab, this);
35772     },
35773
35774     initPanelAsTab : function(panel){
35775         var ti = this.tabs.addTab(
35776             panel.getEl().id,
35777             panel.getTitle(),
35778             null,
35779             this.config.closeOnTab && panel.isClosable(),
35780             panel.tpl
35781         );
35782         if(panel.tabTip !== undefined){
35783             ti.setTooltip(panel.tabTip);
35784         }
35785         ti.on("activate", function(){
35786               this.setActivePanel(panel);
35787         }, this);
35788         
35789         if(this.config.closeOnTab){
35790             ti.on("beforeclose", function(t, e){
35791                 e.cancel = true;
35792                 this.remove(panel);
35793             }, this);
35794         }
35795         
35796         panel.tabItem = ti;
35797         
35798         return ti;
35799     },
35800
35801     updatePanelTitle : function(panel, title)
35802     {
35803         if(this.activePanel == panel){
35804             this.updateTitle(title);
35805         }
35806         if(this.tabs){
35807             var ti = this.tabs.getTab(panel.getEl().id);
35808             ti.setText(title);
35809             if(panel.tabTip !== undefined){
35810                 ti.setTooltip(panel.tabTip);
35811             }
35812         }
35813     },
35814
35815     updateTitle : function(title){
35816         if(this.titleTextEl && !this.config.title){
35817             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35818         }
35819     },
35820
35821     setActivePanel : function(panel)
35822     {
35823         panel = this.getPanel(panel);
35824         if(this.activePanel && this.activePanel != panel){
35825             if(this.activePanel.setActiveState(false) === false){
35826                 return;
35827             }
35828         }
35829         this.activePanel = panel;
35830         panel.setActiveState(true);
35831         if(this.panelSize){
35832             panel.setSize(this.panelSize.width, this.panelSize.height);
35833         }
35834         if(this.closeBtn){
35835             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35836         }
35837         this.updateTitle(panel.getTitle());
35838         if(this.tabs){
35839             this.fireEvent("invalidated", this);
35840         }
35841         this.fireEvent("panelactivated", this, panel);
35842     },
35843
35844     /**
35845      * Shows the specified panel.
35846      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35847      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35848      */
35849     showPanel : function(panel)
35850     {
35851         panel = this.getPanel(panel);
35852         if(panel){
35853             if(this.tabs){
35854                 var tab = this.tabs.getTab(panel.getEl().id);
35855                 if(tab.isHidden()){
35856                     this.tabs.unhideTab(tab.id);
35857                 }
35858                 tab.activate();
35859             }else{
35860                 this.setActivePanel(panel);
35861             }
35862         }
35863         return panel;
35864     },
35865
35866     /**
35867      * Get the active panel for this region.
35868      * @return {Roo.ContentPanel} The active panel or null
35869      */
35870     getActivePanel : function(){
35871         return this.activePanel;
35872     },
35873
35874     validateVisibility : function(){
35875         if(this.panels.getCount() < 1){
35876             this.updateTitle("&#160;");
35877             this.closeBtn.hide();
35878             this.hide();
35879         }else{
35880             if(!this.isVisible()){
35881                 this.show();
35882             }
35883         }
35884     },
35885
35886     /**
35887      * Adds the passed ContentPanel(s) to this region.
35888      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35889      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35890      */
35891     add : function(panel)
35892     {
35893         if(arguments.length > 1){
35894             for(var i = 0, len = arguments.length; i < len; i++) {
35895                 this.add(arguments[i]);
35896             }
35897             return null;
35898         }
35899         
35900         // if we have not been rendered yet, then we can not really do much of this..
35901         if (!this.bodyEl) {
35902             this.unrendered_panels.push(panel);
35903             return panel;
35904         }
35905         
35906         
35907         
35908         
35909         if(this.hasPanel(panel)){
35910             this.showPanel(panel);
35911             return panel;
35912         }
35913         panel.setRegion(this);
35914         this.panels.add(panel);
35915        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35916             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35917             // and hide them... ???
35918             this.bodyEl.dom.appendChild(panel.getEl().dom);
35919             if(panel.background !== true){
35920                 this.setActivePanel(panel);
35921             }
35922             this.fireEvent("paneladded", this, panel);
35923             return panel;
35924         }
35925         */
35926         if(!this.tabs){
35927             this.initTabs();
35928         }else{
35929             this.initPanelAsTab(panel);
35930         }
35931         
35932         
35933         if(panel.background !== true){
35934             this.tabs.activate(panel.getEl().id);
35935         }
35936         this.fireEvent("paneladded", this, panel);
35937         return panel;
35938     },
35939
35940     /**
35941      * Hides the tab for the specified panel.
35942      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35943      */
35944     hidePanel : function(panel){
35945         if(this.tabs && (panel = this.getPanel(panel))){
35946             this.tabs.hideTab(panel.getEl().id);
35947         }
35948     },
35949
35950     /**
35951      * Unhides the tab for a previously hidden panel.
35952      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35953      */
35954     unhidePanel : function(panel){
35955         if(this.tabs && (panel = this.getPanel(panel))){
35956             this.tabs.unhideTab(panel.getEl().id);
35957         }
35958     },
35959
35960     clearPanels : function(){
35961         while(this.panels.getCount() > 0){
35962              this.remove(this.panels.first());
35963         }
35964     },
35965
35966     /**
35967      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35968      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35969      * @param {Boolean} preservePanel Overrides the config preservePanel option
35970      * @return {Roo.ContentPanel} The panel that was removed
35971      */
35972     remove : function(panel, preservePanel)
35973     {
35974         panel = this.getPanel(panel);
35975         if(!panel){
35976             return null;
35977         }
35978         var e = {};
35979         this.fireEvent("beforeremove", this, panel, e);
35980         if(e.cancel === true){
35981             return null;
35982         }
35983         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35984         var panelId = panel.getId();
35985         this.panels.removeKey(panelId);
35986         if(preservePanel){
35987             document.body.appendChild(panel.getEl().dom);
35988         }
35989         if(this.tabs){
35990             this.tabs.removeTab(panel.getEl().id);
35991         }else if (!preservePanel){
35992             this.bodyEl.dom.removeChild(panel.getEl().dom);
35993         }
35994         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35995             var p = this.panels.first();
35996             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35997             tempEl.appendChild(p.getEl().dom);
35998             this.bodyEl.update("");
35999             this.bodyEl.dom.appendChild(p.getEl().dom);
36000             tempEl = null;
36001             this.updateTitle(p.getTitle());
36002             this.tabs = null;
36003             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36004             this.setActivePanel(p);
36005         }
36006         panel.setRegion(null);
36007         if(this.activePanel == panel){
36008             this.activePanel = null;
36009         }
36010         if(this.config.autoDestroy !== false && preservePanel !== true){
36011             try{panel.destroy();}catch(e){}
36012         }
36013         this.fireEvent("panelremoved", this, panel);
36014         return panel;
36015     },
36016
36017     /**
36018      * Returns the TabPanel component used by this region
36019      * @return {Roo.TabPanel}
36020      */
36021     getTabs : function(){
36022         return this.tabs;
36023     },
36024
36025     createTool : function(parentEl, className){
36026         var btn = Roo.DomHelper.append(parentEl, {
36027             tag: "div",
36028             cls: "x-layout-tools-button",
36029             children: [ {
36030                 tag: "div",
36031                 cls: "roo-layout-tools-button-inner " + className,
36032                 html: "&#160;"
36033             }]
36034         }, true);
36035         btn.addClassOnOver("roo-layout-tools-button-over");
36036         return btn;
36037     }
36038 });/*
36039  * Based on:
36040  * Ext JS Library 1.1.1
36041  * Copyright(c) 2006-2007, Ext JS, LLC.
36042  *
36043  * Originally Released Under LGPL - original licence link has changed is not relivant.
36044  *
36045  * Fork - LGPL
36046  * <script type="text/javascript">
36047  */
36048  
36049
36050
36051 /**
36052  * @class Roo.SplitLayoutRegion
36053  * @extends Roo.LayoutRegion
36054  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36055  */
36056 Roo.bootstrap.layout.Split = function(config){
36057     this.cursor = config.cursor;
36058     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36059 };
36060
36061 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36062 {
36063     splitTip : "Drag to resize.",
36064     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36065     useSplitTips : false,
36066
36067     applyConfig : function(config){
36068         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36069     },
36070     
36071     onRender : function(ctr,pos) {
36072         
36073         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36074         if(!this.config.split){
36075             return;
36076         }
36077         if(!this.split){
36078             
36079             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36080                             tag: "div",
36081                             id: this.el.id + "-split",
36082                             cls: "roo-layout-split roo-layout-split-"+this.position,
36083                             html: "&#160;"
36084             });
36085             /** The SplitBar for this region 
36086             * @type Roo.SplitBar */
36087             // does not exist yet...
36088             Roo.log([this.position, this.orientation]);
36089             
36090             this.split = new Roo.bootstrap.SplitBar({
36091                 dragElement : splitEl,
36092                 resizingElement: this.el,
36093                 orientation : this.orientation
36094             });
36095             
36096             this.split.on("moved", this.onSplitMove, this);
36097             this.split.useShim = this.config.useShim === true;
36098             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36099             if(this.useSplitTips){
36100                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36101             }
36102             //if(config.collapsible){
36103             //    this.split.el.on("dblclick", this.collapse,  this);
36104             //}
36105         }
36106         if(typeof this.config.minSize != "undefined"){
36107             this.split.minSize = this.config.minSize;
36108         }
36109         if(typeof this.config.maxSize != "undefined"){
36110             this.split.maxSize = this.config.maxSize;
36111         }
36112         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36113             this.hideSplitter();
36114         }
36115         
36116     },
36117
36118     getHMaxSize : function(){
36119          var cmax = this.config.maxSize || 10000;
36120          var center = this.mgr.getRegion("center");
36121          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36122     },
36123
36124     getVMaxSize : function(){
36125          var cmax = this.config.maxSize || 10000;
36126          var center = this.mgr.getRegion("center");
36127          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36128     },
36129
36130     onSplitMove : function(split, newSize){
36131         this.fireEvent("resized", this, newSize);
36132     },
36133     
36134     /** 
36135      * Returns the {@link Roo.SplitBar} for this region.
36136      * @return {Roo.SplitBar}
36137      */
36138     getSplitBar : function(){
36139         return this.split;
36140     },
36141     
36142     hide : function(){
36143         this.hideSplitter();
36144         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36145     },
36146
36147     hideSplitter : function(){
36148         if(this.split){
36149             this.split.el.setLocation(-2000,-2000);
36150             this.split.el.hide();
36151         }
36152     },
36153
36154     show : function(){
36155         if(this.split){
36156             this.split.el.show();
36157         }
36158         Roo.bootstrap.layout.Split.superclass.show.call(this);
36159     },
36160     
36161     beforeSlide: function(){
36162         if(Roo.isGecko){// firefox overflow auto bug workaround
36163             this.bodyEl.clip();
36164             if(this.tabs) {
36165                 this.tabs.bodyEl.clip();
36166             }
36167             if(this.activePanel){
36168                 this.activePanel.getEl().clip();
36169                 
36170                 if(this.activePanel.beforeSlide){
36171                     this.activePanel.beforeSlide();
36172                 }
36173             }
36174         }
36175     },
36176     
36177     afterSlide : function(){
36178         if(Roo.isGecko){// firefox overflow auto bug workaround
36179             this.bodyEl.unclip();
36180             if(this.tabs) {
36181                 this.tabs.bodyEl.unclip();
36182             }
36183             if(this.activePanel){
36184                 this.activePanel.getEl().unclip();
36185                 if(this.activePanel.afterSlide){
36186                     this.activePanel.afterSlide();
36187                 }
36188             }
36189         }
36190     },
36191
36192     initAutoHide : function(){
36193         if(this.autoHide !== false){
36194             if(!this.autoHideHd){
36195                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36196                 this.autoHideHd = {
36197                     "mouseout": function(e){
36198                         if(!e.within(this.el, true)){
36199                             st.delay(500);
36200                         }
36201                     },
36202                     "mouseover" : function(e){
36203                         st.cancel();
36204                     },
36205                     scope : this
36206                 };
36207             }
36208             this.el.on(this.autoHideHd);
36209         }
36210     },
36211
36212     clearAutoHide : function(){
36213         if(this.autoHide !== false){
36214             this.el.un("mouseout", this.autoHideHd.mouseout);
36215             this.el.un("mouseover", this.autoHideHd.mouseover);
36216         }
36217     },
36218
36219     clearMonitor : function(){
36220         Roo.get(document).un("click", this.slideInIf, this);
36221     },
36222
36223     // these names are backwards but not changed for compat
36224     slideOut : function(){
36225         if(this.isSlid || this.el.hasActiveFx()){
36226             return;
36227         }
36228         this.isSlid = true;
36229         if(this.collapseBtn){
36230             this.collapseBtn.hide();
36231         }
36232         this.closeBtnState = this.closeBtn.getStyle('display');
36233         this.closeBtn.hide();
36234         if(this.stickBtn){
36235             this.stickBtn.show();
36236         }
36237         this.el.show();
36238         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36239         this.beforeSlide();
36240         this.el.setStyle("z-index", 10001);
36241         this.el.slideIn(this.getSlideAnchor(), {
36242             callback: function(){
36243                 this.afterSlide();
36244                 this.initAutoHide();
36245                 Roo.get(document).on("click", this.slideInIf, this);
36246                 this.fireEvent("slideshow", this);
36247             },
36248             scope: this,
36249             block: true
36250         });
36251     },
36252
36253     afterSlideIn : function(){
36254         this.clearAutoHide();
36255         this.isSlid = false;
36256         this.clearMonitor();
36257         this.el.setStyle("z-index", "");
36258         if(this.collapseBtn){
36259             this.collapseBtn.show();
36260         }
36261         this.closeBtn.setStyle('display', this.closeBtnState);
36262         if(this.stickBtn){
36263             this.stickBtn.hide();
36264         }
36265         this.fireEvent("slidehide", this);
36266     },
36267
36268     slideIn : function(cb){
36269         if(!this.isSlid || this.el.hasActiveFx()){
36270             Roo.callback(cb);
36271             return;
36272         }
36273         this.isSlid = false;
36274         this.beforeSlide();
36275         this.el.slideOut(this.getSlideAnchor(), {
36276             callback: function(){
36277                 this.el.setLeftTop(-10000, -10000);
36278                 this.afterSlide();
36279                 this.afterSlideIn();
36280                 Roo.callback(cb);
36281             },
36282             scope: this,
36283             block: true
36284         });
36285     },
36286     
36287     slideInIf : function(e){
36288         if(!e.within(this.el)){
36289             this.slideIn();
36290         }
36291     },
36292
36293     animateCollapse : function(){
36294         this.beforeSlide();
36295         this.el.setStyle("z-index", 20000);
36296         var anchor = this.getSlideAnchor();
36297         this.el.slideOut(anchor, {
36298             callback : function(){
36299                 this.el.setStyle("z-index", "");
36300                 this.collapsedEl.slideIn(anchor, {duration:.3});
36301                 this.afterSlide();
36302                 this.el.setLocation(-10000,-10000);
36303                 this.el.hide();
36304                 this.fireEvent("collapsed", this);
36305             },
36306             scope: this,
36307             block: true
36308         });
36309     },
36310
36311     animateExpand : function(){
36312         this.beforeSlide();
36313         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36314         this.el.setStyle("z-index", 20000);
36315         this.collapsedEl.hide({
36316             duration:.1
36317         });
36318         this.el.slideIn(this.getSlideAnchor(), {
36319             callback : function(){
36320                 this.el.setStyle("z-index", "");
36321                 this.afterSlide();
36322                 if(this.split){
36323                     this.split.el.show();
36324                 }
36325                 this.fireEvent("invalidated", this);
36326                 this.fireEvent("expanded", this);
36327             },
36328             scope: this,
36329             block: true
36330         });
36331     },
36332
36333     anchors : {
36334         "west" : "left",
36335         "east" : "right",
36336         "north" : "top",
36337         "south" : "bottom"
36338     },
36339
36340     sanchors : {
36341         "west" : "l",
36342         "east" : "r",
36343         "north" : "t",
36344         "south" : "b"
36345     },
36346
36347     canchors : {
36348         "west" : "tl-tr",
36349         "east" : "tr-tl",
36350         "north" : "tl-bl",
36351         "south" : "bl-tl"
36352     },
36353
36354     getAnchor : function(){
36355         return this.anchors[this.position];
36356     },
36357
36358     getCollapseAnchor : function(){
36359         return this.canchors[this.position];
36360     },
36361
36362     getSlideAnchor : function(){
36363         return this.sanchors[this.position];
36364     },
36365
36366     getAlignAdj : function(){
36367         var cm = this.cmargins;
36368         switch(this.position){
36369             case "west":
36370                 return [0, 0];
36371             break;
36372             case "east":
36373                 return [0, 0];
36374             break;
36375             case "north":
36376                 return [0, 0];
36377             break;
36378             case "south":
36379                 return [0, 0];
36380             break;
36381         }
36382     },
36383
36384     getExpandAdj : function(){
36385         var c = this.collapsedEl, cm = this.cmargins;
36386         switch(this.position){
36387             case "west":
36388                 return [-(cm.right+c.getWidth()+cm.left), 0];
36389             break;
36390             case "east":
36391                 return [cm.right+c.getWidth()+cm.left, 0];
36392             break;
36393             case "north":
36394                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36395             break;
36396             case "south":
36397                 return [0, cm.top+cm.bottom+c.getHeight()];
36398             break;
36399         }
36400     }
36401 });/*
36402  * Based on:
36403  * Ext JS Library 1.1.1
36404  * Copyright(c) 2006-2007, Ext JS, LLC.
36405  *
36406  * Originally Released Under LGPL - original licence link has changed is not relivant.
36407  *
36408  * Fork - LGPL
36409  * <script type="text/javascript">
36410  */
36411 /*
36412  * These classes are private internal classes
36413  */
36414 Roo.bootstrap.layout.Center = function(config){
36415     config.region = "center";
36416     Roo.bootstrap.layout.Region.call(this, config);
36417     this.visible = true;
36418     this.minWidth = config.minWidth || 20;
36419     this.minHeight = config.minHeight || 20;
36420 };
36421
36422 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36423     hide : function(){
36424         // center panel can't be hidden
36425     },
36426     
36427     show : function(){
36428         // center panel can't be hidden
36429     },
36430     
36431     getMinWidth: function(){
36432         return this.minWidth;
36433     },
36434     
36435     getMinHeight: function(){
36436         return this.minHeight;
36437     }
36438 });
36439
36440
36441
36442
36443  
36444
36445
36446
36447
36448
36449 Roo.bootstrap.layout.North = function(config)
36450 {
36451     config.region = 'north';
36452     config.cursor = 'n-resize';
36453     
36454     Roo.bootstrap.layout.Split.call(this, config);
36455     
36456     
36457     if(this.split){
36458         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36459         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36460         this.split.el.addClass("roo-layout-split-v");
36461     }
36462     var size = config.initialSize || config.height;
36463     if(typeof size != "undefined"){
36464         this.el.setHeight(size);
36465     }
36466 };
36467 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36468 {
36469     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36470     
36471     
36472     
36473     getBox : function(){
36474         if(this.collapsed){
36475             return this.collapsedEl.getBox();
36476         }
36477         var box = this.el.getBox();
36478         if(this.split){
36479             box.height += this.split.el.getHeight();
36480         }
36481         return box;
36482     },
36483     
36484     updateBox : function(box){
36485         if(this.split && !this.collapsed){
36486             box.height -= this.split.el.getHeight();
36487             this.split.el.setLeft(box.x);
36488             this.split.el.setTop(box.y+box.height);
36489             this.split.el.setWidth(box.width);
36490         }
36491         if(this.collapsed){
36492             this.updateBody(box.width, null);
36493         }
36494         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36495     }
36496 });
36497
36498
36499
36500
36501
36502 Roo.bootstrap.layout.South = function(config){
36503     config.region = 'south';
36504     config.cursor = 's-resize';
36505     Roo.bootstrap.layout.Split.call(this, config);
36506     if(this.split){
36507         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36508         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36509         this.split.el.addClass("roo-layout-split-v");
36510     }
36511     var size = config.initialSize || config.height;
36512     if(typeof size != "undefined"){
36513         this.el.setHeight(size);
36514     }
36515 };
36516
36517 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36518     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36519     getBox : function(){
36520         if(this.collapsed){
36521             return this.collapsedEl.getBox();
36522         }
36523         var box = this.el.getBox();
36524         if(this.split){
36525             var sh = this.split.el.getHeight();
36526             box.height += sh;
36527             box.y -= sh;
36528         }
36529         return box;
36530     },
36531     
36532     updateBox : function(box){
36533         if(this.split && !this.collapsed){
36534             var sh = this.split.el.getHeight();
36535             box.height -= sh;
36536             box.y += sh;
36537             this.split.el.setLeft(box.x);
36538             this.split.el.setTop(box.y-sh);
36539             this.split.el.setWidth(box.width);
36540         }
36541         if(this.collapsed){
36542             this.updateBody(box.width, null);
36543         }
36544         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36545     }
36546 });
36547
36548 Roo.bootstrap.layout.East = function(config){
36549     config.region = "east";
36550     config.cursor = "e-resize";
36551     Roo.bootstrap.layout.Split.call(this, config);
36552     if(this.split){
36553         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36554         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36555         this.split.el.addClass("roo-layout-split-h");
36556     }
36557     var size = config.initialSize || config.width;
36558     if(typeof size != "undefined"){
36559         this.el.setWidth(size);
36560     }
36561 };
36562 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36563     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36564     getBox : function(){
36565         if(this.collapsed){
36566             return this.collapsedEl.getBox();
36567         }
36568         var box = this.el.getBox();
36569         if(this.split){
36570             var sw = this.split.el.getWidth();
36571             box.width += sw;
36572             box.x -= sw;
36573         }
36574         return box;
36575     },
36576
36577     updateBox : function(box){
36578         if(this.split && !this.collapsed){
36579             var sw = this.split.el.getWidth();
36580             box.width -= sw;
36581             this.split.el.setLeft(box.x);
36582             this.split.el.setTop(box.y);
36583             this.split.el.setHeight(box.height);
36584             box.x += sw;
36585         }
36586         if(this.collapsed){
36587             this.updateBody(null, box.height);
36588         }
36589         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36590     }
36591 });
36592
36593 Roo.bootstrap.layout.West = function(config){
36594     config.region = "west";
36595     config.cursor = "w-resize";
36596     
36597     Roo.bootstrap.layout.Split.call(this, config);
36598     if(this.split){
36599         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36600         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36601         this.split.el.addClass("roo-layout-split-h");
36602     }
36603     
36604 };
36605 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36606     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36607     
36608     onRender: function(ctr, pos)
36609     {
36610         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36611         var size = this.config.initialSize || this.config.width;
36612         if(typeof size != "undefined"){
36613             this.el.setWidth(size);
36614         }
36615     },
36616     
36617     getBox : function(){
36618         if(this.collapsed){
36619             return this.collapsedEl.getBox();
36620         }
36621         var box = this.el.getBox();
36622         if(this.split){
36623             box.width += this.split.el.getWidth();
36624         }
36625         return box;
36626     },
36627     
36628     updateBox : function(box){
36629         if(this.split && !this.collapsed){
36630             var sw = this.split.el.getWidth();
36631             box.width -= sw;
36632             this.split.el.setLeft(box.x+box.width);
36633             this.split.el.setTop(box.y);
36634             this.split.el.setHeight(box.height);
36635         }
36636         if(this.collapsed){
36637             this.updateBody(null, box.height);
36638         }
36639         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36640     }
36641 });
36642 Roo.namespace("Roo.bootstrap.panel");/*
36643  * Based on:
36644  * Ext JS Library 1.1.1
36645  * Copyright(c) 2006-2007, Ext JS, LLC.
36646  *
36647  * Originally Released Under LGPL - original licence link has changed is not relivant.
36648  *
36649  * Fork - LGPL
36650  * <script type="text/javascript">
36651  */
36652 /**
36653  * @class Roo.ContentPanel
36654  * @extends Roo.util.Observable
36655  * A basic ContentPanel element.
36656  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36657  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36658  * @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
36659  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36660  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36661  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36662  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36663  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36664  * @cfg {String} title          The title for this panel
36665  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36666  * @cfg {String} url            Calls {@link #setUrl} with this value
36667  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36668  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36669  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36670  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36671  * @cfg {Boolean} badges render the badges
36672
36673  * @constructor
36674  * Create a new ContentPanel.
36675  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36676  * @param {String/Object} config A string to set only the title or a config object
36677  * @param {String} content (optional) Set the HTML content for this panel
36678  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36679  */
36680 Roo.bootstrap.panel.Content = function( config){
36681     
36682     this.tpl = config.tpl || false;
36683     
36684     var el = config.el;
36685     var content = config.content;
36686
36687     if(config.autoCreate){ // xtype is available if this is called from factory
36688         el = Roo.id();
36689     }
36690     this.el = Roo.get(el);
36691     if(!this.el && config && config.autoCreate){
36692         if(typeof config.autoCreate == "object"){
36693             if(!config.autoCreate.id){
36694                 config.autoCreate.id = config.id||el;
36695             }
36696             this.el = Roo.DomHelper.append(document.body,
36697                         config.autoCreate, true);
36698         }else{
36699             var elcfg =  {   tag: "div",
36700                             cls: "roo-layout-inactive-content",
36701                             id: config.id||el
36702                             };
36703             if (config.html) {
36704                 elcfg.html = config.html;
36705                 
36706             }
36707                         
36708             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36709         }
36710     } 
36711     this.closable = false;
36712     this.loaded = false;
36713     this.active = false;
36714    
36715       
36716     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36717         
36718         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36719         
36720         this.wrapEl = this.el; //this.el.wrap();
36721         var ti = [];
36722         if (config.toolbar.items) {
36723             ti = config.toolbar.items ;
36724             delete config.toolbar.items ;
36725         }
36726         
36727         var nitems = [];
36728         this.toolbar.render(this.wrapEl, 'before');
36729         for(var i =0;i < ti.length;i++) {
36730           //  Roo.log(['add child', items[i]]);
36731             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36732         }
36733         this.toolbar.items = nitems;
36734         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36735         delete config.toolbar;
36736         
36737     }
36738     /*
36739     // xtype created footer. - not sure if will work as we normally have to render first..
36740     if (this.footer && !this.footer.el && this.footer.xtype) {
36741         if (!this.wrapEl) {
36742             this.wrapEl = this.el.wrap();
36743         }
36744     
36745         this.footer.container = this.wrapEl.createChild();
36746          
36747         this.footer = Roo.factory(this.footer, Roo);
36748         
36749     }
36750     */
36751     
36752      if(typeof config == "string"){
36753         this.title = config;
36754     }else{
36755         Roo.apply(this, config);
36756     }
36757     
36758     if(this.resizeEl){
36759         this.resizeEl = Roo.get(this.resizeEl, true);
36760     }else{
36761         this.resizeEl = this.el;
36762     }
36763     // handle view.xtype
36764     
36765  
36766     
36767     
36768     this.addEvents({
36769         /**
36770          * @event activate
36771          * Fires when this panel is activated. 
36772          * @param {Roo.ContentPanel} this
36773          */
36774         "activate" : true,
36775         /**
36776          * @event deactivate
36777          * Fires when this panel is activated. 
36778          * @param {Roo.ContentPanel} this
36779          */
36780         "deactivate" : true,
36781
36782         /**
36783          * @event resize
36784          * Fires when this panel is resized if fitToFrame is true.
36785          * @param {Roo.ContentPanel} this
36786          * @param {Number} width The width after any component adjustments
36787          * @param {Number} height The height after any component adjustments
36788          */
36789         "resize" : true,
36790         
36791          /**
36792          * @event render
36793          * Fires when this tab is created
36794          * @param {Roo.ContentPanel} this
36795          */
36796         "render" : true
36797         
36798         
36799         
36800     });
36801     
36802
36803     
36804     
36805     if(this.autoScroll){
36806         this.resizeEl.setStyle("overflow", "auto");
36807     } else {
36808         // fix randome scrolling
36809         //this.el.on('scroll', function() {
36810         //    Roo.log('fix random scolling');
36811         //    this.scrollTo('top',0); 
36812         //});
36813     }
36814     content = content || this.content;
36815     if(content){
36816         this.setContent(content);
36817     }
36818     if(config && config.url){
36819         this.setUrl(this.url, this.params, this.loadOnce);
36820     }
36821     
36822     
36823     
36824     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36825     
36826     if (this.view && typeof(this.view.xtype) != 'undefined') {
36827         this.view.el = this.el.appendChild(document.createElement("div"));
36828         this.view = Roo.factory(this.view); 
36829         this.view.render  &&  this.view.render(false, '');  
36830     }
36831     
36832     
36833     this.fireEvent('render', this);
36834 };
36835
36836 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36837     
36838     tabTip : '',
36839     
36840     setRegion : function(region){
36841         this.region = region;
36842         this.setActiveClass(region && !this.background);
36843     },
36844     
36845     
36846     setActiveClass: function(state)
36847     {
36848         if(state){
36849            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36850            this.el.setStyle('position','relative');
36851         }else{
36852            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36853            this.el.setStyle('position', 'absolute');
36854         } 
36855     },
36856     
36857     /**
36858      * Returns the toolbar for this Panel if one was configured. 
36859      * @return {Roo.Toolbar} 
36860      */
36861     getToolbar : function(){
36862         return this.toolbar;
36863     },
36864     
36865     setActiveState : function(active)
36866     {
36867         this.active = active;
36868         this.setActiveClass(active);
36869         if(!active){
36870             if(this.fireEvent("deactivate", this) === false){
36871                 return false;
36872             }
36873             return true;
36874         }
36875         this.fireEvent("activate", this);
36876         return true;
36877     },
36878     /**
36879      * Updates this panel's element
36880      * @param {String} content The new content
36881      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36882     */
36883     setContent : function(content, loadScripts){
36884         this.el.update(content, loadScripts);
36885     },
36886
36887     ignoreResize : function(w, h){
36888         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36889             return true;
36890         }else{
36891             this.lastSize = {width: w, height: h};
36892             return false;
36893         }
36894     },
36895     /**
36896      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36897      * @return {Roo.UpdateManager} The UpdateManager
36898      */
36899     getUpdateManager : function(){
36900         return this.el.getUpdateManager();
36901     },
36902      /**
36903      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36904      * @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:
36905 <pre><code>
36906 panel.load({
36907     url: "your-url.php",
36908     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36909     callback: yourFunction,
36910     scope: yourObject, //(optional scope)
36911     discardUrl: false,
36912     nocache: false,
36913     text: "Loading...",
36914     timeout: 30,
36915     scripts: false
36916 });
36917 </code></pre>
36918      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36919      * 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.
36920      * @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}
36921      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36922      * @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.
36923      * @return {Roo.ContentPanel} this
36924      */
36925     load : function(){
36926         var um = this.el.getUpdateManager();
36927         um.update.apply(um, arguments);
36928         return this;
36929     },
36930
36931
36932     /**
36933      * 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.
36934      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36935      * @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)
36936      * @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)
36937      * @return {Roo.UpdateManager} The UpdateManager
36938      */
36939     setUrl : function(url, params, loadOnce){
36940         if(this.refreshDelegate){
36941             this.removeListener("activate", this.refreshDelegate);
36942         }
36943         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36944         this.on("activate", this.refreshDelegate);
36945         return this.el.getUpdateManager();
36946     },
36947     
36948     _handleRefresh : function(url, params, loadOnce){
36949         if(!loadOnce || !this.loaded){
36950             var updater = this.el.getUpdateManager();
36951             updater.update(url, params, this._setLoaded.createDelegate(this));
36952         }
36953     },
36954     
36955     _setLoaded : function(){
36956         this.loaded = true;
36957     }, 
36958     
36959     /**
36960      * Returns this panel's id
36961      * @return {String} 
36962      */
36963     getId : function(){
36964         return this.el.id;
36965     },
36966     
36967     /** 
36968      * Returns this panel's element - used by regiosn to add.
36969      * @return {Roo.Element} 
36970      */
36971     getEl : function(){
36972         return this.wrapEl || this.el;
36973     },
36974     
36975    
36976     
36977     adjustForComponents : function(width, height)
36978     {
36979         //Roo.log('adjustForComponents ');
36980         if(this.resizeEl != this.el){
36981             width -= this.el.getFrameWidth('lr');
36982             height -= this.el.getFrameWidth('tb');
36983         }
36984         if(this.toolbar){
36985             var te = this.toolbar.getEl();
36986             te.setWidth(width);
36987             height -= te.getHeight();
36988         }
36989         if(this.footer){
36990             var te = this.footer.getEl();
36991             te.setWidth(width);
36992             height -= te.getHeight();
36993         }
36994         
36995         
36996         if(this.adjustments){
36997             width += this.adjustments[0];
36998             height += this.adjustments[1];
36999         }
37000         return {"width": width, "height": height};
37001     },
37002     
37003     setSize : function(width, height){
37004         if(this.fitToFrame && !this.ignoreResize(width, height)){
37005             if(this.fitContainer && this.resizeEl != this.el){
37006                 this.el.setSize(width, height);
37007             }
37008             var size = this.adjustForComponents(width, height);
37009             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37010             this.fireEvent('resize', this, size.width, size.height);
37011         }
37012     },
37013     
37014     /**
37015      * Returns this panel's title
37016      * @return {String} 
37017      */
37018     getTitle : function(){
37019         
37020         if (typeof(this.title) != 'object') {
37021             return this.title;
37022         }
37023         
37024         var t = '';
37025         for (var k in this.title) {
37026             if (!this.title.hasOwnProperty(k)) {
37027                 continue;
37028             }
37029             
37030             if (k.indexOf('-') >= 0) {
37031                 var s = k.split('-');
37032                 for (var i = 0; i<s.length; i++) {
37033                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37034                 }
37035             } else {
37036                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37037             }
37038         }
37039         return t;
37040     },
37041     
37042     /**
37043      * Set this panel's title
37044      * @param {String} title
37045      */
37046     setTitle : function(title){
37047         this.title = title;
37048         if(this.region){
37049             this.region.updatePanelTitle(this, title);
37050         }
37051     },
37052     
37053     /**
37054      * Returns true is this panel was configured to be closable
37055      * @return {Boolean} 
37056      */
37057     isClosable : function(){
37058         return this.closable;
37059     },
37060     
37061     beforeSlide : function(){
37062         this.el.clip();
37063         this.resizeEl.clip();
37064     },
37065     
37066     afterSlide : function(){
37067         this.el.unclip();
37068         this.resizeEl.unclip();
37069     },
37070     
37071     /**
37072      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37073      *   Will fail silently if the {@link #setUrl} method has not been called.
37074      *   This does not activate the panel, just updates its content.
37075      */
37076     refresh : function(){
37077         if(this.refreshDelegate){
37078            this.loaded = false;
37079            this.refreshDelegate();
37080         }
37081     },
37082     
37083     /**
37084      * Destroys this panel
37085      */
37086     destroy : function(){
37087         this.el.removeAllListeners();
37088         var tempEl = document.createElement("span");
37089         tempEl.appendChild(this.el.dom);
37090         tempEl.innerHTML = "";
37091         this.el.remove();
37092         this.el = null;
37093     },
37094     
37095     /**
37096      * form - if the content panel contains a form - this is a reference to it.
37097      * @type {Roo.form.Form}
37098      */
37099     form : false,
37100     /**
37101      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37102      *    This contains a reference to it.
37103      * @type {Roo.View}
37104      */
37105     view : false,
37106     
37107       /**
37108      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37109      * <pre><code>
37110
37111 layout.addxtype({
37112        xtype : 'Form',
37113        items: [ .... ]
37114    }
37115 );
37116
37117 </code></pre>
37118      * @param {Object} cfg Xtype definition of item to add.
37119      */
37120     
37121     
37122     getChildContainer: function () {
37123         return this.getEl();
37124     }
37125     
37126     
37127     /*
37128         var  ret = new Roo.factory(cfg);
37129         return ret;
37130         
37131         
37132         // add form..
37133         if (cfg.xtype.match(/^Form$/)) {
37134             
37135             var el;
37136             //if (this.footer) {
37137             //    el = this.footer.container.insertSibling(false, 'before');
37138             //} else {
37139                 el = this.el.createChild();
37140             //}
37141
37142             this.form = new  Roo.form.Form(cfg);
37143             
37144             
37145             if ( this.form.allItems.length) {
37146                 this.form.render(el.dom);
37147             }
37148             return this.form;
37149         }
37150         // should only have one of theses..
37151         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37152             // views.. should not be just added - used named prop 'view''
37153             
37154             cfg.el = this.el.appendChild(document.createElement("div"));
37155             // factory?
37156             
37157             var ret = new Roo.factory(cfg);
37158              
37159              ret.render && ret.render(false, ''); // render blank..
37160             this.view = ret;
37161             return ret;
37162         }
37163         return false;
37164     }
37165     \*/
37166 });
37167  
37168 /**
37169  * @class Roo.bootstrap.panel.Grid
37170  * @extends Roo.bootstrap.panel.Content
37171  * @constructor
37172  * Create a new GridPanel.
37173  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37174  * @param {Object} config A the config object
37175   
37176  */
37177
37178
37179
37180 Roo.bootstrap.panel.Grid = function(config)
37181 {
37182     
37183       
37184     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37185         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37186
37187     config.el = this.wrapper;
37188     //this.el = this.wrapper;
37189     
37190       if (config.container) {
37191         // ctor'ed from a Border/panel.grid
37192         
37193         
37194         this.wrapper.setStyle("overflow", "hidden");
37195         this.wrapper.addClass('roo-grid-container');
37196
37197     }
37198     
37199     
37200     if(config.toolbar){
37201         var tool_el = this.wrapper.createChild();    
37202         this.toolbar = Roo.factory(config.toolbar);
37203         var ti = [];
37204         if (config.toolbar.items) {
37205             ti = config.toolbar.items ;
37206             delete config.toolbar.items ;
37207         }
37208         
37209         var nitems = [];
37210         this.toolbar.render(tool_el);
37211         for(var i =0;i < ti.length;i++) {
37212           //  Roo.log(['add child', items[i]]);
37213             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37214         }
37215         this.toolbar.items = nitems;
37216         
37217         delete config.toolbar;
37218     }
37219     
37220     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37221     config.grid.scrollBody = true;;
37222     config.grid.monitorWindowResize = false; // turn off autosizing
37223     config.grid.autoHeight = false;
37224     config.grid.autoWidth = false;
37225     
37226     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37227     
37228     if (config.background) {
37229         // render grid on panel activation (if panel background)
37230         this.on('activate', function(gp) {
37231             if (!gp.grid.rendered) {
37232                 gp.grid.render(this.wrapper);
37233                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37234             }
37235         });
37236             
37237     } else {
37238         this.grid.render(this.wrapper);
37239         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37240
37241     }
37242     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37243     // ??? needed ??? config.el = this.wrapper;
37244     
37245     
37246     
37247   
37248     // xtype created footer. - not sure if will work as we normally have to render first..
37249     if (this.footer && !this.footer.el && this.footer.xtype) {
37250         
37251         var ctr = this.grid.getView().getFooterPanel(true);
37252         this.footer.dataSource = this.grid.dataSource;
37253         this.footer = Roo.factory(this.footer, Roo);
37254         this.footer.render(ctr);
37255         
37256     }
37257     
37258     
37259     
37260     
37261      
37262 };
37263
37264 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37265     getId : function(){
37266         return this.grid.id;
37267     },
37268     
37269     /**
37270      * Returns the grid for this panel
37271      * @return {Roo.bootstrap.Table} 
37272      */
37273     getGrid : function(){
37274         return this.grid;    
37275     },
37276     
37277     setSize : function(width, height){
37278         if(!this.ignoreResize(width, height)){
37279             var grid = this.grid;
37280             var size = this.adjustForComponents(width, height);
37281             var gridel = grid.getGridEl();
37282             gridel.setSize(size.width, size.height);
37283             /*
37284             var thd = grid.getGridEl().select('thead',true).first();
37285             var tbd = grid.getGridEl().select('tbody', true).first();
37286             if (tbd) {
37287                 tbd.setSize(width, height - thd.getHeight());
37288             }
37289             */
37290             grid.autoSize();
37291         }
37292     },
37293      
37294     
37295     
37296     beforeSlide : function(){
37297         this.grid.getView().scroller.clip();
37298     },
37299     
37300     afterSlide : function(){
37301         this.grid.getView().scroller.unclip();
37302     },
37303     
37304     destroy : function(){
37305         this.grid.destroy();
37306         delete this.grid;
37307         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37308     }
37309 });
37310
37311 /**
37312  * @class Roo.bootstrap.panel.Nest
37313  * @extends Roo.bootstrap.panel.Content
37314  * @constructor
37315  * Create a new Panel, that can contain a layout.Border.
37316  * 
37317  * 
37318  * @param {Roo.BorderLayout} layout The layout for this panel
37319  * @param {String/Object} config A string to set only the title or a config object
37320  */
37321 Roo.bootstrap.panel.Nest = function(config)
37322 {
37323     // construct with only one argument..
37324     /* FIXME - implement nicer consturctors
37325     if (layout.layout) {
37326         config = layout;
37327         layout = config.layout;
37328         delete config.layout;
37329     }
37330     if (layout.xtype && !layout.getEl) {
37331         // then layout needs constructing..
37332         layout = Roo.factory(layout, Roo);
37333     }
37334     */
37335     
37336     config.el =  config.layout.getEl();
37337     
37338     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37339     
37340     config.layout.monitorWindowResize = false; // turn off autosizing
37341     this.layout = config.layout;
37342     this.layout.getEl().addClass("roo-layout-nested-layout");
37343     
37344     
37345     
37346     
37347 };
37348
37349 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37350
37351     setSize : function(width, height){
37352         if(!this.ignoreResize(width, height)){
37353             var size = this.adjustForComponents(width, height);
37354             var el = this.layout.getEl();
37355             if (size.height < 1) {
37356                 el.setWidth(size.width);   
37357             } else {
37358                 el.setSize(size.width, size.height);
37359             }
37360             var touch = el.dom.offsetWidth;
37361             this.layout.layout();
37362             // ie requires a double layout on the first pass
37363             if(Roo.isIE && !this.initialized){
37364                 this.initialized = true;
37365                 this.layout.layout();
37366             }
37367         }
37368     },
37369     
37370     // activate all subpanels if not currently active..
37371     
37372     setActiveState : function(active){
37373         this.active = active;
37374         this.setActiveClass(active);
37375         
37376         if(!active){
37377             this.fireEvent("deactivate", this);
37378             return;
37379         }
37380         
37381         this.fireEvent("activate", this);
37382         // not sure if this should happen before or after..
37383         if (!this.layout) {
37384             return; // should not happen..
37385         }
37386         var reg = false;
37387         for (var r in this.layout.regions) {
37388             reg = this.layout.getRegion(r);
37389             if (reg.getActivePanel()) {
37390                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37391                 reg.setActivePanel(reg.getActivePanel());
37392                 continue;
37393             }
37394             if (!reg.panels.length) {
37395                 continue;
37396             }
37397             reg.showPanel(reg.getPanel(0));
37398         }
37399         
37400         
37401         
37402         
37403     },
37404     
37405     /**
37406      * Returns the nested BorderLayout for this panel
37407      * @return {Roo.BorderLayout} 
37408      */
37409     getLayout : function(){
37410         return this.layout;
37411     },
37412     
37413      /**
37414      * Adds a xtype elements to the layout of the nested panel
37415      * <pre><code>
37416
37417 panel.addxtype({
37418        xtype : 'ContentPanel',
37419        region: 'west',
37420        items: [ .... ]
37421    }
37422 );
37423
37424 panel.addxtype({
37425         xtype : 'NestedLayoutPanel',
37426         region: 'west',
37427         layout: {
37428            center: { },
37429            west: { }   
37430         },
37431         items : [ ... list of content panels or nested layout panels.. ]
37432    }
37433 );
37434 </code></pre>
37435      * @param {Object} cfg Xtype definition of item to add.
37436      */
37437     addxtype : function(cfg) {
37438         return this.layout.addxtype(cfg);
37439     
37440     }
37441 });        /*
37442  * Based on:
37443  * Ext JS Library 1.1.1
37444  * Copyright(c) 2006-2007, Ext JS, LLC.
37445  *
37446  * Originally Released Under LGPL - original licence link has changed is not relivant.
37447  *
37448  * Fork - LGPL
37449  * <script type="text/javascript">
37450  */
37451 /**
37452  * @class Roo.TabPanel
37453  * @extends Roo.util.Observable
37454  * A lightweight tab container.
37455  * <br><br>
37456  * Usage:
37457  * <pre><code>
37458 // basic tabs 1, built from existing content
37459 var tabs = new Roo.TabPanel("tabs1");
37460 tabs.addTab("script", "View Script");
37461 tabs.addTab("markup", "View Markup");
37462 tabs.activate("script");
37463
37464 // more advanced tabs, built from javascript
37465 var jtabs = new Roo.TabPanel("jtabs");
37466 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37467
37468 // set up the UpdateManager
37469 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37470 var updater = tab2.getUpdateManager();
37471 updater.setDefaultUrl("ajax1.htm");
37472 tab2.on('activate', updater.refresh, updater, true);
37473
37474 // Use setUrl for Ajax loading
37475 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37476 tab3.setUrl("ajax2.htm", null, true);
37477
37478 // Disabled tab
37479 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37480 tab4.disable();
37481
37482 jtabs.activate("jtabs-1");
37483  * </code></pre>
37484  * @constructor
37485  * Create a new TabPanel.
37486  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37487  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37488  */
37489 Roo.bootstrap.panel.Tabs = function(config){
37490     /**
37491     * The container element for this TabPanel.
37492     * @type Roo.Element
37493     */
37494     this.el = Roo.get(config.el);
37495     delete config.el;
37496     if(config){
37497         if(typeof config == "boolean"){
37498             this.tabPosition = config ? "bottom" : "top";
37499         }else{
37500             Roo.apply(this, config);
37501         }
37502     }
37503     
37504     if(this.tabPosition == "bottom"){
37505         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37506         this.el.addClass("roo-tabs-bottom");
37507     }
37508     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37509     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37510     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37511     if(Roo.isIE){
37512         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37513     }
37514     if(this.tabPosition != "bottom"){
37515         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37516          * @type Roo.Element
37517          */
37518         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37519         this.el.addClass("roo-tabs-top");
37520     }
37521     this.items = [];
37522
37523     this.bodyEl.setStyle("position", "relative");
37524
37525     this.active = null;
37526     this.activateDelegate = this.activate.createDelegate(this);
37527
37528     this.addEvents({
37529         /**
37530          * @event tabchange
37531          * Fires when the active tab changes
37532          * @param {Roo.TabPanel} this
37533          * @param {Roo.TabPanelItem} activePanel The new active tab
37534          */
37535         "tabchange": true,
37536         /**
37537          * @event beforetabchange
37538          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37539          * @param {Roo.TabPanel} this
37540          * @param {Object} e Set cancel to true on this object to cancel the tab change
37541          * @param {Roo.TabPanelItem} tab The tab being changed to
37542          */
37543         "beforetabchange" : true
37544     });
37545
37546     Roo.EventManager.onWindowResize(this.onResize, this);
37547     this.cpad = this.el.getPadding("lr");
37548     this.hiddenCount = 0;
37549
37550
37551     // toolbar on the tabbar support...
37552     if (this.toolbar) {
37553         alert("no toolbar support yet");
37554         this.toolbar  = false;
37555         /*
37556         var tcfg = this.toolbar;
37557         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37558         this.toolbar = new Roo.Toolbar(tcfg);
37559         if (Roo.isSafari) {
37560             var tbl = tcfg.container.child('table', true);
37561             tbl.setAttribute('width', '100%');
37562         }
37563         */
37564         
37565     }
37566    
37567
37568
37569     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37570 };
37571
37572 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37573     /*
37574      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37575      */
37576     tabPosition : "top",
37577     /*
37578      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37579      */
37580     currentTabWidth : 0,
37581     /*
37582      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37583      */
37584     minTabWidth : 40,
37585     /*
37586      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37587      */
37588     maxTabWidth : 250,
37589     /*
37590      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37591      */
37592     preferredTabWidth : 175,
37593     /*
37594      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37595      */
37596     resizeTabs : false,
37597     /*
37598      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37599      */
37600     monitorResize : true,
37601     /*
37602      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37603      */
37604     toolbar : false,
37605
37606     /**
37607      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37608      * @param {String} id The id of the div to use <b>or create</b>
37609      * @param {String} text The text for the tab
37610      * @param {String} content (optional) Content to put in the TabPanelItem body
37611      * @param {Boolean} closable (optional) True to create a close icon on the tab
37612      * @return {Roo.TabPanelItem} The created TabPanelItem
37613      */
37614     addTab : function(id, text, content, closable, tpl)
37615     {
37616         var item = new Roo.bootstrap.panel.TabItem({
37617             panel: this,
37618             id : id,
37619             text : text,
37620             closable : closable,
37621             tpl : tpl
37622         });
37623         this.addTabItem(item);
37624         if(content){
37625             item.setContent(content);
37626         }
37627         return item;
37628     },
37629
37630     /**
37631      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37632      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37633      * @return {Roo.TabPanelItem}
37634      */
37635     getTab : function(id){
37636         return this.items[id];
37637     },
37638
37639     /**
37640      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37641      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37642      */
37643     hideTab : function(id){
37644         var t = this.items[id];
37645         if(!t.isHidden()){
37646            t.setHidden(true);
37647            this.hiddenCount++;
37648            this.autoSizeTabs();
37649         }
37650     },
37651
37652     /**
37653      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37654      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37655      */
37656     unhideTab : function(id){
37657         var t = this.items[id];
37658         if(t.isHidden()){
37659            t.setHidden(false);
37660            this.hiddenCount--;
37661            this.autoSizeTabs();
37662         }
37663     },
37664
37665     /**
37666      * Adds an existing {@link Roo.TabPanelItem}.
37667      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37668      */
37669     addTabItem : function(item){
37670         this.items[item.id] = item;
37671         this.items.push(item);
37672       //  if(this.resizeTabs){
37673     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37674   //         this.autoSizeTabs();
37675 //        }else{
37676 //            item.autoSize();
37677        // }
37678     },
37679
37680     /**
37681      * Removes a {@link Roo.TabPanelItem}.
37682      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37683      */
37684     removeTab : function(id){
37685         var items = this.items;
37686         var tab = items[id];
37687         if(!tab) { return; }
37688         var index = items.indexOf(tab);
37689         if(this.active == tab && items.length > 1){
37690             var newTab = this.getNextAvailable(index);
37691             if(newTab) {
37692                 newTab.activate();
37693             }
37694         }
37695         this.stripEl.dom.removeChild(tab.pnode.dom);
37696         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37697             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37698         }
37699         items.splice(index, 1);
37700         delete this.items[tab.id];
37701         tab.fireEvent("close", tab);
37702         tab.purgeListeners();
37703         this.autoSizeTabs();
37704     },
37705
37706     getNextAvailable : function(start){
37707         var items = this.items;
37708         var index = start;
37709         // look for a next tab that will slide over to
37710         // replace the one being removed
37711         while(index < items.length){
37712             var item = items[++index];
37713             if(item && !item.isHidden()){
37714                 return item;
37715             }
37716         }
37717         // if one isn't found select the previous tab (on the left)
37718         index = start;
37719         while(index >= 0){
37720             var item = items[--index];
37721             if(item && !item.isHidden()){
37722                 return item;
37723             }
37724         }
37725         return null;
37726     },
37727
37728     /**
37729      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37730      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37731      */
37732     disableTab : function(id){
37733         var tab = this.items[id];
37734         if(tab && this.active != tab){
37735             tab.disable();
37736         }
37737     },
37738
37739     /**
37740      * Enables a {@link Roo.TabPanelItem} that is disabled.
37741      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37742      */
37743     enableTab : function(id){
37744         var tab = this.items[id];
37745         tab.enable();
37746     },
37747
37748     /**
37749      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37750      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37751      * @return {Roo.TabPanelItem} The TabPanelItem.
37752      */
37753     activate : function(id){
37754         var tab = this.items[id];
37755         if(!tab){
37756             return null;
37757         }
37758         if(tab == this.active || tab.disabled){
37759             return tab;
37760         }
37761         var e = {};
37762         this.fireEvent("beforetabchange", this, e, tab);
37763         if(e.cancel !== true && !tab.disabled){
37764             if(this.active){
37765                 this.active.hide();
37766             }
37767             this.active = this.items[id];
37768             this.active.show();
37769             this.fireEvent("tabchange", this, this.active);
37770         }
37771         return tab;
37772     },
37773
37774     /**
37775      * Gets the active {@link Roo.TabPanelItem}.
37776      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37777      */
37778     getActiveTab : function(){
37779         return this.active;
37780     },
37781
37782     /**
37783      * Updates the tab body element to fit the height of the container element
37784      * for overflow scrolling
37785      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37786      */
37787     syncHeight : function(targetHeight){
37788         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37789         var bm = this.bodyEl.getMargins();
37790         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37791         this.bodyEl.setHeight(newHeight);
37792         return newHeight;
37793     },
37794
37795     onResize : function(){
37796         if(this.monitorResize){
37797             this.autoSizeTabs();
37798         }
37799     },
37800
37801     /**
37802      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37803      */
37804     beginUpdate : function(){
37805         this.updating = true;
37806     },
37807
37808     /**
37809      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37810      */
37811     endUpdate : function(){
37812         this.updating = false;
37813         this.autoSizeTabs();
37814     },
37815
37816     /**
37817      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37818      */
37819     autoSizeTabs : function(){
37820         var count = this.items.length;
37821         var vcount = count - this.hiddenCount;
37822         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37823             return;
37824         }
37825         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37826         var availWidth = Math.floor(w / vcount);
37827         var b = this.stripBody;
37828         if(b.getWidth() > w){
37829             var tabs = this.items;
37830             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37831             if(availWidth < this.minTabWidth){
37832                 /*if(!this.sleft){    // incomplete scrolling code
37833                     this.createScrollButtons();
37834                 }
37835                 this.showScroll();
37836                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37837             }
37838         }else{
37839             if(this.currentTabWidth < this.preferredTabWidth){
37840                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37841             }
37842         }
37843     },
37844
37845     /**
37846      * Returns the number of tabs in this TabPanel.
37847      * @return {Number}
37848      */
37849      getCount : function(){
37850          return this.items.length;
37851      },
37852
37853     /**
37854      * Resizes all the tabs to the passed width
37855      * @param {Number} The new width
37856      */
37857     setTabWidth : function(width){
37858         this.currentTabWidth = width;
37859         for(var i = 0, len = this.items.length; i < len; i++) {
37860                 if(!this.items[i].isHidden()) {
37861                 this.items[i].setWidth(width);
37862             }
37863         }
37864     },
37865
37866     /**
37867      * Destroys this TabPanel
37868      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37869      */
37870     destroy : function(removeEl){
37871         Roo.EventManager.removeResizeListener(this.onResize, this);
37872         for(var i = 0, len = this.items.length; i < len; i++){
37873             this.items[i].purgeListeners();
37874         }
37875         if(removeEl === true){
37876             this.el.update("");
37877             this.el.remove();
37878         }
37879     },
37880     
37881     createStrip : function(container)
37882     {
37883         var strip = document.createElement("nav");
37884         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37885         container.appendChild(strip);
37886         return strip;
37887     },
37888     
37889     createStripList : function(strip)
37890     {
37891         // div wrapper for retard IE
37892         // returns the "tr" element.
37893         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37894         //'<div class="x-tabs-strip-wrap">'+
37895           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37896           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37897         return strip.firstChild; //.firstChild.firstChild.firstChild;
37898     },
37899     createBody : function(container)
37900     {
37901         var body = document.createElement("div");
37902         Roo.id(body, "tab-body");
37903         //Roo.fly(body).addClass("x-tabs-body");
37904         Roo.fly(body).addClass("tab-content");
37905         container.appendChild(body);
37906         return body;
37907     },
37908     createItemBody :function(bodyEl, id){
37909         var body = Roo.getDom(id);
37910         if(!body){
37911             body = document.createElement("div");
37912             body.id = id;
37913         }
37914         //Roo.fly(body).addClass("x-tabs-item-body");
37915         Roo.fly(body).addClass("tab-pane");
37916          bodyEl.insertBefore(body, bodyEl.firstChild);
37917         return body;
37918     },
37919     /** @private */
37920     createStripElements :  function(stripEl, text, closable, tpl)
37921     {
37922         var td = document.createElement("li"); // was td..
37923         
37924         
37925         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37926         
37927         
37928         stripEl.appendChild(td);
37929         /*if(closable){
37930             td.className = "x-tabs-closable";
37931             if(!this.closeTpl){
37932                 this.closeTpl = new Roo.Template(
37933                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37934                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37935                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37936                 );
37937             }
37938             var el = this.closeTpl.overwrite(td, {"text": text});
37939             var close = el.getElementsByTagName("div")[0];
37940             var inner = el.getElementsByTagName("em")[0];
37941             return {"el": el, "close": close, "inner": inner};
37942         } else {
37943         */
37944         // not sure what this is..
37945 //            if(!this.tabTpl){
37946                 //this.tabTpl = new Roo.Template(
37947                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37948                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37949                 //);
37950 //                this.tabTpl = new Roo.Template(
37951 //                   '<a href="#">' +
37952 //                   '<span unselectable="on"' +
37953 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37954 //                            ' >{text}</span></a>'
37955 //                );
37956 //                
37957 //            }
37958
37959
37960             var template = tpl || this.tabTpl || false;
37961             
37962             if(!template){
37963                 
37964                 template = new Roo.Template(
37965                    '<a href="#">' +
37966                    '<span unselectable="on"' +
37967                             (this.disableTooltips ? '' : ' title="{text}"') +
37968                             ' >{text}</span></a>'
37969                 );
37970             }
37971             
37972             switch (typeof(template)) {
37973                 case 'object' :
37974                     break;
37975                 case 'string' :
37976                     template = new Roo.Template(template);
37977                     break;
37978                 default :
37979                     break;
37980             }
37981             
37982             var el = template.overwrite(td, {"text": text});
37983             
37984             var inner = el.getElementsByTagName("span")[0];
37985             
37986             return {"el": el, "inner": inner};
37987             
37988     }
37989         
37990     
37991 });
37992
37993 /**
37994  * @class Roo.TabPanelItem
37995  * @extends Roo.util.Observable
37996  * Represents an individual item (tab plus body) in a TabPanel.
37997  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37998  * @param {String} id The id of this TabPanelItem
37999  * @param {String} text The text for the tab of this TabPanelItem
38000  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38001  */
38002 Roo.bootstrap.panel.TabItem = function(config){
38003     /**
38004      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38005      * @type Roo.TabPanel
38006      */
38007     this.tabPanel = config.panel;
38008     /**
38009      * The id for this TabPanelItem
38010      * @type String
38011      */
38012     this.id = config.id;
38013     /** @private */
38014     this.disabled = false;
38015     /** @private */
38016     this.text = config.text;
38017     /** @private */
38018     this.loaded = false;
38019     this.closable = config.closable;
38020
38021     /**
38022      * The body element for this TabPanelItem.
38023      * @type Roo.Element
38024      */
38025     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38026     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38027     this.bodyEl.setStyle("display", "block");
38028     this.bodyEl.setStyle("zoom", "1");
38029     //this.hideAction();
38030
38031     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38032     /** @private */
38033     this.el = Roo.get(els.el);
38034     this.inner = Roo.get(els.inner, true);
38035     this.textEl = Roo.get(this.el.dom.firstChild, true);
38036     this.pnode = Roo.get(els.el.parentNode, true);
38037 //    this.el.on("mousedown", this.onTabMouseDown, this);
38038     this.el.on("click", this.onTabClick, this);
38039     /** @private */
38040     if(config.closable){
38041         var c = Roo.get(els.close, true);
38042         c.dom.title = this.closeText;
38043         c.addClassOnOver("close-over");
38044         c.on("click", this.closeClick, this);
38045      }
38046
38047     this.addEvents({
38048          /**
38049          * @event activate
38050          * Fires when this tab becomes the active tab.
38051          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38052          * @param {Roo.TabPanelItem} this
38053          */
38054         "activate": true,
38055         /**
38056          * @event beforeclose
38057          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38058          * @param {Roo.TabPanelItem} this
38059          * @param {Object} e Set cancel to true on this object to cancel the close.
38060          */
38061         "beforeclose": true,
38062         /**
38063          * @event close
38064          * Fires when this tab is closed.
38065          * @param {Roo.TabPanelItem} this
38066          */
38067          "close": true,
38068         /**
38069          * @event deactivate
38070          * Fires when this tab is no longer the active tab.
38071          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38072          * @param {Roo.TabPanelItem} this
38073          */
38074          "deactivate" : true
38075     });
38076     this.hidden = false;
38077
38078     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38079 };
38080
38081 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38082            {
38083     purgeListeners : function(){
38084        Roo.util.Observable.prototype.purgeListeners.call(this);
38085        this.el.removeAllListeners();
38086     },
38087     /**
38088      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38089      */
38090     show : function(){
38091         this.pnode.addClass("active");
38092         this.showAction();
38093         if(Roo.isOpera){
38094             this.tabPanel.stripWrap.repaint();
38095         }
38096         this.fireEvent("activate", this.tabPanel, this);
38097     },
38098
38099     /**
38100      * Returns true if this tab is the active tab.
38101      * @return {Boolean}
38102      */
38103     isActive : function(){
38104         return this.tabPanel.getActiveTab() == this;
38105     },
38106
38107     /**
38108      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38109      */
38110     hide : function(){
38111         this.pnode.removeClass("active");
38112         this.hideAction();
38113         this.fireEvent("deactivate", this.tabPanel, this);
38114     },
38115
38116     hideAction : function(){
38117         this.bodyEl.hide();
38118         this.bodyEl.setStyle("position", "absolute");
38119         this.bodyEl.setLeft("-20000px");
38120         this.bodyEl.setTop("-20000px");
38121     },
38122
38123     showAction : function(){
38124         this.bodyEl.setStyle("position", "relative");
38125         this.bodyEl.setTop("");
38126         this.bodyEl.setLeft("");
38127         this.bodyEl.show();
38128     },
38129
38130     /**
38131      * Set the tooltip for the tab.
38132      * @param {String} tooltip The tab's tooltip
38133      */
38134     setTooltip : function(text){
38135         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38136             this.textEl.dom.qtip = text;
38137             this.textEl.dom.removeAttribute('title');
38138         }else{
38139             this.textEl.dom.title = text;
38140         }
38141     },
38142
38143     onTabClick : function(e){
38144         e.preventDefault();
38145         this.tabPanel.activate(this.id);
38146     },
38147
38148     onTabMouseDown : function(e){
38149         e.preventDefault();
38150         this.tabPanel.activate(this.id);
38151     },
38152 /*
38153     getWidth : function(){
38154         return this.inner.getWidth();
38155     },
38156
38157     setWidth : function(width){
38158         var iwidth = width - this.pnode.getPadding("lr");
38159         this.inner.setWidth(iwidth);
38160         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38161         this.pnode.setWidth(width);
38162     },
38163 */
38164     /**
38165      * Show or hide the tab
38166      * @param {Boolean} hidden True to hide or false to show.
38167      */
38168     setHidden : function(hidden){
38169         this.hidden = hidden;
38170         this.pnode.setStyle("display", hidden ? "none" : "");
38171     },
38172
38173     /**
38174      * Returns true if this tab is "hidden"
38175      * @return {Boolean}
38176      */
38177     isHidden : function(){
38178         return this.hidden;
38179     },
38180
38181     /**
38182      * Returns the text for this tab
38183      * @return {String}
38184      */
38185     getText : function(){
38186         return this.text;
38187     },
38188     /*
38189     autoSize : function(){
38190         //this.el.beginMeasure();
38191         this.textEl.setWidth(1);
38192         /*
38193          *  #2804 [new] Tabs in Roojs
38194          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38195          */
38196         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38197         //this.el.endMeasure();
38198     //},
38199
38200     /**
38201      * Sets the text for the tab (Note: this also sets the tooltip text)
38202      * @param {String} text The tab's text and tooltip
38203      */
38204     setText : function(text){
38205         this.text = text;
38206         this.textEl.update(text);
38207         this.setTooltip(text);
38208         //if(!this.tabPanel.resizeTabs){
38209         //    this.autoSize();
38210         //}
38211     },
38212     /**
38213      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38214      */
38215     activate : function(){
38216         this.tabPanel.activate(this.id);
38217     },
38218
38219     /**
38220      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38221      */
38222     disable : function(){
38223         if(this.tabPanel.active != this){
38224             this.disabled = true;
38225             this.pnode.addClass("disabled");
38226         }
38227     },
38228
38229     /**
38230      * Enables this TabPanelItem if it was previously disabled.
38231      */
38232     enable : function(){
38233         this.disabled = false;
38234         this.pnode.removeClass("disabled");
38235     },
38236
38237     /**
38238      * Sets the content for this TabPanelItem.
38239      * @param {String} content The content
38240      * @param {Boolean} loadScripts true to look for and load scripts
38241      */
38242     setContent : function(content, loadScripts){
38243         this.bodyEl.update(content, loadScripts);
38244     },
38245
38246     /**
38247      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38248      * @return {Roo.UpdateManager} The UpdateManager
38249      */
38250     getUpdateManager : function(){
38251         return this.bodyEl.getUpdateManager();
38252     },
38253
38254     /**
38255      * Set a URL to be used to load the content for this TabPanelItem.
38256      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38257      * @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)
38258      * @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)
38259      * @return {Roo.UpdateManager} The UpdateManager
38260      */
38261     setUrl : function(url, params, loadOnce){
38262         if(this.refreshDelegate){
38263             this.un('activate', this.refreshDelegate);
38264         }
38265         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38266         this.on("activate", this.refreshDelegate);
38267         return this.bodyEl.getUpdateManager();
38268     },
38269
38270     /** @private */
38271     _handleRefresh : function(url, params, loadOnce){
38272         if(!loadOnce || !this.loaded){
38273             var updater = this.bodyEl.getUpdateManager();
38274             updater.update(url, params, this._setLoaded.createDelegate(this));
38275         }
38276     },
38277
38278     /**
38279      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38280      *   Will fail silently if the setUrl method has not been called.
38281      *   This does not activate the panel, just updates its content.
38282      */
38283     refresh : function(){
38284         if(this.refreshDelegate){
38285            this.loaded = false;
38286            this.refreshDelegate();
38287         }
38288     },
38289
38290     /** @private */
38291     _setLoaded : function(){
38292         this.loaded = true;
38293     },
38294
38295     /** @private */
38296     closeClick : function(e){
38297         var o = {};
38298         e.stopEvent();
38299         this.fireEvent("beforeclose", this, o);
38300         if(o.cancel !== true){
38301             this.tabPanel.removeTab(this.id);
38302         }
38303     },
38304     /**
38305      * The text displayed in the tooltip for the close icon.
38306      * @type String
38307      */
38308     closeText : "Close this tab"
38309 });
38310 /**
38311 *    This script refer to:
38312 *    Title: International Telephone Input
38313 *    Author: Jack O'Connor
38314 *    Code version:  v12.1.12
38315 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38316 **/
38317
38318 Roo.bootstrap.PhoneInputData = function() {
38319     var d = [
38320       [
38321         "Afghanistan (‫افغانستان‬‎)",
38322         "af",
38323         "93"
38324       ],
38325       [
38326         "Albania (Shqipëri)",
38327         "al",
38328         "355"
38329       ],
38330       [
38331         "Algeria (‫الجزائر‬‎)",
38332         "dz",
38333         "213"
38334       ],
38335       [
38336         "American Samoa",
38337         "as",
38338         "1684"
38339       ],
38340       [
38341         "Andorra",
38342         "ad",
38343         "376"
38344       ],
38345       [
38346         "Angola",
38347         "ao",
38348         "244"
38349       ],
38350       [
38351         "Anguilla",
38352         "ai",
38353         "1264"
38354       ],
38355       [
38356         "Antigua and Barbuda",
38357         "ag",
38358         "1268"
38359       ],
38360       [
38361         "Argentina",
38362         "ar",
38363         "54"
38364       ],
38365       [
38366         "Armenia (Հայաստան)",
38367         "am",
38368         "374"
38369       ],
38370       [
38371         "Aruba",
38372         "aw",
38373         "297"
38374       ],
38375       [
38376         "Australia",
38377         "au",
38378         "61",
38379         0
38380       ],
38381       [
38382         "Austria (Österreich)",
38383         "at",
38384         "43"
38385       ],
38386       [
38387         "Azerbaijan (Azərbaycan)",
38388         "az",
38389         "994"
38390       ],
38391       [
38392         "Bahamas",
38393         "bs",
38394         "1242"
38395       ],
38396       [
38397         "Bahrain (‫البحرين‬‎)",
38398         "bh",
38399         "973"
38400       ],
38401       [
38402         "Bangladesh (বাংলাদেশ)",
38403         "bd",
38404         "880"
38405       ],
38406       [
38407         "Barbados",
38408         "bb",
38409         "1246"
38410       ],
38411       [
38412         "Belarus (Беларусь)",
38413         "by",
38414         "375"
38415       ],
38416       [
38417         "Belgium (België)",
38418         "be",
38419         "32"
38420       ],
38421       [
38422         "Belize",
38423         "bz",
38424         "501"
38425       ],
38426       [
38427         "Benin (Bénin)",
38428         "bj",
38429         "229"
38430       ],
38431       [
38432         "Bermuda",
38433         "bm",
38434         "1441"
38435       ],
38436       [
38437         "Bhutan (འབྲུག)",
38438         "bt",
38439         "975"
38440       ],
38441       [
38442         "Bolivia",
38443         "bo",
38444         "591"
38445       ],
38446       [
38447         "Bosnia and Herzegovina (Босна и Херцеговина)",
38448         "ba",
38449         "387"
38450       ],
38451       [
38452         "Botswana",
38453         "bw",
38454         "267"
38455       ],
38456       [
38457         "Brazil (Brasil)",
38458         "br",
38459         "55"
38460       ],
38461       [
38462         "British Indian Ocean Territory",
38463         "io",
38464         "246"
38465       ],
38466       [
38467         "British Virgin Islands",
38468         "vg",
38469         "1284"
38470       ],
38471       [
38472         "Brunei",
38473         "bn",
38474         "673"
38475       ],
38476       [
38477         "Bulgaria (България)",
38478         "bg",
38479         "359"
38480       ],
38481       [
38482         "Burkina Faso",
38483         "bf",
38484         "226"
38485       ],
38486       [
38487         "Burundi (Uburundi)",
38488         "bi",
38489         "257"
38490       ],
38491       [
38492         "Cambodia (កម្ពុជា)",
38493         "kh",
38494         "855"
38495       ],
38496       [
38497         "Cameroon (Cameroun)",
38498         "cm",
38499         "237"
38500       ],
38501       [
38502         "Canada",
38503         "ca",
38504         "1",
38505         1,
38506         ["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"]
38507       ],
38508       [
38509         "Cape Verde (Kabu Verdi)",
38510         "cv",
38511         "238"
38512       ],
38513       [
38514         "Caribbean Netherlands",
38515         "bq",
38516         "599",
38517         1
38518       ],
38519       [
38520         "Cayman Islands",
38521         "ky",
38522         "1345"
38523       ],
38524       [
38525         "Central African Republic (République centrafricaine)",
38526         "cf",
38527         "236"
38528       ],
38529       [
38530         "Chad (Tchad)",
38531         "td",
38532         "235"
38533       ],
38534       [
38535         "Chile",
38536         "cl",
38537         "56"
38538       ],
38539       [
38540         "China (中国)",
38541         "cn",
38542         "86"
38543       ],
38544       [
38545         "Christmas Island",
38546         "cx",
38547         "61",
38548         2
38549       ],
38550       [
38551         "Cocos (Keeling) Islands",
38552         "cc",
38553         "61",
38554         1
38555       ],
38556       [
38557         "Colombia",
38558         "co",
38559         "57"
38560       ],
38561       [
38562         "Comoros (‫جزر القمر‬‎)",
38563         "km",
38564         "269"
38565       ],
38566       [
38567         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38568         "cd",
38569         "243"
38570       ],
38571       [
38572         "Congo (Republic) (Congo-Brazzaville)",
38573         "cg",
38574         "242"
38575       ],
38576       [
38577         "Cook Islands",
38578         "ck",
38579         "682"
38580       ],
38581       [
38582         "Costa Rica",
38583         "cr",
38584         "506"
38585       ],
38586       [
38587         "Côte d’Ivoire",
38588         "ci",
38589         "225"
38590       ],
38591       [
38592         "Croatia (Hrvatska)",
38593         "hr",
38594         "385"
38595       ],
38596       [
38597         "Cuba",
38598         "cu",
38599         "53"
38600       ],
38601       [
38602         "Curaçao",
38603         "cw",
38604         "599",
38605         0
38606       ],
38607       [
38608         "Cyprus (Κύπρος)",
38609         "cy",
38610         "357"
38611       ],
38612       [
38613         "Czech Republic (Česká republika)",
38614         "cz",
38615         "420"
38616       ],
38617       [
38618         "Denmark (Danmark)",
38619         "dk",
38620         "45"
38621       ],
38622       [
38623         "Djibouti",
38624         "dj",
38625         "253"
38626       ],
38627       [
38628         "Dominica",
38629         "dm",
38630         "1767"
38631       ],
38632       [
38633         "Dominican Republic (República Dominicana)",
38634         "do",
38635         "1",
38636         2,
38637         ["809", "829", "849"]
38638       ],
38639       [
38640         "Ecuador",
38641         "ec",
38642         "593"
38643       ],
38644       [
38645         "Egypt (‫مصر‬‎)",
38646         "eg",
38647         "20"
38648       ],
38649       [
38650         "El Salvador",
38651         "sv",
38652         "503"
38653       ],
38654       [
38655         "Equatorial Guinea (Guinea Ecuatorial)",
38656         "gq",
38657         "240"
38658       ],
38659       [
38660         "Eritrea",
38661         "er",
38662         "291"
38663       ],
38664       [
38665         "Estonia (Eesti)",
38666         "ee",
38667         "372"
38668       ],
38669       [
38670         "Ethiopia",
38671         "et",
38672         "251"
38673       ],
38674       [
38675         "Falkland Islands (Islas Malvinas)",
38676         "fk",
38677         "500"
38678       ],
38679       [
38680         "Faroe Islands (Føroyar)",
38681         "fo",
38682         "298"
38683       ],
38684       [
38685         "Fiji",
38686         "fj",
38687         "679"
38688       ],
38689       [
38690         "Finland (Suomi)",
38691         "fi",
38692         "358",
38693         0
38694       ],
38695       [
38696         "France",
38697         "fr",
38698         "33"
38699       ],
38700       [
38701         "French Guiana (Guyane française)",
38702         "gf",
38703         "594"
38704       ],
38705       [
38706         "French Polynesia (Polynésie française)",
38707         "pf",
38708         "689"
38709       ],
38710       [
38711         "Gabon",
38712         "ga",
38713         "241"
38714       ],
38715       [
38716         "Gambia",
38717         "gm",
38718         "220"
38719       ],
38720       [
38721         "Georgia (საქართველო)",
38722         "ge",
38723         "995"
38724       ],
38725       [
38726         "Germany (Deutschland)",
38727         "de",
38728         "49"
38729       ],
38730       [
38731         "Ghana (Gaana)",
38732         "gh",
38733         "233"
38734       ],
38735       [
38736         "Gibraltar",
38737         "gi",
38738         "350"
38739       ],
38740       [
38741         "Greece (Ελλάδα)",
38742         "gr",
38743         "30"
38744       ],
38745       [
38746         "Greenland (Kalaallit Nunaat)",
38747         "gl",
38748         "299"
38749       ],
38750       [
38751         "Grenada",
38752         "gd",
38753         "1473"
38754       ],
38755       [
38756         "Guadeloupe",
38757         "gp",
38758         "590",
38759         0
38760       ],
38761       [
38762         "Guam",
38763         "gu",
38764         "1671"
38765       ],
38766       [
38767         "Guatemala",
38768         "gt",
38769         "502"
38770       ],
38771       [
38772         "Guernsey",
38773         "gg",
38774         "44",
38775         1
38776       ],
38777       [
38778         "Guinea (Guinée)",
38779         "gn",
38780         "224"
38781       ],
38782       [
38783         "Guinea-Bissau (Guiné Bissau)",
38784         "gw",
38785         "245"
38786       ],
38787       [
38788         "Guyana",
38789         "gy",
38790         "592"
38791       ],
38792       [
38793         "Haiti",
38794         "ht",
38795         "509"
38796       ],
38797       [
38798         "Honduras",
38799         "hn",
38800         "504"
38801       ],
38802       [
38803         "Hong Kong (香港)",
38804         "hk",
38805         "852"
38806       ],
38807       [
38808         "Hungary (Magyarország)",
38809         "hu",
38810         "36"
38811       ],
38812       [
38813         "Iceland (Ísland)",
38814         "is",
38815         "354"
38816       ],
38817       [
38818         "India (भारत)",
38819         "in",
38820         "91"
38821       ],
38822       [
38823         "Indonesia",
38824         "id",
38825         "62"
38826       ],
38827       [
38828         "Iran (‫ایران‬‎)",
38829         "ir",
38830         "98"
38831       ],
38832       [
38833         "Iraq (‫العراق‬‎)",
38834         "iq",
38835         "964"
38836       ],
38837       [
38838         "Ireland",
38839         "ie",
38840         "353"
38841       ],
38842       [
38843         "Isle of Man",
38844         "im",
38845         "44",
38846         2
38847       ],
38848       [
38849         "Israel (‫ישראל‬‎)",
38850         "il",
38851         "972"
38852       ],
38853       [
38854         "Italy (Italia)",
38855         "it",
38856         "39",
38857         0
38858       ],
38859       [
38860         "Jamaica",
38861         "jm",
38862         "1876"
38863       ],
38864       [
38865         "Japan (日本)",
38866         "jp",
38867         "81"
38868       ],
38869       [
38870         "Jersey",
38871         "je",
38872         "44",
38873         3
38874       ],
38875       [
38876         "Jordan (‫الأردن‬‎)",
38877         "jo",
38878         "962"
38879       ],
38880       [
38881         "Kazakhstan (Казахстан)",
38882         "kz",
38883         "7",
38884         1
38885       ],
38886       [
38887         "Kenya",
38888         "ke",
38889         "254"
38890       ],
38891       [
38892         "Kiribati",
38893         "ki",
38894         "686"
38895       ],
38896       [
38897         "Kosovo",
38898         "xk",
38899         "383"
38900       ],
38901       [
38902         "Kuwait (‫الكويت‬‎)",
38903         "kw",
38904         "965"
38905       ],
38906       [
38907         "Kyrgyzstan (Кыргызстан)",
38908         "kg",
38909         "996"
38910       ],
38911       [
38912         "Laos (ລາວ)",
38913         "la",
38914         "856"
38915       ],
38916       [
38917         "Latvia (Latvija)",
38918         "lv",
38919         "371"
38920       ],
38921       [
38922         "Lebanon (‫لبنان‬‎)",
38923         "lb",
38924         "961"
38925       ],
38926       [
38927         "Lesotho",
38928         "ls",
38929         "266"
38930       ],
38931       [
38932         "Liberia",
38933         "lr",
38934         "231"
38935       ],
38936       [
38937         "Libya (‫ليبيا‬‎)",
38938         "ly",
38939         "218"
38940       ],
38941       [
38942         "Liechtenstein",
38943         "li",
38944         "423"
38945       ],
38946       [
38947         "Lithuania (Lietuva)",
38948         "lt",
38949         "370"
38950       ],
38951       [
38952         "Luxembourg",
38953         "lu",
38954         "352"
38955       ],
38956       [
38957         "Macau (澳門)",
38958         "mo",
38959         "853"
38960       ],
38961       [
38962         "Macedonia (FYROM) (Македонија)",
38963         "mk",
38964         "389"
38965       ],
38966       [
38967         "Madagascar (Madagasikara)",
38968         "mg",
38969         "261"
38970       ],
38971       [
38972         "Malawi",
38973         "mw",
38974         "265"
38975       ],
38976       [
38977         "Malaysia",
38978         "my",
38979         "60"
38980       ],
38981       [
38982         "Maldives",
38983         "mv",
38984         "960"
38985       ],
38986       [
38987         "Mali",
38988         "ml",
38989         "223"
38990       ],
38991       [
38992         "Malta",
38993         "mt",
38994         "356"
38995       ],
38996       [
38997         "Marshall Islands",
38998         "mh",
38999         "692"
39000       ],
39001       [
39002         "Martinique",
39003         "mq",
39004         "596"
39005       ],
39006       [
39007         "Mauritania (‫موريتانيا‬‎)",
39008         "mr",
39009         "222"
39010       ],
39011       [
39012         "Mauritius (Moris)",
39013         "mu",
39014         "230"
39015       ],
39016       [
39017         "Mayotte",
39018         "yt",
39019         "262",
39020         1
39021       ],
39022       [
39023         "Mexico (México)",
39024         "mx",
39025         "52"
39026       ],
39027       [
39028         "Micronesia",
39029         "fm",
39030         "691"
39031       ],
39032       [
39033         "Moldova (Republica Moldova)",
39034         "md",
39035         "373"
39036       ],
39037       [
39038         "Monaco",
39039         "mc",
39040         "377"
39041       ],
39042       [
39043         "Mongolia (Монгол)",
39044         "mn",
39045         "976"
39046       ],
39047       [
39048         "Montenegro (Crna Gora)",
39049         "me",
39050         "382"
39051       ],
39052       [
39053         "Montserrat",
39054         "ms",
39055         "1664"
39056       ],
39057       [
39058         "Morocco (‫المغرب‬‎)",
39059         "ma",
39060         "212",
39061         0
39062       ],
39063       [
39064         "Mozambique (Moçambique)",
39065         "mz",
39066         "258"
39067       ],
39068       [
39069         "Myanmar (Burma) (မြန်မာ)",
39070         "mm",
39071         "95"
39072       ],
39073       [
39074         "Namibia (Namibië)",
39075         "na",
39076         "264"
39077       ],
39078       [
39079         "Nauru",
39080         "nr",
39081         "674"
39082       ],
39083       [
39084         "Nepal (नेपाल)",
39085         "np",
39086         "977"
39087       ],
39088       [
39089         "Netherlands (Nederland)",
39090         "nl",
39091         "31"
39092       ],
39093       [
39094         "New Caledonia (Nouvelle-Calédonie)",
39095         "nc",
39096         "687"
39097       ],
39098       [
39099         "New Zealand",
39100         "nz",
39101         "64"
39102       ],
39103       [
39104         "Nicaragua",
39105         "ni",
39106         "505"
39107       ],
39108       [
39109         "Niger (Nijar)",
39110         "ne",
39111         "227"
39112       ],
39113       [
39114         "Nigeria",
39115         "ng",
39116         "234"
39117       ],
39118       [
39119         "Niue",
39120         "nu",
39121         "683"
39122       ],
39123       [
39124         "Norfolk Island",
39125         "nf",
39126         "672"
39127       ],
39128       [
39129         "North Korea (조선 민주주의 인민 공화국)",
39130         "kp",
39131         "850"
39132       ],
39133       [
39134         "Northern Mariana Islands",
39135         "mp",
39136         "1670"
39137       ],
39138       [
39139         "Norway (Norge)",
39140         "no",
39141         "47",
39142         0
39143       ],
39144       [
39145         "Oman (‫عُمان‬‎)",
39146         "om",
39147         "968"
39148       ],
39149       [
39150         "Pakistan (‫پاکستان‬‎)",
39151         "pk",
39152         "92"
39153       ],
39154       [
39155         "Palau",
39156         "pw",
39157         "680"
39158       ],
39159       [
39160         "Palestine (‫فلسطين‬‎)",
39161         "ps",
39162         "970"
39163       ],
39164       [
39165         "Panama (Panamá)",
39166         "pa",
39167         "507"
39168       ],
39169       [
39170         "Papua New Guinea",
39171         "pg",
39172         "675"
39173       ],
39174       [
39175         "Paraguay",
39176         "py",
39177         "595"
39178       ],
39179       [
39180         "Peru (Perú)",
39181         "pe",
39182         "51"
39183       ],
39184       [
39185         "Philippines",
39186         "ph",
39187         "63"
39188       ],
39189       [
39190         "Poland (Polska)",
39191         "pl",
39192         "48"
39193       ],
39194       [
39195         "Portugal",
39196         "pt",
39197         "351"
39198       ],
39199       [
39200         "Puerto Rico",
39201         "pr",
39202         "1",
39203         3,
39204         ["787", "939"]
39205       ],
39206       [
39207         "Qatar (‫قطر‬‎)",
39208         "qa",
39209         "974"
39210       ],
39211       [
39212         "Réunion (La Réunion)",
39213         "re",
39214         "262",
39215         0
39216       ],
39217       [
39218         "Romania (România)",
39219         "ro",
39220         "40"
39221       ],
39222       [
39223         "Russia (Россия)",
39224         "ru",
39225         "7",
39226         0
39227       ],
39228       [
39229         "Rwanda",
39230         "rw",
39231         "250"
39232       ],
39233       [
39234         "Saint Barthélemy",
39235         "bl",
39236         "590",
39237         1
39238       ],
39239       [
39240         "Saint Helena",
39241         "sh",
39242         "290"
39243       ],
39244       [
39245         "Saint Kitts and Nevis",
39246         "kn",
39247         "1869"
39248       ],
39249       [
39250         "Saint Lucia",
39251         "lc",
39252         "1758"
39253       ],
39254       [
39255         "Saint Martin (Saint-Martin (partie française))",
39256         "mf",
39257         "590",
39258         2
39259       ],
39260       [
39261         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39262         "pm",
39263         "508"
39264       ],
39265       [
39266         "Saint Vincent and the Grenadines",
39267         "vc",
39268         "1784"
39269       ],
39270       [
39271         "Samoa",
39272         "ws",
39273         "685"
39274       ],
39275       [
39276         "San Marino",
39277         "sm",
39278         "378"
39279       ],
39280       [
39281         "São Tomé and Príncipe (São Tomé e Príncipe)",
39282         "st",
39283         "239"
39284       ],
39285       [
39286         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39287         "sa",
39288         "966"
39289       ],
39290       [
39291         "Senegal (Sénégal)",
39292         "sn",
39293         "221"
39294       ],
39295       [
39296         "Serbia (Србија)",
39297         "rs",
39298         "381"
39299       ],
39300       [
39301         "Seychelles",
39302         "sc",
39303         "248"
39304       ],
39305       [
39306         "Sierra Leone",
39307         "sl",
39308         "232"
39309       ],
39310       [
39311         "Singapore",
39312         "sg",
39313         "65"
39314       ],
39315       [
39316         "Sint Maarten",
39317         "sx",
39318         "1721"
39319       ],
39320       [
39321         "Slovakia (Slovensko)",
39322         "sk",
39323         "421"
39324       ],
39325       [
39326         "Slovenia (Slovenija)",
39327         "si",
39328         "386"
39329       ],
39330       [
39331         "Solomon Islands",
39332         "sb",
39333         "677"
39334       ],
39335       [
39336         "Somalia (Soomaaliya)",
39337         "so",
39338         "252"
39339       ],
39340       [
39341         "South Africa",
39342         "za",
39343         "27"
39344       ],
39345       [
39346         "South Korea (대한민국)",
39347         "kr",
39348         "82"
39349       ],
39350       [
39351         "South Sudan (‫جنوب السودان‬‎)",
39352         "ss",
39353         "211"
39354       ],
39355       [
39356         "Spain (España)",
39357         "es",
39358         "34"
39359       ],
39360       [
39361         "Sri Lanka (ශ්‍රී ලංකාව)",
39362         "lk",
39363         "94"
39364       ],
39365       [
39366         "Sudan (‫السودان‬‎)",
39367         "sd",
39368         "249"
39369       ],
39370       [
39371         "Suriname",
39372         "sr",
39373         "597"
39374       ],
39375       [
39376         "Svalbard and Jan Mayen",
39377         "sj",
39378         "47",
39379         1
39380       ],
39381       [
39382         "Swaziland",
39383         "sz",
39384         "268"
39385       ],
39386       [
39387         "Sweden (Sverige)",
39388         "se",
39389         "46"
39390       ],
39391       [
39392         "Switzerland (Schweiz)",
39393         "ch",
39394         "41"
39395       ],
39396       [
39397         "Syria (‫سوريا‬‎)",
39398         "sy",
39399         "963"
39400       ],
39401       [
39402         "Taiwan (台灣)",
39403         "tw",
39404         "886"
39405       ],
39406       [
39407         "Tajikistan",
39408         "tj",
39409         "992"
39410       ],
39411       [
39412         "Tanzania",
39413         "tz",
39414         "255"
39415       ],
39416       [
39417         "Thailand (ไทย)",
39418         "th",
39419         "66"
39420       ],
39421       [
39422         "Timor-Leste",
39423         "tl",
39424         "670"
39425       ],
39426       [
39427         "Togo",
39428         "tg",
39429         "228"
39430       ],
39431       [
39432         "Tokelau",
39433         "tk",
39434         "690"
39435       ],
39436       [
39437         "Tonga",
39438         "to",
39439         "676"
39440       ],
39441       [
39442         "Trinidad and Tobago",
39443         "tt",
39444         "1868"
39445       ],
39446       [
39447         "Tunisia (‫تونس‬‎)",
39448         "tn",
39449         "216"
39450       ],
39451       [
39452         "Turkey (Türkiye)",
39453         "tr",
39454         "90"
39455       ],
39456       [
39457         "Turkmenistan",
39458         "tm",
39459         "993"
39460       ],
39461       [
39462         "Turks and Caicos Islands",
39463         "tc",
39464         "1649"
39465       ],
39466       [
39467         "Tuvalu",
39468         "tv",
39469         "688"
39470       ],
39471       [
39472         "U.S. Virgin Islands",
39473         "vi",
39474         "1340"
39475       ],
39476       [
39477         "Uganda",
39478         "ug",
39479         "256"
39480       ],
39481       [
39482         "Ukraine (Україна)",
39483         "ua",
39484         "380"
39485       ],
39486       [
39487         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39488         "ae",
39489         "971"
39490       ],
39491       [
39492         "United Kingdom",
39493         "gb",
39494         "44",
39495         0
39496       ],
39497       [
39498         "United States",
39499         "us",
39500         "1",
39501         0
39502       ],
39503       [
39504         "Uruguay",
39505         "uy",
39506         "598"
39507       ],
39508       [
39509         "Uzbekistan (Oʻzbekiston)",
39510         "uz",
39511         "998"
39512       ],
39513       [
39514         "Vanuatu",
39515         "vu",
39516         "678"
39517       ],
39518       [
39519         "Vatican City (Città del Vaticano)",
39520         "va",
39521         "39",
39522         1
39523       ],
39524       [
39525         "Venezuela",
39526         "ve",
39527         "58"
39528       ],
39529       [
39530         "Vietnam (Việt Nam)",
39531         "vn",
39532         "84"
39533       ],
39534       [
39535         "Wallis and Futuna (Wallis-et-Futuna)",
39536         "wf",
39537         "681"
39538       ],
39539       [
39540         "Western Sahara (‫الصحراء الغربية‬‎)",
39541         "eh",
39542         "212",
39543         1
39544       ],
39545       [
39546         "Yemen (‫اليمن‬‎)",
39547         "ye",
39548         "967"
39549       ],
39550       [
39551         "Zambia",
39552         "zm",
39553         "260"
39554       ],
39555       [
39556         "Zimbabwe",
39557         "zw",
39558         "263"
39559       ],
39560       [
39561         "Åland Islands",
39562         "ax",
39563         "358",
39564         1
39565       ]
39566   ];
39567   
39568   return d;
39569 }/**
39570 *    This script refer to:
39571 *    Title: International Telephone Input
39572 *    Author: Jack O'Connor
39573 *    Code version:  v12.1.12
39574 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39575 **/
39576
39577 /**
39578  * @class Roo.bootstrap.PhoneInput
39579  * @extends Roo.bootstrap.TriggerField
39580  * An input with International dial-code selection
39581  
39582  * @cfg {String} defaultDialCode default '+852'
39583  * @cfg {Array} preferedCountries default []
39584   
39585  * @constructor
39586  * Create a new PhoneInput.
39587  * @param {Object} config Configuration options
39588  */
39589
39590 Roo.bootstrap.PhoneInput = function(config) {
39591     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39592 };
39593
39594 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39595         
39596         listWidth: undefined,
39597         
39598         selectedClass: 'active',
39599         
39600         invalidClass : "has-warning",
39601         
39602         validClass: 'has-success',
39603         
39604         allowed: '0123456789',
39605         
39606         /**
39607          * @cfg {String} defaultDialCode The default dial code when initializing the input
39608          */
39609         defaultDialCode: '+852',
39610         
39611         /**
39612          * @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
39613          */
39614         preferedCountries: false,
39615         
39616         getAutoCreate : function()
39617         {
39618             var data = Roo.bootstrap.PhoneInputData();
39619             var align = this.labelAlign || this.parentLabelAlign();
39620             var id = Roo.id();
39621             
39622             this.allCountries = [];
39623             this.dialCodeMapping = [];
39624             
39625             for (var i = 0; i < data.length; i++) {
39626               var c = data[i];
39627               this.allCountries[i] = {
39628                 name: c[0],
39629                 iso2: c[1],
39630                 dialCode: c[2],
39631                 priority: c[3] || 0,
39632                 areaCodes: c[4] || null
39633               };
39634               this.dialCodeMapping[c[2]] = {
39635                   name: c[0],
39636                   iso2: c[1],
39637                   priority: c[3] || 0,
39638                   areaCodes: c[4] || null
39639               };
39640             }
39641             
39642             var cfg = {
39643                 cls: 'form-group',
39644                 cn: []
39645             };
39646             
39647             var input =  {
39648                 tag: 'input',
39649                 id : id,
39650                 cls : 'form-control tel-input',
39651                 autocomplete: 'new-password'
39652             };
39653             
39654             var hiddenInput = {
39655                 tag: 'input',
39656                 type: 'hidden',
39657                 cls: 'hidden-tel-input'
39658             };
39659             
39660             if (this.name) {
39661                 hiddenInput.name = this.name;
39662             }
39663             
39664             if (this.disabled) {
39665                 input.disabled = true;
39666             }
39667             
39668             var flag_container = {
39669                 tag: 'div',
39670                 cls: 'flag-box',
39671                 cn: [
39672                     {
39673                         tag: 'div',
39674                         cls: 'flag'
39675                     },
39676                     {
39677                         tag: 'div',
39678                         cls: 'caret'
39679                     }
39680                 ]
39681             };
39682             
39683             var box = {
39684                 tag: 'div',
39685                 cls: this.hasFeedback ? 'has-feedback' : '',
39686                 cn: [
39687                     hiddenInput,
39688                     input,
39689                     {
39690                         tag: 'input',
39691                         cls: 'dial-code-holder',
39692                         disabled: true
39693                     }
39694                 ]
39695             };
39696             
39697             var container = {
39698                 cls: 'roo-select2-container input-group',
39699                 cn: [
39700                     flag_container,
39701                     box
39702                 ]
39703             };
39704             
39705             if (this.fieldLabel.length) {
39706                 var indicator = {
39707                     tag: 'i',
39708                     tooltip: 'This field is required'
39709                 };
39710                 
39711                 var label = {
39712                     tag: 'label',
39713                     'for':  id,
39714                     cls: 'control-label',
39715                     cn: []
39716                 };
39717                 
39718                 var label_text = {
39719                     tag: 'span',
39720                     html: this.fieldLabel
39721                 };
39722                 
39723                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39724                 label.cn = [
39725                     indicator,
39726                     label_text
39727                 ];
39728                 
39729                 if(this.indicatorpos == 'right') {
39730                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39731                     label.cn = [
39732                         label_text,
39733                         indicator
39734                     ];
39735                 }
39736                 
39737                 if(align == 'left') {
39738                     container = {
39739                         tag: 'div',
39740                         cn: [
39741                             container
39742                         ]
39743                     };
39744                     
39745                     if(this.labelWidth > 12){
39746                         label.style = "width: " + this.labelWidth + 'px';
39747                     }
39748                     if(this.labelWidth < 13 && this.labelmd == 0){
39749                         this.labelmd = this.labelWidth;
39750                     }
39751                     if(this.labellg > 0){
39752                         label.cls += ' col-lg-' + this.labellg;
39753                         input.cls += ' col-lg-' + (12 - this.labellg);
39754                     }
39755                     if(this.labelmd > 0){
39756                         label.cls += ' col-md-' + this.labelmd;
39757                         container.cls += ' col-md-' + (12 - this.labelmd);
39758                     }
39759                     if(this.labelsm > 0){
39760                         label.cls += ' col-sm-' + this.labelsm;
39761                         container.cls += ' col-sm-' + (12 - this.labelsm);
39762                     }
39763                     if(this.labelxs > 0){
39764                         label.cls += ' col-xs-' + this.labelxs;
39765                         container.cls += ' col-xs-' + (12 - this.labelxs);
39766                     }
39767                 }
39768             }
39769             
39770             cfg.cn = [
39771                 label,
39772                 container
39773             ];
39774             
39775             var settings = this;
39776             
39777             ['xs','sm','md','lg'].map(function(size){
39778                 if (settings[size]) {
39779                     cfg.cls += ' col-' + size + '-' + settings[size];
39780                 }
39781             });
39782             
39783             this.store = new Roo.data.Store({
39784                 proxy : new Roo.data.MemoryProxy({}),
39785                 reader : new Roo.data.JsonReader({
39786                     fields : [
39787                         {
39788                             'name' : 'name',
39789                             'type' : 'string'
39790                         },
39791                         {
39792                             'name' : 'iso2',
39793                             'type' : 'string'
39794                         },
39795                         {
39796                             'name' : 'dialCode',
39797                             'type' : 'string'
39798                         },
39799                         {
39800                             'name' : 'priority',
39801                             'type' : 'string'
39802                         },
39803                         {
39804                             'name' : 'areaCodes',
39805                             'type' : 'string'
39806                         }
39807                     ]
39808                 })
39809             });
39810             
39811             if(!this.preferedCountries) {
39812                 this.preferedCountries = [
39813                     'hk',
39814                     'gb',
39815                     'us'
39816                 ];
39817             }
39818             
39819             var p = this.preferedCountries.reverse();
39820             
39821             if(p) {
39822                 for (var i = 0; i < p.length; i++) {
39823                     for (var j = 0; j < this.allCountries.length; j++) {
39824                         if(this.allCountries[j].iso2 == p[i]) {
39825                             var t = this.allCountries[j];
39826                             this.allCountries.splice(j,1);
39827                             this.allCountries.unshift(t);
39828                         }
39829                     } 
39830                 }
39831             }
39832             
39833             this.store.proxy.data = {
39834                 success: true,
39835                 data: this.allCountries
39836             };
39837             
39838             return cfg;
39839         },
39840         
39841         initEvents : function()
39842         {
39843             this.createList();
39844             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39845             
39846             this.indicator = this.indicatorEl();
39847             this.flag = this.flagEl();
39848             this.dialCodeHolder = this.dialCodeHolderEl();
39849             
39850             this.trigger = this.el.select('div.flag-box',true).first();
39851             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39852             
39853             var _this = this;
39854             
39855             (function(){
39856                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39857                 _this.list.setWidth(lw);
39858             }).defer(100);
39859             
39860             this.list.on('mouseover', this.onViewOver, this);
39861             this.list.on('mousemove', this.onViewMove, this);
39862             this.inputEl().on("keyup", this.onKeyUp, this);
39863             
39864             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39865
39866             this.view = new Roo.View(this.list, this.tpl, {
39867                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39868             });
39869             
39870             this.view.on('click', this.onViewClick, this);
39871             this.setValue(this.defaultDialCode);
39872         },
39873         
39874         onTriggerClick : function(e)
39875         {
39876             Roo.log('trigger click');
39877             if(this.disabled){
39878                 return;
39879             }
39880             
39881             if(this.isExpanded()){
39882                 this.collapse();
39883                 this.hasFocus = false;
39884             }else {
39885                 this.store.load({});
39886                 this.hasFocus = true;
39887                 this.expand();
39888             }
39889         },
39890         
39891         isExpanded : function()
39892         {
39893             return this.list.isVisible();
39894         },
39895         
39896         collapse : function()
39897         {
39898             if(!this.isExpanded()){
39899                 return;
39900             }
39901             this.list.hide();
39902             Roo.get(document).un('mousedown', this.collapseIf, this);
39903             Roo.get(document).un('mousewheel', this.collapseIf, this);
39904             this.fireEvent('collapse', this);
39905             this.validate();
39906         },
39907         
39908         expand : function()
39909         {
39910             Roo.log('expand');
39911
39912             if(this.isExpanded() || !this.hasFocus){
39913                 return;
39914             }
39915             
39916             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39917             this.list.setWidth(lw);
39918             
39919             this.list.show();
39920             this.restrictHeight();
39921             
39922             Roo.get(document).on('mousedown', this.collapseIf, this);
39923             Roo.get(document).on('mousewheel', this.collapseIf, this);
39924             
39925             this.fireEvent('expand', this);
39926         },
39927         
39928         restrictHeight : function()
39929         {
39930             this.list.alignTo(this.inputEl(), this.listAlign);
39931             this.list.alignTo(this.inputEl(), this.listAlign);
39932         },
39933         
39934         onViewOver : function(e, t)
39935         {
39936             if(this.inKeyMode){
39937                 return;
39938             }
39939             var item = this.view.findItemFromChild(t);
39940             
39941             if(item){
39942                 var index = this.view.indexOf(item);
39943                 this.select(index, false);
39944             }
39945         },
39946
39947         // private
39948         onViewClick : function(view, doFocus, el, e)
39949         {
39950             var index = this.view.getSelectedIndexes()[0];
39951             
39952             var r = this.store.getAt(index);
39953             
39954             if(r){
39955                 this.onSelect(r, index);
39956             }
39957             if(doFocus !== false && !this.blockFocus){
39958                 this.inputEl().focus();
39959             }
39960         },
39961         
39962         onViewMove : function(e, t)
39963         {
39964             this.inKeyMode = false;
39965         },
39966         
39967         select : function(index, scrollIntoView)
39968         {
39969             this.selectedIndex = index;
39970             this.view.select(index);
39971             if(scrollIntoView !== false){
39972                 var el = this.view.getNode(index);
39973                 if(el){
39974                     this.list.scrollChildIntoView(el, false);
39975                 }
39976             }
39977         },
39978         
39979         createList : function()
39980         {
39981             this.list = Roo.get(document.body).createChild({
39982                 tag: 'ul',
39983                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39984                 style: 'display:none'
39985             });
39986             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39987         },
39988         
39989         collapseIf : function(e)
39990         {
39991             var in_combo  = e.within(this.el);
39992             var in_list =  e.within(this.list);
39993             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39994             
39995             if (in_combo || in_list || is_list) {
39996                 return;
39997             }
39998             this.collapse();
39999         },
40000         
40001         onSelect : function(record, index)
40002         {
40003             if(this.fireEvent('beforeselect', this, record, index) !== false){
40004                 
40005                 this.setFlagClass(record.data.iso2);
40006                 this.setDialCode(record.data.dialCode);
40007                 this.hasFocus = false;
40008                 this.collapse();
40009                 this.fireEvent('select', this, record, index);
40010             }
40011         },
40012         
40013         flagEl : function()
40014         {
40015             var flag = this.el.select('div.flag',true).first();
40016             if(!flag){
40017                 return false;
40018             }
40019             return flag;
40020         },
40021         
40022         dialCodeHolderEl : function()
40023         {
40024             var d = this.el.select('input.dial-code-holder',true).first();
40025             if(!d){
40026                 return false;
40027             }
40028             return d;
40029         },
40030         
40031         setDialCode : function(v)
40032         {
40033             this.dialCodeHolder.dom.value = '+'+v;
40034         },
40035         
40036         setFlagClass : function(n)
40037         {
40038             this.flag.dom.className = 'flag '+n;
40039         },
40040         
40041         getValue : function()
40042         {
40043             var v = this.inputEl().getValue();
40044             if(this.dialCodeHolder) {
40045                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40046             }
40047             return v;
40048         },
40049         
40050         setValue : function(v)
40051         {
40052             var d = this.getDialCode(v);
40053             
40054             //invalid dial code
40055             if(v.length == 0 || !d || d.length == 0) {
40056                 if(this.rendered){
40057                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40058                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40059                 }
40060                 return;
40061             }
40062             
40063             //valid dial code
40064             this.setFlagClass(this.dialCodeMapping[d].iso2);
40065             this.setDialCode(d);
40066             this.inputEl().dom.value = v.replace('+'+d,'');
40067             this.hiddenEl().dom.value = this.getValue();
40068             
40069             this.validate();
40070         },
40071         
40072         getDialCode : function(v = '')
40073         {
40074             if (v.length == 0) {
40075                 return this.dialCodeHolder.dom.value;
40076             }
40077             
40078             var dialCode = "";
40079             if (v.charAt(0) != "+") {
40080                 return false;
40081             }
40082             var numericChars = "";
40083             for (var i = 1; i < v.length; i++) {
40084               var c = v.charAt(i);
40085               if (!isNaN(c)) {
40086                 numericChars += c;
40087                 if (this.dialCodeMapping[numericChars]) {
40088                   dialCode = v.substr(1, i);
40089                 }
40090                 if (numericChars.length == 4) {
40091                   break;
40092                 }
40093               }
40094             }
40095             return dialCode;
40096         },
40097         
40098         reset : function()
40099         {
40100             this.setValue(this.defaultDialCode);
40101             this.validate();
40102         },
40103         
40104         hiddenEl : function()
40105         {
40106             return this.el.select('input.hidden-tel-input',true).first();
40107         },
40108         
40109         onKeyUp : function(e){
40110             
40111             var k = e.getKey();
40112             var c = e.getCharCode();
40113             
40114             if(
40115                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40116                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40117             ){
40118                 e.stopEvent();
40119             }
40120             
40121             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40122             //     return;
40123             // }
40124             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40125                 e.stopEvent();
40126             }
40127             
40128             this.setValue(this.getValue());
40129         }
40130         
40131 });
40132 /**
40133  * @class Roo.bootstrap.MoneyField
40134  * @extends Roo.bootstrap.ComboBox
40135  * Bootstrap MoneyField class
40136  * 
40137  * @constructor
40138  * Create a new MoneyField.
40139  * @param {Object} config Configuration options
40140  */
40141
40142 Roo.bootstrap.MoneyField = function(config) {
40143     
40144     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40145     
40146 };
40147
40148 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40149     
40150     /**
40151      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40152      */
40153     allowDecimals : true,
40154     /**
40155      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40156      */
40157     decimalSeparator : ".",
40158     /**
40159      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40160      */
40161     decimalPrecision : 0,
40162     /**
40163      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40164      */
40165     allowNegative : true,
40166     /**
40167      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40168      */
40169     minValue : Number.NEGATIVE_INFINITY,
40170     /**
40171      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40172      */
40173     maxValue : Number.MAX_VALUE,
40174     /**
40175      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40176      */
40177     minText : "The minimum value for this field is {0}",
40178     /**
40179      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40180      */
40181     maxText : "The maximum value for this field is {0}",
40182     /**
40183      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40184      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40185      */
40186     nanText : "{0} is not a valid number",
40187     /**
40188      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40189      */
40190     castInt : true,
40191     /**
40192      * @cfg {String} defaults currency of the MoneyField
40193      * value should be in lkey
40194      */
40195     defaultCurrency : false,
40196     /**
40197      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40198      */
40199     thousandsDelimiter : false,
40200     
40201     
40202     inputlg : 9,
40203     inputmd : 9,
40204     inputsm : 9,
40205     inputxs : 6,
40206     
40207     store : false,
40208     
40209     getAutoCreate : function()
40210     {
40211         var align = this.labelAlign || this.parentLabelAlign();
40212         
40213         var id = Roo.id();
40214
40215         var cfg = {
40216             cls: 'form-group',
40217             cn: []
40218         };
40219
40220         var input =  {
40221             tag: 'input',
40222             id : id,
40223             cls : 'form-control roo-money-amount-input',
40224             autocomplete: 'new-password'
40225         };
40226         
40227         var hiddenInput = {
40228             tag: 'input',
40229             type: 'hidden',
40230             id: Roo.id(),
40231             cls: 'hidden-number-input'
40232         };
40233         
40234         if (this.name) {
40235             hiddenInput.name = this.name;
40236         }
40237
40238         if (this.disabled) {
40239             input.disabled = true;
40240         }
40241
40242         var clg = 12 - this.inputlg;
40243         var cmd = 12 - this.inputmd;
40244         var csm = 12 - this.inputsm;
40245         var cxs = 12 - this.inputxs;
40246         
40247         var container = {
40248             tag : 'div',
40249             cls : 'row roo-money-field',
40250             cn : [
40251                 {
40252                     tag : 'div',
40253                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40254                     cn : [
40255                         {
40256                             tag : 'div',
40257                             cls: 'roo-select2-container input-group',
40258                             cn: [
40259                                 {
40260                                     tag : 'input',
40261                                     cls : 'form-control roo-money-currency-input',
40262                                     autocomplete: 'new-password',
40263                                     readOnly : 1,
40264                                     name : this.currencyName
40265                                 },
40266                                 {
40267                                     tag :'span',
40268                                     cls : 'input-group-addon',
40269                                     cn : [
40270                                         {
40271                                             tag: 'span',
40272                                             cls: 'caret'
40273                                         }
40274                                     ]
40275                                 }
40276                             ]
40277                         }
40278                     ]
40279                 },
40280                 {
40281                     tag : 'div',
40282                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40283                     cn : [
40284                         {
40285                             tag: 'div',
40286                             cls: this.hasFeedback ? 'has-feedback' : '',
40287                             cn: [
40288                                 input
40289                             ]
40290                         }
40291                     ]
40292                 }
40293             ]
40294             
40295         };
40296         
40297         if (this.fieldLabel.length) {
40298             var indicator = {
40299                 tag: 'i',
40300                 tooltip: 'This field is required'
40301             };
40302
40303             var label = {
40304                 tag: 'label',
40305                 'for':  id,
40306                 cls: 'control-label',
40307                 cn: []
40308             };
40309
40310             var label_text = {
40311                 tag: 'span',
40312                 html: this.fieldLabel
40313             };
40314
40315             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40316             label.cn = [
40317                 indicator,
40318                 label_text
40319             ];
40320
40321             if(this.indicatorpos == 'right') {
40322                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40323                 label.cn = [
40324                     label_text,
40325                     indicator
40326                 ];
40327             }
40328
40329             if(align == 'left') {
40330                 container = {
40331                     tag: 'div',
40332                     cn: [
40333                         container
40334                     ]
40335                 };
40336
40337                 if(this.labelWidth > 12){
40338                     label.style = "width: " + this.labelWidth + 'px';
40339                 }
40340                 if(this.labelWidth < 13 && this.labelmd == 0){
40341                     this.labelmd = this.labelWidth;
40342                 }
40343                 if(this.labellg > 0){
40344                     label.cls += ' col-lg-' + this.labellg;
40345                     input.cls += ' col-lg-' + (12 - this.labellg);
40346                 }
40347                 if(this.labelmd > 0){
40348                     label.cls += ' col-md-' + this.labelmd;
40349                     container.cls += ' col-md-' + (12 - this.labelmd);
40350                 }
40351                 if(this.labelsm > 0){
40352                     label.cls += ' col-sm-' + this.labelsm;
40353                     container.cls += ' col-sm-' + (12 - this.labelsm);
40354                 }
40355                 if(this.labelxs > 0){
40356                     label.cls += ' col-xs-' + this.labelxs;
40357                     container.cls += ' col-xs-' + (12 - this.labelxs);
40358                 }
40359             }
40360         }
40361
40362         cfg.cn = [
40363             label,
40364             container,
40365             hiddenInput
40366         ];
40367         
40368         var settings = this;
40369
40370         ['xs','sm','md','lg'].map(function(size){
40371             if (settings[size]) {
40372                 cfg.cls += ' col-' + size + '-' + settings[size];
40373             }
40374         });
40375         
40376         return cfg;
40377     },
40378     
40379     initEvents : function()
40380     {
40381         this.indicator = this.indicatorEl();
40382         
40383         this.initCurrencyEvent();
40384         
40385         this.initNumberEvent();
40386     },
40387     
40388     initCurrencyEvent : function()
40389     {
40390         if (!this.store) {
40391             throw "can not find store for combo";
40392         }
40393         
40394         this.store = Roo.factory(this.store, Roo.data);
40395         this.store.parent = this;
40396         
40397         this.createList();
40398         
40399         this.triggerEl = this.el.select('.input-group-addon', true).first();
40400         
40401         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40402         
40403         var _this = this;
40404         
40405         (function(){
40406             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40407             _this.list.setWidth(lw);
40408         }).defer(100);
40409         
40410         this.list.on('mouseover', this.onViewOver, this);
40411         this.list.on('mousemove', this.onViewMove, this);
40412         this.list.on('scroll', this.onViewScroll, this);
40413         
40414         if(!this.tpl){
40415             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40416         }
40417         
40418         this.view = new Roo.View(this.list, this.tpl, {
40419             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40420         });
40421         
40422         this.view.on('click', this.onViewClick, this);
40423         
40424         this.store.on('beforeload', this.onBeforeLoad, this);
40425         this.store.on('load', this.onLoad, this);
40426         this.store.on('loadexception', this.onLoadException, this);
40427         
40428         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40429             "up" : function(e){
40430                 this.inKeyMode = true;
40431                 this.selectPrev();
40432             },
40433
40434             "down" : function(e){
40435                 if(!this.isExpanded()){
40436                     this.onTriggerClick();
40437                 }else{
40438                     this.inKeyMode = true;
40439                     this.selectNext();
40440                 }
40441             },
40442
40443             "enter" : function(e){
40444                 this.collapse();
40445                 
40446                 if(this.fireEvent("specialkey", this, e)){
40447                     this.onViewClick(false);
40448                 }
40449                 
40450                 return true;
40451             },
40452
40453             "esc" : function(e){
40454                 this.collapse();
40455             },
40456
40457             "tab" : function(e){
40458                 this.collapse();
40459                 
40460                 if(this.fireEvent("specialkey", this, e)){
40461                     this.onViewClick(false);
40462                 }
40463                 
40464                 return true;
40465             },
40466
40467             scope : this,
40468
40469             doRelay : function(foo, bar, hname){
40470                 if(hname == 'down' || this.scope.isExpanded()){
40471                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40472                 }
40473                 return true;
40474             },
40475
40476             forceKeyDown: true
40477         });
40478         
40479         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40480         
40481     },
40482     
40483     initNumberEvent : function(e)
40484     {
40485         this.inputEl().on("keydown" , this.fireKey,  this);
40486         this.inputEl().on("focus", this.onFocus,  this);
40487         this.inputEl().on("blur", this.onBlur,  this);
40488         
40489         this.inputEl().relayEvent('keyup', this);
40490         
40491         if(this.indicator){
40492             this.indicator.addClass('invisible');
40493         }
40494  
40495         this.originalValue = this.getValue();
40496         
40497         if(this.validationEvent == 'keyup'){
40498             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40499             this.inputEl().on('keyup', this.filterValidation, this);
40500         }
40501         else if(this.validationEvent !== false){
40502             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40503         }
40504         
40505         if(this.selectOnFocus){
40506             this.on("focus", this.preFocus, this);
40507             
40508         }
40509         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40510             this.inputEl().on("keypress", this.filterKeys, this);
40511         } else {
40512             this.inputEl().relayEvent('keypress', this);
40513         }
40514         
40515         var allowed = "0123456789";
40516         
40517         if(this.allowDecimals){
40518             allowed += this.decimalSeparator;
40519         }
40520         
40521         if(this.allowNegative){
40522             allowed += "-";
40523         }
40524         
40525         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40526         
40527         var keyPress = function(e){
40528             
40529             var k = e.getKey();
40530             
40531             var c = e.getCharCode();
40532             
40533             if(
40534                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40535                     allowed.indexOf(String.fromCharCode(c)) === -1
40536             ){
40537                 e.stopEvent();
40538                 return;
40539             }
40540             
40541             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40542                 return;
40543             }
40544             
40545             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40546                 e.stopEvent();
40547             }
40548         };
40549         
40550         this.inputEl().on("keypress", keyPress, this);
40551         
40552     },
40553     
40554     onTriggerClick : function(e)
40555     {   
40556         if(this.disabled){
40557             return;
40558         }
40559         
40560         this.page = 0;
40561         this.loadNext = false;
40562         
40563         if(this.isExpanded()){
40564             this.collapse();
40565             return;
40566         }
40567         
40568         this.hasFocus = true;
40569         
40570         if(this.triggerAction == 'all') {
40571             this.doQuery(this.allQuery, true);
40572             return;
40573         }
40574         
40575         this.doQuery(this.getRawValue());
40576     },
40577     
40578     getCurrency : function()
40579     {   
40580         var v = this.currencyEl().getValue();
40581         
40582         return v;
40583     },
40584     
40585     restrictHeight : function()
40586     {
40587         this.list.alignTo(this.currencyEl(), this.listAlign);
40588         this.list.alignTo(this.currencyEl(), this.listAlign);
40589     },
40590     
40591     onViewClick : function(view, doFocus, el, e)
40592     {
40593         var index = this.view.getSelectedIndexes()[0];
40594         
40595         var r = this.store.getAt(index);
40596         
40597         if(r){
40598             this.onSelect(r, index);
40599         }
40600     },
40601     
40602     onSelect : function(record, index){
40603         
40604         if(this.fireEvent('beforeselect', this, record, index) !== false){
40605         
40606             this.setFromCurrencyData(index > -1 ? record.data : false);
40607             
40608             this.collapse();
40609             
40610             this.fireEvent('select', this, record, index);
40611         }
40612     },
40613     
40614     setFromCurrencyData : function(o)
40615     {
40616         var currency = '';
40617         
40618         this.lastCurrency = o;
40619         
40620         if (this.currencyField) {
40621             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40622         } else {
40623             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40624         }
40625         
40626         this.lastSelectionText = currency;
40627         
40628         //setting default currency
40629         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40630             this.setCurrency(this.defaultCurrency);
40631             return;
40632         }
40633         
40634         this.setCurrency(currency);
40635     },
40636     
40637     setFromData : function(o)
40638     {
40639         var c = {};
40640         
40641         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40642         
40643         this.setFromCurrencyData(c);
40644         
40645         var value = '';
40646         
40647         if (this.name) {
40648             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40649         } else {
40650             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40651         }
40652         
40653         this.setValue(value);
40654         
40655     },
40656     
40657     setCurrency : function(v)
40658     {   
40659         this.currencyValue = v;
40660         
40661         if(this.rendered){
40662             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40663             this.validate();
40664         }
40665     },
40666     
40667     setValue : function(v)
40668     {
40669         v = this.fixPrecision(v);
40670         
40671         v = String(v).replace(".", this.decimalSeparator);
40672         
40673         this.value = v;
40674         
40675         if(this.rendered){
40676             
40677             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40678             
40679             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40680                 this.thousandsDelimiter || ','
40681             );
40682             
40683             if(this.allowBlank && !v) {
40684                 this.inputEl().dom.value = '';
40685             }
40686             
40687             this.validate();
40688         }
40689     },
40690     
40691     getRawValue : function()
40692     {
40693         var v = this.inputEl().getValue();
40694         
40695         return v;
40696     },
40697     
40698     getValue : function()
40699     {
40700         return this.fixPrecision(this.parseValue(this.getRawValue()));
40701     },
40702     
40703     parseValue : function(value)
40704     {
40705         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40706         return isNaN(value) ? '' : value;
40707     },
40708     
40709     fixPrecision : function(value)
40710     {
40711         var nan = isNaN(value);
40712         
40713         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40714             return nan ? '' : value;
40715         }
40716         
40717         return parseFloat(value).toFixed(this.decimalPrecision);
40718     },
40719     
40720     decimalPrecisionFcn : function(v)
40721     {
40722         return Math.floor(v);
40723     },
40724     
40725     validateValue : function(value)
40726     {
40727         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40728             return false;
40729         }
40730         
40731         var num = this.parseValue(value);
40732         
40733         if(isNaN(num)){
40734             this.markInvalid(String.format(this.nanText, value));
40735             return false;
40736         }
40737         
40738         if(num < this.minValue){
40739             this.markInvalid(String.format(this.minText, this.minValue));
40740             return false;
40741         }
40742         
40743         if(num > this.maxValue){
40744             this.markInvalid(String.format(this.maxText, this.maxValue));
40745             return false;
40746         }
40747         
40748         return true;
40749     },
40750     
40751     validate : function()
40752     {
40753         if(this.disabled || this.allowBlank){
40754             this.markValid();
40755             return true;
40756         }
40757         
40758         var currency = this.getCurrency();
40759         
40760         if(this.validateValue(this.getRawValue()) && currency.length){
40761             this.markValid();
40762             return true;
40763         }
40764         
40765         this.markInvalid();
40766         return false;
40767     },
40768     
40769     getName: function()
40770     {
40771         return this.name;
40772     },
40773     
40774     beforeBlur : function()
40775     {
40776         if(!this.castInt){
40777             return;
40778         }
40779         
40780         var v = this.parseValue(this.getRawValue());
40781         
40782         if(v || v == 0){
40783             this.setValue(v);
40784         }
40785     },
40786     
40787     onBlur : function()
40788     {
40789         this.beforeBlur();
40790         
40791         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40792             //this.el.removeClass(this.focusClass);
40793         }
40794         
40795         this.hasFocus = false;
40796         
40797         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40798             this.validate();
40799         }
40800         
40801         var v = this.getValue();
40802         
40803         if(String(v) !== String(this.startValue)){
40804             this.fireEvent('change', this, v, this.startValue);
40805         }
40806         
40807         this.fireEvent("blur", this);
40808     },
40809     
40810     inputEl : function()
40811     {
40812         return this.el.select('.roo-money-amount-input', true).first();
40813     },
40814     
40815     currencyEl : function()
40816     {
40817         return this.el.select('.roo-money-currency-input', true).first();
40818     },
40819     
40820     hiddenEl : function()
40821     {
40822         return this.el.select('input.hidden-number-input',true).first();
40823     }
40824     
40825 });