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 is it a slidy toggle button
566  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567  * @cfg {String} ontext text for on toggle state
568  * @cfg {String} offtext text for off toggle state
569  * @cfg {Boolean} defaulton 
570  * @cfg {Boolean} preventDefault  default true
571  * @cfg {Boolean} removeClass remove the standard class..
572  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
573  * 
574  * @constructor
575  * Create a new button
576  * @param {Object} config The config object
577  */
578
579
580 Roo.bootstrap.Button = function(config){
581     Roo.bootstrap.Button.superclass.constructor.call(this, config);
582     this.weightClass = ["btn-default", 
583                        "btn-primary", 
584                        "btn-success", 
585                        "btn-info", 
586                        "btn-warning",
587                        "btn-danger",
588                        "btn-link"
589                       ],  
590     this.addEvents({
591         // raw events
592         /**
593          * @event click
594          * When a butotn is pressed
595          * @param {Roo.bootstrap.Button} btn
596          * @param {Roo.EventObject} e
597          */
598         "click" : true,
599          /**
600          * @event toggle
601          * After the button has been toggles
602          * @param {Roo.bootstrap.Button} btn
603          * @param {Roo.EventObject} e
604          * @param {boolean} pressed (also available as button.pressed)
605          */
606         "toggle" : true
607     });
608 };
609
610 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
611     html: false,
612     active: false,
613     weight: '',
614     size: '',
615     tag: 'button',
616     href: '',
617     disabled: false,
618     isClose: false,
619     glyphicon: '',
620     badge: '',
621     theme: 'default',
622     inverse: false,
623     
624     toggle: false,
625     ontext: 'ON',
626     offtext: 'OFF',
627     defaulton: true,
628     preventDefault: true,
629     removeClass: false,
630     name: false,
631     target: false,
632     
633     
634     pressed : null,
635      
636     
637     getAutoCreate : function(){
638         
639         var cfg = {
640             tag : 'button',
641             cls : 'roo-button',
642             html: ''
643         };
644         
645         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
646             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
647             this.tag = 'button';
648         } else {
649             cfg.tag = this.tag;
650         }
651         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
652         
653         if (this.toggle == true) {
654             cfg={
655                 tag: 'div',
656                 cls: 'slider-frame roo-button',
657                 cn: [
658                     {
659                         tag: 'span',
660                         'data-on-text':'ON',
661                         'data-off-text':'OFF',
662                         cls: 'slider-button',
663                         html: this.offtext
664                     }
665                 ]
666             };
667             
668             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
669                 cfg.cls += ' '+this.weight;
670             }
671             
672             return cfg;
673         }
674         
675         if (this.isClose) {
676             cfg.cls += ' close';
677             
678             cfg["aria-hidden"] = true;
679             
680             cfg.html = "&times;";
681             
682             return cfg;
683         }
684         
685          
686         if (this.theme==='default') {
687             cfg.cls = 'btn roo-button';
688             
689             //if (this.parentType != 'Navbar') {
690             this.weight = this.weight.length ?  this.weight : 'default';
691             //}
692             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
693                 
694                 cfg.cls += ' btn-' + this.weight;
695             }
696         } else if (this.theme==='glow') {
697             
698             cfg.tag = 'a';
699             cfg.cls = 'btn-glow roo-button';
700             
701             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 
703                 cfg.cls += ' ' + this.weight;
704             }
705         }
706    
707         
708         if (this.inverse) {
709             this.cls += ' inverse';
710         }
711         
712         
713         if (this.active || this.pressed === true) {
714             cfg.cls += ' active';
715         }
716         
717         if (this.disabled) {
718             cfg.disabled = 'disabled';
719         }
720         
721         if (this.items) {
722             Roo.log('changing to ul' );
723             cfg.tag = 'ul';
724             this.glyphicon = 'caret';
725         }
726         
727         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
728          
729         //gsRoo.log(this.parentType);
730         if (this.parentType === 'Navbar' && !this.parent().bar) {
731             Roo.log('changing to li?');
732             
733             cfg.tag = 'li';
734             
735             cfg.cls = '';
736             cfg.cn =  [{
737                 tag : 'a',
738                 cls : 'roo-button',
739                 html : this.html,
740                 href : this.href || '#'
741             }];
742             if (this.menu) {
743                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
744                 cfg.cls += ' dropdown';
745             }   
746             
747             delete cfg.html;
748             
749         }
750         
751        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
752         
753         if (this.glyphicon) {
754             cfg.html = ' ' + cfg.html;
755             
756             cfg.cn = [
757                 {
758                     tag: 'span',
759                     cls: 'glyphicon glyphicon-' + this.glyphicon
760                 }
761             ];
762         }
763         
764         if (this.badge) {
765             cfg.html += ' ';
766             
767             cfg.tag = 'a';
768             
769 //            cfg.cls='btn roo-button';
770             
771             cfg.href=this.href;
772             
773             var value = cfg.html;
774             
775             if(this.glyphicon){
776                 value = {
777                             tag: 'span',
778                             cls: 'glyphicon glyphicon-' + this.glyphicon,
779                             html: this.html
780                         };
781                 
782             }
783             
784             cfg.cn = [
785                 value,
786                 {
787                     tag: 'span',
788                     cls: 'badge',
789                     html: this.badge
790                 }
791             ];
792             
793             cfg.html='';
794         }
795         
796         if (this.menu) {
797             cfg.cls += ' dropdown';
798             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
799         }
800         
801         if (cfg.tag !== 'a' && this.href !== '') {
802             throw "Tag must be a to set href.";
803         } else if (this.href.length > 0) {
804             cfg.href = this.href;
805         }
806         
807         if(this.removeClass){
808             cfg.cls = '';
809         }
810         
811         if(this.target){
812             cfg.target = this.target;
813         }
814         
815         return cfg;
816     },
817     initEvents: function() {
818        // Roo.log('init events?');
819 //        Roo.log(this.el.dom);
820         // add the menu...
821         
822         if (typeof (this.menu) != 'undefined') {
823             this.menu.parentType = this.xtype;
824             this.menu.triggerEl = this.el;
825             this.addxtype(Roo.apply({}, this.menu));
826         }
827
828
829        if (this.el.hasClass('roo-button')) {
830             this.el.on('click', this.onClick, this);
831        } else {
832             this.el.select('.roo-button').on('click', this.onClick, this);
833        }
834        
835        if(this.removeClass){
836            this.el.on('click', this.onClick, this);
837        }
838        
839        this.el.enableDisplayMode();
840         
841     },
842     onClick : function(e)
843     {
844         if (this.disabled) {
845             return;
846         }
847         
848         
849         Roo.log('button on click ');
850         if(this.preventDefault){
851             e.preventDefault();
852         }
853         
854         if (this.pressed === true || this.pressed === false) {
855             this.pressed = !this.pressed;
856             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
857             this.fireEvent('toggle', this, e, this.pressed);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function()
894     {
895        var active = this.el.hasClass('active');
896        this.setActive(!active);
897        
898         
899     },
900      /**
901      * get the current active state
902      * @return {boolean} true if it's active
903      */
904     isActive : function()
905     {
906         return this.el.hasClass('active');
907     },
908     /**
909      * set the text of the first selected button
910      */
911     setText : function(str)
912     {
913         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914     },
915     /**
916      * get the text of the first selected button
917      */
918     getText : function()
919     {
920         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
921     },
922     hide: function() {
923        
924      
925         this.el.hide();   
926     },
927     show: function() {
928        
929         this.el.show();   
930     },
931     setWeight : function(str)
932     {
933         this.el.removeClass(this.weightClass);
934         this.el.addClass('btn-' + str);        
935     }
936     
937     
938 });
939
940  /*
941  * - LGPL
942  *
943  * column
944  * 
945  */
946
947 /**
948  * @class Roo.bootstrap.Column
949  * @extends Roo.bootstrap.Component
950  * Bootstrap Column class
951  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
952  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
953  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
954  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
955  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
956  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
957  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
958  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959  *
960  * 
961  * @cfg {Boolean} hidden (true|false) hide the element
962  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
963  * @cfg {String} fa (ban|check|...) font awesome icon
964  * @cfg {Number} fasize (1|2|....) font awsome size
965
966  * @cfg {String} icon (info-sign|check|...) glyphicon name
967
968  * @cfg {String} html content of column.
969  * 
970  * @constructor
971  * Create a new Column
972  * @param {Object} config The config object
973  */
974
975 Roo.bootstrap.Column = function(config){
976     Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 };
978
979 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
980     
981     xs: false,
982     sm: false,
983     md: false,
984     lg: false,
985     xsoff: false,
986     smoff: false,
987     mdoff: false,
988     lgoff: false,
989     html: '',
990     offset: 0,
991     alert: false,
992     fa: false,
993     icon : false,
994     hidden : false,
995     fasize : 1,
996     
997     getAutoCreate : function(){
998         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
999         
1000         cfg = {
1001             tag: 'div',
1002             cls: 'column'
1003         };
1004         
1005         var settings=this;
1006         ['xs','sm','md','lg'].map(function(size){
1007             //Roo.log( size + ':' + settings[size]);
1008             
1009             if (settings[size+'off'] !== false) {
1010                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011             }
1012             
1013             if (settings[size] === false) {
1014                 return;
1015             }
1016             
1017             if (!settings[size]) { // 0 = hidden
1018                 cfg.cls += ' hidden-' + size;
1019                 return;
1020             }
1021             cfg.cls += ' col-' + size + '-' + settings[size];
1022             
1023         });
1024         
1025         if (this.hidden) {
1026             cfg.cls += ' hidden';
1027         }
1028         
1029         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1030             cfg.cls +=' alert alert-' + this.alert;
1031         }
1032         
1033         
1034         if (this.html.length) {
1035             cfg.html = this.html;
1036         }
1037         if (this.fa) {
1038             var fasize = '';
1039             if (this.fasize > 1) {
1040                 fasize = ' fa-' + this.fasize + 'x';
1041             }
1042             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1043             
1044             
1045         }
1046         if (this.icon) {
1047             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1048         }
1049         
1050         return cfg;
1051     }
1052    
1053 });
1054
1055  
1056
1057  /*
1058  * - LGPL
1059  *
1060  * page container.
1061  * 
1062  */
1063
1064
1065 /**
1066  * @class Roo.bootstrap.Container
1067  * @extends Roo.bootstrap.Component
1068  * Bootstrap Container class
1069  * @cfg {Boolean} jumbotron is it a jumbotron element
1070  * @cfg {String} html content of element
1071  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1072  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1073  * @cfg {String} header content of header (for panel)
1074  * @cfg {String} footer content of footer (for panel)
1075  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1076  * @cfg {String} tag (header|aside|section) type of HTML tag.
1077  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1078  * @cfg {String} fa font awesome icon
1079  * @cfg {String} icon (info-sign|check|...) glyphicon name
1080  * @cfg {Boolean} hidden (true|false) hide the element
1081  * @cfg {Boolean} expandable (true|false) default false
1082  * @cfg {Boolean} expanded (true|false) default true
1083  * @cfg {String} rheader contet on the right of header
1084  * @cfg {Boolean} clickable (true|false) default false
1085
1086  *     
1087  * @constructor
1088  * Create a new Container
1089  * @param {Object} config The config object
1090  */
1091
1092 Roo.bootstrap.Container = function(config){
1093     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1094     
1095     this.addEvents({
1096         // raw events
1097          /**
1098          * @event expand
1099          * After the panel has been expand
1100          * 
1101          * @param {Roo.bootstrap.Container} this
1102          */
1103         "expand" : true,
1104         /**
1105          * @event collapse
1106          * After the panel has been collapsed
1107          * 
1108          * @param {Roo.bootstrap.Container} this
1109          */
1110         "collapse" : true,
1111         /**
1112          * @event click
1113          * When a element is chick
1114          * @param {Roo.bootstrap.Container} this
1115          * @param {Roo.EventObject} e
1116          */
1117         "click" : true
1118     });
1119 };
1120
1121 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1122     
1123     jumbotron : false,
1124     well: '',
1125     panel : '',
1126     header: '',
1127     footer : '',
1128     sticky: '',
1129     tag : false,
1130     alert : false,
1131     fa: false,
1132     icon : false,
1133     expandable : false,
1134     rheader : '',
1135     expanded : true,
1136     clickable: false,
1137   
1138      
1139     getChildContainer : function() {
1140         
1141         if(!this.el){
1142             return false;
1143         }
1144         
1145         if (this.panel.length) {
1146             return this.el.select('.panel-body',true).first();
1147         }
1148         
1149         return this.el;
1150     },
1151     
1152     
1153     getAutoCreate : function(){
1154         
1155         var cfg = {
1156             tag : this.tag || 'div',
1157             html : '',
1158             cls : ''
1159         };
1160         if (this.jumbotron) {
1161             cfg.cls = 'jumbotron';
1162         }
1163         
1164         
1165         
1166         // - this is applied by the parent..
1167         //if (this.cls) {
1168         //    cfg.cls = this.cls + '';
1169         //}
1170         
1171         if (this.sticky.length) {
1172             
1173             var bd = Roo.get(document.body);
1174             if (!bd.hasClass('bootstrap-sticky')) {
1175                 bd.addClass('bootstrap-sticky');
1176                 Roo.select('html',true).setStyle('height', '100%');
1177             }
1178              
1179             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1180         }
1181         
1182         
1183         if (this.well.length) {
1184             switch (this.well) {
1185                 case 'lg':
1186                 case 'sm':
1187                     cfg.cls +=' well well-' +this.well;
1188                     break;
1189                 default:
1190                     cfg.cls +=' well';
1191                     break;
1192             }
1193         }
1194         
1195         if (this.hidden) {
1196             cfg.cls += ' hidden';
1197         }
1198         
1199         
1200         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1201             cfg.cls +=' alert alert-' + this.alert;
1202         }
1203         
1204         var body = cfg;
1205         
1206         if (this.panel.length) {
1207             cfg.cls += ' panel panel-' + this.panel;
1208             cfg.cn = [];
1209             if (this.header.length) {
1210                 
1211                 var h = [];
1212                 
1213                 if(this.expandable){
1214                     
1215                     cfg.cls = cfg.cls + ' expandable';
1216                     
1217                     h.push({
1218                         tag: 'i',
1219                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1220                     });
1221                     
1222                 }
1223                 
1224                 h.push(
1225                     {
1226                         tag: 'span',
1227                         cls : 'panel-title',
1228                         html : (this.expandable ? '&nbsp;' : '') + this.header
1229                     },
1230                     {
1231                         tag: 'span',
1232                         cls: 'panel-header-right',
1233                         html: this.rheader
1234                     }
1235                 );
1236                 
1237                 cfg.cn.push({
1238                     cls : 'panel-heading',
1239                     style : this.expandable ? 'cursor: pointer' : '',
1240                     cn : h
1241                 });
1242                 
1243             }
1244             
1245             body = false;
1246             cfg.cn.push({
1247                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1248                 html : this.html
1249             });
1250             
1251             
1252             if (this.footer.length) {
1253                 cfg.cn.push({
1254                     cls : 'panel-footer',
1255                     html : this.footer
1256                     
1257                 });
1258             }
1259             
1260         }
1261         
1262         if (body) {
1263             body.html = this.html || cfg.html;
1264             // prefix with the icons..
1265             if (this.fa) {
1266                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267             }
1268             if (this.icon) {
1269                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1270             }
1271             
1272             
1273         }
1274         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1275             cfg.cls =  'container';
1276         }
1277         
1278         return cfg;
1279     },
1280     
1281     initEvents: function() 
1282     {
1283         if(this.expandable){
1284             var headerEl = this.headerEl();
1285         
1286             if(headerEl){
1287                 headerEl.on('click', this.onToggleClick, this);
1288             }
1289         }
1290         
1291         if(this.clickable){
1292             this.el.on('click', this.onClick, this);
1293         }
1294         
1295     },
1296     
1297     onToggleClick : function()
1298     {
1299         var headerEl = this.headerEl();
1300         
1301         if(!headerEl){
1302             return;
1303         }
1304         
1305         if(this.expanded){
1306             this.collapse();
1307             return;
1308         }
1309         
1310         this.expand();
1311     },
1312     
1313     expand : function()
1314     {
1315         if(this.fireEvent('expand', this)) {
1316             
1317             this.expanded = true;
1318             
1319             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1320             
1321             this.el.select('.panel-body',true).first().removeClass('hide');
1322             
1323             var toggleEl = this.toggleEl();
1324
1325             if(!toggleEl){
1326                 return;
1327             }
1328
1329             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1330         }
1331         
1332     },
1333     
1334     collapse : function()
1335     {
1336         if(this.fireEvent('collapse', this)) {
1337             
1338             this.expanded = false;
1339             
1340             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1341             this.el.select('.panel-body',true).first().addClass('hide');
1342         
1343             var toggleEl = this.toggleEl();
1344
1345             if(!toggleEl){
1346                 return;
1347             }
1348
1349             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1350         }
1351     },
1352     
1353     toggleEl : function()
1354     {
1355         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1356             return;
1357         }
1358         
1359         return this.el.select('.panel-heading .fa',true).first();
1360     },
1361     
1362     headerEl : function()
1363     {
1364         if(!this.el || !this.panel.length || !this.header.length){
1365             return;
1366         }
1367         
1368         return this.el.select('.panel-heading',true).first()
1369     },
1370     
1371     bodyEl : function()
1372     {
1373         if(!this.el || !this.panel.length){
1374             return;
1375         }
1376         
1377         return this.el.select('.panel-body',true).first()
1378     },
1379     
1380     titleEl : function()
1381     {
1382         if(!this.el || !this.panel.length || !this.header.length){
1383             return;
1384         }
1385         
1386         return this.el.select('.panel-title',true).first();
1387     },
1388     
1389     setTitle : function(v)
1390     {
1391         var titleEl = this.titleEl();
1392         
1393         if(!titleEl){
1394             return;
1395         }
1396         
1397         titleEl.dom.innerHTML = v;
1398     },
1399     
1400     getTitle : function()
1401     {
1402         
1403         var titleEl = this.titleEl();
1404         
1405         if(!titleEl){
1406             return '';
1407         }
1408         
1409         return titleEl.dom.innerHTML;
1410     },
1411     
1412     setRightTitle : function(v)
1413     {
1414         var t = this.el.select('.panel-header-right',true).first();
1415         
1416         if(!t){
1417             return;
1418         }
1419         
1420         t.dom.innerHTML = v;
1421     },
1422     
1423     onClick : function(e)
1424     {
1425         e.preventDefault();
1426         
1427         this.fireEvent('click', this, e);
1428     }
1429 });
1430
1431  /*
1432  * - LGPL
1433  *
1434  * image
1435  * 
1436  */
1437
1438
1439 /**
1440  * @class Roo.bootstrap.Img
1441  * @extends Roo.bootstrap.Component
1442  * Bootstrap Img class
1443  * @cfg {Boolean} imgResponsive false | true
1444  * @cfg {String} border rounded | circle | thumbnail
1445  * @cfg {String} src image source
1446  * @cfg {String} alt image alternative text
1447  * @cfg {String} href a tag href
1448  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1449  * @cfg {String} xsUrl xs image source
1450  * @cfg {String} smUrl sm image source
1451  * @cfg {String} mdUrl md image source
1452  * @cfg {String} lgUrl lg image source
1453  * 
1454  * @constructor
1455  * Create a new Input
1456  * @param {Object} config The config object
1457  */
1458
1459 Roo.bootstrap.Img = function(config){
1460     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1461     
1462     this.addEvents({
1463         // img events
1464         /**
1465          * @event click
1466          * The img click event for the img.
1467          * @param {Roo.EventObject} e
1468          */
1469         "click" : true
1470     });
1471 };
1472
1473 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1474     
1475     imgResponsive: true,
1476     border: '',
1477     src: 'about:blank',
1478     href: false,
1479     target: false,
1480     xsUrl: '',
1481     smUrl: '',
1482     mdUrl: '',
1483     lgUrl: '',
1484
1485     getAutoCreate : function()
1486     {   
1487         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1488             return this.createSingleImg();
1489         }
1490         
1491         var cfg = {
1492             tag: 'div',
1493             cls: 'roo-image-responsive-group',
1494             cn: []
1495         };
1496         var _this = this;
1497         
1498         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1499             
1500             if(!_this[size + 'Url']){
1501                 return;
1502             }
1503             
1504             var img = {
1505                 tag: 'img',
1506                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1507                 html: _this.html || cfg.html,
1508                 src: _this[size + 'Url']
1509             };
1510             
1511             img.cls += ' roo-image-responsive-' + size;
1512             
1513             var s = ['xs', 'sm', 'md', 'lg'];
1514             
1515             s.splice(s.indexOf(size), 1);
1516             
1517             Roo.each(s, function(ss){
1518                 img.cls += ' hidden-' + ss;
1519             });
1520             
1521             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1522                 cfg.cls += ' img-' + _this.border;
1523             }
1524             
1525             if(_this.alt){
1526                 cfg.alt = _this.alt;
1527             }
1528             
1529             if(_this.href){
1530                 var a = {
1531                     tag: 'a',
1532                     href: _this.href,
1533                     cn: [
1534                         img
1535                     ]
1536                 };
1537
1538                 if(this.target){
1539                     a.target = _this.target;
1540                 }
1541             }
1542             
1543             cfg.cn.push((_this.href) ? a : img);
1544             
1545         });
1546         
1547         return cfg;
1548     },
1549     
1550     createSingleImg : function()
1551     {
1552         var cfg = {
1553             tag: 'img',
1554             cls: (this.imgResponsive) ? 'img-responsive' : '',
1555             html : null,
1556             src : 'about:blank'  // just incase src get's set to undefined?!?
1557         };
1558         
1559         cfg.html = this.html || cfg.html;
1560         
1561         cfg.src = this.src || cfg.src;
1562         
1563         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1564             cfg.cls += ' img-' + this.border;
1565         }
1566         
1567         if(this.alt){
1568             cfg.alt = this.alt;
1569         }
1570         
1571         if(this.href){
1572             var a = {
1573                 tag: 'a',
1574                 href: this.href,
1575                 cn: [
1576                     cfg
1577                 ]
1578             };
1579             
1580             if(this.target){
1581                 a.target = this.target;
1582             }
1583             
1584         }
1585         
1586         return (this.href) ? a : cfg;
1587     },
1588     
1589     initEvents: function() 
1590     {
1591         if(!this.href){
1592             this.el.on('click', this.onClick, this);
1593         }
1594         
1595     },
1596     
1597     onClick : function(e)
1598     {
1599         Roo.log('img onclick');
1600         this.fireEvent('click', this, e);
1601     },
1602     /**
1603      * Sets the url of the image - used to update it
1604      * @param {String} url the url of the image
1605      */
1606     
1607     setSrc : function(url)
1608     {
1609         this.src =  url;
1610         
1611         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1612             this.el.dom.src =  url;
1613             return;
1614         }
1615         
1616         this.el.select('img', true).first().dom.src =  url;
1617     }
1618     
1619     
1620    
1621 });
1622
1623  /*
1624  * - LGPL
1625  *
1626  * image
1627  * 
1628  */
1629
1630
1631 /**
1632  * @class Roo.bootstrap.Link
1633  * @extends Roo.bootstrap.Component
1634  * Bootstrap Link Class
1635  * @cfg {String} alt image alternative text
1636  * @cfg {String} href a tag href
1637  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1638  * @cfg {String} html the content of the link.
1639  * @cfg {String} anchor name for the anchor link
1640  * @cfg {String} fa - favicon
1641
1642  * @cfg {Boolean} preventDefault (true | false) default false
1643
1644  * 
1645  * @constructor
1646  * Create a new Input
1647  * @param {Object} config The config object
1648  */
1649
1650 Roo.bootstrap.Link = function(config){
1651     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1652     
1653     this.addEvents({
1654         // img events
1655         /**
1656          * @event click
1657          * The img click event for the img.
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1665     
1666     href: false,
1667     target: false,
1668     preventDefault: false,
1669     anchor : false,
1670     alt : false,
1671     fa: false,
1672
1673
1674     getAutoCreate : function()
1675     {
1676         var html = this.html || '';
1677         
1678         if (this.fa !== false) {
1679             html = '<i class="fa fa-' + this.fa + '"></i>';
1680         }
1681         var cfg = {
1682             tag: 'a'
1683         };
1684         // anchor's do not require html/href...
1685         if (this.anchor === false) {
1686             cfg.html = html;
1687             cfg.href = this.href || '#';
1688         } else {
1689             cfg.name = this.anchor;
1690             if (this.html !== false || this.fa !== false) {
1691                 cfg.html = html;
1692             }
1693             if (this.href !== false) {
1694                 cfg.href = this.href;
1695             }
1696         }
1697         
1698         if(this.alt !== false){
1699             cfg.alt = this.alt;
1700         }
1701         
1702         
1703         if(this.target !== false) {
1704             cfg.target = this.target;
1705         }
1706         
1707         return cfg;
1708     },
1709     
1710     initEvents: function() {
1711         
1712         if(!this.href || this.preventDefault){
1713             this.el.on('click', this.onClick, this);
1714         }
1715     },
1716     
1717     onClick : function(e)
1718     {
1719         if(this.preventDefault){
1720             e.preventDefault();
1721         }
1722         //Roo.log('img onclick');
1723         this.fireEvent('click', this, e);
1724     }
1725    
1726 });
1727
1728  /*
1729  * - LGPL
1730  *
1731  * header
1732  * 
1733  */
1734
1735 /**
1736  * @class Roo.bootstrap.Header
1737  * @extends Roo.bootstrap.Component
1738  * Bootstrap Header class
1739  * @cfg {String} html content of header
1740  * @cfg {Number} level (1|2|3|4|5|6) default 1
1741  * 
1742  * @constructor
1743  * Create a new Header
1744  * @param {Object} config The config object
1745  */
1746
1747
1748 Roo.bootstrap.Header  = function(config){
1749     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 };
1751
1752 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1753     
1754     //href : false,
1755     html : false,
1756     level : 1,
1757     
1758     
1759     
1760     getAutoCreate : function(){
1761         
1762         
1763         
1764         var cfg = {
1765             tag: 'h' + (1 *this.level),
1766             html: this.html || ''
1767         } ;
1768         
1769         return cfg;
1770     }
1771    
1772 });
1773
1774  
1775
1776  /*
1777  * Based on:
1778  * Ext JS Library 1.1.1
1779  * Copyright(c) 2006-2007, Ext JS, LLC.
1780  *
1781  * Originally Released Under LGPL - original licence link has changed is not relivant.
1782  *
1783  * Fork - LGPL
1784  * <script type="text/javascript">
1785  */
1786  
1787 /**
1788  * @class Roo.bootstrap.MenuMgr
1789  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790  * @singleton
1791  */
1792 Roo.bootstrap.MenuMgr = function(){
1793    var menus, active, groups = {}, attached = false, lastShow = new Date();
1794
1795    // private - called when first menu is created
1796    function init(){
1797        menus = {};
1798        active = new Roo.util.MixedCollection();
1799        Roo.get(document).addKeyListener(27, function(){
1800            if(active.length > 0){
1801                hideAll();
1802            }
1803        });
1804    }
1805
1806    // private
1807    function hideAll(){
1808        if(active && active.length > 0){
1809            var c = active.clone();
1810            c.each(function(m){
1811                m.hide();
1812            });
1813        }
1814    }
1815
1816    // private
1817    function onHide(m){
1818        active.remove(m);
1819        if(active.length < 1){
1820            Roo.get(document).un("mouseup", onMouseDown);
1821             
1822            attached = false;
1823        }
1824    }
1825
1826    // private
1827    function onShow(m){
1828        var last = active.last();
1829        lastShow = new Date();
1830        active.add(m);
1831        if(!attached){
1832           Roo.get(document).on("mouseup", onMouseDown);
1833            
1834            attached = true;
1835        }
1836        if(m.parentMenu){
1837           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1838           m.parentMenu.activeChild = m;
1839        }else if(last && last.isVisible()){
1840           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1841        }
1842    }
1843
1844    // private
1845    function onBeforeHide(m){
1846        if(m.activeChild){
1847            m.activeChild.hide();
1848        }
1849        if(m.autoHideTimer){
1850            clearTimeout(m.autoHideTimer);
1851            delete m.autoHideTimer;
1852        }
1853    }
1854
1855    // private
1856    function onBeforeShow(m){
1857        var pm = m.parentMenu;
1858        if(!pm && !m.allowOtherMenus){
1859            hideAll();
1860        }else if(pm && pm.activeChild && active != m){
1861            pm.activeChild.hide();
1862        }
1863    }
1864
1865    // private this should really trigger on mouseup..
1866    function onMouseDown(e){
1867         Roo.log("on Mouse Up");
1868         
1869         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1870             Roo.log("MenuManager hideAll");
1871             hideAll();
1872             e.stopEvent();
1873         }
1874         
1875         
1876    }
1877
1878    // private
1879    function onBeforeCheck(mi, state){
1880        if(state){
1881            var g = groups[mi.group];
1882            for(var i = 0, l = g.length; i < l; i++){
1883                if(g[i] != mi){
1884                    g[i].setChecked(false);
1885                }
1886            }
1887        }
1888    }
1889
1890    return {
1891
1892        /**
1893         * Hides all menus that are currently visible
1894         */
1895        hideAll : function(){
1896             hideAll();  
1897        },
1898
1899        // private
1900        register : function(menu){
1901            if(!menus){
1902                init();
1903            }
1904            menus[menu.id] = menu;
1905            menu.on("beforehide", onBeforeHide);
1906            menu.on("hide", onHide);
1907            menu.on("beforeshow", onBeforeShow);
1908            menu.on("show", onShow);
1909            var g = menu.group;
1910            if(g && menu.events["checkchange"]){
1911                if(!groups[g]){
1912                    groups[g] = [];
1913                }
1914                groups[g].push(menu);
1915                menu.on("checkchange", onCheck);
1916            }
1917        },
1918
1919         /**
1920          * Returns a {@link Roo.menu.Menu} object
1921          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1922          * be used to generate and return a new Menu instance.
1923          */
1924        get : function(menu){
1925            if(typeof menu == "string"){ // menu id
1926                return menus[menu];
1927            }else if(menu.events){  // menu instance
1928                return menu;
1929            }
1930            /*else if(typeof menu.length == 'number'){ // array of menu items?
1931                return new Roo.bootstrap.Menu({items:menu});
1932            }else{ // otherwise, must be a config
1933                return new Roo.bootstrap.Menu(menu);
1934            }
1935            */
1936            return false;
1937        },
1938
1939        // private
1940        unregister : function(menu){
1941            delete menus[menu.id];
1942            menu.un("beforehide", onBeforeHide);
1943            menu.un("hide", onHide);
1944            menu.un("beforeshow", onBeforeShow);
1945            menu.un("show", onShow);
1946            var g = menu.group;
1947            if(g && menu.events["checkchange"]){
1948                groups[g].remove(menu);
1949                menu.un("checkchange", onCheck);
1950            }
1951        },
1952
1953        // private
1954        registerCheckable : function(menuItem){
1955            var g = menuItem.group;
1956            if(g){
1957                if(!groups[g]){
1958                    groups[g] = [];
1959                }
1960                groups[g].push(menuItem);
1961                menuItem.on("beforecheckchange", onBeforeCheck);
1962            }
1963        },
1964
1965        // private
1966        unregisterCheckable : function(menuItem){
1967            var g = menuItem.group;
1968            if(g){
1969                groups[g].remove(menuItem);
1970                menuItem.un("beforecheckchange", onBeforeCheck);
1971            }
1972        }
1973    };
1974 }();/*
1975  * - LGPL
1976  *
1977  * menu
1978  * 
1979  */
1980
1981 /**
1982  * @class Roo.bootstrap.Menu
1983  * @extends Roo.bootstrap.Component
1984  * Bootstrap Menu class - container for MenuItems
1985  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1986  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1987  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1988  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1989  * 
1990  * @constructor
1991  * Create a new Menu
1992  * @param {Object} config The config object
1993  */
1994
1995
1996 Roo.bootstrap.Menu = function(config){
1997     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1998     if (this.registerMenu && this.type != 'treeview')  {
1999         Roo.bootstrap.MenuMgr.register(this);
2000     }
2001     this.addEvents({
2002         /**
2003          * @event beforeshow
2004          * Fires before this menu is displayed
2005          * @param {Roo.menu.Menu} this
2006          */
2007         beforeshow : true,
2008         /**
2009          * @event beforehide
2010          * Fires before this menu is hidden
2011          * @param {Roo.menu.Menu} this
2012          */
2013         beforehide : true,
2014         /**
2015          * @event show
2016          * Fires after this menu is displayed
2017          * @param {Roo.menu.Menu} this
2018          */
2019         show : true,
2020         /**
2021          * @event hide
2022          * Fires after this menu is hidden
2023          * @param {Roo.menu.Menu} this
2024          */
2025         hide : true,
2026         /**
2027          * @event click
2028          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2029          * @param {Roo.menu.Menu} this
2030          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2031          * @param {Roo.EventObject} e
2032          */
2033         click : true,
2034         /**
2035          * @event mouseover
2036          * Fires when the mouse is hovering over this menu
2037          * @param {Roo.menu.Menu} this
2038          * @param {Roo.EventObject} e
2039          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2040          */
2041         mouseover : true,
2042         /**
2043          * @event mouseout
2044          * Fires when the mouse exits this menu
2045          * @param {Roo.menu.Menu} this
2046          * @param {Roo.EventObject} e
2047          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2048          */
2049         mouseout : true,
2050         /**
2051          * @event itemclick
2052          * Fires when a menu item contained in this menu is clicked
2053          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2054          * @param {Roo.EventObject} e
2055          */
2056         itemclick: true
2057     });
2058     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 };
2060
2061 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2062     
2063    /// html : false,
2064     //align : '',
2065     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2066     type: false,
2067     /**
2068      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2069      */
2070     registerMenu : true,
2071     
2072     menuItems :false, // stores the menu items..
2073     
2074     hidden:true,
2075         
2076     parentMenu : false,
2077     
2078     stopEvent : true,
2079     
2080     isLink : false,
2081     
2082     getChildContainer : function() {
2083         return this.el;  
2084     },
2085     
2086     getAutoCreate : function(){
2087          
2088         //if (['right'].indexOf(this.align)!==-1) {
2089         //    cfg.cn[1].cls += ' pull-right'
2090         //}
2091         
2092         
2093         var cfg = {
2094             tag : 'ul',
2095             cls : 'dropdown-menu' ,
2096             style : 'z-index:1000'
2097             
2098         };
2099         
2100         if (this.type === 'submenu') {
2101             cfg.cls = 'submenu active';
2102         }
2103         if (this.type === 'treeview') {
2104             cfg.cls = 'treeview-menu';
2105         }
2106         
2107         return cfg;
2108     },
2109     initEvents : function() {
2110         
2111        // Roo.log("ADD event");
2112        // Roo.log(this.triggerEl.dom);
2113         
2114         this.triggerEl.on('click', this.onTriggerClick, this);
2115         
2116         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2117         
2118         this.triggerEl.addClass('dropdown-toggle');
2119         
2120         if (Roo.isTouch) {
2121             this.el.on('touchstart'  , this.onTouch, this);
2122         }
2123         this.el.on('click' , this.onClick, this);
2124
2125         this.el.on("mouseover", this.onMouseOver, this);
2126         this.el.on("mouseout", this.onMouseOut, this);
2127         
2128     },
2129     
2130     findTargetItem : function(e)
2131     {
2132         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2133         if(!t){
2134             return false;
2135         }
2136         //Roo.log(t);         Roo.log(t.id);
2137         if(t && t.id){
2138             //Roo.log(this.menuitems);
2139             return this.menuitems.get(t.id);
2140             
2141             //return this.items.get(t.menuItemId);
2142         }
2143         
2144         return false;
2145     },
2146     
2147     onTouch : function(e) 
2148     {
2149         Roo.log("menu.onTouch");
2150         //e.stopEvent(); this make the user popdown broken
2151         this.onClick(e);
2152     },
2153     
2154     onClick : function(e)
2155     {
2156         Roo.log("menu.onClick");
2157         
2158         var t = this.findTargetItem(e);
2159         if(!t || t.isContainer){
2160             return;
2161         }
2162         Roo.log(e);
2163         /*
2164         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2165             if(t == this.activeItem && t.shouldDeactivate(e)){
2166                 this.activeItem.deactivate();
2167                 delete this.activeItem;
2168                 return;
2169             }
2170             if(t.canActivate){
2171                 this.setActiveItem(t, true);
2172             }
2173             return;
2174             
2175             
2176         }
2177         */
2178        
2179         Roo.log('pass click event');
2180         
2181         t.onClick(e);
2182         
2183         this.fireEvent("click", this, t, e);
2184         
2185         var _this = this;
2186         
2187         if(!t.href.length || t.href == '#'){
2188             (function() { _this.hide(); }).defer(100);
2189         }
2190         
2191     },
2192     
2193     onMouseOver : function(e){
2194         var t  = this.findTargetItem(e);
2195         //Roo.log(t);
2196         //if(t){
2197         //    if(t.canActivate && !t.disabled){
2198         //        this.setActiveItem(t, true);
2199         //    }
2200         //}
2201         
2202         this.fireEvent("mouseover", this, e, t);
2203     },
2204     isVisible : function(){
2205         return !this.hidden;
2206     },
2207      onMouseOut : function(e){
2208         var t  = this.findTargetItem(e);
2209         
2210         //if(t ){
2211         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2212         //        this.activeItem.deactivate();
2213         //        delete this.activeItem;
2214         //    }
2215         //}
2216         this.fireEvent("mouseout", this, e, t);
2217     },
2218     
2219     
2220     /**
2221      * Displays this menu relative to another element
2222      * @param {String/HTMLElement/Roo.Element} element The element to align to
2223      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2224      * the element (defaults to this.defaultAlign)
2225      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2226      */
2227     show : function(el, pos, parentMenu){
2228         this.parentMenu = parentMenu;
2229         if(!this.el){
2230             this.render();
2231         }
2232         this.fireEvent("beforeshow", this);
2233         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234     },
2235      /**
2236      * Displays this menu at a specific xy position
2237      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2238      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2239      */
2240     showAt : function(xy, parentMenu, /* private: */_e){
2241         this.parentMenu = parentMenu;
2242         if(!this.el){
2243             this.render();
2244         }
2245         if(_e !== false){
2246             this.fireEvent("beforeshow", this);
2247             //xy = this.el.adjustForConstraints(xy);
2248         }
2249         
2250         //this.el.show();
2251         this.hideMenuItems();
2252         this.hidden = false;
2253         this.triggerEl.addClass('open');
2254         
2255         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2256             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2257         }
2258         
2259         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2260             this.el.setXY(xy);
2261         }
2262         
2263         this.focus();
2264         this.fireEvent("show", this);
2265     },
2266     
2267     focus : function(){
2268         return;
2269         if(!this.hidden){
2270             this.doFocus.defer(50, this);
2271         }
2272     },
2273
2274     doFocus : function(){
2275         if(!this.hidden){
2276             this.focusEl.focus();
2277         }
2278     },
2279
2280     /**
2281      * Hides this menu and optionally all parent menus
2282      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2283      */
2284     hide : function(deep)
2285     {
2286         
2287         this.hideMenuItems();
2288         if(this.el && this.isVisible()){
2289             this.fireEvent("beforehide", this);
2290             if(this.activeItem){
2291                 this.activeItem.deactivate();
2292                 this.activeItem = null;
2293             }
2294             this.triggerEl.removeClass('open');;
2295             this.hidden = true;
2296             this.fireEvent("hide", this);
2297         }
2298         if(deep === true && this.parentMenu){
2299             this.parentMenu.hide(true);
2300         }
2301     },
2302     
2303     onTriggerClick : function(e)
2304     {
2305         Roo.log('trigger click');
2306         
2307         var target = e.getTarget();
2308         
2309         Roo.log(target.nodeName.toLowerCase());
2310         
2311         if(target.nodeName.toLowerCase() === 'i'){
2312             e.preventDefault();
2313         }
2314         
2315     },
2316     
2317     onTriggerPress  : function(e)
2318     {
2319         Roo.log('trigger press');
2320         //Roo.log(e.getTarget());
2321        // Roo.log(this.triggerEl.dom);
2322        
2323         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2324         var pel = Roo.get(e.getTarget());
2325         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2326             Roo.log('is treeview or dropdown?');
2327             return;
2328         }
2329         
2330         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2331             return;
2332         }
2333         
2334         if (this.isVisible()) {
2335             Roo.log('hide');
2336             this.hide();
2337         } else {
2338             Roo.log('show');
2339             this.show(this.triggerEl, false, false);
2340         }
2341         
2342         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2343             e.stopEvent();
2344         }
2345         
2346     },
2347        
2348     
2349     hideMenuItems : function()
2350     {
2351         Roo.log("hide Menu Items");
2352         if (!this.el) { 
2353             return;
2354         }
2355         //$(backdrop).remove()
2356         this.el.select('.open',true).each(function(aa) {
2357             
2358             aa.removeClass('open');
2359           //var parent = getParent($(this))
2360           //var relatedTarget = { relatedTarget: this }
2361           
2362            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2363           //if (e.isDefaultPrevented()) return
2364            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2365         });
2366     },
2367     addxtypeChild : function (tree, cntr) {
2368         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2369           
2370         this.menuitems.add(comp);
2371         return comp;
2372
2373     },
2374     getEl : function()
2375     {
2376         Roo.log(this.el);
2377         return this.el;
2378     },
2379     
2380     clear : function()
2381     {
2382         this.getEl().dom.innerHTML = '';
2383         this.menuitems.clear();
2384     }
2385 });
2386
2387  
2388  /*
2389  * - LGPL
2390  *
2391  * menu item
2392  * 
2393  */
2394
2395
2396 /**
2397  * @class Roo.bootstrap.MenuItem
2398  * @extends Roo.bootstrap.Component
2399  * Bootstrap MenuItem class
2400  * @cfg {String} html the menu label
2401  * @cfg {String} href the link
2402  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2403  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2404  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2405  * @cfg {String} fa favicon to show on left of menu item.
2406  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2407  * 
2408  * 
2409  * @constructor
2410  * Create a new MenuItem
2411  * @param {Object} config The config object
2412  */
2413
2414
2415 Roo.bootstrap.MenuItem = function(config){
2416     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2417     this.addEvents({
2418         // raw events
2419         /**
2420          * @event click
2421          * The raw click event for the entire grid.
2422          * @param {Roo.bootstrap.MenuItem} this
2423          * @param {Roo.EventObject} e
2424          */
2425         "click" : true
2426     });
2427 };
2428
2429 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2430     
2431     href : false,
2432     html : false,
2433     preventDefault: false,
2434     isContainer : false,
2435     active : false,
2436     fa: false,
2437     
2438     getAutoCreate : function(){
2439         
2440         if(this.isContainer){
2441             return {
2442                 tag: 'li',
2443                 cls: 'dropdown-menu-item'
2444             };
2445         }
2446         var ctag = {
2447             tag: 'span',
2448             html: 'Link'
2449         };
2450         
2451         var anc = {
2452             tag : 'a',
2453             href : '#',
2454             cn : [  ]
2455         };
2456         
2457         if (this.fa !== false) {
2458             anc.cn.push({
2459                 tag : 'i',
2460                 cls : 'fa fa-' + this.fa
2461             });
2462         }
2463         
2464         anc.cn.push(ctag);
2465         
2466         
2467         var cfg= {
2468             tag: 'li',
2469             cls: 'dropdown-menu-item',
2470             cn: [ anc ]
2471         };
2472         if (this.parent().type == 'treeview') {
2473             cfg.cls = 'treeview-menu';
2474         }
2475         if (this.active) {
2476             cfg.cls += ' active';
2477         }
2478         
2479         
2480         
2481         anc.href = this.href || cfg.cn[0].href ;
2482         ctag.html = this.html || cfg.cn[0].html ;
2483         return cfg;
2484     },
2485     
2486     initEvents: function()
2487     {
2488         if (this.parent().type == 'treeview') {
2489             this.el.select('a').on('click', this.onClick, this);
2490         }
2491         
2492         if (this.menu) {
2493             this.menu.parentType = this.xtype;
2494             this.menu.triggerEl = this.el;
2495             this.menu = this.addxtype(Roo.apply({}, this.menu));
2496         }
2497         
2498     },
2499     onClick : function(e)
2500     {
2501         Roo.log('item on click ');
2502         
2503         if(this.preventDefault){
2504             e.preventDefault();
2505         }
2506         //this.parent().hideMenuItems();
2507         
2508         this.fireEvent('click', this, e);
2509     },
2510     getEl : function()
2511     {
2512         return this.el;
2513     } 
2514 });
2515
2516  
2517
2518  /*
2519  * - LGPL
2520  *
2521  * menu separator
2522  * 
2523  */
2524
2525
2526 /**
2527  * @class Roo.bootstrap.MenuSeparator
2528  * @extends Roo.bootstrap.Component
2529  * Bootstrap MenuSeparator class
2530  * 
2531  * @constructor
2532  * Create a new MenuItem
2533  * @param {Object} config The config object
2534  */
2535
2536
2537 Roo.bootstrap.MenuSeparator = function(config){
2538     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2539 };
2540
2541 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2542     
2543     getAutoCreate : function(){
2544         var cfg = {
2545             cls: 'divider',
2546             tag : 'li'
2547         };
2548         
2549         return cfg;
2550     }
2551    
2552 });
2553
2554  
2555
2556  
2557 /*
2558 * Licence: LGPL
2559 */
2560
2561 /**
2562  * @class Roo.bootstrap.Modal
2563  * @extends Roo.bootstrap.Component
2564  * Bootstrap Modal class
2565  * @cfg {String} title Title of dialog
2566  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2567  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2568  * @cfg {Boolean} specificTitle default false
2569  * @cfg {Array} buttons Array of buttons or standard button set..
2570  * @cfg {String} buttonPosition (left|right|center) default right
2571  * @cfg {Boolean} animate default true
2572  * @cfg {Boolean} allow_close default true
2573  * @cfg {Boolean} fitwindow default false
2574  * @cfg {String} size (sm|lg) default empty
2575  *
2576  *
2577  * @constructor
2578  * Create a new Modal Dialog
2579  * @param {Object} config The config object
2580  */
2581
2582 Roo.bootstrap.Modal = function(config){
2583     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2584     this.addEvents({
2585         // raw events
2586         /**
2587          * @event btnclick
2588          * The raw btnclick event for the button
2589          * @param {Roo.EventObject} e
2590          */
2591         "btnclick" : true,
2592         /**
2593          * @event resize
2594          * Fire when dialog resize
2595          * @param {Roo.bootstrap.Modal} this
2596          * @param {Roo.EventObject} e
2597          */
2598         "resize" : true
2599     });
2600     this.buttons = this.buttons || [];
2601
2602     if (this.tmpl) {
2603         this.tmpl = Roo.factory(this.tmpl);
2604     }
2605
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2609
2610     title : 'test dialog',
2611
2612     buttons : false,
2613
2614     // set on load...
2615
2616     html: false,
2617
2618     tmp: false,
2619
2620     specificTitle: false,
2621
2622     buttonPosition: 'right',
2623
2624     allow_close : true,
2625
2626     animate : true,
2627
2628     fitwindow: false,
2629
2630
2631      // private
2632     dialogEl: false,
2633     bodyEl:  false,
2634     footerEl:  false,
2635     titleEl:  false,
2636     closeEl:  false,
2637
2638     size: '',
2639
2640
2641     onRender : function(ct, position)
2642     {
2643         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2644
2645         if(!this.el){
2646             var cfg = Roo.apply({},  this.getAutoCreate());
2647             cfg.id = Roo.id();
2648             //if(!cfg.name){
2649             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2650             //}
2651             //if (!cfg.name.length) {
2652             //    delete cfg.name;
2653            // }
2654             if (this.cls) {
2655                 cfg.cls += ' ' + this.cls;
2656             }
2657             if (this.style) {
2658                 cfg.style = this.style;
2659             }
2660             this.el = Roo.get(document.body).createChild(cfg, position);
2661         }
2662         //var type = this.el.dom.type;
2663
2664
2665         if(this.tabIndex !== undefined){
2666             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2667         }
2668
2669         this.dialogEl = this.el.select('.modal-dialog',true).first();
2670         this.bodyEl = this.el.select('.modal-body',true).first();
2671         this.closeEl = this.el.select('.modal-header .close', true).first();
2672         this.headerEl = this.el.select('.modal-header',true).first();
2673         this.titleEl = this.el.select('.modal-title',true).first();
2674         this.footerEl = this.el.select('.modal-footer',true).first();
2675
2676         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2677         this.maskEl.enableDisplayMode("block");
2678         this.maskEl.hide();
2679         //this.el.addClass("x-dlg-modal");
2680
2681         if (this.buttons.length) {
2682             Roo.each(this.buttons, function(bb) {
2683                 var b = Roo.apply({}, bb);
2684                 b.xns = b.xns || Roo.bootstrap;
2685                 b.xtype = b.xtype || 'Button';
2686                 if (typeof(b.listeners) == 'undefined') {
2687                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2688                 }
2689
2690                 var btn = Roo.factory(b);
2691
2692                 btn.render(this.el.select('.modal-footer div').first());
2693
2694             },this);
2695         }
2696         // render the children.
2697         var nitems = [];
2698
2699         if(typeof(this.items) != 'undefined'){
2700             var items = this.items;
2701             delete this.items;
2702
2703             for(var i =0;i < items.length;i++) {
2704                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2705             }
2706         }
2707
2708         this.items = nitems;
2709
2710         // where are these used - they used to be body/close/footer
2711
2712
2713         this.initEvents();
2714         //this.el.addClass([this.fieldClass, this.cls]);
2715
2716     },
2717
2718     getAutoCreate : function(){
2719
2720
2721         var bdy = {
2722                 cls : 'modal-body',
2723                 html : this.html || ''
2724         };
2725
2726         var title = {
2727             tag: 'h4',
2728             cls : 'modal-title',
2729             html : this.title
2730         };
2731
2732         if(this.specificTitle){
2733             title = this.title;
2734
2735         };
2736
2737         var header = [];
2738         if (this.allow_close) {
2739             header.push({
2740                 tag: 'button',
2741                 cls : 'close',
2742                 html : '&times'
2743             });
2744         }
2745
2746         header.push(title);
2747
2748         var size = '';
2749
2750         if(this.size.length){
2751             size = 'modal-' + this.size;
2752         }
2753
2754         var modal = {
2755             cls: "modal",
2756             style : 'display: none',
2757             cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2819         if (this.fitwindow) {
2820             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2821             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2822             this.setSize(w,h);
2823         }
2824     },
2825
2826     setSize : function(w,h)
2827     {
2828         if (!w && !h) {
2829             return;
2830         }
2831         this.resizeTo(w,h);
2832     },
2833
2834     show : function() {
2835
2836         if (!this.rendered) {
2837             this.render();
2838         }
2839
2840         this.el.setStyle('display', 'block');
2841
2842         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2843             var _this = this;
2844             (function(){
2845                 this.el.addClass('in');
2846             }).defer(50, this);
2847         }else{
2848             this.el.addClass('in');
2849
2850         }
2851
2852         // not sure how we can show data in here..
2853         //if (this.tmpl) {
2854         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2855         //}
2856
2857         Roo.get(document.body).addClass("x-body-masked");
2858         
2859         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2860         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2861         this.maskEl.show();
2862         
2863         this.resize();
2864         
2865         this.fireEvent('show', this);
2866
2867         // set zindex here - otherwise it appears to be ignored...
2868         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2869
2870         (function () {
2871             this.items.forEach( function(e) {
2872                 e.layout ? e.layout() : false;
2873
2874             });
2875         }).defer(100,this);
2876
2877     },
2878     hide : function()
2879     {
2880         if(this.fireEvent("beforehide", this) !== false){
2881             this.maskEl.hide();
2882             Roo.get(document.body).removeClass("x-body-masked");
2883             this.el.removeClass('in');
2884             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2885
2886             if(this.animate){ // why
2887                 var _this = this;
2888                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2889             }else{
2890                 this.el.setStyle('display', 'none');
2891             }
2892             this.fireEvent('hide', this);
2893         }
2894     },
2895
2896     addButton : function(str, cb)
2897     {
2898
2899
2900         var b = Roo.apply({}, { html : str } );
2901         b.xns = b.xns || Roo.bootstrap;
2902         b.xtype = b.xtype || 'Button';
2903         if (typeof(b.listeners) == 'undefined') {
2904             b.listeners = { click : cb.createDelegate(this)  };
2905         }
2906
2907         var btn = Roo.factory(b);
2908
2909         btn.render(this.el.select('.modal-footer div').first());
2910
2911         return btn;
2912
2913     },
2914
2915     setDefaultButton : function(btn)
2916     {
2917         //this.el.select('.modal-footer').()
2918     },
2919     diff : false,
2920
2921     resizeTo: function(w,h)
2922     {
2923         // skip.. ?? why??
2924
2925         this.dialogEl.setWidth(w);
2926         if (this.diff === false) {
2927             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2928         }
2929
2930         this.bodyEl.setHeight(h-this.diff);
2931
2932         this.fireEvent('resize', this);
2933
2934     },
2935     setContentSize  : function(w, h)
2936     {
2937
2938     },
2939     onButtonClick: function(btn,e)
2940     {
2941         //Roo.log([a,b,c]);
2942         this.fireEvent('btnclick', btn.name, e);
2943     },
2944      /**
2945      * Set the title of the Dialog
2946      * @param {String} str new Title
2947      */
2948     setTitle: function(str) {
2949         this.titleEl.dom.innerHTML = str;
2950     },
2951     /**
2952      * Set the body of the Dialog
2953      * @param {String} str new Title
2954      */
2955     setBody: function(str) {
2956         this.bodyEl.dom.innerHTML = str;
2957     },
2958     /**
2959      * Set the body of the Dialog using the template
2960      * @param {Obj} data - apply this data to the template and replace the body contents.
2961      */
2962     applyBody: function(obj)
2963     {
2964         if (!this.tmpl) {
2965             Roo.log("Error - using apply Body without a template");
2966             //code
2967         }
2968         this.tmpl.overwrite(this.bodyEl, obj);
2969     }
2970
2971 });
2972
2973
2974 Roo.apply(Roo.bootstrap.Modal,  {
2975     /**
2976          * Button config that displays a single OK button
2977          * @type Object
2978          */
2979         OK :  [{
2980             name : 'ok',
2981             weight : 'primary',
2982             html : 'OK'
2983         }],
2984         /**
2985          * Button config that displays Yes and No buttons
2986          * @type Object
2987          */
2988         YESNO : [
2989             {
2990                 name  : 'no',
2991                 html : 'No'
2992             },
2993             {
2994                 name  :'yes',
2995                 weight : 'primary',
2996                 html : 'Yes'
2997             }
2998         ],
2999
3000         /**
3001          * Button config that displays OK and Cancel buttons
3002          * @type Object
3003          */
3004         OKCANCEL : [
3005             {
3006                name : 'cancel',
3007                 html : 'Cancel'
3008             },
3009             {
3010                 name : 'ok',
3011                 weight : 'primary',
3012                 html : 'OK'
3013             }
3014         ],
3015         /**
3016          * Button config that displays Yes, No and Cancel buttons
3017          * @type Object
3018          */
3019         YESNOCANCEL : [
3020             {
3021                 name : 'yes',
3022                 weight : 'primary',
3023                 html : 'Yes'
3024             },
3025             {
3026                 name : 'no',
3027                 html : 'No'
3028             },
3029             {
3030                 name : 'cancel',
3031                 html : 'Cancel'
3032             }
3033         ],
3034         
3035         zIndex : 10001
3036 });
3037 /*
3038  * - LGPL
3039  *
3040  * messagebox - can be used as a replace
3041  * 
3042  */
3043 /**
3044  * @class Roo.MessageBox
3045  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3046  * Example usage:
3047  *<pre><code>
3048 // Basic alert:
3049 Roo.Msg.alert('Status', 'Changes saved successfully.');
3050
3051 // Prompt for user data:
3052 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3053     if (btn == 'ok'){
3054         // process text value...
3055     }
3056 });
3057
3058 // Show a dialog using config options:
3059 Roo.Msg.show({
3060    title:'Save Changes?',
3061    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3062    buttons: Roo.Msg.YESNOCANCEL,
3063    fn: processResult,
3064    animEl: 'elId'
3065 });
3066 </code></pre>
3067  * @singleton
3068  */
3069 Roo.bootstrap.MessageBox = function(){
3070     var dlg, opt, mask, waitTimer;
3071     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3072     var buttons, activeTextEl, bwidth;
3073
3074     
3075     // private
3076     var handleButton = function(button){
3077         dlg.hide();
3078         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3079     };
3080
3081     // private
3082     var handleHide = function(){
3083         if(opt && opt.cls){
3084             dlg.el.removeClass(opt.cls);
3085         }
3086         //if(waitTimer){
3087         //    Roo.TaskMgr.stop(waitTimer);
3088         //    waitTimer = null;
3089         //}
3090     };
3091
3092     // private
3093     var updateButtons = function(b){
3094         var width = 0;
3095         if(!b){
3096             buttons["ok"].hide();
3097             buttons["cancel"].hide();
3098             buttons["yes"].hide();
3099             buttons["no"].hide();
3100             //dlg.footer.dom.style.display = 'none';
3101             return width;
3102         }
3103         dlg.footerEl.dom.style.display = '';
3104         for(var k in buttons){
3105             if(typeof buttons[k] != "function"){
3106                 if(b[k]){
3107                     buttons[k].show();
3108                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3109                     width += buttons[k].el.getWidth()+15;
3110                 }else{
3111                     buttons[k].hide();
3112                 }
3113             }
3114         }
3115         return width;
3116     };
3117
3118     // private
3119     var handleEsc = function(d, k, e){
3120         if(opt && opt.closable !== false){
3121             dlg.hide();
3122         }
3123         if(e){
3124             e.stopEvent();
3125         }
3126     };
3127
3128     return {
3129         /**
3130          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3131          * @return {Roo.BasicDialog} The BasicDialog element
3132          */
3133         getDialog : function(){
3134            if(!dlg){
3135                 dlg = new Roo.bootstrap.Modal( {
3136                     //draggable: true,
3137                     //resizable:false,
3138                     //constraintoviewport:false,
3139                     //fixedcenter:true,
3140                     //collapsible : false,
3141                     //shim:true,
3142                     //modal: true,
3143                 //    width: 'auto',
3144                   //  height:100,
3145                     //buttonAlign:"center",
3146                     closeClick : function(){
3147                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3148                             handleButton("no");
3149                         }else{
3150                             handleButton("cancel");
3151                         }
3152                     }
3153                 });
3154                 dlg.render();
3155                 dlg.on("hide", handleHide);
3156                 mask = dlg.mask;
3157                 //dlg.addKeyListener(27, handleEsc);
3158                 buttons = {};
3159                 this.buttons = buttons;
3160                 var bt = this.buttonText;
3161                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3162                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3163                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3164                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3165                 //Roo.log(buttons);
3166                 bodyEl = dlg.bodyEl.createChild({
3167
3168                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3169                         '<textarea class="roo-mb-textarea"></textarea>' +
3170                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3171                 });
3172                 msgEl = bodyEl.dom.firstChild;
3173                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3174                 textboxEl.enableDisplayMode();
3175                 textboxEl.addKeyListener([10,13], function(){
3176                     if(dlg.isVisible() && opt && opt.buttons){
3177                         if(opt.buttons.ok){
3178                             handleButton("ok");
3179                         }else if(opt.buttons.yes){
3180                             handleButton("yes");
3181                         }
3182                     }
3183                 });
3184                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3185                 textareaEl.enableDisplayMode();
3186                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3187                 progressEl.enableDisplayMode();
3188                 
3189                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3190                 var pf = progressEl.dom.firstChild;
3191                 if (pf) {
3192                     pp = Roo.get(pf.firstChild);
3193                     pp.setHeight(pf.offsetHeight);
3194                 }
3195                 
3196             }
3197             return dlg;
3198         },
3199
3200         /**
3201          * Updates the message box body text
3202          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3203          * the XHTML-compliant non-breaking space character '&amp;#160;')
3204          * @return {Roo.MessageBox} This message box
3205          */
3206         updateText : function(text)
3207         {
3208             if(!dlg.isVisible() && !opt.width){
3209                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3210                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3211             }
3212             msgEl.innerHTML = text || '&#160;';
3213       
3214             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3215             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3216             var w = Math.max(
3217                     Math.min(opt.width || cw , this.maxWidth), 
3218                     Math.max(opt.minWidth || this.minWidth, bwidth)
3219             );
3220             if(opt.prompt){
3221                 activeTextEl.setWidth(w);
3222             }
3223             if(dlg.isVisible()){
3224                 dlg.fixedcenter = false;
3225             }
3226             // to big, make it scroll. = But as usual stupid IE does not support
3227             // !important..
3228             
3229             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3230                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3231                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3232             } else {
3233                 bodyEl.dom.style.height = '';
3234                 bodyEl.dom.style.overflowY = '';
3235             }
3236             if (cw > w) {
3237                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3238             } else {
3239                 bodyEl.dom.style.overflowX = '';
3240             }
3241             
3242             dlg.setContentSize(w, bodyEl.getHeight());
3243             if(dlg.isVisible()){
3244                 dlg.fixedcenter = true;
3245             }
3246             return this;
3247         },
3248
3249         /**
3250          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3251          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3252          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3253          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3254          * @return {Roo.MessageBox} This message box
3255          */
3256         updateProgress : function(value, text){
3257             if(text){
3258                 this.updateText(text);
3259             }
3260             
3261             if (pp) { // weird bug on my firefox - for some reason this is not defined
3262                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3263                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3264             }
3265             return this;
3266         },        
3267
3268         /**
3269          * Returns true if the message box is currently displayed
3270          * @return {Boolean} True if the message box is visible, else false
3271          */
3272         isVisible : function(){
3273             return dlg && dlg.isVisible();  
3274         },
3275
3276         /**
3277          * Hides the message box if it is displayed
3278          */
3279         hide : function(){
3280             if(this.isVisible()){
3281                 dlg.hide();
3282             }  
3283         },
3284
3285         /**
3286          * Displays a new message box, or reinitializes an existing message box, based on the config options
3287          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3288          * The following config object properties are supported:
3289          * <pre>
3290 Property    Type             Description
3291 ----------  ---------------  ------------------------------------------------------------------------------------
3292 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3293                                    closes (defaults to undefined)
3294 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3295                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3296 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3297                                    progress and wait dialogs will ignore this property and always hide the
3298                                    close button as they can only be closed programmatically.
3299 cls               String           A custom CSS class to apply to the message box element
3300 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3301                                    displayed (defaults to 75)
3302 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3303                                    function will be btn (the name of the button that was clicked, if applicable,
3304                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3305                                    Progress and wait dialogs will ignore this option since they do not respond to
3306                                    user actions and can only be closed programmatically, so any required function
3307                                    should be called by the same code after it closes the dialog.
3308 icon              String           A CSS class that provides a background image to be used as an icon for
3309                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3310 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3311 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3312 modal             Boolean          False to allow user interaction with the page while the message box is
3313                                    displayed (defaults to true)
3314 msg               String           A string that will replace the existing message box body text (defaults
3315                                    to the XHTML-compliant non-breaking space character '&#160;')
3316 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3317 progress          Boolean          True to display a progress bar (defaults to false)
3318 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3319 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3320 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3321 title             String           The title text
3322 value             String           The string value to set into the active textbox element if displayed
3323 wait              Boolean          True to display a progress bar (defaults to false)
3324 width             Number           The width of the dialog in pixels
3325 </pre>
3326          *
3327          * Example usage:
3328          * <pre><code>
3329 Roo.Msg.show({
3330    title: 'Address',
3331    msg: 'Please enter your address:',
3332    width: 300,
3333    buttons: Roo.MessageBox.OKCANCEL,
3334    multiline: true,
3335    fn: saveAddress,
3336    animEl: 'addAddressBtn'
3337 });
3338 </code></pre>
3339          * @param {Object} config Configuration options
3340          * @return {Roo.MessageBox} This message box
3341          */
3342         show : function(options)
3343         {
3344             
3345             // this causes nightmares if you show one dialog after another
3346             // especially on callbacks..
3347              
3348             if(this.isVisible()){
3349                 
3350                 this.hide();
3351                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3352                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3353                 Roo.log("New Dialog Message:" +  options.msg )
3354                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3355                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3356                 
3357             }
3358             var d = this.getDialog();
3359             opt = options;
3360             d.setTitle(opt.title || "&#160;");
3361             d.closeEl.setDisplayed(opt.closable !== false);
3362             activeTextEl = textboxEl;
3363             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3364             if(opt.prompt){
3365                 if(opt.multiline){
3366                     textboxEl.hide();
3367                     textareaEl.show();
3368                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3369                         opt.multiline : this.defaultTextHeight);
3370                     activeTextEl = textareaEl;
3371                 }else{
3372                     textboxEl.show();
3373                     textareaEl.hide();
3374                 }
3375             }else{
3376                 textboxEl.hide();
3377                 textareaEl.hide();
3378             }
3379             progressEl.setDisplayed(opt.progress === true);
3380             this.updateProgress(0);
3381             activeTextEl.dom.value = opt.value || "";
3382             if(opt.prompt){
3383                 dlg.setDefaultButton(activeTextEl);
3384             }else{
3385                 var bs = opt.buttons;
3386                 var db = null;
3387                 if(bs && bs.ok){
3388                     db = buttons["ok"];
3389                 }else if(bs && bs.yes){
3390                     db = buttons["yes"];
3391                 }
3392                 dlg.setDefaultButton(db);
3393             }
3394             bwidth = updateButtons(opt.buttons);
3395             this.updateText(opt.msg);
3396             if(opt.cls){
3397                 d.el.addClass(opt.cls);
3398             }
3399             d.proxyDrag = opt.proxyDrag === true;
3400             d.modal = opt.modal !== false;
3401             d.mask = opt.modal !== false ? mask : false;
3402             if(!d.isVisible()){
3403                 // force it to the end of the z-index stack so it gets a cursor in FF
3404                 document.body.appendChild(dlg.el.dom);
3405                 d.animateTarget = null;
3406                 d.show(options.animEl);
3407             }
3408             return this;
3409         },
3410
3411         /**
3412          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3413          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3414          * and closing the message box when the process is complete.
3415          * @param {String} title The title bar text
3416          * @param {String} msg The message box body text
3417          * @return {Roo.MessageBox} This message box
3418          */
3419         progress : function(title, msg){
3420             this.show({
3421                 title : title,
3422                 msg : msg,
3423                 buttons: false,
3424                 progress:true,
3425                 closable:false,
3426                 minWidth: this.minProgressWidth,
3427                 modal : true
3428             });
3429             return this;
3430         },
3431
3432         /**
3433          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3434          * If a callback function is passed it will be called after the user clicks the button, and the
3435          * id of the button that was clicked will be passed as the only parameter to the callback
3436          * (could also be the top-right close button).
3437          * @param {String} title The title bar text
3438          * @param {String} msg The message box body text
3439          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3440          * @param {Object} scope (optional) The scope of the callback function
3441          * @return {Roo.MessageBox} This message box
3442          */
3443         alert : function(title, msg, fn, scope)
3444         {
3445             this.show({
3446                 title : title,
3447                 msg : msg,
3448                 buttons: this.OK,
3449                 fn: fn,
3450                 closable : false,
3451                 scope : scope,
3452                 modal : true
3453             });
3454             return this;
3455         },
3456
3457         /**
3458          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3459          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3460          * You are responsible for closing the message box when the process is complete.
3461          * @param {String} msg The message box body text
3462          * @param {String} title (optional) The title bar text
3463          * @return {Roo.MessageBox} This message box
3464          */
3465         wait : function(msg, title){
3466             this.show({
3467                 title : title,
3468                 msg : msg,
3469                 buttons: false,
3470                 closable:false,
3471                 progress:true,
3472                 modal:true,
3473                 width:300,
3474                 wait:true
3475             });
3476             waitTimer = Roo.TaskMgr.start({
3477                 run: function(i){
3478                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3479                 },
3480                 interval: 1000
3481             });
3482             return this;
3483         },
3484
3485         /**
3486          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3487          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3488          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3489          * @param {String} title The title bar text
3490          * @param {String} msg The message box body text
3491          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3492          * @param {Object} scope (optional) The scope of the callback function
3493          * @return {Roo.MessageBox} This message box
3494          */
3495         confirm : function(title, msg, fn, scope){
3496             this.show({
3497                 title : title,
3498                 msg : msg,
3499                 buttons: this.YESNO,
3500                 fn: fn,
3501                 scope : scope,
3502                 modal : true
3503             });
3504             return this;
3505         },
3506
3507         /**
3508          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3509          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3510          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3511          * (could also be the top-right close button) and the text that was entered will be passed as the two
3512          * parameters to the callback.
3513          * @param {String} title The title bar text
3514          * @param {String} msg The message box body text
3515          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3516          * @param {Object} scope (optional) The scope of the callback function
3517          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3518          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3519          * @return {Roo.MessageBox} This message box
3520          */
3521         prompt : function(title, msg, fn, scope, multiline){
3522             this.show({
3523                 title : title,
3524                 msg : msg,
3525                 buttons: this.OKCANCEL,
3526                 fn: fn,
3527                 minWidth:250,
3528                 scope : scope,
3529                 prompt:true,
3530                 multiline: multiline,
3531                 modal : true
3532             });
3533             return this;
3534         },
3535
3536         /**
3537          * Button config that displays a single OK button
3538          * @type Object
3539          */
3540         OK : {ok:true},
3541         /**
3542          * Button config that displays Yes and No buttons
3543          * @type Object
3544          */
3545         YESNO : {yes:true, no:true},
3546         /**
3547          * Button config that displays OK and Cancel buttons
3548          * @type Object
3549          */
3550         OKCANCEL : {ok:true, cancel:true},
3551         /**
3552          * Button config that displays Yes, No and Cancel buttons
3553          * @type Object
3554          */
3555         YESNOCANCEL : {yes:true, no:true, cancel:true},
3556
3557         /**
3558          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3559          * @type Number
3560          */
3561         defaultTextHeight : 75,
3562         /**
3563          * The maximum width in pixels of the message box (defaults to 600)
3564          * @type Number
3565          */
3566         maxWidth : 600,
3567         /**
3568          * The minimum width in pixels of the message box (defaults to 100)
3569          * @type Number
3570          */
3571         minWidth : 100,
3572         /**
3573          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3574          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3575          * @type Number
3576          */
3577         minProgressWidth : 250,
3578         /**
3579          * An object containing the default button text strings that can be overriden for localized language support.
3580          * Supported properties are: ok, cancel, yes and no.
3581          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3582          * @type Object
3583          */
3584         buttonText : {
3585             ok : "OK",
3586             cancel : "Cancel",
3587             yes : "Yes",
3588             no : "No"
3589         }
3590     };
3591 }();
3592
3593 /**
3594  * Shorthand for {@link Roo.MessageBox}
3595  */
3596 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3597 Roo.Msg = Roo.Msg || Roo.MessageBox;
3598 /*
3599  * - LGPL
3600  *
3601  * navbar
3602  * 
3603  */
3604
3605 /**
3606  * @class Roo.bootstrap.Navbar
3607  * @extends Roo.bootstrap.Component
3608  * Bootstrap Navbar class
3609
3610  * @constructor
3611  * Create a new Navbar
3612  * @param {Object} config The config object
3613  */
3614
3615
3616 Roo.bootstrap.Navbar = function(config){
3617     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3618     this.addEvents({
3619         // raw events
3620         /**
3621          * @event beforetoggle
3622          * Fire before toggle the menu
3623          * @param {Roo.EventObject} e
3624          */
3625         "beforetoggle" : true
3626     });
3627 };
3628
3629 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3630     
3631     
3632    
3633     // private
3634     navItems : false,
3635     loadMask : false,
3636     
3637     
3638     getAutoCreate : function(){
3639         
3640         
3641         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3642         
3643     },
3644     
3645     initEvents :function ()
3646     {
3647         //Roo.log(this.el.select('.navbar-toggle',true));
3648         this.el.select('.navbar-toggle',true).on('click', function() {
3649             if(this.fireEvent('beforetoggle', this) !== false){
3650                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3651             }
3652             
3653         }, this);
3654         
3655         var mark = {
3656             tag: "div",
3657             cls:"x-dlg-mask"
3658         };
3659         
3660         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3661         
3662         var size = this.el.getSize();
3663         this.maskEl.setSize(size.width, size.height);
3664         this.maskEl.enableDisplayMode("block");
3665         this.maskEl.hide();
3666         
3667         if(this.loadMask){
3668             this.maskEl.show();
3669         }
3670     },
3671     
3672     
3673     getChildContainer : function()
3674     {
3675         if (this.el.select('.collapse').getCount()) {
3676             return this.el.select('.collapse',true).first();
3677         }
3678         
3679         return this.el;
3680     },
3681     
3682     mask : function()
3683     {
3684         this.maskEl.show();
3685     },
3686     
3687     unmask : function()
3688     {
3689         this.maskEl.hide();
3690     } 
3691     
3692     
3693     
3694     
3695 });
3696
3697
3698
3699  
3700
3701  /*
3702  * - LGPL
3703  *
3704  * navbar
3705  * 
3706  */
3707
3708 /**
3709  * @class Roo.bootstrap.NavSimplebar
3710  * @extends Roo.bootstrap.Navbar
3711  * Bootstrap Sidebar class
3712  *
3713  * @cfg {Boolean} inverse is inverted color
3714  * 
3715  * @cfg {String} type (nav | pills | tabs)
3716  * @cfg {Boolean} arrangement stacked | justified
3717  * @cfg {String} align (left | right) alignment
3718  * 
3719  * @cfg {Boolean} main (true|false) main nav bar? default false
3720  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3721  * 
3722  * @cfg {String} tag (header|footer|nav|div) default is nav 
3723
3724  * 
3725  * 
3726  * 
3727  * @constructor
3728  * Create a new Sidebar
3729  * @param {Object} config The config object
3730  */
3731
3732
3733 Roo.bootstrap.NavSimplebar = function(config){
3734     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3735 };
3736
3737 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3738     
3739     inverse: false,
3740     
3741     type: false,
3742     arrangement: '',
3743     align : false,
3744     
3745     
3746     
3747     main : false,
3748     
3749     
3750     tag : false,
3751     
3752     
3753     getAutoCreate : function(){
3754         
3755         
3756         var cfg = {
3757             tag : this.tag || 'div',
3758             cls : 'navbar'
3759         };
3760           
3761         
3762         cfg.cn = [
3763             {
3764                 cls: 'nav',
3765                 tag : 'ul'
3766             }
3767         ];
3768         
3769          
3770         this.type = this.type || 'nav';
3771         if (['tabs','pills'].indexOf(this.type)!==-1) {
3772             cfg.cn[0].cls += ' nav-' + this.type
3773         
3774         
3775         } else {
3776             if (this.type!=='nav') {
3777                 Roo.log('nav type must be nav/tabs/pills')
3778             }
3779             cfg.cn[0].cls += ' navbar-nav'
3780         }
3781         
3782         
3783         
3784         
3785         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3786             cfg.cn[0].cls += ' nav-' + this.arrangement;
3787         }
3788         
3789         
3790         if (this.align === 'right') {
3791             cfg.cn[0].cls += ' navbar-right';
3792         }
3793         
3794         if (this.inverse) {
3795             cfg.cls += ' navbar-inverse';
3796             
3797         }
3798         
3799         
3800         return cfg;
3801     
3802         
3803     }
3804     
3805     
3806     
3807 });
3808
3809
3810
3811  
3812
3813  
3814        /*
3815  * - LGPL
3816  *
3817  * navbar
3818  * 
3819  */
3820
3821 /**
3822  * @class Roo.bootstrap.NavHeaderbar
3823  * @extends Roo.bootstrap.NavSimplebar
3824  * Bootstrap Sidebar class
3825  *
3826  * @cfg {String} brand what is brand
3827  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3828  * @cfg {String} brand_href href of the brand
3829  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3830  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3831  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3832  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3833  * 
3834  * @constructor
3835  * Create a new Sidebar
3836  * @param {Object} config The config object
3837  */
3838
3839
3840 Roo.bootstrap.NavHeaderbar = function(config){
3841     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3842       
3843 };
3844
3845 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3846     
3847     position: '',
3848     brand: '',
3849     brand_href: false,
3850     srButton : true,
3851     autohide : false,
3852     desktopCenter : false,
3853    
3854     
3855     getAutoCreate : function(){
3856         
3857         var   cfg = {
3858             tag: this.nav || 'nav',
3859             cls: 'navbar',
3860             role: 'navigation',
3861             cn: []
3862         };
3863         
3864         var cn = cfg.cn;
3865         if (this.desktopCenter) {
3866             cn.push({cls : 'container', cn : []});
3867             cn = cn[0].cn;
3868         }
3869         
3870         if(this.srButton){
3871             cn.push({
3872                 tag: 'div',
3873                 cls: 'navbar-header',
3874                 cn: [
3875                     {
3876                         tag: 'button',
3877                         type: 'button',
3878                         cls: 'navbar-toggle',
3879                         'data-toggle': 'collapse',
3880                         cn: [
3881                             {
3882                                 tag: 'span',
3883                                 cls: 'sr-only',
3884                                 html: 'Toggle navigation'
3885                             },
3886                             {
3887                                 tag: 'span',
3888                                 cls: 'icon-bar'
3889                             },
3890                             {
3891                                 tag: 'span',
3892                                 cls: 'icon-bar'
3893                             },
3894                             {
3895                                 tag: 'span',
3896                                 cls: 'icon-bar'
3897                             }
3898                         ]
3899                     }
3900                 ]
3901             });
3902         }
3903         
3904         cn.push({
3905             tag: 'div',
3906             cls: 'collapse navbar-collapse',
3907             cn : []
3908         });
3909         
3910         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3911         
3912         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3913             cfg.cls += ' navbar-' + this.position;
3914             
3915             // tag can override this..
3916             
3917             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3918         }
3919         
3920         if (this.brand !== '') {
3921             cn[0].cn.push({
3922                 tag: 'a',
3923                 href: this.brand_href ? this.brand_href : '#',
3924                 cls: 'navbar-brand',
3925                 cn: [
3926                 this.brand
3927                 ]
3928             });
3929         }
3930         
3931         if(this.main){
3932             cfg.cls += ' main-nav';
3933         }
3934         
3935         
3936         return cfg;
3937
3938         
3939     },
3940     getHeaderChildContainer : function()
3941     {
3942         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3943             return this.el.select('.navbar-header',true).first();
3944         }
3945         
3946         return this.getChildContainer();
3947     },
3948     
3949     
3950     initEvents : function()
3951     {
3952         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3953         
3954         if (this.autohide) {
3955             
3956             var prevScroll = 0;
3957             var ft = this.el;
3958             
3959             Roo.get(document).on('scroll',function(e) {
3960                 var ns = Roo.get(document).getScroll().top;
3961                 var os = prevScroll;
3962                 prevScroll = ns;
3963                 
3964                 if(ns > os){
3965                     ft.removeClass('slideDown');
3966                     ft.addClass('slideUp');
3967                     return;
3968                 }
3969                 ft.removeClass('slideUp');
3970                 ft.addClass('slideDown');
3971                  
3972               
3973           },this);
3974         }
3975     }    
3976     
3977 });
3978
3979
3980
3981  
3982
3983  /*
3984  * - LGPL
3985  *
3986  * navbar
3987  * 
3988  */
3989
3990 /**
3991  * @class Roo.bootstrap.NavSidebar
3992  * @extends Roo.bootstrap.Navbar
3993  * Bootstrap Sidebar class
3994  * 
3995  * @constructor
3996  * Create a new Sidebar
3997  * @param {Object} config The config object
3998  */
3999
4000
4001 Roo.bootstrap.NavSidebar = function(config){
4002     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4003 };
4004
4005 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4006     
4007     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4008     
4009     getAutoCreate : function(){
4010         
4011         
4012         return  {
4013             tag: 'div',
4014             cls: 'sidebar sidebar-nav'
4015         };
4016     
4017         
4018     }
4019     
4020     
4021     
4022 });
4023
4024
4025
4026  
4027
4028  /*
4029  * - LGPL
4030  *
4031  * nav group
4032  * 
4033  */
4034
4035 /**
4036  * @class Roo.bootstrap.NavGroup
4037  * @extends Roo.bootstrap.Component
4038  * Bootstrap NavGroup class
4039  * @cfg {String} align (left|right)
4040  * @cfg {Boolean} inverse
4041  * @cfg {String} type (nav|pills|tab) default nav
4042  * @cfg {String} navId - reference Id for navbar.
4043
4044  * 
4045  * @constructor
4046  * Create a new nav group
4047  * @param {Object} config The config object
4048  */
4049
4050 Roo.bootstrap.NavGroup = function(config){
4051     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4052     this.navItems = [];
4053    
4054     Roo.bootstrap.NavGroup.register(this);
4055      this.addEvents({
4056         /**
4057              * @event changed
4058              * Fires when the active item changes
4059              * @param {Roo.bootstrap.NavGroup} this
4060              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4061              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4062          */
4063         'changed': true
4064      });
4065     
4066 };
4067
4068 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4069     
4070     align: '',
4071     inverse: false,
4072     form: false,
4073     type: 'nav',
4074     navId : '',
4075     // private
4076     
4077     navItems : false, 
4078     
4079     getAutoCreate : function()
4080     {
4081         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4082         
4083         cfg = {
4084             tag : 'ul',
4085             cls: 'nav' 
4086         };
4087         
4088         if (['tabs','pills'].indexOf(this.type)!==-1) {
4089             cfg.cls += ' nav-' + this.type
4090         } else {
4091             if (this.type!=='nav') {
4092                 Roo.log('nav type must be nav/tabs/pills')
4093             }
4094             cfg.cls += ' navbar-nav'
4095         }
4096         
4097         if (this.parent() && this.parent().sidebar) {
4098             cfg = {
4099                 tag: 'ul',
4100                 cls: 'dashboard-menu sidebar-menu'
4101             };
4102             
4103             return cfg;
4104         }
4105         
4106         if (this.form === true) {
4107             cfg = {
4108                 tag: 'form',
4109                 cls: 'navbar-form'
4110             };
4111             
4112             if (this.align === 'right') {
4113                 cfg.cls += ' navbar-right';
4114             } else {
4115                 cfg.cls += ' navbar-left';
4116             }
4117         }
4118         
4119         if (this.align === 'right') {
4120             cfg.cls += ' navbar-right';
4121         }
4122         
4123         if (this.inverse) {
4124             cfg.cls += ' navbar-inverse';
4125             
4126         }
4127         
4128         
4129         return cfg;
4130     },
4131     /**
4132     * sets the active Navigation item
4133     * @param {Roo.bootstrap.NavItem} the new current navitem
4134     */
4135     setActiveItem : function(item)
4136     {
4137         var prev = false;
4138         Roo.each(this.navItems, function(v){
4139             if (v == item) {
4140                 return ;
4141             }
4142             if (v.isActive()) {
4143                 v.setActive(false, true);
4144                 prev = v;
4145                 
4146             }
4147             
4148         });
4149
4150         item.setActive(true, true);
4151         this.fireEvent('changed', this, item, prev);
4152         
4153         
4154     },
4155     /**
4156     * gets the active Navigation item
4157     * @return {Roo.bootstrap.NavItem} the current navitem
4158     */
4159     getActive : function()
4160     {
4161         
4162         var prev = false;
4163         Roo.each(this.navItems, function(v){
4164             
4165             if (v.isActive()) {
4166                 prev = v;
4167                 
4168             }
4169             
4170         });
4171         return prev;
4172     },
4173     
4174     indexOfNav : function()
4175     {
4176         
4177         var prev = false;
4178         Roo.each(this.navItems, function(v,i){
4179             
4180             if (v.isActive()) {
4181                 prev = i;
4182                 
4183             }
4184             
4185         });
4186         return prev;
4187     },
4188     /**
4189     * adds a Navigation item
4190     * @param {Roo.bootstrap.NavItem} the navitem to add
4191     */
4192     addItem : function(cfg)
4193     {
4194         var cn = new Roo.bootstrap.NavItem(cfg);
4195         this.register(cn);
4196         cn.parentId = this.id;
4197         cn.onRender(this.el, null);
4198         return cn;
4199     },
4200     /**
4201     * register a Navigation item
4202     * @param {Roo.bootstrap.NavItem} the navitem to add
4203     */
4204     register : function(item)
4205     {
4206         this.navItems.push( item);
4207         item.navId = this.navId;
4208     
4209     },
4210     
4211     /**
4212     * clear all the Navigation item
4213     */
4214    
4215     clearAll : function()
4216     {
4217         this.navItems = [];
4218         this.el.dom.innerHTML = '';
4219     },
4220     
4221     getNavItem: function(tabId)
4222     {
4223         var ret = false;
4224         Roo.each(this.navItems, function(e) {
4225             if (e.tabId == tabId) {
4226                ret =  e;
4227                return false;
4228             }
4229             return true;
4230             
4231         });
4232         return ret;
4233     },
4234     
4235     setActiveNext : function()
4236     {
4237         var i = this.indexOfNav(this.getActive());
4238         if (i > this.navItems.length) {
4239             return;
4240         }
4241         this.setActiveItem(this.navItems[i+1]);
4242     },
4243     setActivePrev : function()
4244     {
4245         var i = this.indexOfNav(this.getActive());
4246         if (i  < 1) {
4247             return;
4248         }
4249         this.setActiveItem(this.navItems[i-1]);
4250     },
4251     clearWasActive : function(except) {
4252         Roo.each(this.navItems, function(e) {
4253             if (e.tabId != except.tabId && e.was_active) {
4254                e.was_active = false;
4255                return false;
4256             }
4257             return true;
4258             
4259         });
4260     },
4261     getWasActive : function ()
4262     {
4263         var r = false;
4264         Roo.each(this.navItems, function(e) {
4265             if (e.was_active) {
4266                r = e;
4267                return false;
4268             }
4269             return true;
4270             
4271         });
4272         return r;
4273     }
4274     
4275     
4276 });
4277
4278  
4279 Roo.apply(Roo.bootstrap.NavGroup, {
4280     
4281     groups: {},
4282      /**
4283     * register a Navigation Group
4284     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4285     */
4286     register : function(navgrp)
4287     {
4288         this.groups[navgrp.navId] = navgrp;
4289         
4290     },
4291     /**
4292     * fetch a Navigation Group based on the navigation ID
4293     * @param {string} the navgroup to add
4294     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4295     */
4296     get: function(navId) {
4297         if (typeof(this.groups[navId]) == 'undefined') {
4298             return false;
4299             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4300         }
4301         return this.groups[navId] ;
4302     }
4303     
4304     
4305     
4306 });
4307
4308  /*
4309  * - LGPL
4310  *
4311  * row
4312  * 
4313  */
4314
4315 /**
4316  * @class Roo.bootstrap.NavItem
4317  * @extends Roo.bootstrap.Component
4318  * Bootstrap Navbar.NavItem class
4319  * @cfg {String} href  link to
4320  * @cfg {String} html content of button
4321  * @cfg {String} badge text inside badge
4322  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4323  * @cfg {String} glyphicon name of glyphicon
4324  * @cfg {String} icon name of font awesome icon
4325  * @cfg {Boolean} active Is item active
4326  * @cfg {Boolean} disabled Is item disabled
4327  
4328  * @cfg {Boolean} preventDefault (true | false) default false
4329  * @cfg {String} tabId the tab that this item activates.
4330  * @cfg {String} tagtype (a|span) render as a href or span?
4331  * @cfg {Boolean} animateRef (true|false) link to element default false  
4332   
4333  * @constructor
4334  * Create a new Navbar Item
4335  * @param {Object} config The config object
4336  */
4337 Roo.bootstrap.NavItem = function(config){
4338     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4339     this.addEvents({
4340         // raw events
4341         /**
4342          * @event click
4343          * The raw click event for the entire grid.
4344          * @param {Roo.EventObject} e
4345          */
4346         "click" : true,
4347          /**
4348             * @event changed
4349             * Fires when the active item active state changes
4350             * @param {Roo.bootstrap.NavItem} this
4351             * @param {boolean} state the new state
4352              
4353          */
4354         'changed': true,
4355         /**
4356             * @event scrollto
4357             * Fires when scroll to element
4358             * @param {Roo.bootstrap.NavItem} this
4359             * @param {Object} options
4360             * @param {Roo.EventObject} e
4361              
4362          */
4363         'scrollto': true
4364     });
4365    
4366 };
4367
4368 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4369     
4370     href: false,
4371     html: '',
4372     badge: '',
4373     icon: false,
4374     glyphicon: false,
4375     active: false,
4376     preventDefault : false,
4377     tabId : false,
4378     tagtype : 'a',
4379     disabled : false,
4380     animateRef : false,
4381     was_active : false,
4382     
4383     getAutoCreate : function(){
4384          
4385         var cfg = {
4386             tag: 'li',
4387             cls: 'nav-item'
4388             
4389         };
4390         
4391         if (this.active) {
4392             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4393         }
4394         if (this.disabled) {
4395             cfg.cls += ' disabled';
4396         }
4397         
4398         if (this.href || this.html || this.glyphicon || this.icon) {
4399             cfg.cn = [
4400                 {
4401                     tag: this.tagtype,
4402                     href : this.href || "#",
4403                     html: this.html || ''
4404                 }
4405             ];
4406             
4407             if (this.icon) {
4408                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4409             }
4410
4411             if(this.glyphicon) {
4412                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4413             }
4414             
4415             if (this.menu) {
4416                 
4417                 cfg.cn[0].html += " <span class='caret'></span>";
4418              
4419             }
4420             
4421             if (this.badge !== '') {
4422                  
4423                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4424             }
4425         }
4426         
4427         
4428         
4429         return cfg;
4430     },
4431     initEvents: function() 
4432     {
4433         if (typeof (this.menu) != 'undefined') {
4434             this.menu.parentType = this.xtype;
4435             this.menu.triggerEl = this.el;
4436             this.menu = this.addxtype(Roo.apply({}, this.menu));
4437         }
4438         
4439         this.el.select('a',true).on('click', this.onClick, this);
4440         
4441         if(this.tagtype == 'span'){
4442             this.el.select('span',true).on('click', this.onClick, this);
4443         }
4444        
4445         // at this point parent should be available..
4446         this.parent().register(this);
4447     },
4448     
4449     onClick : function(e)
4450     {
4451         if (e.getTarget('.dropdown-menu-item')) {
4452             // did you click on a menu itemm.... - then don't trigger onclick..
4453             return;
4454         }
4455         
4456         if(
4457                 this.preventDefault || 
4458                 this.href == '#' 
4459         ){
4460             Roo.log("NavItem - prevent Default?");
4461             e.preventDefault();
4462         }
4463         
4464         if (this.disabled) {
4465             return;
4466         }
4467         
4468         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4469         if (tg && tg.transition) {
4470             Roo.log("waiting for the transitionend");
4471             return;
4472         }
4473         
4474         
4475         
4476         //Roo.log("fire event clicked");
4477         if(this.fireEvent('click', this, e) === false){
4478             return;
4479         };
4480         
4481         if(this.tagtype == 'span'){
4482             return;
4483         }
4484         
4485         //Roo.log(this.href);
4486         var ael = this.el.select('a',true).first();
4487         //Roo.log(ael);
4488         
4489         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4490             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4491             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4492                 return; // ignore... - it's a 'hash' to another page.
4493             }
4494             Roo.log("NavItem - prevent Default?");
4495             e.preventDefault();
4496             this.scrollToElement(e);
4497         }
4498         
4499         
4500         var p =  this.parent();
4501    
4502         if (['tabs','pills'].indexOf(p.type)!==-1) {
4503             if (typeof(p.setActiveItem) !== 'undefined') {
4504                 p.setActiveItem(this);
4505             }
4506         }
4507         
4508         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4509         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4510             // remove the collapsed menu expand...
4511             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4512         }
4513     },
4514     
4515     isActive: function () {
4516         return this.active
4517     },
4518     setActive : function(state, fire, is_was_active)
4519     {
4520         if (this.active && !state && this.navId) {
4521             this.was_active = true;
4522             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4523             if (nv) {
4524                 nv.clearWasActive(this);
4525             }
4526             
4527         }
4528         this.active = state;
4529         
4530         if (!state ) {
4531             this.el.removeClass('active');
4532         } else if (!this.el.hasClass('active')) {
4533             this.el.addClass('active');
4534         }
4535         if (fire) {
4536             this.fireEvent('changed', this, state);
4537         }
4538         
4539         // show a panel if it's registered and related..
4540         
4541         if (!this.navId || !this.tabId || !state || is_was_active) {
4542             return;
4543         }
4544         
4545         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4546         if (!tg) {
4547             return;
4548         }
4549         var pan = tg.getPanelByName(this.tabId);
4550         if (!pan) {
4551             return;
4552         }
4553         // if we can not flip to new panel - go back to old nav highlight..
4554         if (false == tg.showPanel(pan)) {
4555             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4556             if (nv) {
4557                 var onav = nv.getWasActive();
4558                 if (onav) {
4559                     onav.setActive(true, false, true);
4560                 }
4561             }
4562             
4563         }
4564         
4565         
4566         
4567     },
4568      // this should not be here...
4569     setDisabled : function(state)
4570     {
4571         this.disabled = state;
4572         if (!state ) {
4573             this.el.removeClass('disabled');
4574         } else if (!this.el.hasClass('disabled')) {
4575             this.el.addClass('disabled');
4576         }
4577         
4578     },
4579     
4580     /**
4581      * Fetch the element to display the tooltip on.
4582      * @return {Roo.Element} defaults to this.el
4583      */
4584     tooltipEl : function()
4585     {
4586         return this.el.select('' + this.tagtype + '', true).first();
4587     },
4588     
4589     scrollToElement : function(e)
4590     {
4591         var c = document.body;
4592         
4593         /*
4594          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4595          */
4596         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4597             c = document.documentElement;
4598         }
4599         
4600         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4601         
4602         if(!target){
4603             return;
4604         }
4605
4606         var o = target.calcOffsetsTo(c);
4607         
4608         var options = {
4609             target : target,
4610             value : o[1]
4611         };
4612         
4613         this.fireEvent('scrollto', this, options, e);
4614         
4615         Roo.get(c).scrollTo('top', options.value, true);
4616         
4617         return;
4618     }
4619 });
4620  
4621
4622  /*
4623  * - LGPL
4624  *
4625  * sidebar item
4626  *
4627  *  li
4628  *    <span> icon </span>
4629  *    <span> text </span>
4630  *    <span>badge </span>
4631  */
4632
4633 /**
4634  * @class Roo.bootstrap.NavSidebarItem
4635  * @extends Roo.bootstrap.NavItem
4636  * Bootstrap Navbar.NavSidebarItem class
4637  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4638  * {Boolean} open is the menu open
4639  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4640  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4641  * {String} buttonSize (sm|md|lg)the extra classes for the button
4642  * {Boolean} showArrow show arrow next to the text (default true)
4643  * @constructor
4644  * Create a new Navbar Button
4645  * @param {Object} config The config object
4646  */
4647 Roo.bootstrap.NavSidebarItem = function(config){
4648     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4649     this.addEvents({
4650         // raw events
4651         /**
4652          * @event click
4653          * The raw click event for the entire grid.
4654          * @param {Roo.EventObject} e
4655          */
4656         "click" : true,
4657          /**
4658             * @event changed
4659             * Fires when the active item active state changes
4660             * @param {Roo.bootstrap.NavSidebarItem} this
4661             * @param {boolean} state the new state
4662              
4663          */
4664         'changed': true
4665     });
4666    
4667 };
4668
4669 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4670     
4671     badgeWeight : 'default',
4672     
4673     open: false,
4674     
4675     buttonView : false,
4676     
4677     buttonWeight : 'default',
4678     
4679     buttonSize : 'md',
4680     
4681     showArrow : true,
4682     
4683     getAutoCreate : function(){
4684         
4685         
4686         var a = {
4687                 tag: 'a',
4688                 href : this.href || '#',
4689                 cls: '',
4690                 html : '',
4691                 cn : []
4692         };
4693         
4694         if(this.buttonView){
4695             a = {
4696                 tag: 'button',
4697                 href : this.href || '#',
4698                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4699                 html : this.html,
4700                 cn : []
4701             };
4702         }
4703         
4704         var cfg = {
4705             tag: 'li',
4706             cls: '',
4707             cn: [ a ]
4708         };
4709         
4710         if (this.active) {
4711             cfg.cls += ' active';
4712         }
4713         
4714         if (this.disabled) {
4715             cfg.cls += ' disabled';
4716         }
4717         if (this.open) {
4718             cfg.cls += ' open x-open';
4719         }
4720         // left icon..
4721         if (this.glyphicon || this.icon) {
4722             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4723             a.cn.push({ tag : 'i', cls : c }) ;
4724         }
4725         
4726         if(!this.buttonView){
4727             var span = {
4728                 tag: 'span',
4729                 html : this.html || ''
4730             };
4731
4732             a.cn.push(span);
4733             
4734         }
4735         
4736         if (this.badge !== '') {
4737             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4738         }
4739         
4740         if (this.menu) {
4741             
4742             if(this.showArrow){
4743                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4744             }
4745             
4746             a.cls += ' dropdown-toggle treeview' ;
4747         }
4748         
4749         return cfg;
4750     },
4751     
4752     initEvents : function()
4753     { 
4754         if (typeof (this.menu) != 'undefined') {
4755             this.menu.parentType = this.xtype;
4756             this.menu.triggerEl = this.el;
4757             this.menu = this.addxtype(Roo.apply({}, this.menu));
4758         }
4759         
4760         this.el.on('click', this.onClick, this);
4761         
4762         if(this.badge !== ''){
4763             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4764         }
4765         
4766     },
4767     
4768     onClick : function(e)
4769     {
4770         if(this.disabled){
4771             e.preventDefault();
4772             return;
4773         }
4774         
4775         if(this.preventDefault){
4776             e.preventDefault();
4777         }
4778         
4779         this.fireEvent('click', this);
4780     },
4781     
4782     disable : function()
4783     {
4784         this.setDisabled(true);
4785     },
4786     
4787     enable : function()
4788     {
4789         this.setDisabled(false);
4790     },
4791     
4792     setDisabled : function(state)
4793     {
4794         if(this.disabled == state){
4795             return;
4796         }
4797         
4798         this.disabled = state;
4799         
4800         if (state) {
4801             this.el.addClass('disabled');
4802             return;
4803         }
4804         
4805         this.el.removeClass('disabled');
4806         
4807         return;
4808     },
4809     
4810     setActive : function(state)
4811     {
4812         if(this.active == state){
4813             return;
4814         }
4815         
4816         this.active = state;
4817         
4818         if (state) {
4819             this.el.addClass('active');
4820             return;
4821         }
4822         
4823         this.el.removeClass('active');
4824         
4825         return;
4826     },
4827     
4828     isActive: function () 
4829     {
4830         return this.active;
4831     },
4832     
4833     setBadge : function(str)
4834     {
4835         if(!this.badgeEl){
4836             return;
4837         }
4838         
4839         this.badgeEl.dom.innerHTML = str;
4840     }
4841     
4842    
4843      
4844  
4845 });
4846  
4847
4848  /*
4849  * - LGPL
4850  *
4851  * row
4852  * 
4853  */
4854
4855 /**
4856  * @class Roo.bootstrap.Row
4857  * @extends Roo.bootstrap.Component
4858  * Bootstrap Row class (contains columns...)
4859  * 
4860  * @constructor
4861  * Create a new Row
4862  * @param {Object} config The config object
4863  */
4864
4865 Roo.bootstrap.Row = function(config){
4866     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4867 };
4868
4869 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4870     
4871     getAutoCreate : function(){
4872        return {
4873             cls: 'row clearfix'
4874        };
4875     }
4876     
4877     
4878 });
4879
4880  
4881
4882  /*
4883  * - LGPL
4884  *
4885  * element
4886  * 
4887  */
4888
4889 /**
4890  * @class Roo.bootstrap.Element
4891  * @extends Roo.bootstrap.Component
4892  * Bootstrap Element class
4893  * @cfg {String} html contents of the element
4894  * @cfg {String} tag tag of the element
4895  * @cfg {String} cls class of the element
4896  * @cfg {Boolean} preventDefault (true|false) default false
4897  * @cfg {Boolean} clickable (true|false) default false
4898  * 
4899  * @constructor
4900  * Create a new Element
4901  * @param {Object} config The config object
4902  */
4903
4904 Roo.bootstrap.Element = function(config){
4905     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4906     
4907     this.addEvents({
4908         // raw events
4909         /**
4910          * @event click
4911          * When a element is chick
4912          * @param {Roo.bootstrap.Element} this
4913          * @param {Roo.EventObject} e
4914          */
4915         "click" : true
4916     });
4917 };
4918
4919 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4920     
4921     tag: 'div',
4922     cls: '',
4923     html: '',
4924     preventDefault: false, 
4925     clickable: false,
4926     
4927     getAutoCreate : function(){
4928         
4929         var cfg = {
4930             tag: this.tag,
4931             // cls: this.cls, double assign in parent class Component.js :: onRender
4932             html: this.html
4933         };
4934         
4935         return cfg;
4936     },
4937     
4938     initEvents: function() 
4939     {
4940         Roo.bootstrap.Element.superclass.initEvents.call(this);
4941         
4942         if(this.clickable){
4943             this.el.on('click', this.onClick, this);
4944         }
4945         
4946     },
4947     
4948     onClick : function(e)
4949     {
4950         if(this.preventDefault){
4951             e.preventDefault();
4952         }
4953         
4954         this.fireEvent('click', this, e);
4955     },
4956     
4957     getValue : function()
4958     {
4959         return this.el.dom.innerHTML;
4960     },
4961     
4962     setValue : function(value)
4963     {
4964         this.el.dom.innerHTML = value;
4965     }
4966    
4967 });
4968
4969  
4970
4971  /*
4972  * - LGPL
4973  *
4974  * pagination
4975  * 
4976  */
4977
4978 /**
4979  * @class Roo.bootstrap.Pagination
4980  * @extends Roo.bootstrap.Component
4981  * Bootstrap Pagination class
4982  * @cfg {String} size xs | sm | md | lg
4983  * @cfg {Boolean} inverse false | true
4984  * 
4985  * @constructor
4986  * Create a new Pagination
4987  * @param {Object} config The config object
4988  */
4989
4990 Roo.bootstrap.Pagination = function(config){
4991     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4992 };
4993
4994 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4995     
4996     cls: false,
4997     size: false,
4998     inverse: false,
4999     
5000     getAutoCreate : function(){
5001         var cfg = {
5002             tag: 'ul',
5003                 cls: 'pagination'
5004         };
5005         if (this.inverse) {
5006             cfg.cls += ' inverse';
5007         }
5008         if (this.html) {
5009             cfg.html=this.html;
5010         }
5011         if (this.cls) {
5012             cfg.cls += " " + this.cls;
5013         }
5014         return cfg;
5015     }
5016    
5017 });
5018
5019  
5020
5021  /*
5022  * - LGPL
5023  *
5024  * Pagination item
5025  * 
5026  */
5027
5028
5029 /**
5030  * @class Roo.bootstrap.PaginationItem
5031  * @extends Roo.bootstrap.Component
5032  * Bootstrap PaginationItem class
5033  * @cfg {String} html text
5034  * @cfg {String} href the link
5035  * @cfg {Boolean} preventDefault (true | false) default true
5036  * @cfg {Boolean} active (true | false) default false
5037  * @cfg {Boolean} disabled default false
5038  * 
5039  * 
5040  * @constructor
5041  * Create a new PaginationItem
5042  * @param {Object} config The config object
5043  */
5044
5045
5046 Roo.bootstrap.PaginationItem = function(config){
5047     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5048     this.addEvents({
5049         // raw events
5050         /**
5051          * @event click
5052          * The raw click event for the entire grid.
5053          * @param {Roo.EventObject} e
5054          */
5055         "click" : true
5056     });
5057 };
5058
5059 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5060     
5061     href : false,
5062     html : false,
5063     preventDefault: true,
5064     active : false,
5065     cls : false,
5066     disabled: false,
5067     
5068     getAutoCreate : function(){
5069         var cfg= {
5070             tag: 'li',
5071             cn: [
5072                 {
5073                     tag : 'a',
5074                     href : this.href ? this.href : '#',
5075                     html : this.html ? this.html : ''
5076                 }
5077             ]
5078         };
5079         
5080         if(this.cls){
5081             cfg.cls = this.cls;
5082         }
5083         
5084         if(this.disabled){
5085             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5086         }
5087         
5088         if(this.active){
5089             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5090         }
5091         
5092         return cfg;
5093     },
5094     
5095     initEvents: function() {
5096         
5097         this.el.on('click', this.onClick, this);
5098         
5099     },
5100     onClick : function(e)
5101     {
5102         Roo.log('PaginationItem on click ');
5103         if(this.preventDefault){
5104             e.preventDefault();
5105         }
5106         
5107         if(this.disabled){
5108             return;
5109         }
5110         
5111         this.fireEvent('click', this, e);
5112     }
5113    
5114 });
5115
5116  
5117
5118  /*
5119  * - LGPL
5120  *
5121  * slider
5122  * 
5123  */
5124
5125
5126 /**
5127  * @class Roo.bootstrap.Slider
5128  * @extends Roo.bootstrap.Component
5129  * Bootstrap Slider class
5130  *    
5131  * @constructor
5132  * Create a new Slider
5133  * @param {Object} config The config object
5134  */
5135
5136 Roo.bootstrap.Slider = function(config){
5137     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5138 };
5139
5140 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5141     
5142     getAutoCreate : function(){
5143         
5144         var cfg = {
5145             tag: 'div',
5146             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5147             cn: [
5148                 {
5149                     tag: 'a',
5150                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5151                 }
5152             ]
5153         };
5154         
5155         return cfg;
5156     }
5157    
5158 });
5159
5160  /*
5161  * Based on:
5162  * Ext JS Library 1.1.1
5163  * Copyright(c) 2006-2007, Ext JS, LLC.
5164  *
5165  * Originally Released Under LGPL - original licence link has changed is not relivant.
5166  *
5167  * Fork - LGPL
5168  * <script type="text/javascript">
5169  */
5170  
5171
5172 /**
5173  * @class Roo.grid.ColumnModel
5174  * @extends Roo.util.Observable
5175  * This is the default implementation of a ColumnModel used by the Grid. It defines
5176  * the columns in the grid.
5177  * <br>Usage:<br>
5178  <pre><code>
5179  var colModel = new Roo.grid.ColumnModel([
5180         {header: "Ticker", width: 60, sortable: true, locked: true},
5181         {header: "Company Name", width: 150, sortable: true},
5182         {header: "Market Cap.", width: 100, sortable: true},
5183         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5184         {header: "Employees", width: 100, sortable: true, resizable: false}
5185  ]);
5186  </code></pre>
5187  * <p>
5188  
5189  * The config options listed for this class are options which may appear in each
5190  * individual column definition.
5191  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5192  * @constructor
5193  * @param {Object} config An Array of column config objects. See this class's
5194  * config objects for details.
5195 */
5196 Roo.grid.ColumnModel = function(config){
5197         /**
5198      * The config passed into the constructor
5199      */
5200     this.config = config;
5201     this.lookup = {};
5202
5203     // if no id, create one
5204     // if the column does not have a dataIndex mapping,
5205     // map it to the order it is in the config
5206     for(var i = 0, len = config.length; i < len; i++){
5207         var c = config[i];
5208         if(typeof c.dataIndex == "undefined"){
5209             c.dataIndex = i;
5210         }
5211         if(typeof c.renderer == "string"){
5212             c.renderer = Roo.util.Format[c.renderer];
5213         }
5214         if(typeof c.id == "undefined"){
5215             c.id = Roo.id();
5216         }
5217         if(c.editor && c.editor.xtype){
5218             c.editor  = Roo.factory(c.editor, Roo.grid);
5219         }
5220         if(c.editor && c.editor.isFormField){
5221             c.editor = new Roo.grid.GridEditor(c.editor);
5222         }
5223         this.lookup[c.id] = c;
5224     }
5225
5226     /**
5227      * The width of columns which have no width specified (defaults to 100)
5228      * @type Number
5229      */
5230     this.defaultWidth = 100;
5231
5232     /**
5233      * Default sortable of columns which have no sortable specified (defaults to false)
5234      * @type Boolean
5235      */
5236     this.defaultSortable = false;
5237
5238     this.addEvents({
5239         /**
5240              * @event widthchange
5241              * Fires when the width of a column changes.
5242              * @param {ColumnModel} this
5243              * @param {Number} columnIndex The column index
5244              * @param {Number} newWidth The new width
5245              */
5246             "widthchange": true,
5247         /**
5248              * @event headerchange
5249              * Fires when the text of a header changes.
5250              * @param {ColumnModel} this
5251              * @param {Number} columnIndex The column index
5252              * @param {Number} newText The new header text
5253              */
5254             "headerchange": true,
5255         /**
5256              * @event hiddenchange
5257              * Fires when a column is hidden or "unhidden".
5258              * @param {ColumnModel} this
5259              * @param {Number} columnIndex The column index
5260              * @param {Boolean} hidden true if hidden, false otherwise
5261              */
5262             "hiddenchange": true,
5263             /**
5264          * @event columnmoved
5265          * Fires when a column is moved.
5266          * @param {ColumnModel} this
5267          * @param {Number} oldIndex
5268          * @param {Number} newIndex
5269          */
5270         "columnmoved" : true,
5271         /**
5272          * @event columlockchange
5273          * Fires when a column's locked state is changed
5274          * @param {ColumnModel} this
5275          * @param {Number} colIndex
5276          * @param {Boolean} locked true if locked
5277          */
5278         "columnlockchange" : true
5279     });
5280     Roo.grid.ColumnModel.superclass.constructor.call(this);
5281 };
5282 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5283     /**
5284      * @cfg {String} header The header text to display in the Grid view.
5285      */
5286     /**
5287      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5288      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5289      * specified, the column's index is used as an index into the Record's data Array.
5290      */
5291     /**
5292      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5293      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5294      */
5295     /**
5296      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5297      * Defaults to the value of the {@link #defaultSortable} property.
5298      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5299      */
5300     /**
5301      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5302      */
5303     /**
5304      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5305      */
5306     /**
5307      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5308      */
5309     /**
5310      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5311      */
5312     /**
5313      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5314      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5315      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5316      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5317      */
5318        /**
5319      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5320      */
5321     /**
5322      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5323      */
5324     /**
5325      * @cfg {String} cursor (Optional)
5326      */
5327     /**
5328      * @cfg {String} tooltip (Optional)
5329      */
5330     /**
5331      * @cfg {Number} xs (Optional)
5332      */
5333     /**
5334      * @cfg {Number} sm (Optional)
5335      */
5336     /**
5337      * @cfg {Number} md (Optional)
5338      */
5339     /**
5340      * @cfg {Number} lg (Optional)
5341      */
5342     /**
5343      * Returns the id of the column at the specified index.
5344      * @param {Number} index The column index
5345      * @return {String} the id
5346      */
5347     getColumnId : function(index){
5348         return this.config[index].id;
5349     },
5350
5351     /**
5352      * Returns the column for a specified id.
5353      * @param {String} id The column id
5354      * @return {Object} the column
5355      */
5356     getColumnById : function(id){
5357         return this.lookup[id];
5358     },
5359
5360     
5361     /**
5362      * Returns the column for a specified dataIndex.
5363      * @param {String} dataIndex The column dataIndex
5364      * @return {Object|Boolean} the column or false if not found
5365      */
5366     getColumnByDataIndex: function(dataIndex){
5367         var index = this.findColumnIndex(dataIndex);
5368         return index > -1 ? this.config[index] : false;
5369     },
5370     
5371     /**
5372      * Returns the index for a specified column id.
5373      * @param {String} id The column id
5374      * @return {Number} the index, or -1 if not found
5375      */
5376     getIndexById : function(id){
5377         for(var i = 0, len = this.config.length; i < len; i++){
5378             if(this.config[i].id == id){
5379                 return i;
5380             }
5381         }
5382         return -1;
5383     },
5384     
5385     /**
5386      * Returns the index for a specified column dataIndex.
5387      * @param {String} dataIndex The column dataIndex
5388      * @return {Number} the index, or -1 if not found
5389      */
5390     
5391     findColumnIndex : function(dataIndex){
5392         for(var i = 0, len = this.config.length; i < len; i++){
5393             if(this.config[i].dataIndex == dataIndex){
5394                 return i;
5395             }
5396         }
5397         return -1;
5398     },
5399     
5400     
5401     moveColumn : function(oldIndex, newIndex){
5402         var c = this.config[oldIndex];
5403         this.config.splice(oldIndex, 1);
5404         this.config.splice(newIndex, 0, c);
5405         this.dataMap = null;
5406         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5407     },
5408
5409     isLocked : function(colIndex){
5410         return this.config[colIndex].locked === true;
5411     },
5412
5413     setLocked : function(colIndex, value, suppressEvent){
5414         if(this.isLocked(colIndex) == value){
5415             return;
5416         }
5417         this.config[colIndex].locked = value;
5418         if(!suppressEvent){
5419             this.fireEvent("columnlockchange", this, colIndex, value);
5420         }
5421     },
5422
5423     getTotalLockedWidth : function(){
5424         var totalWidth = 0;
5425         for(var i = 0; i < this.config.length; i++){
5426             if(this.isLocked(i) && !this.isHidden(i)){
5427                 this.totalWidth += this.getColumnWidth(i);
5428             }
5429         }
5430         return totalWidth;
5431     },
5432
5433     getLockedCount : function(){
5434         for(var i = 0, len = this.config.length; i < len; i++){
5435             if(!this.isLocked(i)){
5436                 return i;
5437             }
5438         }
5439         
5440         return this.config.length;
5441     },
5442
5443     /**
5444      * Returns the number of columns.
5445      * @return {Number}
5446      */
5447     getColumnCount : function(visibleOnly){
5448         if(visibleOnly === true){
5449             var c = 0;
5450             for(var i = 0, len = this.config.length; i < len; i++){
5451                 if(!this.isHidden(i)){
5452                     c++;
5453                 }
5454             }
5455             return c;
5456         }
5457         return this.config.length;
5458     },
5459
5460     /**
5461      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5462      * @param {Function} fn
5463      * @param {Object} scope (optional)
5464      * @return {Array} result
5465      */
5466     getColumnsBy : function(fn, scope){
5467         var r = [];
5468         for(var i = 0, len = this.config.length; i < len; i++){
5469             var c = this.config[i];
5470             if(fn.call(scope||this, c, i) === true){
5471                 r[r.length] = c;
5472             }
5473         }
5474         return r;
5475     },
5476
5477     /**
5478      * Returns true if the specified column is sortable.
5479      * @param {Number} col The column index
5480      * @return {Boolean}
5481      */
5482     isSortable : function(col){
5483         if(typeof this.config[col].sortable == "undefined"){
5484             return this.defaultSortable;
5485         }
5486         return this.config[col].sortable;
5487     },
5488
5489     /**
5490      * Returns the rendering (formatting) function defined for the column.
5491      * @param {Number} col The column index.
5492      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5493      */
5494     getRenderer : function(col){
5495         if(!this.config[col].renderer){
5496             return Roo.grid.ColumnModel.defaultRenderer;
5497         }
5498         return this.config[col].renderer;
5499     },
5500
5501     /**
5502      * Sets the rendering (formatting) function for a column.
5503      * @param {Number} col The column index
5504      * @param {Function} fn The function to use to process the cell's raw data
5505      * to return HTML markup for the grid view. The render function is called with
5506      * the following parameters:<ul>
5507      * <li>Data value.</li>
5508      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5509      * <li>css A CSS style string to apply to the table cell.</li>
5510      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5511      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5512      * <li>Row index</li>
5513      * <li>Column index</li>
5514      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5515      */
5516     setRenderer : function(col, fn){
5517         this.config[col].renderer = fn;
5518     },
5519
5520     /**
5521      * Returns the width for the specified column.
5522      * @param {Number} col The column index
5523      * @return {Number}
5524      */
5525     getColumnWidth : function(col){
5526         return this.config[col].width * 1 || this.defaultWidth;
5527     },
5528
5529     /**
5530      * Sets the width for a column.
5531      * @param {Number} col The column index
5532      * @param {Number} width The new width
5533      */
5534     setColumnWidth : function(col, width, suppressEvent){
5535         this.config[col].width = width;
5536         this.totalWidth = null;
5537         if(!suppressEvent){
5538              this.fireEvent("widthchange", this, col, width);
5539         }
5540     },
5541
5542     /**
5543      * Returns the total width of all columns.
5544      * @param {Boolean} includeHidden True to include hidden column widths
5545      * @return {Number}
5546      */
5547     getTotalWidth : function(includeHidden){
5548         if(!this.totalWidth){
5549             this.totalWidth = 0;
5550             for(var i = 0, len = this.config.length; i < len; i++){
5551                 if(includeHidden || !this.isHidden(i)){
5552                     this.totalWidth += this.getColumnWidth(i);
5553                 }
5554             }
5555         }
5556         return this.totalWidth;
5557     },
5558
5559     /**
5560      * Returns the header for the specified column.
5561      * @param {Number} col The column index
5562      * @return {String}
5563      */
5564     getColumnHeader : function(col){
5565         return this.config[col].header;
5566     },
5567
5568     /**
5569      * Sets the header for a column.
5570      * @param {Number} col The column index
5571      * @param {String} header The new header
5572      */
5573     setColumnHeader : function(col, header){
5574         this.config[col].header = header;
5575         this.fireEvent("headerchange", this, col, header);
5576     },
5577
5578     /**
5579      * Returns the tooltip for the specified column.
5580      * @param {Number} col The column index
5581      * @return {String}
5582      */
5583     getColumnTooltip : function(col){
5584             return this.config[col].tooltip;
5585     },
5586     /**
5587      * Sets the tooltip for a column.
5588      * @param {Number} col The column index
5589      * @param {String} tooltip The new tooltip
5590      */
5591     setColumnTooltip : function(col, tooltip){
5592             this.config[col].tooltip = tooltip;
5593     },
5594
5595     /**
5596      * Returns the dataIndex for the specified column.
5597      * @param {Number} col The column index
5598      * @return {Number}
5599      */
5600     getDataIndex : function(col){
5601         return this.config[col].dataIndex;
5602     },
5603
5604     /**
5605      * Sets the dataIndex for a column.
5606      * @param {Number} col The column index
5607      * @param {Number} dataIndex The new dataIndex
5608      */
5609     setDataIndex : function(col, dataIndex){
5610         this.config[col].dataIndex = dataIndex;
5611     },
5612
5613     
5614     
5615     /**
5616      * Returns true if the cell is editable.
5617      * @param {Number} colIndex The column index
5618      * @param {Number} rowIndex The row index - this is nto actually used..?
5619      * @return {Boolean}
5620      */
5621     isCellEditable : function(colIndex, rowIndex){
5622         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5623     },
5624
5625     /**
5626      * Returns the editor defined for the cell/column.
5627      * return false or null to disable editing.
5628      * @param {Number} colIndex The column index
5629      * @param {Number} rowIndex The row index
5630      * @return {Object}
5631      */
5632     getCellEditor : function(colIndex, rowIndex){
5633         return this.config[colIndex].editor;
5634     },
5635
5636     /**
5637      * Sets if a column is editable.
5638      * @param {Number} col The column index
5639      * @param {Boolean} editable True if the column is editable
5640      */
5641     setEditable : function(col, editable){
5642         this.config[col].editable = editable;
5643     },
5644
5645
5646     /**
5647      * Returns true if the column is hidden.
5648      * @param {Number} colIndex The column index
5649      * @return {Boolean}
5650      */
5651     isHidden : function(colIndex){
5652         return this.config[colIndex].hidden;
5653     },
5654
5655
5656     /**
5657      * Returns true if the column width cannot be changed
5658      */
5659     isFixed : function(colIndex){
5660         return this.config[colIndex].fixed;
5661     },
5662
5663     /**
5664      * Returns true if the column can be resized
5665      * @return {Boolean}
5666      */
5667     isResizable : function(colIndex){
5668         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5669     },
5670     /**
5671      * Sets if a column is hidden.
5672      * @param {Number} colIndex The column index
5673      * @param {Boolean} hidden True if the column is hidden
5674      */
5675     setHidden : function(colIndex, hidden){
5676         this.config[colIndex].hidden = hidden;
5677         this.totalWidth = null;
5678         this.fireEvent("hiddenchange", this, colIndex, hidden);
5679     },
5680
5681     /**
5682      * Sets the editor for a column.
5683      * @param {Number} col The column index
5684      * @param {Object} editor The editor object
5685      */
5686     setEditor : function(col, editor){
5687         this.config[col].editor = editor;
5688     }
5689 });
5690
5691 Roo.grid.ColumnModel.defaultRenderer = function(value)
5692 {
5693     if(typeof value == "object") {
5694         return value;
5695     }
5696         if(typeof value == "string" && value.length < 1){
5697             return "&#160;";
5698         }
5699     
5700         return String.format("{0}", value);
5701 };
5702
5703 // Alias for backwards compatibility
5704 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5705 /*
5706  * Based on:
5707  * Ext JS Library 1.1.1
5708  * Copyright(c) 2006-2007, Ext JS, LLC.
5709  *
5710  * Originally Released Under LGPL - original licence link has changed is not relivant.
5711  *
5712  * Fork - LGPL
5713  * <script type="text/javascript">
5714  */
5715  
5716 /**
5717  * @class Roo.LoadMask
5718  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5719  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5720  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5721  * element's UpdateManager load indicator and will be destroyed after the initial load.
5722  * @constructor
5723  * Create a new LoadMask
5724  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5725  * @param {Object} config The config object
5726  */
5727 Roo.LoadMask = function(el, config){
5728     this.el = Roo.get(el);
5729     Roo.apply(this, config);
5730     if(this.store){
5731         this.store.on('beforeload', this.onBeforeLoad, this);
5732         this.store.on('load', this.onLoad, this);
5733         this.store.on('loadexception', this.onLoadException, this);
5734         this.removeMask = false;
5735     }else{
5736         var um = this.el.getUpdateManager();
5737         um.showLoadIndicator = false; // disable the default indicator
5738         um.on('beforeupdate', this.onBeforeLoad, this);
5739         um.on('update', this.onLoad, this);
5740         um.on('failure', this.onLoad, this);
5741         this.removeMask = true;
5742     }
5743 };
5744
5745 Roo.LoadMask.prototype = {
5746     /**
5747      * @cfg {Boolean} removeMask
5748      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5749      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5750      */
5751     /**
5752      * @cfg {String} msg
5753      * The text to display in a centered loading message box (defaults to 'Loading...')
5754      */
5755     msg : 'Loading...',
5756     /**
5757      * @cfg {String} msgCls
5758      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5759      */
5760     msgCls : 'x-mask-loading',
5761
5762     /**
5763      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5764      * @type Boolean
5765      */
5766     disabled: false,
5767
5768     /**
5769      * Disables the mask to prevent it from being displayed
5770      */
5771     disable : function(){
5772        this.disabled = true;
5773     },
5774
5775     /**
5776      * Enables the mask so that it can be displayed
5777      */
5778     enable : function(){
5779         this.disabled = false;
5780     },
5781     
5782     onLoadException : function()
5783     {
5784         Roo.log(arguments);
5785         
5786         if (typeof(arguments[3]) != 'undefined') {
5787             Roo.MessageBox.alert("Error loading",arguments[3]);
5788         } 
5789         /*
5790         try {
5791             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5792                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5793             }   
5794         } catch(e) {
5795             
5796         }
5797         */
5798     
5799         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5800     },
5801     // private
5802     onLoad : function()
5803     {
5804         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5805     },
5806
5807     // private
5808     onBeforeLoad : function(){
5809         if(!this.disabled){
5810             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5811         }
5812     },
5813
5814     // private
5815     destroy : function(){
5816         if(this.store){
5817             this.store.un('beforeload', this.onBeforeLoad, this);
5818             this.store.un('load', this.onLoad, this);
5819             this.store.un('loadexception', this.onLoadException, this);
5820         }else{
5821             var um = this.el.getUpdateManager();
5822             um.un('beforeupdate', this.onBeforeLoad, this);
5823             um.un('update', this.onLoad, this);
5824             um.un('failure', this.onLoad, this);
5825         }
5826     }
5827 };/*
5828  * - LGPL
5829  *
5830  * table
5831  * 
5832  */
5833
5834 /**
5835  * @class Roo.bootstrap.Table
5836  * @extends Roo.bootstrap.Component
5837  * Bootstrap Table class
5838  * @cfg {String} cls table class
5839  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5840  * @cfg {String} bgcolor Specifies the background color for a table
5841  * @cfg {Number} border Specifies whether the table cells should have borders or not
5842  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5843  * @cfg {Number} cellspacing Specifies the space between cells
5844  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5845  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5846  * @cfg {String} sortable Specifies that the table should be sortable
5847  * @cfg {String} summary Specifies a summary of the content of a table
5848  * @cfg {Number} width Specifies the width of a table
5849  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5850  * 
5851  * @cfg {boolean} striped Should the rows be alternative striped
5852  * @cfg {boolean} bordered Add borders to the table
5853  * @cfg {boolean} hover Add hover highlighting
5854  * @cfg {boolean} condensed Format condensed
5855  * @cfg {boolean} responsive Format condensed
5856  * @cfg {Boolean} loadMask (true|false) default false
5857  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5858  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5859  * @cfg {Boolean} rowSelection (true|false) default false
5860  * @cfg {Boolean} cellSelection (true|false) default false
5861  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5862  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5863  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5864  
5865  * 
5866  * @constructor
5867  * Create a new Table
5868  * @param {Object} config The config object
5869  */
5870
5871 Roo.bootstrap.Table = function(config){
5872     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5873     
5874   
5875     
5876     // BC...
5877     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5878     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5879     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5880     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5881     
5882     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5883     if (this.sm) {
5884         this.sm.grid = this;
5885         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5886         this.sm = this.selModel;
5887         this.sm.xmodule = this.xmodule || false;
5888     }
5889     
5890     if (this.cm && typeof(this.cm.config) == 'undefined') {
5891         this.colModel = new Roo.grid.ColumnModel(this.cm);
5892         this.cm = this.colModel;
5893         this.cm.xmodule = this.xmodule || false;
5894     }
5895     if (this.store) {
5896         this.store= Roo.factory(this.store, Roo.data);
5897         this.ds = this.store;
5898         this.ds.xmodule = this.xmodule || false;
5899          
5900     }
5901     if (this.footer && this.store) {
5902         this.footer.dataSource = this.ds;
5903         this.footer = Roo.factory(this.footer);
5904     }
5905     
5906     /** @private */
5907     this.addEvents({
5908         /**
5909          * @event cellclick
5910          * Fires when a cell is clicked
5911          * @param {Roo.bootstrap.Table} this
5912          * @param {Roo.Element} el
5913          * @param {Number} rowIndex
5914          * @param {Number} columnIndex
5915          * @param {Roo.EventObject} e
5916          */
5917         "cellclick" : true,
5918         /**
5919          * @event celldblclick
5920          * Fires when a cell is double clicked
5921          * @param {Roo.bootstrap.Table} this
5922          * @param {Roo.Element} el
5923          * @param {Number} rowIndex
5924          * @param {Number} columnIndex
5925          * @param {Roo.EventObject} e
5926          */
5927         "celldblclick" : true,
5928         /**
5929          * @event rowclick
5930          * Fires when a row is clicked
5931          * @param {Roo.bootstrap.Table} this
5932          * @param {Roo.Element} el
5933          * @param {Number} rowIndex
5934          * @param {Roo.EventObject} e
5935          */
5936         "rowclick" : true,
5937         /**
5938          * @event rowdblclick
5939          * Fires when a row is double clicked
5940          * @param {Roo.bootstrap.Table} this
5941          * @param {Roo.Element} el
5942          * @param {Number} rowIndex
5943          * @param {Roo.EventObject} e
5944          */
5945         "rowdblclick" : true,
5946         /**
5947          * @event mouseover
5948          * Fires when a mouseover occur
5949          * @param {Roo.bootstrap.Table} this
5950          * @param {Roo.Element} el
5951          * @param {Number} rowIndex
5952          * @param {Number} columnIndex
5953          * @param {Roo.EventObject} e
5954          */
5955         "mouseover" : true,
5956         /**
5957          * @event mouseout
5958          * Fires when a mouseout occur
5959          * @param {Roo.bootstrap.Table} this
5960          * @param {Roo.Element} el
5961          * @param {Number} rowIndex
5962          * @param {Number} columnIndex
5963          * @param {Roo.EventObject} e
5964          */
5965         "mouseout" : true,
5966         /**
5967          * @event rowclass
5968          * Fires when a row is rendered, so you can change add a style to it.
5969          * @param {Roo.bootstrap.Table} this
5970          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5971          */
5972         'rowclass' : true,
5973           /**
5974          * @event rowsrendered
5975          * Fires when all the  rows have been rendered
5976          * @param {Roo.bootstrap.Table} this
5977          */
5978         'rowsrendered' : true,
5979         /**
5980          * @event contextmenu
5981          * The raw contextmenu event for the entire grid.
5982          * @param {Roo.EventObject} e
5983          */
5984         "contextmenu" : true,
5985         /**
5986          * @event rowcontextmenu
5987          * Fires when a row is right clicked
5988          * @param {Roo.bootstrap.Table} this
5989          * @param {Number} rowIndex
5990          * @param {Roo.EventObject} e
5991          */
5992         "rowcontextmenu" : true,
5993         /**
5994          * @event cellcontextmenu
5995          * Fires when a cell is right clicked
5996          * @param {Roo.bootstrap.Table} this
5997          * @param {Number} rowIndex
5998          * @param {Number} cellIndex
5999          * @param {Roo.EventObject} e
6000          */
6001          "cellcontextmenu" : true,
6002          /**
6003          * @event headercontextmenu
6004          * Fires when a header is right clicked
6005          * @param {Roo.bootstrap.Table} this
6006          * @param {Number} columnIndex
6007          * @param {Roo.EventObject} e
6008          */
6009         "headercontextmenu" : true
6010     });
6011 };
6012
6013 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6014     
6015     cls: false,
6016     align: false,
6017     bgcolor: false,
6018     border: false,
6019     cellpadding: false,
6020     cellspacing: false,
6021     frame: false,
6022     rules: false,
6023     sortable: false,
6024     summary: false,
6025     width: false,
6026     striped : false,
6027     scrollBody : false,
6028     bordered: false,
6029     hover:  false,
6030     condensed : false,
6031     responsive : false,
6032     sm : false,
6033     cm : false,
6034     store : false,
6035     loadMask : false,
6036     footerShow : true,
6037     headerShow : true,
6038   
6039     rowSelection : false,
6040     cellSelection : false,
6041     layout : false,
6042     
6043     // Roo.Element - the tbody
6044     mainBody: false,
6045     // Roo.Element - thead element
6046     mainHead: false,
6047     
6048     container: false, // used by gridpanel...
6049     
6050     lazyLoad : false,
6051     
6052     CSS : Roo.util.CSS,
6053     
6054     getAutoCreate : function()
6055     {
6056         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6057         
6058         cfg = {
6059             tag: 'table',
6060             cls : 'table',
6061             cn : []
6062         };
6063         if (this.scrollBody) {
6064             cfg.cls += ' table-body-fixed';
6065         }    
6066         if (this.striped) {
6067             cfg.cls += ' table-striped';
6068         }
6069         
6070         if (this.hover) {
6071             cfg.cls += ' table-hover';
6072         }
6073         if (this.bordered) {
6074             cfg.cls += ' table-bordered';
6075         }
6076         if (this.condensed) {
6077             cfg.cls += ' table-condensed';
6078         }
6079         if (this.responsive) {
6080             cfg.cls += ' table-responsive';
6081         }
6082         
6083         if (this.cls) {
6084             cfg.cls+=  ' ' +this.cls;
6085         }
6086         
6087         // this lot should be simplifed...
6088         
6089         if (this.align) {
6090             cfg.align=this.align;
6091         }
6092         if (this.bgcolor) {
6093             cfg.bgcolor=this.bgcolor;
6094         }
6095         if (this.border) {
6096             cfg.border=this.border;
6097         }
6098         if (this.cellpadding) {
6099             cfg.cellpadding=this.cellpadding;
6100         }
6101         if (this.cellspacing) {
6102             cfg.cellspacing=this.cellspacing;
6103         }
6104         if (this.frame) {
6105             cfg.frame=this.frame;
6106         }
6107         if (this.rules) {
6108             cfg.rules=this.rules;
6109         }
6110         if (this.sortable) {
6111             cfg.sortable=this.sortable;
6112         }
6113         if (this.summary) {
6114             cfg.summary=this.summary;
6115         }
6116         if (this.width) {
6117             cfg.width=this.width;
6118         }
6119         if (this.layout) {
6120             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6121         }
6122         
6123         if(this.store || this.cm){
6124             if(this.headerShow){
6125                 cfg.cn.push(this.renderHeader());
6126             }
6127             
6128             cfg.cn.push(this.renderBody());
6129             
6130             if(this.footerShow){
6131                 cfg.cn.push(this.renderFooter());
6132             }
6133             // where does this come from?
6134             //cfg.cls+=  ' TableGrid';
6135         }
6136         
6137         return { cn : [ cfg ] };
6138     },
6139     
6140     initEvents : function()
6141     {   
6142         if(!this.store || !this.cm){
6143             return;
6144         }
6145         if (this.selModel) {
6146             this.selModel.initEvents();
6147         }
6148         
6149         
6150         //Roo.log('initEvents with ds!!!!');
6151         
6152         this.mainBody = this.el.select('tbody', true).first();
6153         this.mainHead = this.el.select('thead', true).first();
6154         
6155         
6156         
6157         
6158         var _this = this;
6159         
6160         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6161             e.on('click', _this.sort, _this);
6162         });
6163         
6164         this.mainBody.on("click", this.onClick, this);
6165         this.mainBody.on("dblclick", this.onDblClick, this);
6166         
6167         // why is this done????? = it breaks dialogs??
6168         //this.parent().el.setStyle('position', 'relative');
6169         
6170         
6171         if (this.footer) {
6172             this.footer.parentId = this.id;
6173             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6174             
6175             if(this.lazyLoad){
6176                 this.el.select('tfoot tr td').first().addClass('hide');
6177             }
6178         } 
6179         
6180         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6181         
6182         this.store.on('load', this.onLoad, this);
6183         this.store.on('beforeload', this.onBeforeLoad, this);
6184         this.store.on('update', this.onUpdate, this);
6185         this.store.on('add', this.onAdd, this);
6186         this.store.on("clear", this.clear, this);
6187         
6188         this.el.on("contextmenu", this.onContextMenu, this);
6189         
6190         this.mainBody.on('scroll', this.onBodyScroll, this);
6191         
6192         this.cm.on("headerchange", this.onHeaderChange, this);
6193         
6194         this.cm.on("hiddenchange", this.onHiddenChange, this);
6195         
6196     },
6197     
6198     onContextMenu : function(e, t)
6199     {
6200         this.processEvent("contextmenu", e);
6201     },
6202     
6203     processEvent : function(name, e)
6204     {
6205         if (name != 'touchstart' ) {
6206             this.fireEvent(name, e);    
6207         }
6208         
6209         var t = e.getTarget();
6210         
6211         var cell = Roo.get(t);
6212         
6213         if(!cell){
6214             return;
6215         }
6216         
6217         if(cell.findParent('tfoot', false, true)){
6218             return;
6219         }
6220         
6221         if(cell.findParent('thead', false, true)){
6222             
6223             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6224                 cell = Roo.get(t).findParent('th', false, true);
6225                 if (!cell) {
6226                     Roo.log("failed to find th in thead?");
6227                     Roo.log(e.getTarget());
6228                     return;
6229                 }
6230             }
6231             
6232             var cellIndex = cell.dom.cellIndex;
6233             
6234             var ename = name == 'touchstart' ? 'click' : name;
6235             this.fireEvent("header" + ename, this, cellIndex, e);
6236             
6237             return;
6238         }
6239         
6240         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6241             cell = Roo.get(t).findParent('td', false, true);
6242             if (!cell) {
6243                 Roo.log("failed to find th in tbody?");
6244                 Roo.log(e.getTarget());
6245                 return;
6246             }
6247         }
6248         
6249         var row = cell.findParent('tr', false, true);
6250         var cellIndex = cell.dom.cellIndex;
6251         var rowIndex = row.dom.rowIndex - 1;
6252         
6253         if(row !== false){
6254             
6255             this.fireEvent("row" + name, this, rowIndex, e);
6256             
6257             if(cell !== false){
6258             
6259                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6260             }
6261         }
6262         
6263     },
6264     
6265     onMouseover : function(e, el)
6266     {
6267         var cell = Roo.get(el);
6268         
6269         if(!cell){
6270             return;
6271         }
6272         
6273         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6274             cell = cell.findParent('td', false, true);
6275         }
6276         
6277         var row = cell.findParent('tr', false, true);
6278         var cellIndex = cell.dom.cellIndex;
6279         var rowIndex = row.dom.rowIndex - 1; // start from 0
6280         
6281         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6282         
6283     },
6284     
6285     onMouseout : function(e, el)
6286     {
6287         var cell = Roo.get(el);
6288         
6289         if(!cell){
6290             return;
6291         }
6292         
6293         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6294             cell = cell.findParent('td', false, true);
6295         }
6296         
6297         var row = cell.findParent('tr', false, true);
6298         var cellIndex = cell.dom.cellIndex;
6299         var rowIndex = row.dom.rowIndex - 1; // start from 0
6300         
6301         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6302         
6303     },
6304     
6305     onClick : function(e, el)
6306     {
6307         var cell = Roo.get(el);
6308         
6309         if(!cell || (!this.cellSelection && !this.rowSelection)){
6310             return;
6311         }
6312         
6313         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6314             cell = cell.findParent('td', false, true);
6315         }
6316         
6317         if(!cell || typeof(cell) == 'undefined'){
6318             return;
6319         }
6320         
6321         var row = cell.findParent('tr', false, true);
6322         
6323         if(!row || typeof(row) == 'undefined'){
6324             return;
6325         }
6326         
6327         var cellIndex = cell.dom.cellIndex;
6328         var rowIndex = this.getRowIndex(row);
6329         
6330         // why??? - should these not be based on SelectionModel?
6331         if(this.cellSelection){
6332             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6333         }
6334         
6335         if(this.rowSelection){
6336             this.fireEvent('rowclick', this, row, rowIndex, e);
6337         }
6338         
6339         
6340     },
6341         
6342     onDblClick : function(e,el)
6343     {
6344         var cell = Roo.get(el);
6345         
6346         if(!cell || (!this.cellSelection && !this.rowSelection)){
6347             return;
6348         }
6349         
6350         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6351             cell = cell.findParent('td', false, true);
6352         }
6353         
6354         if(!cell || typeof(cell) == 'undefined'){
6355             return;
6356         }
6357         
6358         var row = cell.findParent('tr', false, true);
6359         
6360         if(!row || typeof(row) == 'undefined'){
6361             return;
6362         }
6363         
6364         var cellIndex = cell.dom.cellIndex;
6365         var rowIndex = this.getRowIndex(row);
6366         
6367         if(this.cellSelection){
6368             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6369         }
6370         
6371         if(this.rowSelection){
6372             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6373         }
6374     },
6375     
6376     sort : function(e,el)
6377     {
6378         var col = Roo.get(el);
6379         
6380         if(!col.hasClass('sortable')){
6381             return;
6382         }
6383         
6384         var sort = col.attr('sort');
6385         var dir = 'ASC';
6386         
6387         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6388             dir = 'DESC';
6389         }
6390         
6391         this.store.sortInfo = {field : sort, direction : dir};
6392         
6393         if (this.footer) {
6394             Roo.log("calling footer first");
6395             this.footer.onClick('first');
6396         } else {
6397         
6398             this.store.load({ params : { start : 0 } });
6399         }
6400     },
6401     
6402     renderHeader : function()
6403     {
6404         var header = {
6405             tag: 'thead',
6406             cn : []
6407         };
6408         
6409         var cm = this.cm;
6410         this.totalWidth = 0;
6411         
6412         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6413             
6414             var config = cm.config[i];
6415             
6416             var c = {
6417                 tag: 'th',
6418                 cls : 'roo-bootstrap-thead-col-' + i,
6419                 style : '',
6420                 html: cm.getColumnHeader(i)
6421             };
6422             
6423             var hh = '';
6424             
6425             if(typeof(config.sortable) != 'undefined' && config.sortable){
6426                 c.cls = 'sortable';
6427                 c.html = '<i class="glyphicon"></i>' + c.html;
6428             }
6429             
6430             if(typeof(config.lgHeader) != 'undefined'){
6431                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6432             }
6433             
6434             if(typeof(config.mdHeader) != 'undefined'){
6435                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6436             }
6437             
6438             if(typeof(config.smHeader) != 'undefined'){
6439                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6440             }
6441             
6442             if(typeof(config.xsHeader) != 'undefined'){
6443                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6444             }
6445             
6446             if(hh.length){
6447                 c.html = hh;
6448             }
6449             
6450             if(typeof(config.tooltip) != 'undefined'){
6451                 c.tooltip = config.tooltip;
6452             }
6453             
6454             if(typeof(config.colspan) != 'undefined'){
6455                 c.colspan = config.colspan;
6456             }
6457             
6458             if(typeof(config.hidden) != 'undefined' && config.hidden){
6459                 c.style += ' display:none;';
6460             }
6461             
6462             if(typeof(config.dataIndex) != 'undefined'){
6463                 c.sort = config.dataIndex;
6464             }
6465             
6466            
6467             
6468             if(typeof(config.align) != 'undefined' && config.align.length){
6469                 c.style += ' text-align:' + config.align + ';';
6470             }
6471             
6472             if(typeof(config.width) != 'undefined'){
6473                 c.style += ' width:' + config.width + 'px;';
6474                 this.totalWidth += config.width;
6475             } else {
6476                 this.totalWidth += 100; // assume minimum of 100 per column?
6477             }
6478             
6479             if(typeof(config.cls) != 'undefined'){
6480                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6481             }
6482             
6483             ['xs','sm','md','lg'].map(function(size){
6484                 
6485                 if(typeof(config[size]) == 'undefined'){
6486                     return;
6487                 }
6488                 
6489                 if (!config[size]) { // 0 = hidden
6490                     c.cls += ' hidden-' + size;
6491                     return;
6492                 }
6493                 
6494                 c.cls += ' col-' + size + '-' + config[size];
6495
6496             });
6497             
6498             if(config.hidden){
6499                 c.cls += ' hidden';
6500             }
6501             
6502             header.cn.push(c)
6503         }
6504         
6505         return header;
6506     },
6507     
6508     renderBody : function()
6509     {
6510         var body = {
6511             tag: 'tbody',
6512             cn : [
6513                 {
6514                     tag: 'tr',
6515                     cn : [
6516                         {
6517                             tag : 'td',
6518                             colspan :  this.cm.getColumnCount()
6519                         }
6520                     ]
6521                 }
6522             ]
6523         };
6524         
6525         return body;
6526     },
6527     
6528     renderFooter : function()
6529     {
6530         var footer = {
6531             tag: 'tfoot',
6532             cn : [
6533                 {
6534                     tag: 'tr',
6535                     cn : [
6536                         {
6537                             tag : 'td',
6538                             colspan :  this.cm.getColumnCount()
6539                         }
6540                     ]
6541                 }
6542             ]
6543         };
6544         
6545         return footer;
6546     },
6547     
6548     
6549     
6550     onLoad : function()
6551     {
6552 //        Roo.log('ds onload');
6553         this.clear();
6554         
6555         var _this = this;
6556         var cm = this.cm;
6557         var ds = this.store;
6558         
6559         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6560             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6561             if (_this.store.sortInfo) {
6562                     
6563                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6564                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6565                 }
6566                 
6567                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6568                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6569                 }
6570             }
6571         });
6572         
6573         var tbody =  this.mainBody;
6574               
6575         if(ds.getCount() > 0){
6576             ds.data.each(function(d,rowIndex){
6577                 var row =  this.renderRow(cm, ds, rowIndex);
6578                 
6579                 tbody.createChild(row);
6580                 
6581                 var _this = this;
6582                 
6583                 if(row.cellObjects.length){
6584                     Roo.each(row.cellObjects, function(r){
6585                         _this.renderCellObject(r);
6586                     })
6587                 }
6588                 
6589             }, this);
6590         }
6591         
6592         Roo.each(this.el.select('tbody td', true).elements, function(e){
6593             e.on('mouseover', _this.onMouseover, _this);
6594         });
6595         
6596         Roo.each(this.el.select('tbody td', true).elements, function(e){
6597             e.on('mouseout', _this.onMouseout, _this);
6598         });
6599         this.fireEvent('rowsrendered', this);
6600         //if(this.loadMask){
6601         //    this.maskEl.hide();
6602         //}
6603         
6604         this.autoSize();
6605     },
6606     
6607     
6608     onUpdate : function(ds,record)
6609     {
6610         this.refreshRow(record);
6611         this.autoSize();
6612     },
6613     
6614     onRemove : function(ds, record, index, isUpdate){
6615         if(isUpdate !== true){
6616             this.fireEvent("beforerowremoved", this, index, record);
6617         }
6618         var bt = this.mainBody.dom;
6619         
6620         var rows = this.el.select('tbody > tr', true).elements;
6621         
6622         if(typeof(rows[index]) != 'undefined'){
6623             bt.removeChild(rows[index].dom);
6624         }
6625         
6626 //        if(bt.rows[index]){
6627 //            bt.removeChild(bt.rows[index]);
6628 //        }
6629         
6630         if(isUpdate !== true){
6631             //this.stripeRows(index);
6632             //this.syncRowHeights(index, index);
6633             //this.layout();
6634             this.fireEvent("rowremoved", this, index, record);
6635         }
6636     },
6637     
6638     onAdd : function(ds, records, rowIndex)
6639     {
6640         //Roo.log('on Add called');
6641         // - note this does not handle multiple adding very well..
6642         var bt = this.mainBody.dom;
6643         for (var i =0 ; i < records.length;i++) {
6644             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6645             //Roo.log(records[i]);
6646             //Roo.log(this.store.getAt(rowIndex+i));
6647             this.insertRow(this.store, rowIndex + i, false);
6648             return;
6649         }
6650         
6651     },
6652     
6653     
6654     refreshRow : function(record){
6655         var ds = this.store, index;
6656         if(typeof record == 'number'){
6657             index = record;
6658             record = ds.getAt(index);
6659         }else{
6660             index = ds.indexOf(record);
6661         }
6662         this.insertRow(ds, index, true);
6663         this.autoSize();
6664         this.onRemove(ds, record, index+1, true);
6665         this.autoSize();
6666         //this.syncRowHeights(index, index);
6667         //this.layout();
6668         this.fireEvent("rowupdated", this, index, record);
6669     },
6670     
6671     insertRow : function(dm, rowIndex, isUpdate){
6672         
6673         if(!isUpdate){
6674             this.fireEvent("beforerowsinserted", this, rowIndex);
6675         }
6676             //var s = this.getScrollState();
6677         var row = this.renderRow(this.cm, this.store, rowIndex);
6678         // insert before rowIndex..
6679         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6680         
6681         var _this = this;
6682                 
6683         if(row.cellObjects.length){
6684             Roo.each(row.cellObjects, function(r){
6685                 _this.renderCellObject(r);
6686             })
6687         }
6688             
6689         if(!isUpdate){
6690             this.fireEvent("rowsinserted", this, rowIndex);
6691             //this.syncRowHeights(firstRow, lastRow);
6692             //this.stripeRows(firstRow);
6693             //this.layout();
6694         }
6695         
6696     },
6697     
6698     
6699     getRowDom : function(rowIndex)
6700     {
6701         var rows = this.el.select('tbody > tr', true).elements;
6702         
6703         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6704         
6705     },
6706     // returns the object tree for a tr..
6707   
6708     
6709     renderRow : function(cm, ds, rowIndex) 
6710     {
6711         var d = ds.getAt(rowIndex);
6712         
6713         var row = {
6714             tag : 'tr',
6715             cls : 'roo-bootstrap-tbody-row-' + rowIndex,
6716             cn : []
6717         };
6718             
6719         var cellObjects = [];
6720         
6721         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6722             var config = cm.config[i];
6723             
6724             var renderer = cm.getRenderer(i);
6725             var value = '';
6726             var id = false;
6727             
6728             if(typeof(renderer) !== 'undefined'){
6729                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6730             }
6731             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6732             // and are rendered into the cells after the row is rendered - using the id for the element.
6733             
6734             if(typeof(value) === 'object'){
6735                 id = Roo.id();
6736                 cellObjects.push({
6737                     container : id,
6738                     cfg : value 
6739                 })
6740             }
6741             
6742             var rowcfg = {
6743                 record: d,
6744                 rowIndex : rowIndex,
6745                 colIndex : i,
6746                 rowClass : ''
6747             };
6748
6749             this.fireEvent('rowclass', this, rowcfg);
6750             
6751             var td = {
6752                 tag: 'td',
6753                 cls : rowcfg.rowClass + ' roo-bootstrap-tbody-col-' + i,
6754                 style: '',
6755                 html: (typeof(value) === 'object') ? '' : value
6756             };
6757             
6758             if (id) {
6759                 td.id = id;
6760             }
6761             
6762             if(typeof(config.colspan) != 'undefined'){
6763                 td.colspan = config.colspan;
6764             }
6765             
6766             if(typeof(config.hidden) != 'undefined' && config.hidden){
6767                 td.style += ' display:none;';
6768             }
6769             
6770             if(typeof(config.align) != 'undefined' && config.align.length){
6771                 td.style += ' text-align:' + config.align + ';';
6772             }
6773             
6774             if(typeof(config.width) != 'undefined'){
6775                 td.style += ' width:' +  config.width + 'px;';
6776             }
6777             
6778             if(typeof(config.cursor) != 'undefined'){
6779                 td.style += ' cursor:' +  config.cursor + ';';
6780             }
6781             
6782             if(typeof(config.cls) != 'undefined'){
6783                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6784             }
6785             
6786             ['xs','sm','md','lg'].map(function(size){
6787                 
6788                 if(typeof(config[size]) == 'undefined'){
6789                     return;
6790                 }
6791                 
6792                 if (!config[size]) { // 0 = hidden
6793                     td.cls += ' hidden-' + size;
6794                     return;
6795                 }
6796                 
6797                 td.cls += ' col-' + size + '-' + config[size];
6798
6799             });
6800             
6801             if(config.hidden){
6802                 td.cls += ' hidden';
6803             }
6804              
6805             row.cn.push(td);
6806            
6807         }
6808         
6809         row.cellObjects = cellObjects;
6810         
6811         return row;
6812           
6813     },
6814     
6815     
6816     
6817     onBeforeLoad : function()
6818     {
6819         //Roo.log('ds onBeforeLoad');
6820         
6821         //this.clear();
6822         
6823         //if(this.loadMask){
6824         //    this.maskEl.show();
6825         //}
6826     },
6827      /**
6828      * Remove all rows
6829      */
6830     clear : function()
6831     {
6832         this.el.select('tbody', true).first().dom.innerHTML = '';
6833     },
6834     /**
6835      * Show or hide a row.
6836      * @param {Number} rowIndex to show or hide
6837      * @param {Boolean} state hide
6838      */
6839     setRowVisibility : function(rowIndex, state)
6840     {
6841         var bt = this.mainBody.dom;
6842         
6843         var rows = this.el.select('tbody > tr', true).elements;
6844         
6845         if(typeof(rows[rowIndex]) == 'undefined'){
6846             return;
6847         }
6848         rows[rowIndex].dom.style.display = state ? '' : 'none';
6849     },
6850     
6851     
6852     getSelectionModel : function(){
6853         if(!this.selModel){
6854             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6855         }
6856         return this.selModel;
6857     },
6858     /*
6859      * Render the Roo.bootstrap object from renderder
6860      */
6861     renderCellObject : function(r)
6862     {
6863         var _this = this;
6864         
6865         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6866         
6867         var t = r.cfg.render(r.container);
6868         
6869         if(r.cfg.cn){
6870             Roo.each(r.cfg.cn, function(c){
6871                 var child = {
6872                     container: t.getChildContainer(),
6873                     cfg: c
6874                 };
6875                 _this.renderCellObject(child);
6876             })
6877         }
6878     },
6879     
6880     getRowIndex : function(row)
6881     {
6882         var rowIndex = -1;
6883         
6884         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6885             if(el != row){
6886                 return;
6887             }
6888             
6889             rowIndex = index;
6890         });
6891         
6892         return rowIndex;
6893     },
6894      /**
6895      * Returns the grid's underlying element = used by panel.Grid
6896      * @return {Element} The element
6897      */
6898     getGridEl : function(){
6899         return this.el;
6900     },
6901      /**
6902      * Forces a resize - used by panel.Grid
6903      * @return {Element} The element
6904      */
6905     autoSize : function()
6906     {
6907         //var ctr = Roo.get(this.container.dom.parentElement);
6908         var ctr = Roo.get(this.el.dom);
6909         
6910         var thd = this.getGridEl().select('thead',true).first();
6911         var tbd = this.getGridEl().select('tbody', true).first();
6912         var tfd = this.getGridEl().select('tfoot', true).first();
6913         
6914         var cw = ctr.getWidth();
6915         
6916         if (tbd) {
6917             
6918             tbd.setSize(ctr.getWidth(),
6919                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6920             );
6921             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6922             cw -= barsize;
6923         }
6924         cw = Math.max(cw, this.totalWidth);
6925         this.getGridEl().select('tr',true).setWidth(cw);
6926         // resize 'expandable coloumn?
6927         
6928         return; // we doe not have a view in this design..
6929         
6930     },
6931     onBodyScroll: function()
6932     {
6933         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6934         if(this.mainHead){
6935             this.mainHead.setStyle({
6936                 'position' : 'relative',
6937                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6938             });
6939         }
6940         
6941         if(this.lazyLoad){
6942             
6943             var scrollHeight = this.mainBody.dom.scrollHeight;
6944             
6945             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6946             
6947             var height = this.mainBody.getHeight();
6948             
6949             if(scrollHeight - height == scrollTop) {
6950                 
6951                 var total = this.ds.getTotalCount();
6952                 
6953                 if(this.footer.cursor + this.footer.pageSize < total){
6954                     
6955                     this.footer.ds.load({
6956                         params : {
6957                             start : this.footer.cursor + this.footer.pageSize,
6958                             limit : this.footer.pageSize
6959                         },
6960                         add : true
6961                     });
6962                 }
6963             }
6964             
6965         }
6966     },
6967     
6968     onHeaderChange : function()
6969     {
6970         
6971         var header = this.renderHeader();
6972         var table = this.el.select('table', true).first();
6973         
6974         this.mainHead.remove();
6975         this.mainHead = table.createChild(header, this.mainBody, false);
6976     },
6977     
6978     onHiddenChange : function()
6979     {
6980         /*
6981         this.onHeaderChange();
6982         this.onLoad();
6983         */
6984     }
6985     
6986 });
6987
6988  
6989
6990  /*
6991  * - LGPL
6992  *
6993  * table cell
6994  * 
6995  */
6996
6997 /**
6998  * @class Roo.bootstrap.TableCell
6999  * @extends Roo.bootstrap.Component
7000  * Bootstrap TableCell class
7001  * @cfg {String} html cell contain text
7002  * @cfg {String} cls cell class
7003  * @cfg {String} tag cell tag (td|th) default td
7004  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7005  * @cfg {String} align Aligns the content in a cell
7006  * @cfg {String} axis Categorizes cells
7007  * @cfg {String} bgcolor Specifies the background color of a cell
7008  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7009  * @cfg {Number} colspan Specifies the number of columns a cell should span
7010  * @cfg {String} headers Specifies one or more header cells a cell is related to
7011  * @cfg {Number} height Sets the height of a cell
7012  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7013  * @cfg {Number} rowspan Sets the number of rows a cell should span
7014  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7015  * @cfg {String} valign Vertical aligns the content in a cell
7016  * @cfg {Number} width Specifies the width of a cell
7017  * 
7018  * @constructor
7019  * Create a new TableCell
7020  * @param {Object} config The config object
7021  */
7022
7023 Roo.bootstrap.TableCell = function(config){
7024     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7025 };
7026
7027 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7028     
7029     html: false,
7030     cls: false,
7031     tag: false,
7032     abbr: false,
7033     align: false,
7034     axis: false,
7035     bgcolor: false,
7036     charoff: false,
7037     colspan: false,
7038     headers: false,
7039     height: false,
7040     nowrap: false,
7041     rowspan: false,
7042     scope: false,
7043     valign: false,
7044     width: false,
7045     
7046     
7047     getAutoCreate : function(){
7048         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7049         
7050         cfg = {
7051             tag: 'td'
7052         };
7053         
7054         if(this.tag){
7055             cfg.tag = this.tag;
7056         }
7057         
7058         if (this.html) {
7059             cfg.html=this.html
7060         }
7061         if (this.cls) {
7062             cfg.cls=this.cls
7063         }
7064         if (this.abbr) {
7065             cfg.abbr=this.abbr
7066         }
7067         if (this.align) {
7068             cfg.align=this.align
7069         }
7070         if (this.axis) {
7071             cfg.axis=this.axis
7072         }
7073         if (this.bgcolor) {
7074             cfg.bgcolor=this.bgcolor
7075         }
7076         if (this.charoff) {
7077             cfg.charoff=this.charoff
7078         }
7079         if (this.colspan) {
7080             cfg.colspan=this.colspan
7081         }
7082         if (this.headers) {
7083             cfg.headers=this.headers
7084         }
7085         if (this.height) {
7086             cfg.height=this.height
7087         }
7088         if (this.nowrap) {
7089             cfg.nowrap=this.nowrap
7090         }
7091         if (this.rowspan) {
7092             cfg.rowspan=this.rowspan
7093         }
7094         if (this.scope) {
7095             cfg.scope=this.scope
7096         }
7097         if (this.valign) {
7098             cfg.valign=this.valign
7099         }
7100         if (this.width) {
7101             cfg.width=this.width
7102         }
7103         
7104         
7105         return cfg;
7106     }
7107    
7108 });
7109
7110  
7111
7112  /*
7113  * - LGPL
7114  *
7115  * table row
7116  * 
7117  */
7118
7119 /**
7120  * @class Roo.bootstrap.TableRow
7121  * @extends Roo.bootstrap.Component
7122  * Bootstrap TableRow class
7123  * @cfg {String} cls row class
7124  * @cfg {String} align Aligns the content in a table row
7125  * @cfg {String} bgcolor Specifies a background color for a table row
7126  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7127  * @cfg {String} valign Vertical aligns the content in a table row
7128  * 
7129  * @constructor
7130  * Create a new TableRow
7131  * @param {Object} config The config object
7132  */
7133
7134 Roo.bootstrap.TableRow = function(config){
7135     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7136 };
7137
7138 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7139     
7140     cls: false,
7141     align: false,
7142     bgcolor: false,
7143     charoff: false,
7144     valign: false,
7145     
7146     getAutoCreate : function(){
7147         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7148         
7149         cfg = {
7150             tag: 'tr'
7151         };
7152             
7153         if(this.cls){
7154             cfg.cls = this.cls;
7155         }
7156         if(this.align){
7157             cfg.align = this.align;
7158         }
7159         if(this.bgcolor){
7160             cfg.bgcolor = this.bgcolor;
7161         }
7162         if(this.charoff){
7163             cfg.charoff = this.charoff;
7164         }
7165         if(this.valign){
7166             cfg.valign = this.valign;
7167         }
7168         
7169         return cfg;
7170     }
7171    
7172 });
7173
7174  
7175
7176  /*
7177  * - LGPL
7178  *
7179  * table body
7180  * 
7181  */
7182
7183 /**
7184  * @class Roo.bootstrap.TableBody
7185  * @extends Roo.bootstrap.Component
7186  * Bootstrap TableBody class
7187  * @cfg {String} cls element class
7188  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7189  * @cfg {String} align Aligns the content inside the element
7190  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7191  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7192  * 
7193  * @constructor
7194  * Create a new TableBody
7195  * @param {Object} config The config object
7196  */
7197
7198 Roo.bootstrap.TableBody = function(config){
7199     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7200 };
7201
7202 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7203     
7204     cls: false,
7205     tag: false,
7206     align: false,
7207     charoff: false,
7208     valign: false,
7209     
7210     getAutoCreate : function(){
7211         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7212         
7213         cfg = {
7214             tag: 'tbody'
7215         };
7216             
7217         if (this.cls) {
7218             cfg.cls=this.cls
7219         }
7220         if(this.tag){
7221             cfg.tag = this.tag;
7222         }
7223         
7224         if(this.align){
7225             cfg.align = this.align;
7226         }
7227         if(this.charoff){
7228             cfg.charoff = this.charoff;
7229         }
7230         if(this.valign){
7231             cfg.valign = this.valign;
7232         }
7233         
7234         return cfg;
7235     }
7236     
7237     
7238 //    initEvents : function()
7239 //    {
7240 //        
7241 //        if(!this.store){
7242 //            return;
7243 //        }
7244 //        
7245 //        this.store = Roo.factory(this.store, Roo.data);
7246 //        this.store.on('load', this.onLoad, this);
7247 //        
7248 //        this.store.load();
7249 //        
7250 //    },
7251 //    
7252 //    onLoad: function () 
7253 //    {   
7254 //        this.fireEvent('load', this);
7255 //    }
7256 //    
7257 //   
7258 });
7259
7260  
7261
7262  /*
7263  * Based on:
7264  * Ext JS Library 1.1.1
7265  * Copyright(c) 2006-2007, Ext JS, LLC.
7266  *
7267  * Originally Released Under LGPL - original licence link has changed is not relivant.
7268  *
7269  * Fork - LGPL
7270  * <script type="text/javascript">
7271  */
7272
7273 // as we use this in bootstrap.
7274 Roo.namespace('Roo.form');
7275  /**
7276  * @class Roo.form.Action
7277  * Internal Class used to handle form actions
7278  * @constructor
7279  * @param {Roo.form.BasicForm} el The form element or its id
7280  * @param {Object} config Configuration options
7281  */
7282
7283  
7284  
7285 // define the action interface
7286 Roo.form.Action = function(form, options){
7287     this.form = form;
7288     this.options = options || {};
7289 };
7290 /**
7291  * Client Validation Failed
7292  * @const 
7293  */
7294 Roo.form.Action.CLIENT_INVALID = 'client';
7295 /**
7296  * Server Validation Failed
7297  * @const 
7298  */
7299 Roo.form.Action.SERVER_INVALID = 'server';
7300  /**
7301  * Connect to Server Failed
7302  * @const 
7303  */
7304 Roo.form.Action.CONNECT_FAILURE = 'connect';
7305 /**
7306  * Reading Data from Server Failed
7307  * @const 
7308  */
7309 Roo.form.Action.LOAD_FAILURE = 'load';
7310
7311 Roo.form.Action.prototype = {
7312     type : 'default',
7313     failureType : undefined,
7314     response : undefined,
7315     result : undefined,
7316
7317     // interface method
7318     run : function(options){
7319
7320     },
7321
7322     // interface method
7323     success : function(response){
7324
7325     },
7326
7327     // interface method
7328     handleResponse : function(response){
7329
7330     },
7331
7332     // default connection failure
7333     failure : function(response){
7334         
7335         this.response = response;
7336         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7337         this.form.afterAction(this, false);
7338     },
7339
7340     processResponse : function(response){
7341         this.response = response;
7342         if(!response.responseText){
7343             return true;
7344         }
7345         this.result = this.handleResponse(response);
7346         return this.result;
7347     },
7348
7349     // utility functions used internally
7350     getUrl : function(appendParams){
7351         var url = this.options.url || this.form.url || this.form.el.dom.action;
7352         if(appendParams){
7353             var p = this.getParams();
7354             if(p){
7355                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7356             }
7357         }
7358         return url;
7359     },
7360
7361     getMethod : function(){
7362         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7363     },
7364
7365     getParams : function(){
7366         var bp = this.form.baseParams;
7367         var p = this.options.params;
7368         if(p){
7369             if(typeof p == "object"){
7370                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7371             }else if(typeof p == 'string' && bp){
7372                 p += '&' + Roo.urlEncode(bp);
7373             }
7374         }else if(bp){
7375             p = Roo.urlEncode(bp);
7376         }
7377         return p;
7378     },
7379
7380     createCallback : function(){
7381         return {
7382             success: this.success,
7383             failure: this.failure,
7384             scope: this,
7385             timeout: (this.form.timeout*1000),
7386             upload: this.form.fileUpload ? this.success : undefined
7387         };
7388     }
7389 };
7390
7391 Roo.form.Action.Submit = function(form, options){
7392     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7393 };
7394
7395 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7396     type : 'submit',
7397
7398     haveProgress : false,
7399     uploadComplete : false,
7400     
7401     // uploadProgress indicator.
7402     uploadProgress : function()
7403     {
7404         if (!this.form.progressUrl) {
7405             return;
7406         }
7407         
7408         if (!this.haveProgress) {
7409             Roo.MessageBox.progress("Uploading", "Uploading");
7410         }
7411         if (this.uploadComplete) {
7412            Roo.MessageBox.hide();
7413            return;
7414         }
7415         
7416         this.haveProgress = true;
7417    
7418         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7419         
7420         var c = new Roo.data.Connection();
7421         c.request({
7422             url : this.form.progressUrl,
7423             params: {
7424                 id : uid
7425             },
7426             method: 'GET',
7427             success : function(req){
7428                //console.log(data);
7429                 var rdata = false;
7430                 var edata;
7431                 try  {
7432                    rdata = Roo.decode(req.responseText)
7433                 } catch (e) {
7434                     Roo.log("Invalid data from server..");
7435                     Roo.log(edata);
7436                     return;
7437                 }
7438                 if (!rdata || !rdata.success) {
7439                     Roo.log(rdata);
7440                     Roo.MessageBox.alert(Roo.encode(rdata));
7441                     return;
7442                 }
7443                 var data = rdata.data;
7444                 
7445                 if (this.uploadComplete) {
7446                    Roo.MessageBox.hide();
7447                    return;
7448                 }
7449                    
7450                 if (data){
7451                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7452                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7453                     );
7454                 }
7455                 this.uploadProgress.defer(2000,this);
7456             },
7457        
7458             failure: function(data) {
7459                 Roo.log('progress url failed ');
7460                 Roo.log(data);
7461             },
7462             scope : this
7463         });
7464            
7465     },
7466     
7467     
7468     run : function()
7469     {
7470         // run get Values on the form, so it syncs any secondary forms.
7471         this.form.getValues();
7472         
7473         var o = this.options;
7474         var method = this.getMethod();
7475         var isPost = method == 'POST';
7476         if(o.clientValidation === false || this.form.isValid()){
7477             
7478             if (this.form.progressUrl) {
7479                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7480                     (new Date() * 1) + '' + Math.random());
7481                     
7482             } 
7483             
7484             
7485             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7486                 form:this.form.el.dom,
7487                 url:this.getUrl(!isPost),
7488                 method: method,
7489                 params:isPost ? this.getParams() : null,
7490                 isUpload: this.form.fileUpload
7491             }));
7492             
7493             this.uploadProgress();
7494
7495         }else if (o.clientValidation !== false){ // client validation failed
7496             this.failureType = Roo.form.Action.CLIENT_INVALID;
7497             this.form.afterAction(this, false);
7498         }
7499     },
7500
7501     success : function(response)
7502     {
7503         this.uploadComplete= true;
7504         if (this.haveProgress) {
7505             Roo.MessageBox.hide();
7506         }
7507         
7508         
7509         var result = this.processResponse(response);
7510         if(result === true || result.success){
7511             this.form.afterAction(this, true);
7512             return;
7513         }
7514         if(result.errors){
7515             this.form.markInvalid(result.errors);
7516             this.failureType = Roo.form.Action.SERVER_INVALID;
7517         }
7518         this.form.afterAction(this, false);
7519     },
7520     failure : function(response)
7521     {
7522         this.uploadComplete= true;
7523         if (this.haveProgress) {
7524             Roo.MessageBox.hide();
7525         }
7526         
7527         this.response = response;
7528         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7529         this.form.afterAction(this, false);
7530     },
7531     
7532     handleResponse : function(response){
7533         if(this.form.errorReader){
7534             var rs = this.form.errorReader.read(response);
7535             var errors = [];
7536             if(rs.records){
7537                 for(var i = 0, len = rs.records.length; i < len; i++) {
7538                     var r = rs.records[i];
7539                     errors[i] = r.data;
7540                 }
7541             }
7542             if(errors.length < 1){
7543                 errors = null;
7544             }
7545             return {
7546                 success : rs.success,
7547                 errors : errors
7548             };
7549         }
7550         var ret = false;
7551         try {
7552             ret = Roo.decode(response.responseText);
7553         } catch (e) {
7554             ret = {
7555                 success: false,
7556                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7557                 errors : []
7558             };
7559         }
7560         return ret;
7561         
7562     }
7563 });
7564
7565
7566 Roo.form.Action.Load = function(form, options){
7567     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7568     this.reader = this.form.reader;
7569 };
7570
7571 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7572     type : 'load',
7573
7574     run : function(){
7575         
7576         Roo.Ajax.request(Roo.apply(
7577                 this.createCallback(), {
7578                     method:this.getMethod(),
7579                     url:this.getUrl(false),
7580                     params:this.getParams()
7581         }));
7582     },
7583
7584     success : function(response){
7585         
7586         var result = this.processResponse(response);
7587         if(result === true || !result.success || !result.data){
7588             this.failureType = Roo.form.Action.LOAD_FAILURE;
7589             this.form.afterAction(this, false);
7590             return;
7591         }
7592         this.form.clearInvalid();
7593         this.form.setValues(result.data);
7594         this.form.afterAction(this, true);
7595     },
7596
7597     handleResponse : function(response){
7598         if(this.form.reader){
7599             var rs = this.form.reader.read(response);
7600             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7601             return {
7602                 success : rs.success,
7603                 data : data
7604             };
7605         }
7606         return Roo.decode(response.responseText);
7607     }
7608 });
7609
7610 Roo.form.Action.ACTION_TYPES = {
7611     'load' : Roo.form.Action.Load,
7612     'submit' : Roo.form.Action.Submit
7613 };/*
7614  * - LGPL
7615  *
7616  * form
7617  *
7618  */
7619
7620 /**
7621  * @class Roo.bootstrap.Form
7622  * @extends Roo.bootstrap.Component
7623  * Bootstrap Form class
7624  * @cfg {String} method  GET | POST (default POST)
7625  * @cfg {String} labelAlign top | left (default top)
7626  * @cfg {String} align left  | right - for navbars
7627  * @cfg {Boolean} loadMask load mask when submit (default true)
7628
7629  *
7630  * @constructor
7631  * Create a new Form
7632  * @param {Object} config The config object
7633  */
7634
7635
7636 Roo.bootstrap.Form = function(config){
7637     
7638     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7639     
7640     Roo.bootstrap.Form.popover.apply();
7641     
7642     this.addEvents({
7643         /**
7644          * @event clientvalidation
7645          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7646          * @param {Form} this
7647          * @param {Boolean} valid true if the form has passed client-side validation
7648          */
7649         clientvalidation: true,
7650         /**
7651          * @event beforeaction
7652          * Fires before any action is performed. Return false to cancel the action.
7653          * @param {Form} this
7654          * @param {Action} action The action to be performed
7655          */
7656         beforeaction: true,
7657         /**
7658          * @event actionfailed
7659          * Fires when an action fails.
7660          * @param {Form} this
7661          * @param {Action} action The action that failed
7662          */
7663         actionfailed : true,
7664         /**
7665          * @event actioncomplete
7666          * Fires when an action is completed.
7667          * @param {Form} this
7668          * @param {Action} action The action that completed
7669          */
7670         actioncomplete : true
7671     });
7672 };
7673
7674 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7675
7676      /**
7677      * @cfg {String} method
7678      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7679      */
7680     method : 'POST',
7681     /**
7682      * @cfg {String} url
7683      * The URL to use for form actions if one isn't supplied in the action options.
7684      */
7685     /**
7686      * @cfg {Boolean} fileUpload
7687      * Set to true if this form is a file upload.
7688      */
7689
7690     /**
7691      * @cfg {Object} baseParams
7692      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7693      */
7694
7695     /**
7696      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7697      */
7698     timeout: 30,
7699     /**
7700      * @cfg {Sting} align (left|right) for navbar forms
7701      */
7702     align : 'left',
7703
7704     // private
7705     activeAction : null,
7706
7707     /**
7708      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7709      * element by passing it or its id or mask the form itself by passing in true.
7710      * @type Mixed
7711      */
7712     waitMsgTarget : false,
7713
7714     loadMask : true,
7715     
7716     /**
7717      * @cfg {Boolean} errorMask (true|false) default false
7718      */
7719     errorMask : false,
7720     
7721     /**
7722      * @cfg {Number} maskOffset Default 100
7723      */
7724     maskOffset : 100,
7725     
7726     /**
7727      * @cfg {Boolean} maskBody
7728      */
7729     maskBody : false,
7730
7731     getAutoCreate : function(){
7732
7733         var cfg = {
7734             tag: 'form',
7735             method : this.method || 'POST',
7736             id : this.id || Roo.id(),
7737             cls : ''
7738         };
7739         if (this.parent().xtype.match(/^Nav/)) {
7740             cfg.cls = 'navbar-form navbar-' + this.align;
7741
7742         }
7743
7744         if (this.labelAlign == 'left' ) {
7745             cfg.cls += ' form-horizontal';
7746         }
7747
7748
7749         return cfg;
7750     },
7751     initEvents : function()
7752     {
7753         this.el.on('submit', this.onSubmit, this);
7754         // this was added as random key presses on the form where triggering form submit.
7755         this.el.on('keypress', function(e) {
7756             if (e.getCharCode() != 13) {
7757                 return true;
7758             }
7759             // we might need to allow it for textareas.. and some other items.
7760             // check e.getTarget().
7761
7762             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7763                 return true;
7764             }
7765
7766             Roo.log("keypress blocked");
7767
7768             e.preventDefault();
7769             return false;
7770         });
7771         
7772     },
7773     // private
7774     onSubmit : function(e){
7775         e.stopEvent();
7776     },
7777
7778      /**
7779      * Returns true if client-side validation on the form is successful.
7780      * @return Boolean
7781      */
7782     isValid : function(){
7783         var items = this.getItems();
7784         var valid = true;
7785         var target = false;
7786         
7787         items.each(function(f){
7788             if(f.validate()){
7789                 return;
7790             }
7791             valid = false;
7792
7793             if(!target && f.el.isVisible(true)){
7794                 target = f;
7795             }
7796            
7797         });
7798         
7799         if(this.errorMask && !valid){
7800             Roo.bootstrap.Form.popover.mask(this, target);
7801         }
7802         
7803         return valid;
7804     },
7805     
7806     /**
7807      * Returns true if any fields in this form have changed since their original load.
7808      * @return Boolean
7809      */
7810     isDirty : function(){
7811         var dirty = false;
7812         var items = this.getItems();
7813         items.each(function(f){
7814            if(f.isDirty()){
7815                dirty = true;
7816                return false;
7817            }
7818            return true;
7819         });
7820         return dirty;
7821     },
7822      /**
7823      * Performs a predefined action (submit or load) or custom actions you define on this form.
7824      * @param {String} actionName The name of the action type
7825      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7826      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7827      * accept other config options):
7828      * <pre>
7829 Property          Type             Description
7830 ----------------  ---------------  ----------------------------------------------------------------------------------
7831 url               String           The url for the action (defaults to the form's url)
7832 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7833 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7834 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7835                                    validate the form on the client (defaults to false)
7836      * </pre>
7837      * @return {BasicForm} this
7838      */
7839     doAction : function(action, options){
7840         if(typeof action == 'string'){
7841             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7842         }
7843         if(this.fireEvent('beforeaction', this, action) !== false){
7844             this.beforeAction(action);
7845             action.run.defer(100, action);
7846         }
7847         return this;
7848     },
7849
7850     // private
7851     beforeAction : function(action){
7852         var o = action.options;
7853         
7854         if(this.loadMask){
7855             
7856             if(this.maskBody){
7857                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7858             } else {
7859                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7860             }
7861         }
7862         // not really supported yet.. ??
7863
7864         //if(this.waitMsgTarget === true){
7865         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7866         //}else if(this.waitMsgTarget){
7867         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7868         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7869         //}else {
7870         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7871        // }
7872
7873     },
7874
7875     // private
7876     afterAction : function(action, success){
7877         this.activeAction = null;
7878         var o = action.options;
7879
7880         if(this.loadMask){
7881             
7882             if(this.maskBody){
7883                 Roo.get(document.body).unmask();
7884             } else {
7885                 this.el.unmask();
7886             }
7887         }
7888         
7889         //if(this.waitMsgTarget === true){
7890 //            this.el.unmask();
7891         //}else if(this.waitMsgTarget){
7892         //    this.waitMsgTarget.unmask();
7893         //}else{
7894         //    Roo.MessageBox.updateProgress(1);
7895         //    Roo.MessageBox.hide();
7896        // }
7897         //
7898         if(success){
7899             if(o.reset){
7900                 this.reset();
7901             }
7902             Roo.callback(o.success, o.scope, [this, action]);
7903             this.fireEvent('actioncomplete', this, action);
7904
7905         }else{
7906
7907             // failure condition..
7908             // we have a scenario where updates need confirming.
7909             // eg. if a locking scenario exists..
7910             // we look for { errors : { needs_confirm : true }} in the response.
7911             if (
7912                 (typeof(action.result) != 'undefined')  &&
7913                 (typeof(action.result.errors) != 'undefined')  &&
7914                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7915            ){
7916                 var _t = this;
7917                 Roo.log("not supported yet");
7918                  /*
7919
7920                 Roo.MessageBox.confirm(
7921                     "Change requires confirmation",
7922                     action.result.errorMsg,
7923                     function(r) {
7924                         if (r != 'yes') {
7925                             return;
7926                         }
7927                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7928                     }
7929
7930                 );
7931                 */
7932
7933
7934                 return;
7935             }
7936
7937             Roo.callback(o.failure, o.scope, [this, action]);
7938             // show an error message if no failed handler is set..
7939             if (!this.hasListener('actionfailed')) {
7940                 Roo.log("need to add dialog support");
7941                 /*
7942                 Roo.MessageBox.alert("Error",
7943                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7944                         action.result.errorMsg :
7945                         "Saving Failed, please check your entries or try again"
7946                 );
7947                 */
7948             }
7949
7950             this.fireEvent('actionfailed', this, action);
7951         }
7952
7953     },
7954     /**
7955      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7956      * @param {String} id The value to search for
7957      * @return Field
7958      */
7959     findField : function(id){
7960         var items = this.getItems();
7961         var field = items.get(id);
7962         if(!field){
7963              items.each(function(f){
7964                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7965                     field = f;
7966                     return false;
7967                 }
7968                 return true;
7969             });
7970         }
7971         return field || null;
7972     },
7973      /**
7974      * Mark fields in this form invalid in bulk.
7975      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7976      * @return {BasicForm} this
7977      */
7978     markInvalid : function(errors){
7979         if(errors instanceof Array){
7980             for(var i = 0, len = errors.length; i < len; i++){
7981                 var fieldError = errors[i];
7982                 var f = this.findField(fieldError.id);
7983                 if(f){
7984                     f.markInvalid(fieldError.msg);
7985                 }
7986             }
7987         }else{
7988             var field, id;
7989             for(id in errors){
7990                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7991                     field.markInvalid(errors[id]);
7992                 }
7993             }
7994         }
7995         //Roo.each(this.childForms || [], function (f) {
7996         //    f.markInvalid(errors);
7997         //});
7998
7999         return this;
8000     },
8001
8002     /**
8003      * Set values for fields in this form in bulk.
8004      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8005      * @return {BasicForm} this
8006      */
8007     setValues : function(values){
8008         if(values instanceof Array){ // array of objects
8009             for(var i = 0, len = values.length; i < len; i++){
8010                 var v = values[i];
8011                 var f = this.findField(v.id);
8012                 if(f){
8013                     f.setValue(v.value);
8014                     if(this.trackResetOnLoad){
8015                         f.originalValue = f.getValue();
8016                     }
8017                 }
8018             }
8019         }else{ // object hash
8020             var field, id;
8021             for(id in values){
8022                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8023
8024                     if (field.setFromData &&
8025                         field.valueField &&
8026                         field.displayField &&
8027                         // combos' with local stores can
8028                         // be queried via setValue()
8029                         // to set their value..
8030                         (field.store && !field.store.isLocal)
8031                         ) {
8032                         // it's a combo
8033                         var sd = { };
8034                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8035                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8036                         field.setFromData(sd);
8037
8038                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8039                         
8040                         field.setFromData(values);
8041                         
8042                     } else {
8043                         field.setValue(values[id]);
8044                     }
8045
8046
8047                     if(this.trackResetOnLoad){
8048                         field.originalValue = field.getValue();
8049                     }
8050                 }
8051             }
8052         }
8053
8054         //Roo.each(this.childForms || [], function (f) {
8055         //    f.setValues(values);
8056         //});
8057
8058         return this;
8059     },
8060
8061     /**
8062      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8063      * they are returned as an array.
8064      * @param {Boolean} asString
8065      * @return {Object}
8066      */
8067     getValues : function(asString){
8068         //if (this.childForms) {
8069             // copy values from the child forms
8070         //    Roo.each(this.childForms, function (f) {
8071         //        this.setValues(f.getValues());
8072         //    }, this);
8073         //}
8074
8075
8076
8077         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8078         if(asString === true){
8079             return fs;
8080         }
8081         return Roo.urlDecode(fs);
8082     },
8083
8084     /**
8085      * Returns the fields in this form as an object with key/value pairs.
8086      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8087      * @return {Object}
8088      */
8089     getFieldValues : function(with_hidden)
8090     {
8091         var items = this.getItems();
8092         var ret = {};
8093         items.each(function(f){
8094             
8095             if (!f.getName()) {
8096                 return;
8097             }
8098             
8099             var v = f.getValue();
8100             
8101             if (f.inputType =='radio') {
8102                 if (typeof(ret[f.getName()]) == 'undefined') {
8103                     ret[f.getName()] = ''; // empty..
8104                 }
8105
8106                 if (!f.el.dom.checked) {
8107                     return;
8108
8109                 }
8110                 v = f.el.dom.value;
8111
8112             }
8113             
8114             if(f.xtype == 'MoneyField'){
8115                 ret[f.currencyName] = f.getCurrency();
8116             }
8117
8118             // not sure if this supported any more..
8119             if ((typeof(v) == 'object') && f.getRawValue) {
8120                 v = f.getRawValue() ; // dates..
8121             }
8122             // combo boxes where name != hiddenName...
8123             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8124                 ret[f.name] = f.getRawValue();
8125             }
8126             ret[f.getName()] = v;
8127         });
8128
8129         return ret;
8130     },
8131
8132     /**
8133      * Clears all invalid messages in this form.
8134      * @return {BasicForm} this
8135      */
8136     clearInvalid : function(){
8137         var items = this.getItems();
8138
8139         items.each(function(f){
8140            f.clearInvalid();
8141         });
8142
8143         return this;
8144     },
8145
8146     /**
8147      * Resets this form.
8148      * @return {BasicForm} this
8149      */
8150     reset : function(){
8151         var items = this.getItems();
8152         items.each(function(f){
8153             f.reset();
8154         });
8155
8156         Roo.each(this.childForms || [], function (f) {
8157             f.reset();
8158         });
8159
8160
8161         return this;
8162     },
8163     
8164     getItems : function()
8165     {
8166         var r=new Roo.util.MixedCollection(false, function(o){
8167             return o.id || (o.id = Roo.id());
8168         });
8169         var iter = function(el) {
8170             if (el.inputEl) {
8171                 r.add(el);
8172             }
8173             if (!el.items) {
8174                 return;
8175             }
8176             Roo.each(el.items,function(e) {
8177                 iter(e);
8178             });
8179         };
8180
8181         iter(this);
8182         return r;
8183     },
8184     
8185     hideFields : function(items)
8186     {
8187         Roo.each(items, function(i){
8188             
8189             var f = this.findField(i);
8190             
8191             if(!f){
8192                 return;
8193             }
8194             
8195             if(f.xtype == 'DateField'){
8196                 f.setVisible(false);
8197                 return;
8198             }
8199             
8200             f.hide();
8201             
8202         }, this);
8203     },
8204     
8205     showFields : function(items)
8206     {
8207         Roo.each(items, function(i){
8208             
8209             var f = this.findField(i);
8210             
8211             if(!f){
8212                 return;
8213             }
8214             
8215             if(f.xtype == 'DateField'){
8216                 f.setVisible(true);
8217                 return;
8218             }
8219             
8220             f.show();
8221             
8222         }, this);
8223     }
8224
8225 });
8226
8227 Roo.apply(Roo.bootstrap.Form, {
8228     
8229     popover : {
8230         
8231         padding : 5,
8232         
8233         isApplied : false,
8234         
8235         isMasked : false,
8236         
8237         form : false,
8238         
8239         target : false,
8240         
8241         toolTip : false,
8242         
8243         intervalID : false,
8244         
8245         maskEl : false,
8246         
8247         apply : function()
8248         {
8249             if(this.isApplied){
8250                 return;
8251             }
8252             
8253             this.maskEl = {
8254                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8255                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8256                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8257                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8258             };
8259             
8260             this.maskEl.top.enableDisplayMode("block");
8261             this.maskEl.left.enableDisplayMode("block");
8262             this.maskEl.bottom.enableDisplayMode("block");
8263             this.maskEl.right.enableDisplayMode("block");
8264             
8265             this.toolTip = new Roo.bootstrap.Tooltip({
8266                 cls : 'roo-form-error-popover',
8267                 alignment : {
8268                     'left' : ['r-l', [-2,0], 'right'],
8269                     'right' : ['l-r', [2,0], 'left'],
8270                     'bottom' : ['tl-bl', [0,2], 'top'],
8271                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8272                 }
8273             });
8274             
8275             this.toolTip.render(Roo.get(document.body));
8276
8277             this.toolTip.el.enableDisplayMode("block");
8278             
8279             Roo.get(document.body).on('click', function(){
8280                 this.unmask();
8281             }, this);
8282             
8283             Roo.get(document.body).on('touchstart', function(){
8284                 this.unmask();
8285             }, this);
8286             
8287             this.isApplied = true
8288         },
8289         
8290         mask : function(form, target)
8291         {
8292             this.form = form;
8293             
8294             this.target = target;
8295             
8296             if(!this.form.errorMask || !target.el){
8297                 return;
8298             }
8299             
8300             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8301             
8302             Roo.log(scrollable);
8303             
8304             var ot = this.target.el.calcOffsetsTo(scrollable);
8305             
8306             var scrollTo = ot[1] - this.form.maskOffset;
8307             
8308             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8309             
8310             scrollable.scrollTo('top', scrollTo);
8311             
8312             var box = this.target.el.getBox();
8313             Roo.log(box);
8314             var zIndex = Roo.bootstrap.Modal.zIndex++;
8315
8316             
8317             this.maskEl.top.setStyle('position', 'absolute');
8318             this.maskEl.top.setStyle('z-index', zIndex);
8319             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8320             this.maskEl.top.setLeft(0);
8321             this.maskEl.top.setTop(0);
8322             this.maskEl.top.show();
8323             
8324             this.maskEl.left.setStyle('position', 'absolute');
8325             this.maskEl.left.setStyle('z-index', zIndex);
8326             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8327             this.maskEl.left.setLeft(0);
8328             this.maskEl.left.setTop(box.y - this.padding);
8329             this.maskEl.left.show();
8330
8331             this.maskEl.bottom.setStyle('position', 'absolute');
8332             this.maskEl.bottom.setStyle('z-index', zIndex);
8333             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8334             this.maskEl.bottom.setLeft(0);
8335             this.maskEl.bottom.setTop(box.bottom + this.padding);
8336             this.maskEl.bottom.show();
8337
8338             this.maskEl.right.setStyle('position', 'absolute');
8339             this.maskEl.right.setStyle('z-index', zIndex);
8340             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8341             this.maskEl.right.setLeft(box.right + this.padding);
8342             this.maskEl.right.setTop(box.y - this.padding);
8343             this.maskEl.right.show();
8344
8345             this.toolTip.bindEl = this.target.el;
8346
8347             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8348
8349             var tip = this.target.blankText;
8350
8351             if(this.target.getValue() !== '' ) {
8352                 
8353                 if (this.target.invalidText.length) {
8354                     tip = this.target.invalidText;
8355                 } else if (this.target.regexText.length){
8356                     tip = this.target.regexText;
8357                 }
8358             }
8359
8360             this.toolTip.show(tip);
8361
8362             this.intervalID = window.setInterval(function() {
8363                 Roo.bootstrap.Form.popover.unmask();
8364             }, 10000);
8365
8366             window.onwheel = function(){ return false;};
8367             
8368             (function(){ this.isMasked = true; }).defer(500, this);
8369             
8370         },
8371         
8372         unmask : function()
8373         {
8374             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8375                 return;
8376             }
8377             
8378             this.maskEl.top.setStyle('position', 'absolute');
8379             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8380             this.maskEl.top.hide();
8381
8382             this.maskEl.left.setStyle('position', 'absolute');
8383             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8384             this.maskEl.left.hide();
8385
8386             this.maskEl.bottom.setStyle('position', 'absolute');
8387             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8388             this.maskEl.bottom.hide();
8389
8390             this.maskEl.right.setStyle('position', 'absolute');
8391             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8392             this.maskEl.right.hide();
8393             
8394             this.toolTip.hide();
8395             
8396             this.toolTip.el.hide();
8397             
8398             window.onwheel = function(){ return true;};
8399             
8400             if(this.intervalID){
8401                 window.clearInterval(this.intervalID);
8402                 this.intervalID = false;
8403             }
8404             
8405             this.isMasked = false;
8406             
8407         }
8408         
8409     }
8410     
8411 });
8412
8413 /*
8414  * Based on:
8415  * Ext JS Library 1.1.1
8416  * Copyright(c) 2006-2007, Ext JS, LLC.
8417  *
8418  * Originally Released Under LGPL - original licence link has changed is not relivant.
8419  *
8420  * Fork - LGPL
8421  * <script type="text/javascript">
8422  */
8423 /**
8424  * @class Roo.form.VTypes
8425  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8426  * @singleton
8427  */
8428 Roo.form.VTypes = function(){
8429     // closure these in so they are only created once.
8430     var alpha = /^[a-zA-Z_]+$/;
8431     var alphanum = /^[a-zA-Z0-9_]+$/;
8432     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8433     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8434
8435     // All these messages and functions are configurable
8436     return {
8437         /**
8438          * The function used to validate email addresses
8439          * @param {String} value The email address
8440          */
8441         'email' : function(v){
8442             return email.test(v);
8443         },
8444         /**
8445          * The error text to display when the email validation function returns false
8446          * @type String
8447          */
8448         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8449         /**
8450          * The keystroke filter mask to be applied on email input
8451          * @type RegExp
8452          */
8453         'emailMask' : /[a-z0-9_\.\-@]/i,
8454
8455         /**
8456          * The function used to validate URLs
8457          * @param {String} value The URL
8458          */
8459         'url' : function(v){
8460             return url.test(v);
8461         },
8462         /**
8463          * The error text to display when the url validation function returns false
8464          * @type String
8465          */
8466         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8467         
8468         /**
8469          * The function used to validate alpha values
8470          * @param {String} value The value
8471          */
8472         'alpha' : function(v){
8473             return alpha.test(v);
8474         },
8475         /**
8476          * The error text to display when the alpha validation function returns false
8477          * @type String
8478          */
8479         'alphaText' : 'This field should only contain letters and _',
8480         /**
8481          * The keystroke filter mask to be applied on alpha input
8482          * @type RegExp
8483          */
8484         'alphaMask' : /[a-z_]/i,
8485
8486         /**
8487          * The function used to validate alphanumeric values
8488          * @param {String} value The value
8489          */
8490         'alphanum' : function(v){
8491             return alphanum.test(v);
8492         },
8493         /**
8494          * The error text to display when the alphanumeric validation function returns false
8495          * @type String
8496          */
8497         'alphanumText' : 'This field should only contain letters, numbers and _',
8498         /**
8499          * The keystroke filter mask to be applied on alphanumeric input
8500          * @type RegExp
8501          */
8502         'alphanumMask' : /[a-z0-9_]/i
8503     };
8504 }();/*
8505  * - LGPL
8506  *
8507  * Input
8508  * 
8509  */
8510
8511 /**
8512  * @class Roo.bootstrap.Input
8513  * @extends Roo.bootstrap.Component
8514  * Bootstrap Input class
8515  * @cfg {Boolean} disabled is it disabled
8516  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8517  * @cfg {String} name name of the input
8518  * @cfg {string} fieldLabel - the label associated
8519  * @cfg {string} placeholder - placeholder to put in text.
8520  * @cfg {string}  before - input group add on before
8521  * @cfg {string} after - input group add on after
8522  * @cfg {string} size - (lg|sm) or leave empty..
8523  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8524  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8525  * @cfg {Number} md colspan out of 12 for computer-sized screens
8526  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8527  * @cfg {string} value default value of the input
8528  * @cfg {Number} labelWidth set the width of label 
8529  * @cfg {Number} labellg set the width of label (1-12)
8530  * @cfg {Number} labelmd set the width of label (1-12)
8531  * @cfg {Number} labelsm set the width of label (1-12)
8532  * @cfg {Number} labelxs set the width of label (1-12)
8533  * @cfg {String} labelAlign (top|left)
8534  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8535  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8536  * @cfg {String} indicatorpos (left|right) default left
8537
8538  * @cfg {String} align (left|center|right) Default left
8539  * @cfg {Boolean} forceFeedback (true|false) Default false
8540  * 
8541  * @constructor
8542  * Create a new Input
8543  * @param {Object} config The config object
8544  */
8545
8546 Roo.bootstrap.Input = function(config){
8547     
8548     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8549     
8550     this.addEvents({
8551         /**
8552          * @event focus
8553          * Fires when this field receives input focus.
8554          * @param {Roo.form.Field} this
8555          */
8556         focus : true,
8557         /**
8558          * @event blur
8559          * Fires when this field loses input focus.
8560          * @param {Roo.form.Field} this
8561          */
8562         blur : true,
8563         /**
8564          * @event specialkey
8565          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8566          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8567          * @param {Roo.form.Field} this
8568          * @param {Roo.EventObject} e The event object
8569          */
8570         specialkey : true,
8571         /**
8572          * @event change
8573          * Fires just before the field blurs if the field value has changed.
8574          * @param {Roo.form.Field} this
8575          * @param {Mixed} newValue The new value
8576          * @param {Mixed} oldValue The original value
8577          */
8578         change : true,
8579         /**
8580          * @event invalid
8581          * Fires after the field has been marked as invalid.
8582          * @param {Roo.form.Field} this
8583          * @param {String} msg The validation message
8584          */
8585         invalid : true,
8586         /**
8587          * @event valid
8588          * Fires after the field has been validated with no errors.
8589          * @param {Roo.form.Field} this
8590          */
8591         valid : true,
8592          /**
8593          * @event keyup
8594          * Fires after the key up
8595          * @param {Roo.form.Field} this
8596          * @param {Roo.EventObject}  e The event Object
8597          */
8598         keyup : true
8599     });
8600 };
8601
8602 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8603      /**
8604      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8605       automatic validation (defaults to "keyup").
8606      */
8607     validationEvent : "keyup",
8608      /**
8609      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8610      */
8611     validateOnBlur : true,
8612     /**
8613      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8614      */
8615     validationDelay : 250,
8616      /**
8617      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8618      */
8619     focusClass : "x-form-focus",  // not needed???
8620     
8621        
8622     /**
8623      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8624      */
8625     invalidClass : "has-warning",
8626     
8627     /**
8628      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8629      */
8630     validClass : "has-success",
8631     
8632     /**
8633      * @cfg {Boolean} hasFeedback (true|false) default true
8634      */
8635     hasFeedback : true,
8636     
8637     /**
8638      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8639      */
8640     invalidFeedbackClass : "glyphicon-warning-sign",
8641     
8642     /**
8643      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8644      */
8645     validFeedbackClass : "glyphicon-ok",
8646     
8647     /**
8648      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8649      */
8650     selectOnFocus : false,
8651     
8652      /**
8653      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8654      */
8655     maskRe : null,
8656        /**
8657      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8658      */
8659     vtype : null,
8660     
8661       /**
8662      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8663      */
8664     disableKeyFilter : false,
8665     
8666        /**
8667      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8668      */
8669     disabled : false,
8670      /**
8671      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8672      */
8673     allowBlank : true,
8674     /**
8675      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8676      */
8677     blankText : "Please complete this mandatory field",
8678     
8679      /**
8680      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8681      */
8682     minLength : 0,
8683     /**
8684      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8685      */
8686     maxLength : Number.MAX_VALUE,
8687     /**
8688      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8689      */
8690     minLengthText : "The minimum length for this field is {0}",
8691     /**
8692      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8693      */
8694     maxLengthText : "The maximum length for this field is {0}",
8695   
8696     
8697     /**
8698      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8699      * If available, this function will be called only after the basic validators all return true, and will be passed the
8700      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8701      */
8702     validator : null,
8703     /**
8704      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8705      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8706      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8707      */
8708     regex : null,
8709     /**
8710      * @cfg {String} regexText -- Depricated - use Invalid Text
8711      */
8712     regexText : "",
8713     
8714     /**
8715      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8716      */
8717     invalidText : "",
8718     
8719     
8720     
8721     autocomplete: false,
8722     
8723     
8724     fieldLabel : '',
8725     inputType : 'text',
8726     
8727     name : false,
8728     placeholder: false,
8729     before : false,
8730     after : false,
8731     size : false,
8732     hasFocus : false,
8733     preventMark: false,
8734     isFormField : true,
8735     value : '',
8736     labelWidth : 2,
8737     labelAlign : false,
8738     readOnly : false,
8739     align : false,
8740     formatedValue : false,
8741     forceFeedback : false,
8742     
8743     indicatorpos : 'left',
8744     
8745     labellg : 0,
8746     labelmd : 0,
8747     labelsm : 0,
8748     labelxs : 0,
8749     
8750     parentLabelAlign : function()
8751     {
8752         var parent = this;
8753         while (parent.parent()) {
8754             parent = parent.parent();
8755             if (typeof(parent.labelAlign) !='undefined') {
8756                 return parent.labelAlign;
8757             }
8758         }
8759         return 'left';
8760         
8761     },
8762     
8763     getAutoCreate : function()
8764     {
8765         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8766         
8767         var id = Roo.id();
8768         
8769         var cfg = {};
8770         
8771         if(this.inputType != 'hidden'){
8772             cfg.cls = 'form-group' //input-group
8773         }
8774         
8775         var input =  {
8776             tag: 'input',
8777             id : id,
8778             type : this.inputType,
8779             value : this.value,
8780             cls : 'form-control',
8781             placeholder : this.placeholder || '',
8782             autocomplete : this.autocomplete || 'new-password'
8783         };
8784         
8785         if(this.align){
8786             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8787         }
8788         
8789         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8790             input.maxLength = this.maxLength;
8791         }
8792         
8793         if (this.disabled) {
8794             input.disabled=true;
8795         }
8796         
8797         if (this.readOnly) {
8798             input.readonly=true;
8799         }
8800         
8801         if (this.name) {
8802             input.name = this.name;
8803         }
8804         
8805         if (this.size) {
8806             input.cls += ' input-' + this.size;
8807         }
8808         
8809         var settings=this;
8810         ['xs','sm','md','lg'].map(function(size){
8811             if (settings[size]) {
8812                 cfg.cls += ' col-' + size + '-' + settings[size];
8813             }
8814         });
8815         
8816         var inputblock = input;
8817         
8818         var feedback = {
8819             tag: 'span',
8820             cls: 'glyphicon form-control-feedback'
8821         };
8822             
8823         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8824             
8825             inputblock = {
8826                 cls : 'has-feedback',
8827                 cn :  [
8828                     input,
8829                     feedback
8830                 ] 
8831             };  
8832         }
8833         
8834         if (this.before || this.after) {
8835             
8836             inputblock = {
8837                 cls : 'input-group',
8838                 cn :  [] 
8839             };
8840             
8841             if (this.before && typeof(this.before) == 'string') {
8842                 
8843                 inputblock.cn.push({
8844                     tag :'span',
8845                     cls : 'roo-input-before input-group-addon',
8846                     html : this.before
8847                 });
8848             }
8849             if (this.before && typeof(this.before) == 'object') {
8850                 this.before = Roo.factory(this.before);
8851                 
8852                 inputblock.cn.push({
8853                     tag :'span',
8854                     cls : 'roo-input-before input-group-' +
8855                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8856                 });
8857             }
8858             
8859             inputblock.cn.push(input);
8860             
8861             if (this.after && typeof(this.after) == 'string') {
8862                 inputblock.cn.push({
8863                     tag :'span',
8864                     cls : 'roo-input-after input-group-addon',
8865                     html : this.after
8866                 });
8867             }
8868             if (this.after && typeof(this.after) == 'object') {
8869                 this.after = Roo.factory(this.after);
8870                 
8871                 inputblock.cn.push({
8872                     tag :'span',
8873                     cls : 'roo-input-after input-group-' +
8874                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8875                 });
8876             }
8877             
8878             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8879                 inputblock.cls += ' has-feedback';
8880                 inputblock.cn.push(feedback);
8881             }
8882         };
8883         
8884         if (align ==='left' && this.fieldLabel.length) {
8885             
8886             cfg.cls += ' roo-form-group-label-left';
8887             
8888             cfg.cn = [
8889                 {
8890                     tag : 'i',
8891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8892                     tooltip : 'This field is required'
8893                 },
8894                 {
8895                     tag: 'label',
8896                     'for' :  id,
8897                     cls : 'control-label',
8898                     html : this.fieldLabel
8899
8900                 },
8901                 {
8902                     cls : "", 
8903                     cn: [
8904                         inputblock
8905                     ]
8906                 }
8907             ];
8908             
8909             var labelCfg = cfg.cn[1];
8910             var contentCfg = cfg.cn[2];
8911             
8912             if(this.indicatorpos == 'right'){
8913                 cfg.cn = [
8914                     {
8915                         tag: 'label',
8916                         'for' :  id,
8917                         cls : 'control-label',
8918                         cn : [
8919                             {
8920                                 tag : 'span',
8921                                 html : this.fieldLabel
8922                             },
8923                             {
8924                                 tag : 'i',
8925                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8926                                 tooltip : 'This field is required'
8927                             }
8928                         ]
8929                     },
8930                     {
8931                         cls : "",
8932                         cn: [
8933                             inputblock
8934                         ]
8935                     }
8936
8937                 ];
8938                 
8939                 labelCfg = cfg.cn[0];
8940                 contentCfg = cfg.cn[1];
8941             
8942             }
8943             
8944             if(this.labelWidth > 12){
8945                 labelCfg.style = "width: " + this.labelWidth + 'px';
8946             }
8947             
8948             if(this.labelWidth < 13 && this.labelmd == 0){
8949                 this.labelmd = this.labelWidth;
8950             }
8951             
8952             if(this.labellg > 0){
8953                 labelCfg.cls += ' col-lg-' + this.labellg;
8954                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8955             }
8956             
8957             if(this.labelmd > 0){
8958                 labelCfg.cls += ' col-md-' + this.labelmd;
8959                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8960             }
8961             
8962             if(this.labelsm > 0){
8963                 labelCfg.cls += ' col-sm-' + this.labelsm;
8964                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8965             }
8966             
8967             if(this.labelxs > 0){
8968                 labelCfg.cls += ' col-xs-' + this.labelxs;
8969                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8970             }
8971             
8972             
8973         } else if ( this.fieldLabel.length) {
8974                 
8975             cfg.cn = [
8976                 {
8977                     tag : 'i',
8978                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8979                     tooltip : 'This field is required'
8980                 },
8981                 {
8982                     tag: 'label',
8983                    //cls : 'input-group-addon',
8984                     html : this.fieldLabel
8985
8986                 },
8987
8988                inputblock
8989
8990            ];
8991            
8992            if(this.indicatorpos == 'right'){
8993                 
8994                 cfg.cn = [
8995                     {
8996                         tag: 'label',
8997                        //cls : 'input-group-addon',
8998                         html : this.fieldLabel
8999
9000                     },
9001                     {
9002                         tag : 'i',
9003                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9004                         tooltip : 'This field is required'
9005                     },
9006
9007                    inputblock
9008
9009                ];
9010
9011             }
9012
9013         } else {
9014             
9015             cfg.cn = [
9016
9017                     inputblock
9018
9019             ];
9020                 
9021                 
9022         };
9023         
9024         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9025            cfg.cls += ' navbar-form';
9026         }
9027         
9028         if (this.parentType === 'NavGroup') {
9029            cfg.cls += ' navbar-form';
9030            cfg.tag = 'li';
9031         }
9032         
9033         return cfg;
9034         
9035     },
9036     /**
9037      * return the real input element.
9038      */
9039     inputEl: function ()
9040     {
9041         return this.el.select('input.form-control',true).first();
9042     },
9043     
9044     tooltipEl : function()
9045     {
9046         return this.inputEl();
9047     },
9048     
9049     indicatorEl : function()
9050     {
9051         var indicator = this.el.select('i.roo-required-indicator',true).first();
9052         
9053         if(!indicator){
9054             return false;
9055         }
9056         
9057         return indicator;
9058         
9059     },
9060     
9061     setDisabled : function(v)
9062     {
9063         var i  = this.inputEl().dom;
9064         if (!v) {
9065             i.removeAttribute('disabled');
9066             return;
9067             
9068         }
9069         i.setAttribute('disabled','true');
9070     },
9071     initEvents : function()
9072     {
9073           
9074         this.inputEl().on("keydown" , this.fireKey,  this);
9075         this.inputEl().on("focus", this.onFocus,  this);
9076         this.inputEl().on("blur", this.onBlur,  this);
9077         
9078         this.inputEl().relayEvent('keyup', this);
9079         
9080         this.indicator = this.indicatorEl();
9081         
9082         if(this.indicator){
9083             this.indicator.addClass('invisible');
9084         }
9085  
9086         // reference to original value for reset
9087         this.originalValue = this.getValue();
9088         //Roo.form.TextField.superclass.initEvents.call(this);
9089         if(this.validationEvent == 'keyup'){
9090             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9091             this.inputEl().on('keyup', this.filterValidation, this);
9092         }
9093         else if(this.validationEvent !== false){
9094             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9095         }
9096         
9097         if(this.selectOnFocus){
9098             this.on("focus", this.preFocus, this);
9099             
9100         }
9101         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9102             this.inputEl().on("keypress", this.filterKeys, this);
9103         } else {
9104             this.inputEl().relayEvent('keypress', this);
9105         }
9106        /* if(this.grow){
9107             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9108             this.el.on("click", this.autoSize,  this);
9109         }
9110         */
9111         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9112             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9113         }
9114         
9115         if (typeof(this.before) == 'object') {
9116             this.before.render(this.el.select('.roo-input-before',true).first());
9117         }
9118         if (typeof(this.after) == 'object') {
9119             this.after.render(this.el.select('.roo-input-after',true).first());
9120         }
9121         
9122         
9123     },
9124     filterValidation : function(e){
9125         if(!e.isNavKeyPress()){
9126             this.validationTask.delay(this.validationDelay);
9127         }
9128     },
9129      /**
9130      * Validates the field value
9131      * @return {Boolean} True if the value is valid, else false
9132      */
9133     validate : function(){
9134         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9135         if(this.disabled || this.validateValue(this.getRawValue())){
9136             this.markValid();
9137             return true;
9138         }
9139         
9140         this.markInvalid();
9141         return false;
9142     },
9143     
9144     
9145     /**
9146      * Validates a value according to the field's validation rules and marks the field as invalid
9147      * if the validation fails
9148      * @param {Mixed} value The value to validate
9149      * @return {Boolean} True if the value is valid, else false
9150      */
9151     validateValue : function(value)
9152     {
9153         if(this.getVisibilityEl().hasClass('hidden')){
9154             return true;
9155         }
9156         
9157         if(value.length < 1)  { // if it's blank
9158             if(this.allowBlank){
9159                 return true;
9160             }
9161             return false;
9162         }
9163         
9164         if(value.length < this.minLength){
9165             return false;
9166         }
9167         if(value.length > this.maxLength){
9168             return false;
9169         }
9170         if(this.vtype){
9171             var vt = Roo.form.VTypes;
9172             if(!vt[this.vtype](value, this)){
9173                 return false;
9174             }
9175         }
9176         if(typeof this.validator == "function"){
9177             var msg = this.validator(value);
9178             if(msg !== true){
9179                 return false;
9180             }
9181             if (typeof(msg) == 'string') {
9182                 this.invalidText = msg;
9183             }
9184         }
9185         
9186         if(this.regex && !this.regex.test(value)){
9187             return false;
9188         }
9189         
9190         return true;
9191     },
9192     
9193      // private
9194     fireKey : function(e){
9195         //Roo.log('field ' + e.getKey());
9196         if(e.isNavKeyPress()){
9197             this.fireEvent("specialkey", this, e);
9198         }
9199     },
9200     focus : function (selectText){
9201         if(this.rendered){
9202             this.inputEl().focus();
9203             if(selectText === true){
9204                 this.inputEl().dom.select();
9205             }
9206         }
9207         return this;
9208     } ,
9209     
9210     onFocus : function(){
9211         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9212            // this.el.addClass(this.focusClass);
9213         }
9214         if(!this.hasFocus){
9215             this.hasFocus = true;
9216             this.startValue = this.getValue();
9217             this.fireEvent("focus", this);
9218         }
9219     },
9220     
9221     beforeBlur : Roo.emptyFn,
9222
9223     
9224     // private
9225     onBlur : function(){
9226         this.beforeBlur();
9227         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9228             //this.el.removeClass(this.focusClass);
9229         }
9230         this.hasFocus = false;
9231         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9232             this.validate();
9233         }
9234         var v = this.getValue();
9235         if(String(v) !== String(this.startValue)){
9236             this.fireEvent('change', this, v, this.startValue);
9237         }
9238         this.fireEvent("blur", this);
9239     },
9240     
9241     /**
9242      * Resets the current field value to the originally loaded value and clears any validation messages
9243      */
9244     reset : function(){
9245         this.setValue(this.originalValue);
9246         this.validate();
9247     },
9248      /**
9249      * Returns the name of the field
9250      * @return {Mixed} name The name field
9251      */
9252     getName: function(){
9253         return this.name;
9254     },
9255      /**
9256      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9257      * @return {Mixed} value The field value
9258      */
9259     getValue : function(){
9260         
9261         var v = this.inputEl().getValue();
9262         
9263         return v;
9264     },
9265     /**
9266      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9267      * @return {Mixed} value The field value
9268      */
9269     getRawValue : function(){
9270         var v = this.inputEl().getValue();
9271         
9272         return v;
9273     },
9274     
9275     /**
9276      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9277      * @param {Mixed} value The value to set
9278      */
9279     setRawValue : function(v){
9280         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9281     },
9282     
9283     selectText : function(start, end){
9284         var v = this.getRawValue();
9285         if(v.length > 0){
9286             start = start === undefined ? 0 : start;
9287             end = end === undefined ? v.length : end;
9288             var d = this.inputEl().dom;
9289             if(d.setSelectionRange){
9290                 d.setSelectionRange(start, end);
9291             }else if(d.createTextRange){
9292                 var range = d.createTextRange();
9293                 range.moveStart("character", start);
9294                 range.moveEnd("character", v.length-end);
9295                 range.select();
9296             }
9297         }
9298     },
9299     
9300     /**
9301      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9302      * @param {Mixed} value The value to set
9303      */
9304     setValue : function(v){
9305         this.value = v;
9306         if(this.rendered){
9307             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9308             this.validate();
9309         }
9310     },
9311     
9312     /*
9313     processValue : function(value){
9314         if(this.stripCharsRe){
9315             var newValue = value.replace(this.stripCharsRe, '');
9316             if(newValue !== value){
9317                 this.setRawValue(newValue);
9318                 return newValue;
9319             }
9320         }
9321         return value;
9322     },
9323   */
9324     preFocus : function(){
9325         
9326         if(this.selectOnFocus){
9327             this.inputEl().dom.select();
9328         }
9329     },
9330     filterKeys : function(e){
9331         var k = e.getKey();
9332         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9333             return;
9334         }
9335         var c = e.getCharCode(), cc = String.fromCharCode(c);
9336         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9337             return;
9338         }
9339         if(!this.maskRe.test(cc)){
9340             e.stopEvent();
9341         }
9342     },
9343      /**
9344      * Clear any invalid styles/messages for this field
9345      */
9346     clearInvalid : function(){
9347         
9348         if(!this.el || this.preventMark){ // not rendered
9349             return;
9350         }
9351         
9352      
9353         this.el.removeClass(this.invalidClass);
9354         
9355         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9356             
9357             var feedback = this.el.select('.form-control-feedback', true).first();
9358             
9359             if(feedback){
9360                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9361             }
9362             
9363         }
9364         
9365         this.fireEvent('valid', this);
9366     },
9367     
9368      /**
9369      * Mark this field as valid
9370      */
9371     markValid : function()
9372     {
9373         if(!this.el  || this.preventMark){ // not rendered...
9374             return;
9375         }
9376         
9377         this.el.removeClass([this.invalidClass, this.validClass]);
9378         
9379         var feedback = this.el.select('.form-control-feedback', true).first();
9380             
9381         if(feedback){
9382             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9383         }
9384         
9385         if(this.indicator){
9386             this.indicator.removeClass('visible');
9387             this.indicator.addClass('invisible');
9388         }
9389         
9390         if(this.disabled){
9391             return;
9392         }
9393         
9394         if(this.allowBlank && !this.getRawValue().length){
9395             return;
9396         }
9397         
9398         this.el.addClass(this.validClass);
9399         
9400         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9401             
9402             var feedback = this.el.select('.form-control-feedback', true).first();
9403             
9404             if(feedback){
9405                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9406                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9407             }
9408             
9409         }
9410         
9411         this.fireEvent('valid', this);
9412     },
9413     
9414      /**
9415      * Mark this field as invalid
9416      * @param {String} msg The validation message
9417      */
9418     markInvalid : function(msg)
9419     {
9420         if(!this.el  || this.preventMark){ // not rendered
9421             return;
9422         }
9423         
9424         this.el.removeClass([this.invalidClass, this.validClass]);
9425         
9426         var feedback = this.el.select('.form-control-feedback', true).first();
9427             
9428         if(feedback){
9429             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9430         }
9431
9432         if(this.disabled){
9433             return;
9434         }
9435         
9436         if(this.allowBlank && !this.getRawValue().length){
9437             return;
9438         }
9439         
9440         if(this.indicator){
9441             this.indicator.removeClass('invisible');
9442             this.indicator.addClass('visible');
9443         }
9444         
9445         this.el.addClass(this.invalidClass);
9446         
9447         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9448             
9449             var feedback = this.el.select('.form-control-feedback', true).first();
9450             
9451             if(feedback){
9452                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9453                 
9454                 if(this.getValue().length || this.forceFeedback){
9455                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9456                 }
9457                 
9458             }
9459             
9460         }
9461         
9462         this.fireEvent('invalid', this, msg);
9463     },
9464     // private
9465     SafariOnKeyDown : function(event)
9466     {
9467         // this is a workaround for a password hang bug on chrome/ webkit.
9468         if (this.inputEl().dom.type != 'password') {
9469             return;
9470         }
9471         
9472         var isSelectAll = false;
9473         
9474         if(this.inputEl().dom.selectionEnd > 0){
9475             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9476         }
9477         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9478             event.preventDefault();
9479             this.setValue('');
9480             return;
9481         }
9482         
9483         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9484             
9485             event.preventDefault();
9486             // this is very hacky as keydown always get's upper case.
9487             //
9488             var cc = String.fromCharCode(event.getCharCode());
9489             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9490             
9491         }
9492     },
9493     adjustWidth : function(tag, w){
9494         tag = tag.toLowerCase();
9495         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9496             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9497                 if(tag == 'input'){
9498                     return w + 2;
9499                 }
9500                 if(tag == 'textarea'){
9501                     return w-2;
9502                 }
9503             }else if(Roo.isOpera){
9504                 if(tag == 'input'){
9505                     return w + 2;
9506                 }
9507                 if(tag == 'textarea'){
9508                     return w-2;
9509                 }
9510             }
9511         }
9512         return w;
9513     },
9514     
9515     setFieldLabel : function(v)
9516     {
9517         if(!this.rendered){
9518             return;
9519         }
9520         
9521         if(this.indicator){
9522             var ar = this.el.select('label > span',true);
9523             
9524             if (ar.elements.length) {
9525                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9526                 this.fieldLabel = v;
9527                 return;
9528             }
9529             
9530             var br = this.el.select('label',true);
9531             
9532             if(br.elements.length) {
9533                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9534                 this.fieldLabel = v;
9535                 return;
9536             }
9537             
9538             Roo.log('Cannot Found any of label > span || label in input');
9539             return;
9540         }
9541         
9542         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9543         this.fieldLabel = v;
9544         
9545         
9546     }
9547 });
9548
9549  
9550 /*
9551  * - LGPL
9552  *
9553  * Input
9554  * 
9555  */
9556
9557 /**
9558  * @class Roo.bootstrap.TextArea
9559  * @extends Roo.bootstrap.Input
9560  * Bootstrap TextArea class
9561  * @cfg {Number} cols Specifies the visible width of a text area
9562  * @cfg {Number} rows Specifies the visible number of lines in a text area
9563  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9564  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9565  * @cfg {string} html text
9566  * 
9567  * @constructor
9568  * Create a new TextArea
9569  * @param {Object} config The config object
9570  */
9571
9572 Roo.bootstrap.TextArea = function(config){
9573     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9574    
9575 };
9576
9577 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9578      
9579     cols : false,
9580     rows : 5,
9581     readOnly : false,
9582     warp : 'soft',
9583     resize : false,
9584     value: false,
9585     html: false,
9586     
9587     getAutoCreate : function(){
9588         
9589         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9590         
9591         var id = Roo.id();
9592         
9593         var cfg = {};
9594         
9595         if(this.inputType != 'hidden'){
9596             cfg.cls = 'form-group' //input-group
9597         }
9598         
9599         var input =  {
9600             tag: 'textarea',
9601             id : id,
9602             warp : this.warp,
9603             rows : this.rows,
9604             value : this.value || '',
9605             html: this.html || '',
9606             cls : 'form-control',
9607             placeholder : this.placeholder || '' 
9608             
9609         };
9610         
9611         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9612             input.maxLength = this.maxLength;
9613         }
9614         
9615         if(this.resize){
9616             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9617         }
9618         
9619         if(this.cols){
9620             input.cols = this.cols;
9621         }
9622         
9623         if (this.readOnly) {
9624             input.readonly = true;
9625         }
9626         
9627         if (this.name) {
9628             input.name = this.name;
9629         }
9630         
9631         if (this.size) {
9632             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9633         }
9634         
9635         var settings=this;
9636         ['xs','sm','md','lg'].map(function(size){
9637             if (settings[size]) {
9638                 cfg.cls += ' col-' + size + '-' + settings[size];
9639             }
9640         });
9641         
9642         var inputblock = input;
9643         
9644         if(this.hasFeedback && !this.allowBlank){
9645             
9646             var feedback = {
9647                 tag: 'span',
9648                 cls: 'glyphicon form-control-feedback'
9649             };
9650
9651             inputblock = {
9652                 cls : 'has-feedback',
9653                 cn :  [
9654                     input,
9655                     feedback
9656                 ] 
9657             };  
9658         }
9659         
9660         
9661         if (this.before || this.after) {
9662             
9663             inputblock = {
9664                 cls : 'input-group',
9665                 cn :  [] 
9666             };
9667             if (this.before) {
9668                 inputblock.cn.push({
9669                     tag :'span',
9670                     cls : 'input-group-addon',
9671                     html : this.before
9672                 });
9673             }
9674             
9675             inputblock.cn.push(input);
9676             
9677             if(this.hasFeedback && !this.allowBlank){
9678                 inputblock.cls += ' has-feedback';
9679                 inputblock.cn.push(feedback);
9680             }
9681             
9682             if (this.after) {
9683                 inputblock.cn.push({
9684                     tag :'span',
9685                     cls : 'input-group-addon',
9686                     html : this.after
9687                 });
9688             }
9689             
9690         }
9691         
9692         if (align ==='left' && this.fieldLabel.length) {
9693             cfg.cn = [
9694                 {
9695                     tag: 'label',
9696                     'for' :  id,
9697                     cls : 'control-label',
9698                     html : this.fieldLabel
9699                 },
9700                 {
9701                     cls : "",
9702                     cn: [
9703                         inputblock
9704                     ]
9705                 }
9706
9707             ];
9708             
9709             if(this.labelWidth > 12){
9710                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9711             }
9712
9713             if(this.labelWidth < 13 && this.labelmd == 0){
9714                 this.labelmd = this.labelWidth;
9715             }
9716
9717             if(this.labellg > 0){
9718                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9719                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9720             }
9721
9722             if(this.labelmd > 0){
9723                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9724                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9725             }
9726
9727             if(this.labelsm > 0){
9728                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9729                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9730             }
9731
9732             if(this.labelxs > 0){
9733                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9734                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9735             }
9736             
9737         } else if ( this.fieldLabel.length) {
9738             cfg.cn = [
9739
9740                {
9741                    tag: 'label',
9742                    //cls : 'input-group-addon',
9743                    html : this.fieldLabel
9744
9745                },
9746
9747                inputblock
9748
9749            ];
9750
9751         } else {
9752
9753             cfg.cn = [
9754
9755                 inputblock
9756
9757             ];
9758                 
9759         }
9760         
9761         if (this.disabled) {
9762             input.disabled=true;
9763         }
9764         
9765         return cfg;
9766         
9767     },
9768     /**
9769      * return the real textarea element.
9770      */
9771     inputEl: function ()
9772     {
9773         return this.el.select('textarea.form-control',true).first();
9774     },
9775     
9776     /**
9777      * Clear any invalid styles/messages for this field
9778      */
9779     clearInvalid : function()
9780     {
9781         
9782         if(!this.el || this.preventMark){ // not rendered
9783             return;
9784         }
9785         
9786         var label = this.el.select('label', true).first();
9787         var icon = this.el.select('i.fa-star', true).first();
9788         
9789         if(label && icon){
9790             icon.remove();
9791         }
9792         
9793         this.el.removeClass(this.invalidClass);
9794         
9795         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9796             
9797             var feedback = this.el.select('.form-control-feedback', true).first();
9798             
9799             if(feedback){
9800                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9801             }
9802             
9803         }
9804         
9805         this.fireEvent('valid', this);
9806     },
9807     
9808      /**
9809      * Mark this field as valid
9810      */
9811     markValid : function()
9812     {
9813         if(!this.el  || this.preventMark){ // not rendered
9814             return;
9815         }
9816         
9817         this.el.removeClass([this.invalidClass, this.validClass]);
9818         
9819         var feedback = this.el.select('.form-control-feedback', true).first();
9820             
9821         if(feedback){
9822             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9823         }
9824
9825         if(this.disabled || this.allowBlank){
9826             return;
9827         }
9828         
9829         var label = this.el.select('label', true).first();
9830         var icon = this.el.select('i.fa-star', true).first();
9831         
9832         if(label && icon){
9833             icon.remove();
9834         }
9835         
9836         this.el.addClass(this.validClass);
9837         
9838         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9839             
9840             var feedback = this.el.select('.form-control-feedback', true).first();
9841             
9842             if(feedback){
9843                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9844                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9845             }
9846             
9847         }
9848         
9849         this.fireEvent('valid', this);
9850     },
9851     
9852      /**
9853      * Mark this field as invalid
9854      * @param {String} msg The validation message
9855      */
9856     markInvalid : function(msg)
9857     {
9858         if(!this.el  || this.preventMark){ // not rendered
9859             return;
9860         }
9861         
9862         this.el.removeClass([this.invalidClass, this.validClass]);
9863         
9864         var feedback = this.el.select('.form-control-feedback', true).first();
9865             
9866         if(feedback){
9867             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9868         }
9869
9870         if(this.disabled || this.allowBlank){
9871             return;
9872         }
9873         
9874         var label = this.el.select('label', true).first();
9875         var icon = this.el.select('i.fa-star', true).first();
9876         
9877         if(!this.getValue().length && label && !icon){
9878             this.el.createChild({
9879                 tag : 'i',
9880                 cls : 'text-danger fa fa-lg fa-star',
9881                 tooltip : 'This field is required',
9882                 style : 'margin-right:5px;'
9883             }, label, true);
9884         }
9885
9886         this.el.addClass(this.invalidClass);
9887         
9888         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9889             
9890             var feedback = this.el.select('.form-control-feedback', true).first();
9891             
9892             if(feedback){
9893                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9894                 
9895                 if(this.getValue().length || this.forceFeedback){
9896                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9897                 }
9898                 
9899             }
9900             
9901         }
9902         
9903         this.fireEvent('invalid', this, msg);
9904     }
9905 });
9906
9907  
9908 /*
9909  * - LGPL
9910  *
9911  * trigger field - base class for combo..
9912  * 
9913  */
9914  
9915 /**
9916  * @class Roo.bootstrap.TriggerField
9917  * @extends Roo.bootstrap.Input
9918  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9919  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9920  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9921  * for which you can provide a custom implementation.  For example:
9922  * <pre><code>
9923 var trigger = new Roo.bootstrap.TriggerField();
9924 trigger.onTriggerClick = myTriggerFn;
9925 trigger.applyTo('my-field');
9926 </code></pre>
9927  *
9928  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9929  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9930  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9931  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9932  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9933
9934  * @constructor
9935  * Create a new TriggerField.
9936  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9937  * to the base TextField)
9938  */
9939 Roo.bootstrap.TriggerField = function(config){
9940     this.mimicing = false;
9941     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9942 };
9943
9944 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9945     /**
9946      * @cfg {String} triggerClass A CSS class to apply to the trigger
9947      */
9948      /**
9949      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9950      */
9951     hideTrigger:false,
9952
9953     /**
9954      * @cfg {Boolean} removable (true|false) special filter default false
9955      */
9956     removable : false,
9957     
9958     /** @cfg {Boolean} grow @hide */
9959     /** @cfg {Number} growMin @hide */
9960     /** @cfg {Number} growMax @hide */
9961
9962     /**
9963      * @hide 
9964      * @method
9965      */
9966     autoSize: Roo.emptyFn,
9967     // private
9968     monitorTab : true,
9969     // private
9970     deferHeight : true,
9971
9972     
9973     actionMode : 'wrap',
9974     
9975     caret : false,
9976     
9977     
9978     getAutoCreate : function(){
9979        
9980         var align = this.labelAlign || this.parentLabelAlign();
9981         
9982         var id = Roo.id();
9983         
9984         var cfg = {
9985             cls: 'form-group' //input-group
9986         };
9987         
9988         
9989         var input =  {
9990             tag: 'input',
9991             id : id,
9992             type : this.inputType,
9993             cls : 'form-control',
9994             autocomplete: 'new-password',
9995             placeholder : this.placeholder || '' 
9996             
9997         };
9998         if (this.name) {
9999             input.name = this.name;
10000         }
10001         if (this.size) {
10002             input.cls += ' input-' + this.size;
10003         }
10004         
10005         if (this.disabled) {
10006             input.disabled=true;
10007         }
10008         
10009         var inputblock = input;
10010         
10011         if(this.hasFeedback && !this.allowBlank){
10012             
10013             var feedback = {
10014                 tag: 'span',
10015                 cls: 'glyphicon form-control-feedback'
10016             };
10017             
10018             if(this.removable && !this.editable && !this.tickable){
10019                 inputblock = {
10020                     cls : 'has-feedback',
10021                     cn :  [
10022                         inputblock,
10023                         {
10024                             tag: 'button',
10025                             html : 'x',
10026                             cls : 'roo-combo-removable-btn close'
10027                         },
10028                         feedback
10029                     ] 
10030                 };
10031             } else {
10032                 inputblock = {
10033                     cls : 'has-feedback',
10034                     cn :  [
10035                         inputblock,
10036                         feedback
10037                     ] 
10038                 };
10039             }
10040
10041         } else {
10042             if(this.removable && !this.editable && !this.tickable){
10043                 inputblock = {
10044                     cls : 'roo-removable',
10045                     cn :  [
10046                         inputblock,
10047                         {
10048                             tag: 'button',
10049                             html : 'x',
10050                             cls : 'roo-combo-removable-btn close'
10051                         }
10052                     ] 
10053                 };
10054             }
10055         }
10056         
10057         if (this.before || this.after) {
10058             
10059             inputblock = {
10060                 cls : 'input-group',
10061                 cn :  [] 
10062             };
10063             if (this.before) {
10064                 inputblock.cn.push({
10065                     tag :'span',
10066                     cls : 'input-group-addon',
10067                     html : this.before
10068                 });
10069             }
10070             
10071             inputblock.cn.push(input);
10072             
10073             if(this.hasFeedback && !this.allowBlank){
10074                 inputblock.cls += ' has-feedback';
10075                 inputblock.cn.push(feedback);
10076             }
10077             
10078             if (this.after) {
10079                 inputblock.cn.push({
10080                     tag :'span',
10081                     cls : 'input-group-addon',
10082                     html : this.after
10083                 });
10084             }
10085             
10086         };
10087         
10088         var box = {
10089             tag: 'div',
10090             cn: [
10091                 {
10092                     tag: 'input',
10093                     type : 'hidden',
10094                     cls: 'form-hidden-field'
10095                 },
10096                 inputblock
10097             ]
10098             
10099         };
10100         
10101         if(this.multiple){
10102             box = {
10103                 tag: 'div',
10104                 cn: [
10105                     {
10106                         tag: 'input',
10107                         type : 'hidden',
10108                         cls: 'form-hidden-field'
10109                     },
10110                     {
10111                         tag: 'ul',
10112                         cls: 'roo-select2-choices',
10113                         cn:[
10114                             {
10115                                 tag: 'li',
10116                                 cls: 'roo-select2-search-field',
10117                                 cn: [
10118
10119                                     inputblock
10120                                 ]
10121                             }
10122                         ]
10123                     }
10124                 ]
10125             }
10126         };
10127         
10128         var combobox = {
10129             cls: 'roo-select2-container input-group',
10130             cn: [
10131                 box
10132 //                {
10133 //                    tag: 'ul',
10134 //                    cls: 'typeahead typeahead-long dropdown-menu',
10135 //                    style: 'display:none'
10136 //                }
10137             ]
10138         };
10139         
10140         if(!this.multiple && this.showToggleBtn){
10141             
10142             var caret = {
10143                         tag: 'span',
10144                         cls: 'caret'
10145              };
10146             if (this.caret != false) {
10147                 caret = {
10148                      tag: 'i',
10149                      cls: 'fa fa-' + this.caret
10150                 };
10151                 
10152             }
10153             
10154             combobox.cn.push({
10155                 tag :'span',
10156                 cls : 'input-group-addon btn dropdown-toggle',
10157                 cn : [
10158                     caret,
10159                     {
10160                         tag: 'span',
10161                         cls: 'combobox-clear',
10162                         cn  : [
10163                             {
10164                                 tag : 'i',
10165                                 cls: 'icon-remove'
10166                             }
10167                         ]
10168                     }
10169                 ]
10170
10171             })
10172         }
10173         
10174         if(this.multiple){
10175             combobox.cls += ' roo-select2-container-multi';
10176         }
10177         
10178         if (align ==='left' && this.fieldLabel.length) {
10179             
10180             cfg.cls += ' roo-form-group-label-left';
10181
10182             cfg.cn = [
10183                 {
10184                     tag : 'i',
10185                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10186                     tooltip : 'This field is required'
10187                 },
10188                 {
10189                     tag: 'label',
10190                     'for' :  id,
10191                     cls : 'control-label',
10192                     html : this.fieldLabel
10193
10194                 },
10195                 {
10196                     cls : "", 
10197                     cn: [
10198                         combobox
10199                     ]
10200                 }
10201
10202             ];
10203             
10204             var labelCfg = cfg.cn[1];
10205             var contentCfg = cfg.cn[2];
10206             
10207             if(this.indicatorpos == 'right'){
10208                 cfg.cn = [
10209                     {
10210                         tag: 'label',
10211                         'for' :  id,
10212                         cls : 'control-label',
10213                         cn : [
10214                             {
10215                                 tag : 'span',
10216                                 html : this.fieldLabel
10217                             },
10218                             {
10219                                 tag : 'i',
10220                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10221                                 tooltip : 'This field is required'
10222                             }
10223                         ]
10224                     },
10225                     {
10226                         cls : "", 
10227                         cn: [
10228                             combobox
10229                         ]
10230                     }
10231
10232                 ];
10233                 
10234                 labelCfg = cfg.cn[0];
10235                 contentCfg = cfg.cn[1];
10236             }
10237             
10238             if(this.labelWidth > 12){
10239                 labelCfg.style = "width: " + this.labelWidth + 'px';
10240             }
10241             
10242             if(this.labelWidth < 13 && this.labelmd == 0){
10243                 this.labelmd = this.labelWidth;
10244             }
10245             
10246             if(this.labellg > 0){
10247                 labelCfg.cls += ' col-lg-' + this.labellg;
10248                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10249             }
10250             
10251             if(this.labelmd > 0){
10252                 labelCfg.cls += ' col-md-' + this.labelmd;
10253                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10254             }
10255             
10256             if(this.labelsm > 0){
10257                 labelCfg.cls += ' col-sm-' + this.labelsm;
10258                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10259             }
10260             
10261             if(this.labelxs > 0){
10262                 labelCfg.cls += ' col-xs-' + this.labelxs;
10263                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10264             }
10265             
10266         } else if ( this.fieldLabel.length) {
10267 //                Roo.log(" label");
10268             cfg.cn = [
10269                 {
10270                    tag : 'i',
10271                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10272                    tooltip : 'This field is required'
10273                },
10274                {
10275                    tag: 'label',
10276                    //cls : 'input-group-addon',
10277                    html : this.fieldLabel
10278
10279                },
10280
10281                combobox
10282
10283             ];
10284             
10285             if(this.indicatorpos == 'right'){
10286                 
10287                 cfg.cn = [
10288                     {
10289                        tag: 'label',
10290                        cn : [
10291                            {
10292                                tag : 'span',
10293                                html : this.fieldLabel
10294                            },
10295                            {
10296                               tag : 'i',
10297                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10298                               tooltip : 'This field is required'
10299                            }
10300                        ]
10301
10302                     },
10303                     combobox
10304
10305                 ];
10306
10307             }
10308
10309         } else {
10310             
10311 //                Roo.log(" no label && no align");
10312                 cfg = combobox
10313                      
10314                 
10315         }
10316         
10317         var settings=this;
10318         ['xs','sm','md','lg'].map(function(size){
10319             if (settings[size]) {
10320                 cfg.cls += ' col-' + size + '-' + settings[size];
10321             }
10322         });
10323         
10324         return cfg;
10325         
10326     },
10327     
10328     
10329     
10330     // private
10331     onResize : function(w, h){
10332 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10333 //        if(typeof w == 'number'){
10334 //            var x = w - this.trigger.getWidth();
10335 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10336 //            this.trigger.setStyle('left', x+'px');
10337 //        }
10338     },
10339
10340     // private
10341     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10342
10343     // private
10344     getResizeEl : function(){
10345         return this.inputEl();
10346     },
10347
10348     // private
10349     getPositionEl : function(){
10350         return this.inputEl();
10351     },
10352
10353     // private
10354     alignErrorIcon : function(){
10355         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10356     },
10357
10358     // private
10359     initEvents : function(){
10360         
10361         this.createList();
10362         
10363         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10364         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10365         if(!this.multiple && this.showToggleBtn){
10366             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10367             if(this.hideTrigger){
10368                 this.trigger.setDisplayed(false);
10369             }
10370             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10371         }
10372         
10373         if(this.multiple){
10374             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10375         }
10376         
10377         if(this.removable && !this.editable && !this.tickable){
10378             var close = this.closeTriggerEl();
10379             
10380             if(close){
10381                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10382                 close.on('click', this.removeBtnClick, this, close);
10383             }
10384         }
10385         
10386         //this.trigger.addClassOnOver('x-form-trigger-over');
10387         //this.trigger.addClassOnClick('x-form-trigger-click');
10388         
10389         //if(!this.width){
10390         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10391         //}
10392     },
10393     
10394     closeTriggerEl : function()
10395     {
10396         var close = this.el.select('.roo-combo-removable-btn', true).first();
10397         return close ? close : false;
10398     },
10399     
10400     removeBtnClick : function(e, h, el)
10401     {
10402         e.preventDefault();
10403         
10404         if(this.fireEvent("remove", this) !== false){
10405             this.reset();
10406             this.fireEvent("afterremove", this)
10407         }
10408     },
10409     
10410     createList : function()
10411     {
10412         this.list = Roo.get(document.body).createChild({
10413             tag: 'ul',
10414             cls: 'typeahead typeahead-long dropdown-menu',
10415             style: 'display:none'
10416         });
10417         
10418         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10419         
10420     },
10421
10422     // private
10423     initTrigger : function(){
10424        
10425     },
10426
10427     // private
10428     onDestroy : function(){
10429         if(this.trigger){
10430             this.trigger.removeAllListeners();
10431           //  this.trigger.remove();
10432         }
10433         //if(this.wrap){
10434         //    this.wrap.remove();
10435         //}
10436         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10437     },
10438
10439     // private
10440     onFocus : function(){
10441         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10442         /*
10443         if(!this.mimicing){
10444             this.wrap.addClass('x-trigger-wrap-focus');
10445             this.mimicing = true;
10446             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10447             if(this.monitorTab){
10448                 this.el.on("keydown", this.checkTab, this);
10449             }
10450         }
10451         */
10452     },
10453
10454     // private
10455     checkTab : function(e){
10456         if(e.getKey() == e.TAB){
10457             this.triggerBlur();
10458         }
10459     },
10460
10461     // private
10462     onBlur : function(){
10463         // do nothing
10464     },
10465
10466     // private
10467     mimicBlur : function(e, t){
10468         /*
10469         if(!this.wrap.contains(t) && this.validateBlur()){
10470             this.triggerBlur();
10471         }
10472         */
10473     },
10474
10475     // private
10476     triggerBlur : function(){
10477         this.mimicing = false;
10478         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10479         if(this.monitorTab){
10480             this.el.un("keydown", this.checkTab, this);
10481         }
10482         //this.wrap.removeClass('x-trigger-wrap-focus');
10483         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10484     },
10485
10486     // private
10487     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10488     validateBlur : function(e, t){
10489         return true;
10490     },
10491
10492     // private
10493     onDisable : function(){
10494         this.inputEl().dom.disabled = true;
10495         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10496         //if(this.wrap){
10497         //    this.wrap.addClass('x-item-disabled');
10498         //}
10499     },
10500
10501     // private
10502     onEnable : function(){
10503         this.inputEl().dom.disabled = false;
10504         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10505         //if(this.wrap){
10506         //    this.el.removeClass('x-item-disabled');
10507         //}
10508     },
10509
10510     // private
10511     onShow : function(){
10512         var ae = this.getActionEl();
10513         
10514         if(ae){
10515             ae.dom.style.display = '';
10516             ae.dom.style.visibility = 'visible';
10517         }
10518     },
10519
10520     // private
10521     
10522     onHide : function(){
10523         var ae = this.getActionEl();
10524         ae.dom.style.display = 'none';
10525     },
10526
10527     /**
10528      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10529      * by an implementing function.
10530      * @method
10531      * @param {EventObject} e
10532      */
10533     onTriggerClick : Roo.emptyFn
10534 });
10535  /*
10536  * Based on:
10537  * Ext JS Library 1.1.1
10538  * Copyright(c) 2006-2007, Ext JS, LLC.
10539  *
10540  * Originally Released Under LGPL - original licence link has changed is not relivant.
10541  *
10542  * Fork - LGPL
10543  * <script type="text/javascript">
10544  */
10545
10546
10547 /**
10548  * @class Roo.data.SortTypes
10549  * @singleton
10550  * Defines the default sorting (casting?) comparison functions used when sorting data.
10551  */
10552 Roo.data.SortTypes = {
10553     /**
10554      * Default sort that does nothing
10555      * @param {Mixed} s The value being converted
10556      * @return {Mixed} The comparison value
10557      */
10558     none : function(s){
10559         return s;
10560     },
10561     
10562     /**
10563      * The regular expression used to strip tags
10564      * @type {RegExp}
10565      * @property
10566      */
10567     stripTagsRE : /<\/?[^>]+>/gi,
10568     
10569     /**
10570      * Strips all HTML tags to sort on text only
10571      * @param {Mixed} s The value being converted
10572      * @return {String} The comparison value
10573      */
10574     asText : function(s){
10575         return String(s).replace(this.stripTagsRE, "");
10576     },
10577     
10578     /**
10579      * Strips all HTML tags to sort on text only - Case insensitive
10580      * @param {Mixed} s The value being converted
10581      * @return {String} The comparison value
10582      */
10583     asUCText : function(s){
10584         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10585     },
10586     
10587     /**
10588      * Case insensitive string
10589      * @param {Mixed} s The value being converted
10590      * @return {String} The comparison value
10591      */
10592     asUCString : function(s) {
10593         return String(s).toUpperCase();
10594     },
10595     
10596     /**
10597      * Date sorting
10598      * @param {Mixed} s The value being converted
10599      * @return {Number} The comparison value
10600      */
10601     asDate : function(s) {
10602         if(!s){
10603             return 0;
10604         }
10605         if(s instanceof Date){
10606             return s.getTime();
10607         }
10608         return Date.parse(String(s));
10609     },
10610     
10611     /**
10612      * Float sorting
10613      * @param {Mixed} s The value being converted
10614      * @return {Float} The comparison value
10615      */
10616     asFloat : function(s) {
10617         var val = parseFloat(String(s).replace(/,/g, ""));
10618         if(isNaN(val)) {
10619             val = 0;
10620         }
10621         return val;
10622     },
10623     
10624     /**
10625      * Integer sorting
10626      * @param {Mixed} s The value being converted
10627      * @return {Number} The comparison value
10628      */
10629     asInt : function(s) {
10630         var val = parseInt(String(s).replace(/,/g, ""));
10631         if(isNaN(val)) {
10632             val = 0;
10633         }
10634         return val;
10635     }
10636 };/*
10637  * Based on:
10638  * Ext JS Library 1.1.1
10639  * Copyright(c) 2006-2007, Ext JS, LLC.
10640  *
10641  * Originally Released Under LGPL - original licence link has changed is not relivant.
10642  *
10643  * Fork - LGPL
10644  * <script type="text/javascript">
10645  */
10646
10647 /**
10648 * @class Roo.data.Record
10649  * Instances of this class encapsulate both record <em>definition</em> information, and record
10650  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10651  * to access Records cached in an {@link Roo.data.Store} object.<br>
10652  * <p>
10653  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10654  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10655  * objects.<br>
10656  * <p>
10657  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10658  * @constructor
10659  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10660  * {@link #create}. The parameters are the same.
10661  * @param {Array} data An associative Array of data values keyed by the field name.
10662  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10663  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10664  * not specified an integer id is generated.
10665  */
10666 Roo.data.Record = function(data, id){
10667     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10668     this.data = data;
10669 };
10670
10671 /**
10672  * Generate a constructor for a specific record layout.
10673  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10674  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10675  * Each field definition object may contain the following properties: <ul>
10676  * <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,
10677  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10678  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10679  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10680  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10681  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10682  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10683  * this may be omitted.</p></li>
10684  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10685  * <ul><li>auto (Default, implies no conversion)</li>
10686  * <li>string</li>
10687  * <li>int</li>
10688  * <li>float</li>
10689  * <li>boolean</li>
10690  * <li>date</li></ul></p></li>
10691  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10692  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10693  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10694  * by the Reader into an object that will be stored in the Record. It is passed the
10695  * following parameters:<ul>
10696  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10697  * </ul></p></li>
10698  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10699  * </ul>
10700  * <br>usage:<br><pre><code>
10701 var TopicRecord = Roo.data.Record.create(
10702     {name: 'title', mapping: 'topic_title'},
10703     {name: 'author', mapping: 'username'},
10704     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10705     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10706     {name: 'lastPoster', mapping: 'user2'},
10707     {name: 'excerpt', mapping: 'post_text'}
10708 );
10709
10710 var myNewRecord = new TopicRecord({
10711     title: 'Do my job please',
10712     author: 'noobie',
10713     totalPosts: 1,
10714     lastPost: new Date(),
10715     lastPoster: 'Animal',
10716     excerpt: 'No way dude!'
10717 });
10718 myStore.add(myNewRecord);
10719 </code></pre>
10720  * @method create
10721  * @static
10722  */
10723 Roo.data.Record.create = function(o){
10724     var f = function(){
10725         f.superclass.constructor.apply(this, arguments);
10726     };
10727     Roo.extend(f, Roo.data.Record);
10728     var p = f.prototype;
10729     p.fields = new Roo.util.MixedCollection(false, function(field){
10730         return field.name;
10731     });
10732     for(var i = 0, len = o.length; i < len; i++){
10733         p.fields.add(new Roo.data.Field(o[i]));
10734     }
10735     f.getField = function(name){
10736         return p.fields.get(name);  
10737     };
10738     return f;
10739 };
10740
10741 Roo.data.Record.AUTO_ID = 1000;
10742 Roo.data.Record.EDIT = 'edit';
10743 Roo.data.Record.REJECT = 'reject';
10744 Roo.data.Record.COMMIT = 'commit';
10745
10746 Roo.data.Record.prototype = {
10747     /**
10748      * Readonly flag - true if this record has been modified.
10749      * @type Boolean
10750      */
10751     dirty : false,
10752     editing : false,
10753     error: null,
10754     modified: null,
10755
10756     // private
10757     join : function(store){
10758         this.store = store;
10759     },
10760
10761     /**
10762      * Set the named field to the specified value.
10763      * @param {String} name The name of the field to set.
10764      * @param {Object} value The value to set the field to.
10765      */
10766     set : function(name, value){
10767         if(this.data[name] == value){
10768             return;
10769         }
10770         this.dirty = true;
10771         if(!this.modified){
10772             this.modified = {};
10773         }
10774         if(typeof this.modified[name] == 'undefined'){
10775             this.modified[name] = this.data[name];
10776         }
10777         this.data[name] = value;
10778         if(!this.editing && this.store){
10779             this.store.afterEdit(this);
10780         }       
10781     },
10782
10783     /**
10784      * Get the value of the named field.
10785      * @param {String} name The name of the field to get the value of.
10786      * @return {Object} The value of the field.
10787      */
10788     get : function(name){
10789         return this.data[name]; 
10790     },
10791
10792     // private
10793     beginEdit : function(){
10794         this.editing = true;
10795         this.modified = {}; 
10796     },
10797
10798     // private
10799     cancelEdit : function(){
10800         this.editing = false;
10801         delete this.modified;
10802     },
10803
10804     // private
10805     endEdit : function(){
10806         this.editing = false;
10807         if(this.dirty && this.store){
10808             this.store.afterEdit(this);
10809         }
10810     },
10811
10812     /**
10813      * Usually called by the {@link Roo.data.Store} which owns the Record.
10814      * Rejects all changes made to the Record since either creation, or the last commit operation.
10815      * Modified fields are reverted to their original values.
10816      * <p>
10817      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10818      * of reject operations.
10819      */
10820     reject : function(){
10821         var m = this.modified;
10822         for(var n in m){
10823             if(typeof m[n] != "function"){
10824                 this.data[n] = m[n];
10825             }
10826         }
10827         this.dirty = false;
10828         delete this.modified;
10829         this.editing = false;
10830         if(this.store){
10831             this.store.afterReject(this);
10832         }
10833     },
10834
10835     /**
10836      * Usually called by the {@link Roo.data.Store} which owns the Record.
10837      * Commits all changes made to the Record since either creation, or the last commit operation.
10838      * <p>
10839      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10840      * of commit operations.
10841      */
10842     commit : function(){
10843         this.dirty = false;
10844         delete this.modified;
10845         this.editing = false;
10846         if(this.store){
10847             this.store.afterCommit(this);
10848         }
10849     },
10850
10851     // private
10852     hasError : function(){
10853         return this.error != null;
10854     },
10855
10856     // private
10857     clearError : function(){
10858         this.error = null;
10859     },
10860
10861     /**
10862      * Creates a copy of this record.
10863      * @param {String} id (optional) A new record id if you don't want to use this record's id
10864      * @return {Record}
10865      */
10866     copy : function(newId) {
10867         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10868     }
10869 };/*
10870  * Based on:
10871  * Ext JS Library 1.1.1
10872  * Copyright(c) 2006-2007, Ext JS, LLC.
10873  *
10874  * Originally Released Under LGPL - original licence link has changed is not relivant.
10875  *
10876  * Fork - LGPL
10877  * <script type="text/javascript">
10878  */
10879
10880
10881
10882 /**
10883  * @class Roo.data.Store
10884  * @extends Roo.util.Observable
10885  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10886  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10887  * <p>
10888  * 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
10889  * has no knowledge of the format of the data returned by the Proxy.<br>
10890  * <p>
10891  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10892  * instances from the data object. These records are cached and made available through accessor functions.
10893  * @constructor
10894  * Creates a new Store.
10895  * @param {Object} config A config object containing the objects needed for the Store to access data,
10896  * and read the data into Records.
10897  */
10898 Roo.data.Store = function(config){
10899     this.data = new Roo.util.MixedCollection(false);
10900     this.data.getKey = function(o){
10901         return o.id;
10902     };
10903     this.baseParams = {};
10904     // private
10905     this.paramNames = {
10906         "start" : "start",
10907         "limit" : "limit",
10908         "sort" : "sort",
10909         "dir" : "dir",
10910         "multisort" : "_multisort"
10911     };
10912
10913     if(config && config.data){
10914         this.inlineData = config.data;
10915         delete config.data;
10916     }
10917
10918     Roo.apply(this, config);
10919     
10920     if(this.reader){ // reader passed
10921         this.reader = Roo.factory(this.reader, Roo.data);
10922         this.reader.xmodule = this.xmodule || false;
10923         if(!this.recordType){
10924             this.recordType = this.reader.recordType;
10925         }
10926         if(this.reader.onMetaChange){
10927             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10928         }
10929     }
10930
10931     if(this.recordType){
10932         this.fields = this.recordType.prototype.fields;
10933     }
10934     this.modified = [];
10935
10936     this.addEvents({
10937         /**
10938          * @event datachanged
10939          * Fires when the data cache has changed, and a widget which is using this Store
10940          * as a Record cache should refresh its view.
10941          * @param {Store} this
10942          */
10943         datachanged : true,
10944         /**
10945          * @event metachange
10946          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10947          * @param {Store} this
10948          * @param {Object} meta The JSON metadata
10949          */
10950         metachange : true,
10951         /**
10952          * @event add
10953          * Fires when Records have been added to the Store
10954          * @param {Store} this
10955          * @param {Roo.data.Record[]} records The array of Records added
10956          * @param {Number} index The index at which the record(s) were added
10957          */
10958         add : true,
10959         /**
10960          * @event remove
10961          * Fires when a Record has been removed from the Store
10962          * @param {Store} this
10963          * @param {Roo.data.Record} record The Record that was removed
10964          * @param {Number} index The index at which the record was removed
10965          */
10966         remove : true,
10967         /**
10968          * @event update
10969          * Fires when a Record has been updated
10970          * @param {Store} this
10971          * @param {Roo.data.Record} record The Record that was updated
10972          * @param {String} operation The update operation being performed.  Value may be one of:
10973          * <pre><code>
10974  Roo.data.Record.EDIT
10975  Roo.data.Record.REJECT
10976  Roo.data.Record.COMMIT
10977          * </code></pre>
10978          */
10979         update : true,
10980         /**
10981          * @event clear
10982          * Fires when the data cache has been cleared.
10983          * @param {Store} this
10984          */
10985         clear : true,
10986         /**
10987          * @event beforeload
10988          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10989          * the load action will be canceled.
10990          * @param {Store} this
10991          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10992          */
10993         beforeload : true,
10994         /**
10995          * @event beforeloadadd
10996          * Fires after a new set of Records has been loaded.
10997          * @param {Store} this
10998          * @param {Roo.data.Record[]} records The Records that were loaded
10999          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11000          */
11001         beforeloadadd : true,
11002         /**
11003          * @event load
11004          * Fires after a new set of Records has been loaded, before they are added to the store.
11005          * @param {Store} this
11006          * @param {Roo.data.Record[]} records The Records that were loaded
11007          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11008          * @params {Object} return from reader
11009          */
11010         load : true,
11011         /**
11012          * @event loadexception
11013          * Fires if an exception occurs in the Proxy during loading.
11014          * Called with the signature of the Proxy's "loadexception" event.
11015          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11016          * 
11017          * @param {Proxy} 
11018          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11019          * @param {Object} load options 
11020          * @param {Object} jsonData from your request (normally this contains the Exception)
11021          */
11022         loadexception : true
11023     });
11024     
11025     if(this.proxy){
11026         this.proxy = Roo.factory(this.proxy, Roo.data);
11027         this.proxy.xmodule = this.xmodule || false;
11028         this.relayEvents(this.proxy,  ["loadexception"]);
11029     }
11030     this.sortToggle = {};
11031     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11032
11033     Roo.data.Store.superclass.constructor.call(this);
11034
11035     if(this.inlineData){
11036         this.loadData(this.inlineData);
11037         delete this.inlineData;
11038     }
11039 };
11040
11041 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11042      /**
11043     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11044     * without a remote query - used by combo/forms at present.
11045     */
11046     
11047     /**
11048     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11049     */
11050     /**
11051     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11052     */
11053     /**
11054     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11055     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11056     */
11057     /**
11058     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11059     * on any HTTP request
11060     */
11061     /**
11062     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11063     */
11064     /**
11065     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11066     */
11067     multiSort: false,
11068     /**
11069     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11070     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11071     */
11072     remoteSort : false,
11073
11074     /**
11075     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11076      * loaded or when a record is removed. (defaults to false).
11077     */
11078     pruneModifiedRecords : false,
11079
11080     // private
11081     lastOptions : null,
11082
11083     /**
11084      * Add Records to the Store and fires the add event.
11085      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11086      */
11087     add : function(records){
11088         records = [].concat(records);
11089         for(var i = 0, len = records.length; i < len; i++){
11090             records[i].join(this);
11091         }
11092         var index = this.data.length;
11093         this.data.addAll(records);
11094         this.fireEvent("add", this, records, index);
11095     },
11096
11097     /**
11098      * Remove a Record from the Store and fires the remove event.
11099      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11100      */
11101     remove : function(record){
11102         var index = this.data.indexOf(record);
11103         this.data.removeAt(index);
11104         if(this.pruneModifiedRecords){
11105             this.modified.remove(record);
11106         }
11107         this.fireEvent("remove", this, record, index);
11108     },
11109
11110     /**
11111      * Remove all Records from the Store and fires the clear event.
11112      */
11113     removeAll : function(){
11114         this.data.clear();
11115         if(this.pruneModifiedRecords){
11116             this.modified = [];
11117         }
11118         this.fireEvent("clear", this);
11119     },
11120
11121     /**
11122      * Inserts Records to the Store at the given index and fires the add event.
11123      * @param {Number} index The start index at which to insert the passed Records.
11124      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11125      */
11126     insert : function(index, records){
11127         records = [].concat(records);
11128         for(var i = 0, len = records.length; i < len; i++){
11129             this.data.insert(index, records[i]);
11130             records[i].join(this);
11131         }
11132         this.fireEvent("add", this, records, index);
11133     },
11134
11135     /**
11136      * Get the index within the cache of the passed Record.
11137      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11138      * @return {Number} The index of the passed Record. Returns -1 if not found.
11139      */
11140     indexOf : function(record){
11141         return this.data.indexOf(record);
11142     },
11143
11144     /**
11145      * Get the index within the cache of the Record with the passed id.
11146      * @param {String} id The id of the Record to find.
11147      * @return {Number} The index of the Record. Returns -1 if not found.
11148      */
11149     indexOfId : function(id){
11150         return this.data.indexOfKey(id);
11151     },
11152
11153     /**
11154      * Get the Record with the specified id.
11155      * @param {String} id The id of the Record to find.
11156      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11157      */
11158     getById : function(id){
11159         return this.data.key(id);
11160     },
11161
11162     /**
11163      * Get the Record at the specified index.
11164      * @param {Number} index The index of the Record to find.
11165      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11166      */
11167     getAt : function(index){
11168         return this.data.itemAt(index);
11169     },
11170
11171     /**
11172      * Returns a range of Records between specified indices.
11173      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11174      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11175      * @return {Roo.data.Record[]} An array of Records
11176      */
11177     getRange : function(start, end){
11178         return this.data.getRange(start, end);
11179     },
11180
11181     // private
11182     storeOptions : function(o){
11183         o = Roo.apply({}, o);
11184         delete o.callback;
11185         delete o.scope;
11186         this.lastOptions = o;
11187     },
11188
11189     /**
11190      * Loads the Record cache from the configured Proxy using the configured Reader.
11191      * <p>
11192      * If using remote paging, then the first load call must specify the <em>start</em>
11193      * and <em>limit</em> properties in the options.params property to establish the initial
11194      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11195      * <p>
11196      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11197      * and this call will return before the new data has been loaded. Perform any post-processing
11198      * in a callback function, or in a "load" event handler.</strong>
11199      * <p>
11200      * @param {Object} options An object containing properties which control loading options:<ul>
11201      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11202      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11203      * passed the following arguments:<ul>
11204      * <li>r : Roo.data.Record[]</li>
11205      * <li>options: Options object from the load call</li>
11206      * <li>success: Boolean success indicator</li></ul></li>
11207      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11208      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11209      * </ul>
11210      */
11211     load : function(options){
11212         options = options || {};
11213         if(this.fireEvent("beforeload", this, options) !== false){
11214             this.storeOptions(options);
11215             var p = Roo.apply(options.params || {}, this.baseParams);
11216             // if meta was not loaded from remote source.. try requesting it.
11217             if (!this.reader.metaFromRemote) {
11218                 p._requestMeta = 1;
11219             }
11220             if(this.sortInfo && this.remoteSort){
11221                 var pn = this.paramNames;
11222                 p[pn["sort"]] = this.sortInfo.field;
11223                 p[pn["dir"]] = this.sortInfo.direction;
11224             }
11225             if (this.multiSort) {
11226                 var pn = this.paramNames;
11227                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11228             }
11229             
11230             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11231         }
11232     },
11233
11234     /**
11235      * Reloads the Record cache from the configured Proxy using the configured Reader and
11236      * the options from the last load operation performed.
11237      * @param {Object} options (optional) An object containing properties which may override the options
11238      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11239      * the most recently used options are reused).
11240      */
11241     reload : function(options){
11242         this.load(Roo.applyIf(options||{}, this.lastOptions));
11243     },
11244
11245     // private
11246     // Called as a callback by the Reader during a load operation.
11247     loadRecords : function(o, options, success){
11248         if(!o || success === false){
11249             if(success !== false){
11250                 this.fireEvent("load", this, [], options, o);
11251             }
11252             if(options.callback){
11253                 options.callback.call(options.scope || this, [], options, false);
11254             }
11255             return;
11256         }
11257         // if data returned failure - throw an exception.
11258         if (o.success === false) {
11259             // show a message if no listener is registered.
11260             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11261                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11262             }
11263             // loadmask wil be hooked into this..
11264             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11265             return;
11266         }
11267         var r = o.records, t = o.totalRecords || r.length;
11268         
11269         this.fireEvent("beforeloadadd", this, r, options, o);
11270         
11271         if(!options || options.add !== true){
11272             if(this.pruneModifiedRecords){
11273                 this.modified = [];
11274             }
11275             for(var i = 0, len = r.length; i < len; i++){
11276                 r[i].join(this);
11277             }
11278             if(this.snapshot){
11279                 this.data = this.snapshot;
11280                 delete this.snapshot;
11281             }
11282             this.data.clear();
11283             this.data.addAll(r);
11284             this.totalLength = t;
11285             this.applySort();
11286             this.fireEvent("datachanged", this);
11287         }else{
11288             this.totalLength = Math.max(t, this.data.length+r.length);
11289             this.add(r);
11290         }
11291         
11292         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11293                 
11294             var e = new Roo.data.Record({});
11295
11296             e.set(this.parent.displayField, this.parent.emptyTitle);
11297             e.set(this.parent.valueField, '');
11298
11299             this.insert(0, e);
11300         }
11301             
11302         this.fireEvent("load", this, r, options, o);
11303         if(options.callback){
11304             options.callback.call(options.scope || this, r, options, true);
11305         }
11306     },
11307
11308
11309     /**
11310      * Loads data from a passed data block. A Reader which understands the format of the data
11311      * must have been configured in the constructor.
11312      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11313      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11314      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11315      */
11316     loadData : function(o, append){
11317         var r = this.reader.readRecords(o);
11318         this.loadRecords(r, {add: append}, true);
11319     },
11320
11321     /**
11322      * Gets the number of cached records.
11323      * <p>
11324      * <em>If using paging, this may not be the total size of the dataset. If the data object
11325      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11326      * the data set size</em>
11327      */
11328     getCount : function(){
11329         return this.data.length || 0;
11330     },
11331
11332     /**
11333      * Gets the total number of records in the dataset as returned by the server.
11334      * <p>
11335      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11336      * the dataset size</em>
11337      */
11338     getTotalCount : function(){
11339         return this.totalLength || 0;
11340     },
11341
11342     /**
11343      * Returns the sort state of the Store as an object with two properties:
11344      * <pre><code>
11345  field {String} The name of the field by which the Records are sorted
11346  direction {String} The sort order, "ASC" or "DESC"
11347      * </code></pre>
11348      */
11349     getSortState : function(){
11350         return this.sortInfo;
11351     },
11352
11353     // private
11354     applySort : function(){
11355         if(this.sortInfo && !this.remoteSort){
11356             var s = this.sortInfo, f = s.field;
11357             var st = this.fields.get(f).sortType;
11358             var fn = function(r1, r2){
11359                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11360                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11361             };
11362             this.data.sort(s.direction, fn);
11363             if(this.snapshot && this.snapshot != this.data){
11364                 this.snapshot.sort(s.direction, fn);
11365             }
11366         }
11367     },
11368
11369     /**
11370      * Sets the default sort column and order to be used by the next load operation.
11371      * @param {String} fieldName The name of the field to sort by.
11372      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11373      */
11374     setDefaultSort : function(field, dir){
11375         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11376     },
11377
11378     /**
11379      * Sort the Records.
11380      * If remote sorting is used, the sort is performed on the server, and the cache is
11381      * reloaded. If local sorting is used, the cache is sorted internally.
11382      * @param {String} fieldName The name of the field to sort by.
11383      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11384      */
11385     sort : function(fieldName, dir){
11386         var f = this.fields.get(fieldName);
11387         if(!dir){
11388             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11389             
11390             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11391                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11392             }else{
11393                 dir = f.sortDir;
11394             }
11395         }
11396         this.sortToggle[f.name] = dir;
11397         this.sortInfo = {field: f.name, direction: dir};
11398         if(!this.remoteSort){
11399             this.applySort();
11400             this.fireEvent("datachanged", this);
11401         }else{
11402             this.load(this.lastOptions);
11403         }
11404     },
11405
11406     /**
11407      * Calls the specified function for each of the Records in the cache.
11408      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11409      * Returning <em>false</em> aborts and exits the iteration.
11410      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11411      */
11412     each : function(fn, scope){
11413         this.data.each(fn, scope);
11414     },
11415
11416     /**
11417      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11418      * (e.g., during paging).
11419      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11420      */
11421     getModifiedRecords : function(){
11422         return this.modified;
11423     },
11424
11425     // private
11426     createFilterFn : function(property, value, anyMatch){
11427         if(!value.exec){ // not a regex
11428             value = String(value);
11429             if(value.length == 0){
11430                 return false;
11431             }
11432             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11433         }
11434         return function(r){
11435             return value.test(r.data[property]);
11436         };
11437     },
11438
11439     /**
11440      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11441      * @param {String} property A field on your records
11442      * @param {Number} start The record index to start at (defaults to 0)
11443      * @param {Number} end The last record index to include (defaults to length - 1)
11444      * @return {Number} The sum
11445      */
11446     sum : function(property, start, end){
11447         var rs = this.data.items, v = 0;
11448         start = start || 0;
11449         end = (end || end === 0) ? end : rs.length-1;
11450
11451         for(var i = start; i <= end; i++){
11452             v += (rs[i].data[property] || 0);
11453         }
11454         return v;
11455     },
11456
11457     /**
11458      * Filter the records by a specified property.
11459      * @param {String} field A field on your records
11460      * @param {String/RegExp} value Either a string that the field
11461      * should start with or a RegExp to test against the field
11462      * @param {Boolean} anyMatch True to match any part not just the beginning
11463      */
11464     filter : function(property, value, anyMatch){
11465         var fn = this.createFilterFn(property, value, anyMatch);
11466         return fn ? this.filterBy(fn) : this.clearFilter();
11467     },
11468
11469     /**
11470      * Filter by a function. The specified function will be called with each
11471      * record in this data source. If the function returns true the record is included,
11472      * otherwise it is filtered.
11473      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11474      * @param {Object} scope (optional) The scope of the function (defaults to this)
11475      */
11476     filterBy : function(fn, scope){
11477         this.snapshot = this.snapshot || this.data;
11478         this.data = this.queryBy(fn, scope||this);
11479         this.fireEvent("datachanged", this);
11480     },
11481
11482     /**
11483      * Query the records by a specified property.
11484      * @param {String} field A field on your records
11485      * @param {String/RegExp} value Either a string that the field
11486      * should start with or a RegExp to test against the field
11487      * @param {Boolean} anyMatch True to match any part not just the beginning
11488      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11489      */
11490     query : function(property, value, anyMatch){
11491         var fn = this.createFilterFn(property, value, anyMatch);
11492         return fn ? this.queryBy(fn) : this.data.clone();
11493     },
11494
11495     /**
11496      * Query by a function. The specified function will be called with each
11497      * record in this data source. If the function returns true the record is included
11498      * in the results.
11499      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11500      * @param {Object} scope (optional) The scope of the function (defaults to this)
11501       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11502      **/
11503     queryBy : function(fn, scope){
11504         var data = this.snapshot || this.data;
11505         return data.filterBy(fn, scope||this);
11506     },
11507
11508     /**
11509      * Collects unique values for a particular dataIndex from this store.
11510      * @param {String} dataIndex The property to collect
11511      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11512      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11513      * @return {Array} An array of the unique values
11514      **/
11515     collect : function(dataIndex, allowNull, bypassFilter){
11516         var d = (bypassFilter === true && this.snapshot) ?
11517                 this.snapshot.items : this.data.items;
11518         var v, sv, r = [], l = {};
11519         for(var i = 0, len = d.length; i < len; i++){
11520             v = d[i].data[dataIndex];
11521             sv = String(v);
11522             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11523                 l[sv] = true;
11524                 r[r.length] = v;
11525             }
11526         }
11527         return r;
11528     },
11529
11530     /**
11531      * Revert to a view of the Record cache with no filtering applied.
11532      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11533      */
11534     clearFilter : function(suppressEvent){
11535         if(this.snapshot && this.snapshot != this.data){
11536             this.data = this.snapshot;
11537             delete this.snapshot;
11538             if(suppressEvent !== true){
11539                 this.fireEvent("datachanged", this);
11540             }
11541         }
11542     },
11543
11544     // private
11545     afterEdit : function(record){
11546         if(this.modified.indexOf(record) == -1){
11547             this.modified.push(record);
11548         }
11549         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11550     },
11551     
11552     // private
11553     afterReject : function(record){
11554         this.modified.remove(record);
11555         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11556     },
11557
11558     // private
11559     afterCommit : function(record){
11560         this.modified.remove(record);
11561         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11562     },
11563
11564     /**
11565      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11566      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11567      */
11568     commitChanges : function(){
11569         var m = this.modified.slice(0);
11570         this.modified = [];
11571         for(var i = 0, len = m.length; i < len; i++){
11572             m[i].commit();
11573         }
11574     },
11575
11576     /**
11577      * Cancel outstanding changes on all changed records.
11578      */
11579     rejectChanges : function(){
11580         var m = this.modified.slice(0);
11581         this.modified = [];
11582         for(var i = 0, len = m.length; i < len; i++){
11583             m[i].reject();
11584         }
11585     },
11586
11587     onMetaChange : function(meta, rtype, o){
11588         this.recordType = rtype;
11589         this.fields = rtype.prototype.fields;
11590         delete this.snapshot;
11591         this.sortInfo = meta.sortInfo || this.sortInfo;
11592         this.modified = [];
11593         this.fireEvent('metachange', this, this.reader.meta);
11594     },
11595     
11596     moveIndex : function(data, type)
11597     {
11598         var index = this.indexOf(data);
11599         
11600         var newIndex = index + type;
11601         
11602         this.remove(data);
11603         
11604         this.insert(newIndex, data);
11605         
11606     }
11607 });/*
11608  * Based on:
11609  * Ext JS Library 1.1.1
11610  * Copyright(c) 2006-2007, Ext JS, LLC.
11611  *
11612  * Originally Released Under LGPL - original licence link has changed is not relivant.
11613  *
11614  * Fork - LGPL
11615  * <script type="text/javascript">
11616  */
11617
11618 /**
11619  * @class Roo.data.SimpleStore
11620  * @extends Roo.data.Store
11621  * Small helper class to make creating Stores from Array data easier.
11622  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11623  * @cfg {Array} fields An array of field definition objects, or field name strings.
11624  * @cfg {Array} data The multi-dimensional array of data
11625  * @constructor
11626  * @param {Object} config
11627  */
11628 Roo.data.SimpleStore = function(config){
11629     Roo.data.SimpleStore.superclass.constructor.call(this, {
11630         isLocal : true,
11631         reader: new Roo.data.ArrayReader({
11632                 id: config.id
11633             },
11634             Roo.data.Record.create(config.fields)
11635         ),
11636         proxy : new Roo.data.MemoryProxy(config.data)
11637     });
11638     this.load();
11639 };
11640 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11641  * Based on:
11642  * Ext JS Library 1.1.1
11643  * Copyright(c) 2006-2007, Ext JS, LLC.
11644  *
11645  * Originally Released Under LGPL - original licence link has changed is not relivant.
11646  *
11647  * Fork - LGPL
11648  * <script type="text/javascript">
11649  */
11650
11651 /**
11652 /**
11653  * @extends Roo.data.Store
11654  * @class Roo.data.JsonStore
11655  * Small helper class to make creating Stores for JSON data easier. <br/>
11656 <pre><code>
11657 var store = new Roo.data.JsonStore({
11658     url: 'get-images.php',
11659     root: 'images',
11660     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11661 });
11662 </code></pre>
11663  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11664  * JsonReader and HttpProxy (unless inline data is provided).</b>
11665  * @cfg {Array} fields An array of field definition objects, or field name strings.
11666  * @constructor
11667  * @param {Object} config
11668  */
11669 Roo.data.JsonStore = function(c){
11670     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11671         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11672         reader: new Roo.data.JsonReader(c, c.fields)
11673     }));
11674 };
11675 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11676  * Based on:
11677  * Ext JS Library 1.1.1
11678  * Copyright(c) 2006-2007, Ext JS, LLC.
11679  *
11680  * Originally Released Under LGPL - original licence link has changed is not relivant.
11681  *
11682  * Fork - LGPL
11683  * <script type="text/javascript">
11684  */
11685
11686  
11687 Roo.data.Field = function(config){
11688     if(typeof config == "string"){
11689         config = {name: config};
11690     }
11691     Roo.apply(this, config);
11692     
11693     if(!this.type){
11694         this.type = "auto";
11695     }
11696     
11697     var st = Roo.data.SortTypes;
11698     // named sortTypes are supported, here we look them up
11699     if(typeof this.sortType == "string"){
11700         this.sortType = st[this.sortType];
11701     }
11702     
11703     // set default sortType for strings and dates
11704     if(!this.sortType){
11705         switch(this.type){
11706             case "string":
11707                 this.sortType = st.asUCString;
11708                 break;
11709             case "date":
11710                 this.sortType = st.asDate;
11711                 break;
11712             default:
11713                 this.sortType = st.none;
11714         }
11715     }
11716
11717     // define once
11718     var stripRe = /[\$,%]/g;
11719
11720     // prebuilt conversion function for this field, instead of
11721     // switching every time we're reading a value
11722     if(!this.convert){
11723         var cv, dateFormat = this.dateFormat;
11724         switch(this.type){
11725             case "":
11726             case "auto":
11727             case undefined:
11728                 cv = function(v){ return v; };
11729                 break;
11730             case "string":
11731                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11732                 break;
11733             case "int":
11734                 cv = function(v){
11735                     return v !== undefined && v !== null && v !== '' ?
11736                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11737                     };
11738                 break;
11739             case "float":
11740                 cv = function(v){
11741                     return v !== undefined && v !== null && v !== '' ?
11742                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11743                     };
11744                 break;
11745             case "bool":
11746             case "boolean":
11747                 cv = function(v){ return v === true || v === "true" || v == 1; };
11748                 break;
11749             case "date":
11750                 cv = function(v){
11751                     if(!v){
11752                         return '';
11753                     }
11754                     if(v instanceof Date){
11755                         return v;
11756                     }
11757                     if(dateFormat){
11758                         if(dateFormat == "timestamp"){
11759                             return new Date(v*1000);
11760                         }
11761                         return Date.parseDate(v, dateFormat);
11762                     }
11763                     var parsed = Date.parse(v);
11764                     return parsed ? new Date(parsed) : null;
11765                 };
11766              break;
11767             
11768         }
11769         this.convert = cv;
11770     }
11771 };
11772
11773 Roo.data.Field.prototype = {
11774     dateFormat: null,
11775     defaultValue: "",
11776     mapping: null,
11777     sortType : null,
11778     sortDir : "ASC"
11779 };/*
11780  * Based on:
11781  * Ext JS Library 1.1.1
11782  * Copyright(c) 2006-2007, Ext JS, LLC.
11783  *
11784  * Originally Released Under LGPL - original licence link has changed is not relivant.
11785  *
11786  * Fork - LGPL
11787  * <script type="text/javascript">
11788  */
11789  
11790 // Base class for reading structured data from a data source.  This class is intended to be
11791 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11792
11793 /**
11794  * @class Roo.data.DataReader
11795  * Base class for reading structured data from a data source.  This class is intended to be
11796  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11797  */
11798
11799 Roo.data.DataReader = function(meta, recordType){
11800     
11801     this.meta = meta;
11802     
11803     this.recordType = recordType instanceof Array ? 
11804         Roo.data.Record.create(recordType) : recordType;
11805 };
11806
11807 Roo.data.DataReader.prototype = {
11808      /**
11809      * Create an empty record
11810      * @param {Object} data (optional) - overlay some values
11811      * @return {Roo.data.Record} record created.
11812      */
11813     newRow :  function(d) {
11814         var da =  {};
11815         this.recordType.prototype.fields.each(function(c) {
11816             switch( c.type) {
11817                 case 'int' : da[c.name] = 0; break;
11818                 case 'date' : da[c.name] = new Date(); break;
11819                 case 'float' : da[c.name] = 0.0; break;
11820                 case 'boolean' : da[c.name] = false; break;
11821                 default : da[c.name] = ""; break;
11822             }
11823             
11824         });
11825         return new this.recordType(Roo.apply(da, d));
11826     }
11827     
11828 };/*
11829  * Based on:
11830  * Ext JS Library 1.1.1
11831  * Copyright(c) 2006-2007, Ext JS, LLC.
11832  *
11833  * Originally Released Under LGPL - original licence link has changed is not relivant.
11834  *
11835  * Fork - LGPL
11836  * <script type="text/javascript">
11837  */
11838
11839 /**
11840  * @class Roo.data.DataProxy
11841  * @extends Roo.data.Observable
11842  * This class is an abstract base class for implementations which provide retrieval of
11843  * unformatted data objects.<br>
11844  * <p>
11845  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11846  * (of the appropriate type which knows how to parse the data object) to provide a block of
11847  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11848  * <p>
11849  * Custom implementations must implement the load method as described in
11850  * {@link Roo.data.HttpProxy#load}.
11851  */
11852 Roo.data.DataProxy = function(){
11853     this.addEvents({
11854         /**
11855          * @event beforeload
11856          * Fires before a network request is made to retrieve a data object.
11857          * @param {Object} This DataProxy object.
11858          * @param {Object} params The params parameter to the load function.
11859          */
11860         beforeload : true,
11861         /**
11862          * @event load
11863          * Fires before the load method's callback is called.
11864          * @param {Object} This DataProxy object.
11865          * @param {Object} o The data object.
11866          * @param {Object} arg The callback argument object passed to the load function.
11867          */
11868         load : true,
11869         /**
11870          * @event loadexception
11871          * Fires if an Exception occurs during data retrieval.
11872          * @param {Object} This DataProxy object.
11873          * @param {Object} o The data object.
11874          * @param {Object} arg The callback argument object passed to the load function.
11875          * @param {Object} e The Exception.
11876          */
11877         loadexception : true
11878     });
11879     Roo.data.DataProxy.superclass.constructor.call(this);
11880 };
11881
11882 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11883
11884     /**
11885      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11886      */
11887 /*
11888  * Based on:
11889  * Ext JS Library 1.1.1
11890  * Copyright(c) 2006-2007, Ext JS, LLC.
11891  *
11892  * Originally Released Under LGPL - original licence link has changed is not relivant.
11893  *
11894  * Fork - LGPL
11895  * <script type="text/javascript">
11896  */
11897 /**
11898  * @class Roo.data.MemoryProxy
11899  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11900  * to the Reader when its load method is called.
11901  * @constructor
11902  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11903  */
11904 Roo.data.MemoryProxy = function(data){
11905     if (data.data) {
11906         data = data.data;
11907     }
11908     Roo.data.MemoryProxy.superclass.constructor.call(this);
11909     this.data = data;
11910 };
11911
11912 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11913     
11914     /**
11915      * Load data from the requested source (in this case an in-memory
11916      * data object passed to the constructor), read the data object into
11917      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11918      * process that block using the passed callback.
11919      * @param {Object} params This parameter is not used by the MemoryProxy class.
11920      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11921      * object into a block of Roo.data.Records.
11922      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11923      * The function must be passed <ul>
11924      * <li>The Record block object</li>
11925      * <li>The "arg" argument from the load function</li>
11926      * <li>A boolean success indicator</li>
11927      * </ul>
11928      * @param {Object} scope The scope in which to call the callback
11929      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11930      */
11931     load : function(params, reader, callback, scope, arg){
11932         params = params || {};
11933         var result;
11934         try {
11935             result = reader.readRecords(this.data);
11936         }catch(e){
11937             this.fireEvent("loadexception", this, arg, null, e);
11938             callback.call(scope, null, arg, false);
11939             return;
11940         }
11941         callback.call(scope, result, arg, true);
11942     },
11943     
11944     // private
11945     update : function(params, records){
11946         
11947     }
11948 });/*
11949  * Based on:
11950  * Ext JS Library 1.1.1
11951  * Copyright(c) 2006-2007, Ext JS, LLC.
11952  *
11953  * Originally Released Under LGPL - original licence link has changed is not relivant.
11954  *
11955  * Fork - LGPL
11956  * <script type="text/javascript">
11957  */
11958 /**
11959  * @class Roo.data.HttpProxy
11960  * @extends Roo.data.DataProxy
11961  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11962  * configured to reference a certain URL.<br><br>
11963  * <p>
11964  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11965  * from which the running page was served.<br><br>
11966  * <p>
11967  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11968  * <p>
11969  * Be aware that to enable the browser to parse an XML document, the server must set
11970  * the Content-Type header in the HTTP response to "text/xml".
11971  * @constructor
11972  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11973  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11974  * will be used to make the request.
11975  */
11976 Roo.data.HttpProxy = function(conn){
11977     Roo.data.HttpProxy.superclass.constructor.call(this);
11978     // is conn a conn config or a real conn?
11979     this.conn = conn;
11980     this.useAjax = !conn || !conn.events;
11981   
11982 };
11983
11984 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11985     // thse are take from connection...
11986     
11987     /**
11988      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11989      */
11990     /**
11991      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11992      * extra parameters to each request made by this object. (defaults to undefined)
11993      */
11994     /**
11995      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11996      *  to each request made by this object. (defaults to undefined)
11997      */
11998     /**
11999      * @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)
12000      */
12001     /**
12002      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12003      */
12004      /**
12005      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12006      * @type Boolean
12007      */
12008   
12009
12010     /**
12011      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12012      * @type Boolean
12013      */
12014     /**
12015      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12016      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12017      * a finer-grained basis than the DataProxy events.
12018      */
12019     getConnection : function(){
12020         return this.useAjax ? Roo.Ajax : this.conn;
12021     },
12022
12023     /**
12024      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12025      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12026      * process that block using the passed callback.
12027      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12028      * for the request to the remote server.
12029      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12030      * object into a block of Roo.data.Records.
12031      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12032      * The function must be passed <ul>
12033      * <li>The Record block object</li>
12034      * <li>The "arg" argument from the load function</li>
12035      * <li>A boolean success indicator</li>
12036      * </ul>
12037      * @param {Object} scope The scope in which to call the callback
12038      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12039      */
12040     load : function(params, reader, callback, scope, arg){
12041         if(this.fireEvent("beforeload", this, params) !== false){
12042             var  o = {
12043                 params : params || {},
12044                 request: {
12045                     callback : callback,
12046                     scope : scope,
12047                     arg : arg
12048                 },
12049                 reader: reader,
12050                 callback : this.loadResponse,
12051                 scope: this
12052             };
12053             if(this.useAjax){
12054                 Roo.applyIf(o, this.conn);
12055                 if(this.activeRequest){
12056                     Roo.Ajax.abort(this.activeRequest);
12057                 }
12058                 this.activeRequest = Roo.Ajax.request(o);
12059             }else{
12060                 this.conn.request(o);
12061             }
12062         }else{
12063             callback.call(scope||this, null, arg, false);
12064         }
12065     },
12066
12067     // private
12068     loadResponse : function(o, success, response){
12069         delete this.activeRequest;
12070         if(!success){
12071             this.fireEvent("loadexception", this, o, response);
12072             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12073             return;
12074         }
12075         var result;
12076         try {
12077             result = o.reader.read(response);
12078         }catch(e){
12079             this.fireEvent("loadexception", this, o, response, e);
12080             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12081             return;
12082         }
12083         
12084         this.fireEvent("load", this, o, o.request.arg);
12085         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12086     },
12087
12088     // private
12089     update : function(dataSet){
12090
12091     },
12092
12093     // private
12094     updateResponse : function(dataSet){
12095
12096     }
12097 });/*
12098  * Based on:
12099  * Ext JS Library 1.1.1
12100  * Copyright(c) 2006-2007, Ext JS, LLC.
12101  *
12102  * Originally Released Under LGPL - original licence link has changed is not relivant.
12103  *
12104  * Fork - LGPL
12105  * <script type="text/javascript">
12106  */
12107
12108 /**
12109  * @class Roo.data.ScriptTagProxy
12110  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12111  * other than the originating domain of the running page.<br><br>
12112  * <p>
12113  * <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
12114  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12115  * <p>
12116  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12117  * source code that is used as the source inside a &lt;script> tag.<br><br>
12118  * <p>
12119  * In order for the browser to process the returned data, the server must wrap the data object
12120  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12121  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12122  * depending on whether the callback name was passed:
12123  * <p>
12124  * <pre><code>
12125 boolean scriptTag = false;
12126 String cb = request.getParameter("callback");
12127 if (cb != null) {
12128     scriptTag = true;
12129     response.setContentType("text/javascript");
12130 } else {
12131     response.setContentType("application/x-json");
12132 }
12133 Writer out = response.getWriter();
12134 if (scriptTag) {
12135     out.write(cb + "(");
12136 }
12137 out.print(dataBlock.toJsonString());
12138 if (scriptTag) {
12139     out.write(");");
12140 }
12141 </pre></code>
12142  *
12143  * @constructor
12144  * @param {Object} config A configuration object.
12145  */
12146 Roo.data.ScriptTagProxy = function(config){
12147     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12148     Roo.apply(this, config);
12149     this.head = document.getElementsByTagName("head")[0];
12150 };
12151
12152 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12153
12154 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12155     /**
12156      * @cfg {String} url The URL from which to request the data object.
12157      */
12158     /**
12159      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12160      */
12161     timeout : 30000,
12162     /**
12163      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12164      * the server the name of the callback function set up by the load call to process the returned data object.
12165      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12166      * javascript output which calls this named function passing the data object as its only parameter.
12167      */
12168     callbackParam : "callback",
12169     /**
12170      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12171      * name to the request.
12172      */
12173     nocache : true,
12174
12175     /**
12176      * Load data from the configured URL, read the data object into
12177      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12178      * process that block using the passed callback.
12179      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12180      * for the request to the remote server.
12181      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12182      * object into a block of Roo.data.Records.
12183      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12184      * The function must be passed <ul>
12185      * <li>The Record block object</li>
12186      * <li>The "arg" argument from the load function</li>
12187      * <li>A boolean success indicator</li>
12188      * </ul>
12189      * @param {Object} scope The scope in which to call the callback
12190      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12191      */
12192     load : function(params, reader, callback, scope, arg){
12193         if(this.fireEvent("beforeload", this, params) !== false){
12194
12195             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12196
12197             var url = this.url;
12198             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12199             if(this.nocache){
12200                 url += "&_dc=" + (new Date().getTime());
12201             }
12202             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12203             var trans = {
12204                 id : transId,
12205                 cb : "stcCallback"+transId,
12206                 scriptId : "stcScript"+transId,
12207                 params : params,
12208                 arg : arg,
12209                 url : url,
12210                 callback : callback,
12211                 scope : scope,
12212                 reader : reader
12213             };
12214             var conn = this;
12215
12216             window[trans.cb] = function(o){
12217                 conn.handleResponse(o, trans);
12218             };
12219
12220             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12221
12222             if(this.autoAbort !== false){
12223                 this.abort();
12224             }
12225
12226             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12227
12228             var script = document.createElement("script");
12229             script.setAttribute("src", url);
12230             script.setAttribute("type", "text/javascript");
12231             script.setAttribute("id", trans.scriptId);
12232             this.head.appendChild(script);
12233
12234             this.trans = trans;
12235         }else{
12236             callback.call(scope||this, null, arg, false);
12237         }
12238     },
12239
12240     // private
12241     isLoading : function(){
12242         return this.trans ? true : false;
12243     },
12244
12245     /**
12246      * Abort the current server request.
12247      */
12248     abort : function(){
12249         if(this.isLoading()){
12250             this.destroyTrans(this.trans);
12251         }
12252     },
12253
12254     // private
12255     destroyTrans : function(trans, isLoaded){
12256         this.head.removeChild(document.getElementById(trans.scriptId));
12257         clearTimeout(trans.timeoutId);
12258         if(isLoaded){
12259             window[trans.cb] = undefined;
12260             try{
12261                 delete window[trans.cb];
12262             }catch(e){}
12263         }else{
12264             // if hasn't been loaded, wait for load to remove it to prevent script error
12265             window[trans.cb] = function(){
12266                 window[trans.cb] = undefined;
12267                 try{
12268                     delete window[trans.cb];
12269                 }catch(e){}
12270             };
12271         }
12272     },
12273
12274     // private
12275     handleResponse : function(o, trans){
12276         this.trans = false;
12277         this.destroyTrans(trans, true);
12278         var result;
12279         try {
12280             result = trans.reader.readRecords(o);
12281         }catch(e){
12282             this.fireEvent("loadexception", this, o, trans.arg, e);
12283             trans.callback.call(trans.scope||window, null, trans.arg, false);
12284             return;
12285         }
12286         this.fireEvent("load", this, o, trans.arg);
12287         trans.callback.call(trans.scope||window, result, trans.arg, true);
12288     },
12289
12290     // private
12291     handleFailure : function(trans){
12292         this.trans = false;
12293         this.destroyTrans(trans, false);
12294         this.fireEvent("loadexception", this, null, trans.arg);
12295         trans.callback.call(trans.scope||window, null, trans.arg, false);
12296     }
12297 });/*
12298  * Based on:
12299  * Ext JS Library 1.1.1
12300  * Copyright(c) 2006-2007, Ext JS, LLC.
12301  *
12302  * Originally Released Under LGPL - original licence link has changed is not relivant.
12303  *
12304  * Fork - LGPL
12305  * <script type="text/javascript">
12306  */
12307
12308 /**
12309  * @class Roo.data.JsonReader
12310  * @extends Roo.data.DataReader
12311  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12312  * based on mappings in a provided Roo.data.Record constructor.
12313  * 
12314  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12315  * in the reply previously. 
12316  * 
12317  * <p>
12318  * Example code:
12319  * <pre><code>
12320 var RecordDef = Roo.data.Record.create([
12321     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12322     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12323 ]);
12324 var myReader = new Roo.data.JsonReader({
12325     totalProperty: "results",    // The property which contains the total dataset size (optional)
12326     root: "rows",                // The property which contains an Array of row objects
12327     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12328 }, RecordDef);
12329 </code></pre>
12330  * <p>
12331  * This would consume a JSON file like this:
12332  * <pre><code>
12333 { 'results': 2, 'rows': [
12334     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12335     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12336 }
12337 </code></pre>
12338  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12339  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12340  * paged from the remote server.
12341  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12342  * @cfg {String} root name of the property which contains the Array of row objects.
12343  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12344  * @cfg {Array} fields Array of field definition objects
12345  * @constructor
12346  * Create a new JsonReader
12347  * @param {Object} meta Metadata configuration options
12348  * @param {Object} recordType Either an Array of field definition objects,
12349  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12350  */
12351 Roo.data.JsonReader = function(meta, recordType){
12352     
12353     meta = meta || {};
12354     // set some defaults:
12355     Roo.applyIf(meta, {
12356         totalProperty: 'total',
12357         successProperty : 'success',
12358         root : 'data',
12359         id : 'id'
12360     });
12361     
12362     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12363 };
12364 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12365     
12366     /**
12367      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12368      * Used by Store query builder to append _requestMeta to params.
12369      * 
12370      */
12371     metaFromRemote : false,
12372     /**
12373      * This method is only used by a DataProxy which has retrieved data from a remote server.
12374      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12375      * @return {Object} data A data block which is used by an Roo.data.Store object as
12376      * a cache of Roo.data.Records.
12377      */
12378     read : function(response){
12379         var json = response.responseText;
12380        
12381         var o = /* eval:var:o */ eval("("+json+")");
12382         if(!o) {
12383             throw {message: "JsonReader.read: Json object not found"};
12384         }
12385         
12386         if(o.metaData){
12387             
12388             delete this.ef;
12389             this.metaFromRemote = true;
12390             this.meta = o.metaData;
12391             this.recordType = Roo.data.Record.create(o.metaData.fields);
12392             this.onMetaChange(this.meta, this.recordType, o);
12393         }
12394         return this.readRecords(o);
12395     },
12396
12397     // private function a store will implement
12398     onMetaChange : function(meta, recordType, o){
12399
12400     },
12401
12402     /**
12403          * @ignore
12404          */
12405     simpleAccess: function(obj, subsc) {
12406         return obj[subsc];
12407     },
12408
12409         /**
12410          * @ignore
12411          */
12412     getJsonAccessor: function(){
12413         var re = /[\[\.]/;
12414         return function(expr) {
12415             try {
12416                 return(re.test(expr))
12417                     ? new Function("obj", "return obj." + expr)
12418                     : function(obj){
12419                         return obj[expr];
12420                     };
12421             } catch(e){}
12422             return Roo.emptyFn;
12423         };
12424     }(),
12425
12426     /**
12427      * Create a data block containing Roo.data.Records from an XML document.
12428      * @param {Object} o An object which contains an Array of row objects in the property specified
12429      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12430      * which contains the total size of the dataset.
12431      * @return {Object} data A data block which is used by an Roo.data.Store object as
12432      * a cache of Roo.data.Records.
12433      */
12434     readRecords : function(o){
12435         /**
12436          * After any data loads, the raw JSON data is available for further custom processing.
12437          * @type Object
12438          */
12439         this.o = o;
12440         var s = this.meta, Record = this.recordType,
12441             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12442
12443 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12444         if (!this.ef) {
12445             if(s.totalProperty) {
12446                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12447                 }
12448                 if(s.successProperty) {
12449                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12450                 }
12451                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12452                 if (s.id) {
12453                         var g = this.getJsonAccessor(s.id);
12454                         this.getId = function(rec) {
12455                                 var r = g(rec);  
12456                                 return (r === undefined || r === "") ? null : r;
12457                         };
12458                 } else {
12459                         this.getId = function(){return null;};
12460                 }
12461             this.ef = [];
12462             for(var jj = 0; jj < fl; jj++){
12463                 f = fi[jj];
12464                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12465                 this.ef[jj] = this.getJsonAccessor(map);
12466             }
12467         }
12468
12469         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12470         if(s.totalProperty){
12471             var vt = parseInt(this.getTotal(o), 10);
12472             if(!isNaN(vt)){
12473                 totalRecords = vt;
12474             }
12475         }
12476         if(s.successProperty){
12477             var vs = this.getSuccess(o);
12478             if(vs === false || vs === 'false'){
12479                 success = false;
12480             }
12481         }
12482         var records = [];
12483         for(var i = 0; i < c; i++){
12484                 var n = root[i];
12485             var values = {};
12486             var id = this.getId(n);
12487             for(var j = 0; j < fl; j++){
12488                 f = fi[j];
12489             var v = this.ef[j](n);
12490             if (!f.convert) {
12491                 Roo.log('missing convert for ' + f.name);
12492                 Roo.log(f);
12493                 continue;
12494             }
12495             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12496             }
12497             var record = new Record(values, id);
12498             record.json = n;
12499             records[i] = record;
12500         }
12501         return {
12502             raw : o,
12503             success : success,
12504             records : records,
12505             totalRecords : totalRecords
12506         };
12507     }
12508 });/*
12509  * Based on:
12510  * Ext JS Library 1.1.1
12511  * Copyright(c) 2006-2007, Ext JS, LLC.
12512  *
12513  * Originally Released Under LGPL - original licence link has changed is not relivant.
12514  *
12515  * Fork - LGPL
12516  * <script type="text/javascript">
12517  */
12518
12519 /**
12520  * @class Roo.data.ArrayReader
12521  * @extends Roo.data.DataReader
12522  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12523  * Each element of that Array represents a row of data fields. The
12524  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12525  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12526  * <p>
12527  * Example code:.
12528  * <pre><code>
12529 var RecordDef = Roo.data.Record.create([
12530     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12531     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12532 ]);
12533 var myReader = new Roo.data.ArrayReader({
12534     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12535 }, RecordDef);
12536 </code></pre>
12537  * <p>
12538  * This would consume an Array like this:
12539  * <pre><code>
12540 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12541   </code></pre>
12542  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12543  * @constructor
12544  * Create a new JsonReader
12545  * @param {Object} meta Metadata configuration options.
12546  * @param {Object} recordType Either an Array of field definition objects
12547  * as specified to {@link Roo.data.Record#create},
12548  * or an {@link Roo.data.Record} object
12549  * created using {@link Roo.data.Record#create}.
12550  */
12551 Roo.data.ArrayReader = function(meta, recordType){
12552     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12553 };
12554
12555 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12556     /**
12557      * Create a data block containing Roo.data.Records from an XML document.
12558      * @param {Object} o An Array of row objects which represents the dataset.
12559      * @return {Object} data A data block which is used by an Roo.data.Store object as
12560      * a cache of Roo.data.Records.
12561      */
12562     readRecords : function(o){
12563         var sid = this.meta ? this.meta.id : null;
12564         var recordType = this.recordType, fields = recordType.prototype.fields;
12565         var records = [];
12566         var root = o;
12567             for(var i = 0; i < root.length; i++){
12568                     var n = root[i];
12569                 var values = {};
12570                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12571                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12572                 var f = fields.items[j];
12573                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12574                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12575                 v = f.convert(v);
12576                 values[f.name] = v;
12577             }
12578                 var record = new recordType(values, id);
12579                 record.json = n;
12580                 records[records.length] = record;
12581             }
12582             return {
12583                 records : records,
12584                 totalRecords : records.length
12585             };
12586     }
12587 });/*
12588  * - LGPL
12589  * * 
12590  */
12591
12592 /**
12593  * @class Roo.bootstrap.ComboBox
12594  * @extends Roo.bootstrap.TriggerField
12595  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12596  * @cfg {Boolean} append (true|false) default false
12597  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12598  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12599  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12600  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12601  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12602  * @cfg {Boolean} animate default true
12603  * @cfg {Boolean} emptyResultText only for touch device
12604  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12605  * @cfg {String} emptyTitle default ''
12606  * @constructor
12607  * Create a new ComboBox.
12608  * @param {Object} config Configuration options
12609  */
12610 Roo.bootstrap.ComboBox = function(config){
12611     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12612     this.addEvents({
12613         /**
12614          * @event expand
12615          * Fires when the dropdown list is expanded
12616         * @param {Roo.bootstrap.ComboBox} combo This combo box
12617         */
12618         'expand' : true,
12619         /**
12620          * @event collapse
12621          * Fires when the dropdown list is collapsed
12622         * @param {Roo.bootstrap.ComboBox} combo This combo box
12623         */
12624         'collapse' : true,
12625         /**
12626          * @event beforeselect
12627          * Fires before a list item is selected. Return false to cancel the selection.
12628         * @param {Roo.bootstrap.ComboBox} combo This combo box
12629         * @param {Roo.data.Record} record The data record returned from the underlying store
12630         * @param {Number} index The index of the selected item in the dropdown list
12631         */
12632         'beforeselect' : true,
12633         /**
12634          * @event select
12635          * Fires when a list item is selected
12636         * @param {Roo.bootstrap.ComboBox} combo This combo box
12637         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12638         * @param {Number} index The index of the selected item in the dropdown list
12639         */
12640         'select' : true,
12641         /**
12642          * @event beforequery
12643          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12644          * The event object passed has these properties:
12645         * @param {Roo.bootstrap.ComboBox} combo This combo box
12646         * @param {String} query The query
12647         * @param {Boolean} forceAll true to force "all" query
12648         * @param {Boolean} cancel true to cancel the query
12649         * @param {Object} e The query event object
12650         */
12651         'beforequery': true,
12652          /**
12653          * @event add
12654          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12655         * @param {Roo.bootstrap.ComboBox} combo This combo box
12656         */
12657         'add' : true,
12658         /**
12659          * @event edit
12660          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12661         * @param {Roo.bootstrap.ComboBox} combo This combo box
12662         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12663         */
12664         'edit' : true,
12665         /**
12666          * @event remove
12667          * Fires when the remove value from the combobox array
12668         * @param {Roo.bootstrap.ComboBox} combo This combo box
12669         */
12670         'remove' : true,
12671         /**
12672          * @event afterremove
12673          * Fires when the remove value from the combobox array
12674         * @param {Roo.bootstrap.ComboBox} combo This combo box
12675         */
12676         'afterremove' : true,
12677         /**
12678          * @event specialfilter
12679          * Fires when specialfilter
12680             * @param {Roo.bootstrap.ComboBox} combo This combo box
12681             */
12682         'specialfilter' : true,
12683         /**
12684          * @event tick
12685          * Fires when tick the element
12686             * @param {Roo.bootstrap.ComboBox} combo This combo box
12687             */
12688         'tick' : true,
12689         /**
12690          * @event touchviewdisplay
12691          * Fires when touch view require special display (default is using displayField)
12692             * @param {Roo.bootstrap.ComboBox} combo This combo box
12693             * @param {Object} cfg set html .
12694             */
12695         'touchviewdisplay' : true
12696         
12697     });
12698     
12699     this.item = [];
12700     this.tickItems = [];
12701     
12702     this.selectedIndex = -1;
12703     if(this.mode == 'local'){
12704         if(config.queryDelay === undefined){
12705             this.queryDelay = 10;
12706         }
12707         if(config.minChars === undefined){
12708             this.minChars = 0;
12709         }
12710     }
12711 };
12712
12713 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12714      
12715     /**
12716      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12717      * rendering into an Roo.Editor, defaults to false)
12718      */
12719     /**
12720      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12721      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12722      */
12723     /**
12724      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12725      */
12726     /**
12727      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12728      * the dropdown list (defaults to undefined, with no header element)
12729      */
12730
12731      /**
12732      * @cfg {String/Roo.Template} tpl The template to use to render the output
12733      */
12734      
12735      /**
12736      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12737      */
12738     listWidth: undefined,
12739     /**
12740      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12741      * mode = 'remote' or 'text' if mode = 'local')
12742      */
12743     displayField: undefined,
12744     
12745     /**
12746      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12747      * mode = 'remote' or 'value' if mode = 'local'). 
12748      * Note: use of a valueField requires the user make a selection
12749      * in order for a value to be mapped.
12750      */
12751     valueField: undefined,
12752     /**
12753      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12754      */
12755     modalTitle : '',
12756     
12757     /**
12758      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12759      * field's data value (defaults to the underlying DOM element's name)
12760      */
12761     hiddenName: undefined,
12762     /**
12763      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12764      */
12765     listClass: '',
12766     /**
12767      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12768      */
12769     selectedClass: 'active',
12770     
12771     /**
12772      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12773      */
12774     shadow:'sides',
12775     /**
12776      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12777      * anchor positions (defaults to 'tl-bl')
12778      */
12779     listAlign: 'tl-bl?',
12780     /**
12781      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12782      */
12783     maxHeight: 300,
12784     /**
12785      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12786      * query specified by the allQuery config option (defaults to 'query')
12787      */
12788     triggerAction: 'query',
12789     /**
12790      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12791      * (defaults to 4, does not apply if editable = false)
12792      */
12793     minChars : 4,
12794     /**
12795      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12796      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12797      */
12798     typeAhead: false,
12799     /**
12800      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12801      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12802      */
12803     queryDelay: 500,
12804     /**
12805      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12806      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12807      */
12808     pageSize: 0,
12809     /**
12810      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12811      * when editable = true (defaults to false)
12812      */
12813     selectOnFocus:false,
12814     /**
12815      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12816      */
12817     queryParam: 'query',
12818     /**
12819      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12820      * when mode = 'remote' (defaults to 'Loading...')
12821      */
12822     loadingText: 'Loading...',
12823     /**
12824      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12825      */
12826     resizable: false,
12827     /**
12828      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12829      */
12830     handleHeight : 8,
12831     /**
12832      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12833      * traditional select (defaults to true)
12834      */
12835     editable: true,
12836     /**
12837      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12838      */
12839     allQuery: '',
12840     /**
12841      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12842      */
12843     mode: 'remote',
12844     /**
12845      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12846      * listWidth has a higher value)
12847      */
12848     minListWidth : 70,
12849     /**
12850      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12851      * allow the user to set arbitrary text into the field (defaults to false)
12852      */
12853     forceSelection:false,
12854     /**
12855      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12856      * if typeAhead = true (defaults to 250)
12857      */
12858     typeAheadDelay : 250,
12859     /**
12860      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12861      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12862      */
12863     valueNotFoundText : undefined,
12864     /**
12865      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12866      */
12867     blockFocus : false,
12868     
12869     /**
12870      * @cfg {Boolean} disableClear Disable showing of clear button.
12871      */
12872     disableClear : false,
12873     /**
12874      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12875      */
12876     alwaysQuery : false,
12877     
12878     /**
12879      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12880      */
12881     multiple : false,
12882     
12883     /**
12884      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12885      */
12886     invalidClass : "has-warning",
12887     
12888     /**
12889      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12890      */
12891     validClass : "has-success",
12892     
12893     /**
12894      * @cfg {Boolean} specialFilter (true|false) special filter default false
12895      */
12896     specialFilter : false,
12897     
12898     /**
12899      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12900      */
12901     mobileTouchView : true,
12902     
12903     /**
12904      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12905      */
12906     useNativeIOS : false,
12907     
12908     ios_options : false,
12909     
12910     //private
12911     addicon : false,
12912     editicon: false,
12913     
12914     page: 0,
12915     hasQuery: false,
12916     append: false,
12917     loadNext: false,
12918     autoFocus : true,
12919     tickable : false,
12920     btnPosition : 'right',
12921     triggerList : true,
12922     showToggleBtn : true,
12923     animate : true,
12924     emptyResultText: 'Empty',
12925     triggerText : 'Select',
12926     emptyTitle : '',
12927     
12928     // element that contains real text value.. (when hidden is used..)
12929     
12930     getAutoCreate : function()
12931     {   
12932         var cfg = false;
12933         //render
12934         /*
12935          * Render classic select for iso
12936          */
12937         
12938         if(Roo.isIOS && this.useNativeIOS){
12939             cfg = this.getAutoCreateNativeIOS();
12940             return cfg;
12941         }
12942         
12943         /*
12944          * Touch Devices
12945          */
12946         
12947         if(Roo.isTouch && this.mobileTouchView){
12948             cfg = this.getAutoCreateTouchView();
12949             return cfg;;
12950         }
12951         
12952         /*
12953          *  Normal ComboBox
12954          */
12955         if(!this.tickable){
12956             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12957             return cfg;
12958         }
12959         
12960         /*
12961          *  ComboBox with tickable selections
12962          */
12963              
12964         var align = this.labelAlign || this.parentLabelAlign();
12965         
12966         cfg = {
12967             cls : 'form-group roo-combobox-tickable' //input-group
12968         };
12969         
12970         var btn_text_select = '';
12971         var btn_text_done = '';
12972         var btn_text_cancel = '';
12973         
12974         if (this.btn_text_show) {
12975             btn_text_select = 'Select';
12976             btn_text_done = 'Done';
12977             btn_text_cancel = 'Cancel'; 
12978         }
12979         
12980         var buttons = {
12981             tag : 'div',
12982             cls : 'tickable-buttons',
12983             cn : [
12984                 {
12985                     tag : 'button',
12986                     type : 'button',
12987                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12988                     //html : this.triggerText
12989                     html: btn_text_select
12990                 },
12991                 {
12992                     tag : 'button',
12993                     type : 'button',
12994                     name : 'ok',
12995                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12996                     //html : 'Done'
12997                     html: btn_text_done
12998                 },
12999                 {
13000                     tag : 'button',
13001                     type : 'button',
13002                     name : 'cancel',
13003                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13004                     //html : 'Cancel'
13005                     html: btn_text_cancel
13006                 }
13007             ]
13008         };
13009         
13010         if(this.editable){
13011             buttons.cn.unshift({
13012                 tag: 'input',
13013                 cls: 'roo-select2-search-field-input'
13014             });
13015         }
13016         
13017         var _this = this;
13018         
13019         Roo.each(buttons.cn, function(c){
13020             if (_this.size) {
13021                 c.cls += ' btn-' + _this.size;
13022             }
13023
13024             if (_this.disabled) {
13025                 c.disabled = true;
13026             }
13027         });
13028         
13029         var box = {
13030             tag: 'div',
13031             cn: [
13032                 {
13033                     tag: 'input',
13034                     type : 'hidden',
13035                     cls: 'form-hidden-field'
13036                 },
13037                 {
13038                     tag: 'ul',
13039                     cls: 'roo-select2-choices',
13040                     cn:[
13041                         {
13042                             tag: 'li',
13043                             cls: 'roo-select2-search-field',
13044                             cn: [
13045                                 buttons
13046                             ]
13047                         }
13048                     ]
13049                 }
13050             ]
13051         };
13052         
13053         var combobox = {
13054             cls: 'roo-select2-container input-group roo-select2-container-multi',
13055             cn: [
13056                 box
13057 //                {
13058 //                    tag: 'ul',
13059 //                    cls: 'typeahead typeahead-long dropdown-menu',
13060 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13061 //                }
13062             ]
13063         };
13064         
13065         if(this.hasFeedback && !this.allowBlank){
13066             
13067             var feedback = {
13068                 tag: 'span',
13069                 cls: 'glyphicon form-control-feedback'
13070             };
13071
13072             combobox.cn.push(feedback);
13073         }
13074         
13075         
13076         if (align ==='left' && this.fieldLabel.length) {
13077             
13078             cfg.cls += ' roo-form-group-label-left';
13079             
13080             cfg.cn = [
13081                 {
13082                     tag : 'i',
13083                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13084                     tooltip : 'This field is required'
13085                 },
13086                 {
13087                     tag: 'label',
13088                     'for' :  id,
13089                     cls : 'control-label',
13090                     html : this.fieldLabel
13091
13092                 },
13093                 {
13094                     cls : "", 
13095                     cn: [
13096                         combobox
13097                     ]
13098                 }
13099
13100             ];
13101             
13102             var labelCfg = cfg.cn[1];
13103             var contentCfg = cfg.cn[2];
13104             
13105
13106             if(this.indicatorpos == 'right'){
13107                 
13108                 cfg.cn = [
13109                     {
13110                         tag: 'label',
13111                         'for' :  id,
13112                         cls : 'control-label',
13113                         cn : [
13114                             {
13115                                 tag : 'span',
13116                                 html : this.fieldLabel
13117                             },
13118                             {
13119                                 tag : 'i',
13120                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13121                                 tooltip : 'This field is required'
13122                             }
13123                         ]
13124                     },
13125                     {
13126                         cls : "",
13127                         cn: [
13128                             combobox
13129                         ]
13130                     }
13131
13132                 ];
13133                 
13134                 
13135                 
13136                 labelCfg = cfg.cn[0];
13137                 contentCfg = cfg.cn[1];
13138             
13139             }
13140             
13141             if(this.labelWidth > 12){
13142                 labelCfg.style = "width: " + this.labelWidth + 'px';
13143             }
13144             
13145             if(this.labelWidth < 13 && this.labelmd == 0){
13146                 this.labelmd = this.labelWidth;
13147             }
13148             
13149             if(this.labellg > 0){
13150                 labelCfg.cls += ' col-lg-' + this.labellg;
13151                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13152             }
13153             
13154             if(this.labelmd > 0){
13155                 labelCfg.cls += ' col-md-' + this.labelmd;
13156                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13157             }
13158             
13159             if(this.labelsm > 0){
13160                 labelCfg.cls += ' col-sm-' + this.labelsm;
13161                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13162             }
13163             
13164             if(this.labelxs > 0){
13165                 labelCfg.cls += ' col-xs-' + this.labelxs;
13166                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13167             }
13168                 
13169                 
13170         } else if ( this.fieldLabel.length) {
13171 //                Roo.log(" label");
13172                  cfg.cn = [
13173                     {
13174                         tag : 'i',
13175                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13176                         tooltip : 'This field is required'
13177                     },
13178                     {
13179                         tag: 'label',
13180                         //cls : 'input-group-addon',
13181                         html : this.fieldLabel
13182                     },
13183                     combobox
13184                 ];
13185                 
13186                 if(this.indicatorpos == 'right'){
13187                     cfg.cn = [
13188                         {
13189                             tag: 'label',
13190                             //cls : 'input-group-addon',
13191                             html : this.fieldLabel
13192                         },
13193                         {
13194                             tag : 'i',
13195                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13196                             tooltip : 'This field is required'
13197                         },
13198                         combobox
13199                     ];
13200                     
13201                 }
13202
13203         } else {
13204             
13205 //                Roo.log(" no label && no align");
13206                 cfg = combobox
13207                      
13208                 
13209         }
13210          
13211         var settings=this;
13212         ['xs','sm','md','lg'].map(function(size){
13213             if (settings[size]) {
13214                 cfg.cls += ' col-' + size + '-' + settings[size];
13215             }
13216         });
13217         
13218         return cfg;
13219         
13220     },
13221     
13222     _initEventsCalled : false,
13223     
13224     // private
13225     initEvents: function()
13226     {   
13227         if (this._initEventsCalled) { // as we call render... prevent looping...
13228             return;
13229         }
13230         this._initEventsCalled = true;
13231         
13232         if (!this.store) {
13233             throw "can not find store for combo";
13234         }
13235         
13236         this.indicator = this.indicatorEl();
13237         
13238         this.store = Roo.factory(this.store, Roo.data);
13239         this.store.parent = this;
13240         
13241         // if we are building from html. then this element is so complex, that we can not really
13242         // use the rendered HTML.
13243         // so we have to trash and replace the previous code.
13244         if (Roo.XComponent.build_from_html) {
13245             // remove this element....
13246             var e = this.el.dom, k=0;
13247             while (e ) { e = e.previousSibling;  ++k;}
13248
13249             this.el.remove();
13250             
13251             this.el=false;
13252             this.rendered = false;
13253             
13254             this.render(this.parent().getChildContainer(true), k);
13255         }
13256         
13257         if(Roo.isIOS && this.useNativeIOS){
13258             this.initIOSView();
13259             return;
13260         }
13261         
13262         /*
13263          * Touch Devices
13264          */
13265         
13266         if(Roo.isTouch && this.mobileTouchView){
13267             this.initTouchView();
13268             return;
13269         }
13270         
13271         if(this.tickable){
13272             this.initTickableEvents();
13273             return;
13274         }
13275         
13276         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13277         
13278         if(this.hiddenName){
13279             
13280             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13281             
13282             this.hiddenField.dom.value =
13283                 this.hiddenValue !== undefined ? this.hiddenValue :
13284                 this.value !== undefined ? this.value : '';
13285
13286             // prevent input submission
13287             this.el.dom.removeAttribute('name');
13288             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13289              
13290              
13291         }
13292         //if(Roo.isGecko){
13293         //    this.el.dom.setAttribute('autocomplete', 'off');
13294         //}
13295         
13296         var cls = 'x-combo-list';
13297         
13298         //this.list = new Roo.Layer({
13299         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13300         //});
13301         
13302         var _this = this;
13303         
13304         (function(){
13305             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13306             _this.list.setWidth(lw);
13307         }).defer(100);
13308         
13309         this.list.on('mouseover', this.onViewOver, this);
13310         this.list.on('mousemove', this.onViewMove, this);
13311         this.list.on('scroll', this.onViewScroll, this);
13312         
13313         /*
13314         this.list.swallowEvent('mousewheel');
13315         this.assetHeight = 0;
13316
13317         if(this.title){
13318             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13319             this.assetHeight += this.header.getHeight();
13320         }
13321
13322         this.innerList = this.list.createChild({cls:cls+'-inner'});
13323         this.innerList.on('mouseover', this.onViewOver, this);
13324         this.innerList.on('mousemove', this.onViewMove, this);
13325         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13326         
13327         if(this.allowBlank && !this.pageSize && !this.disableClear){
13328             this.footer = this.list.createChild({cls:cls+'-ft'});
13329             this.pageTb = new Roo.Toolbar(this.footer);
13330            
13331         }
13332         if(this.pageSize){
13333             this.footer = this.list.createChild({cls:cls+'-ft'});
13334             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13335                     {pageSize: this.pageSize});
13336             
13337         }
13338         
13339         if (this.pageTb && this.allowBlank && !this.disableClear) {
13340             var _this = this;
13341             this.pageTb.add(new Roo.Toolbar.Fill(), {
13342                 cls: 'x-btn-icon x-btn-clear',
13343                 text: '&#160;',
13344                 handler: function()
13345                 {
13346                     _this.collapse();
13347                     _this.clearValue();
13348                     _this.onSelect(false, -1);
13349                 }
13350             });
13351         }
13352         if (this.footer) {
13353             this.assetHeight += this.footer.getHeight();
13354         }
13355         */
13356             
13357         if(!this.tpl){
13358             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13359         }
13360
13361         this.view = new Roo.View(this.list, this.tpl, {
13362             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13363         });
13364         //this.view.wrapEl.setDisplayed(false);
13365         this.view.on('click', this.onViewClick, this);
13366         
13367         
13368         this.store.on('beforeload', this.onBeforeLoad, this);
13369         this.store.on('load', this.onLoad, this);
13370         this.store.on('loadexception', this.onLoadException, this);
13371         /*
13372         if(this.resizable){
13373             this.resizer = new Roo.Resizable(this.list,  {
13374                pinned:true, handles:'se'
13375             });
13376             this.resizer.on('resize', function(r, w, h){
13377                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13378                 this.listWidth = w;
13379                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13380                 this.restrictHeight();
13381             }, this);
13382             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13383         }
13384         */
13385         if(!this.editable){
13386             this.editable = true;
13387             this.setEditable(false);
13388         }
13389         
13390         /*
13391         
13392         if (typeof(this.events.add.listeners) != 'undefined') {
13393             
13394             this.addicon = this.wrap.createChild(
13395                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13396        
13397             this.addicon.on('click', function(e) {
13398                 this.fireEvent('add', this);
13399             }, this);
13400         }
13401         if (typeof(this.events.edit.listeners) != 'undefined') {
13402             
13403             this.editicon = this.wrap.createChild(
13404                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13405             if (this.addicon) {
13406                 this.editicon.setStyle('margin-left', '40px');
13407             }
13408             this.editicon.on('click', function(e) {
13409                 
13410                 // we fire even  if inothing is selected..
13411                 this.fireEvent('edit', this, this.lastData );
13412                 
13413             }, this);
13414         }
13415         */
13416         
13417         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13418             "up" : function(e){
13419                 this.inKeyMode = true;
13420                 this.selectPrev();
13421             },
13422
13423             "down" : function(e){
13424                 if(!this.isExpanded()){
13425                     this.onTriggerClick();
13426                 }else{
13427                     this.inKeyMode = true;
13428                     this.selectNext();
13429                 }
13430             },
13431
13432             "enter" : function(e){
13433 //                this.onViewClick();
13434                 //return true;
13435                 this.collapse();
13436                 
13437                 if(this.fireEvent("specialkey", this, e)){
13438                     this.onViewClick(false);
13439                 }
13440                 
13441                 return true;
13442             },
13443
13444             "esc" : function(e){
13445                 this.collapse();
13446             },
13447
13448             "tab" : function(e){
13449                 this.collapse();
13450                 
13451                 if(this.fireEvent("specialkey", this, e)){
13452                     this.onViewClick(false);
13453                 }
13454                 
13455                 return true;
13456             },
13457
13458             scope : this,
13459
13460             doRelay : function(foo, bar, hname){
13461                 if(hname == 'down' || this.scope.isExpanded()){
13462                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13463                 }
13464                 return true;
13465             },
13466
13467             forceKeyDown: true
13468         });
13469         
13470         
13471         this.queryDelay = Math.max(this.queryDelay || 10,
13472                 this.mode == 'local' ? 10 : 250);
13473         
13474         
13475         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13476         
13477         if(this.typeAhead){
13478             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13479         }
13480         if(this.editable !== false){
13481             this.inputEl().on("keyup", this.onKeyUp, this);
13482         }
13483         if(this.forceSelection){
13484             this.inputEl().on('blur', this.doForce, this);
13485         }
13486         
13487         if(this.multiple){
13488             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13489             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13490         }
13491     },
13492     
13493     initTickableEvents: function()
13494     {   
13495         this.createList();
13496         
13497         if(this.hiddenName){
13498             
13499             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13500             
13501             this.hiddenField.dom.value =
13502                 this.hiddenValue !== undefined ? this.hiddenValue :
13503                 this.value !== undefined ? this.value : '';
13504
13505             // prevent input submission
13506             this.el.dom.removeAttribute('name');
13507             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13508              
13509              
13510         }
13511         
13512 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13513         
13514         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13515         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13516         if(this.triggerList){
13517             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13518         }
13519          
13520         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13521         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13522         
13523         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13524         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13525         
13526         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13527         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13528         
13529         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13530         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13531         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13532         
13533         this.okBtn.hide();
13534         this.cancelBtn.hide();
13535         
13536         var _this = this;
13537         
13538         (function(){
13539             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13540             _this.list.setWidth(lw);
13541         }).defer(100);
13542         
13543         this.list.on('mouseover', this.onViewOver, this);
13544         this.list.on('mousemove', this.onViewMove, this);
13545         
13546         this.list.on('scroll', this.onViewScroll, this);
13547         
13548         if(!this.tpl){
13549             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>';
13550         }
13551
13552         this.view = new Roo.View(this.list, this.tpl, {
13553             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13554         });
13555         
13556         //this.view.wrapEl.setDisplayed(false);
13557         this.view.on('click', this.onViewClick, this);
13558         
13559         
13560         
13561         this.store.on('beforeload', this.onBeforeLoad, this);
13562         this.store.on('load', this.onLoad, this);
13563         this.store.on('loadexception', this.onLoadException, this);
13564         
13565         if(this.editable){
13566             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13567                 "up" : function(e){
13568                     this.inKeyMode = true;
13569                     this.selectPrev();
13570                 },
13571
13572                 "down" : function(e){
13573                     this.inKeyMode = true;
13574                     this.selectNext();
13575                 },
13576
13577                 "enter" : function(e){
13578                     if(this.fireEvent("specialkey", this, e)){
13579                         this.onViewClick(false);
13580                     }
13581                     
13582                     return true;
13583                 },
13584
13585                 "esc" : function(e){
13586                     this.onTickableFooterButtonClick(e, false, false);
13587                 },
13588
13589                 "tab" : function(e){
13590                     this.fireEvent("specialkey", this, e);
13591                     
13592                     this.onTickableFooterButtonClick(e, false, false);
13593                     
13594                     return true;
13595                 },
13596
13597                 scope : this,
13598
13599                 doRelay : function(e, fn, key){
13600                     if(this.scope.isExpanded()){
13601                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13602                     }
13603                     return true;
13604                 },
13605
13606                 forceKeyDown: true
13607             });
13608         }
13609         
13610         this.queryDelay = Math.max(this.queryDelay || 10,
13611                 this.mode == 'local' ? 10 : 250);
13612         
13613         
13614         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13615         
13616         if(this.typeAhead){
13617             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13618         }
13619         
13620         if(this.editable !== false){
13621             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13622         }
13623         
13624         this.indicator = this.indicatorEl();
13625         
13626         if(this.indicator){
13627             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13628             this.indicator.hide();
13629         }
13630         
13631     },
13632
13633     onDestroy : function(){
13634         if(this.view){
13635             this.view.setStore(null);
13636             this.view.el.removeAllListeners();
13637             this.view.el.remove();
13638             this.view.purgeListeners();
13639         }
13640         if(this.list){
13641             this.list.dom.innerHTML  = '';
13642         }
13643         
13644         if(this.store){
13645             this.store.un('beforeload', this.onBeforeLoad, this);
13646             this.store.un('load', this.onLoad, this);
13647             this.store.un('loadexception', this.onLoadException, this);
13648         }
13649         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13650     },
13651
13652     // private
13653     fireKey : function(e){
13654         if(e.isNavKeyPress() && !this.list.isVisible()){
13655             this.fireEvent("specialkey", this, e);
13656         }
13657     },
13658
13659     // private
13660     onResize: function(w, h){
13661 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13662 //        
13663 //        if(typeof w != 'number'){
13664 //            // we do not handle it!?!?
13665 //            return;
13666 //        }
13667 //        var tw = this.trigger.getWidth();
13668 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13669 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13670 //        var x = w - tw;
13671 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13672 //            
13673 //        //this.trigger.setStyle('left', x+'px');
13674 //        
13675 //        if(this.list && this.listWidth === undefined){
13676 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13677 //            this.list.setWidth(lw);
13678 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13679 //        }
13680         
13681     
13682         
13683     },
13684
13685     /**
13686      * Allow or prevent the user from directly editing the field text.  If false is passed,
13687      * the user will only be able to select from the items defined in the dropdown list.  This method
13688      * is the runtime equivalent of setting the 'editable' config option at config time.
13689      * @param {Boolean} value True to allow the user to directly edit the field text
13690      */
13691     setEditable : function(value){
13692         if(value == this.editable){
13693             return;
13694         }
13695         this.editable = value;
13696         if(!value){
13697             this.inputEl().dom.setAttribute('readOnly', true);
13698             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13699             this.inputEl().addClass('x-combo-noedit');
13700         }else{
13701             this.inputEl().dom.setAttribute('readOnly', false);
13702             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13703             this.inputEl().removeClass('x-combo-noedit');
13704         }
13705     },
13706
13707     // private
13708     
13709     onBeforeLoad : function(combo,opts){
13710         if(!this.hasFocus){
13711             return;
13712         }
13713          if (!opts.add) {
13714             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13715          }
13716         this.restrictHeight();
13717         this.selectedIndex = -1;
13718     },
13719
13720     // private
13721     onLoad : function(){
13722         
13723         this.hasQuery = false;
13724         
13725         if(!this.hasFocus){
13726             return;
13727         }
13728         
13729         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13730             this.loading.hide();
13731         }
13732         
13733         if(this.store.getCount() > 0){
13734             
13735             this.expand();
13736             this.restrictHeight();
13737             if(this.lastQuery == this.allQuery){
13738                 if(this.editable && !this.tickable){
13739                     this.inputEl().dom.select();
13740                 }
13741                 
13742                 if(
13743                     !this.selectByValue(this.value, true) &&
13744                     this.autoFocus && 
13745                     (
13746                         !this.store.lastOptions ||
13747                         typeof(this.store.lastOptions.add) == 'undefined' || 
13748                         this.store.lastOptions.add != true
13749                     )
13750                 ){
13751                     this.select(0, true);
13752                 }
13753             }else{
13754                 if(this.autoFocus){
13755                     this.selectNext();
13756                 }
13757                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13758                     this.taTask.delay(this.typeAheadDelay);
13759                 }
13760             }
13761         }else{
13762             this.onEmptyResults();
13763         }
13764         
13765         //this.el.focus();
13766     },
13767     // private
13768     onLoadException : function()
13769     {
13770         this.hasQuery = false;
13771         
13772         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13773             this.loading.hide();
13774         }
13775         
13776         if(this.tickable && this.editable){
13777             return;
13778         }
13779         
13780         this.collapse();
13781         // only causes errors at present
13782         //Roo.log(this.store.reader.jsonData);
13783         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13784             // fixme
13785             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13786         //}
13787         
13788         
13789     },
13790     // private
13791     onTypeAhead : function(){
13792         if(this.store.getCount() > 0){
13793             var r = this.store.getAt(0);
13794             var newValue = r.data[this.displayField];
13795             var len = newValue.length;
13796             var selStart = this.getRawValue().length;
13797             
13798             if(selStart != len){
13799                 this.setRawValue(newValue);
13800                 this.selectText(selStart, newValue.length);
13801             }
13802         }
13803     },
13804
13805     // private
13806     onSelect : function(record, index){
13807         
13808         if(this.fireEvent('beforeselect', this, record, index) !== false){
13809         
13810             this.setFromData(index > -1 ? record.data : false);
13811             
13812             this.collapse();
13813             this.fireEvent('select', this, record, index);
13814         }
13815     },
13816
13817     /**
13818      * Returns the currently selected field value or empty string if no value is set.
13819      * @return {String} value The selected value
13820      */
13821     getValue : function()
13822     {
13823         if(Roo.isIOS && this.useNativeIOS){
13824             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13825         }
13826         
13827         if(this.multiple){
13828             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13829         }
13830         
13831         if(this.valueField){
13832             return typeof this.value != 'undefined' ? this.value : '';
13833         }else{
13834             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13835         }
13836     },
13837     
13838     getRawValue : function()
13839     {
13840         if(Roo.isIOS && this.useNativeIOS){
13841             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13842         }
13843         
13844         var v = this.inputEl().getValue();
13845         
13846         return v;
13847     },
13848
13849     /**
13850      * Clears any text/value currently set in the field
13851      */
13852     clearValue : function(){
13853         
13854         if(this.hiddenField){
13855             this.hiddenField.dom.value = '';
13856         }
13857         this.value = '';
13858         this.setRawValue('');
13859         this.lastSelectionText = '';
13860         this.lastData = false;
13861         
13862         var close = this.closeTriggerEl();
13863         
13864         if(close){
13865             close.hide();
13866         }
13867         
13868         this.validate();
13869         
13870     },
13871
13872     /**
13873      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13874      * will be displayed in the field.  If the value does not match the data value of an existing item,
13875      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13876      * Otherwise the field will be blank (although the value will still be set).
13877      * @param {String} value The value to match
13878      */
13879     setValue : function(v)
13880     {
13881         if(Roo.isIOS && this.useNativeIOS){
13882             this.setIOSValue(v);
13883             return;
13884         }
13885         
13886         if(this.multiple){
13887             this.syncValue();
13888             return;
13889         }
13890         
13891         var text = v;
13892         if(this.valueField){
13893             var r = this.findRecord(this.valueField, v);
13894             if(r){
13895                 text = r.data[this.displayField];
13896             }else if(this.valueNotFoundText !== undefined){
13897                 text = this.valueNotFoundText;
13898             }
13899         }
13900         this.lastSelectionText = text;
13901         if(this.hiddenField){
13902             this.hiddenField.dom.value = v;
13903         }
13904         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13905         this.value = v;
13906         
13907         var close = this.closeTriggerEl();
13908         
13909         if(close){
13910             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13911         }
13912         
13913         this.validate();
13914     },
13915     /**
13916      * @property {Object} the last set data for the element
13917      */
13918     
13919     lastData : false,
13920     /**
13921      * Sets the value of the field based on a object which is related to the record format for the store.
13922      * @param {Object} value the value to set as. or false on reset?
13923      */
13924     setFromData : function(o){
13925         
13926         if(this.multiple){
13927             this.addItem(o);
13928             return;
13929         }
13930             
13931         var dv = ''; // display value
13932         var vv = ''; // value value..
13933         this.lastData = o;
13934         if (this.displayField) {
13935             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13936         } else {
13937             // this is an error condition!!!
13938             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13939         }
13940         
13941         if(this.valueField){
13942             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13943         }
13944         
13945         var close = this.closeTriggerEl();
13946         
13947         if(close){
13948             if(dv.length || vv * 1 > 0){
13949                 close.show() ;
13950                 this.blockFocus=true;
13951             } else {
13952                 close.hide();
13953             }             
13954         }
13955         
13956         if(this.hiddenField){
13957             this.hiddenField.dom.value = vv;
13958             
13959             this.lastSelectionText = dv;
13960             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13961             this.value = vv;
13962             return;
13963         }
13964         // no hidden field.. - we store the value in 'value', but still display
13965         // display field!!!!
13966         this.lastSelectionText = dv;
13967         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13968         this.value = vv;
13969         
13970         
13971         
13972     },
13973     // private
13974     reset : function(){
13975         // overridden so that last data is reset..
13976         
13977         if(this.multiple){
13978             this.clearItem();
13979             return;
13980         }
13981         
13982         this.setValue(this.originalValue);
13983         //this.clearInvalid();
13984         this.lastData = false;
13985         if (this.view) {
13986             this.view.clearSelections();
13987         }
13988         
13989         this.validate();
13990     },
13991     // private
13992     findRecord : function(prop, value){
13993         var record;
13994         if(this.store.getCount() > 0){
13995             this.store.each(function(r){
13996                 if(r.data[prop] == value){
13997                     record = r;
13998                     return false;
13999                 }
14000                 return true;
14001             });
14002         }
14003         return record;
14004     },
14005     
14006     getName: function()
14007     {
14008         // returns hidden if it's set..
14009         if (!this.rendered) {return ''};
14010         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14011         
14012     },
14013     // private
14014     onViewMove : function(e, t){
14015         this.inKeyMode = false;
14016     },
14017
14018     // private
14019     onViewOver : function(e, t){
14020         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14021             return;
14022         }
14023         var item = this.view.findItemFromChild(t);
14024         
14025         if(item){
14026             var index = this.view.indexOf(item);
14027             this.select(index, false);
14028         }
14029     },
14030
14031     // private
14032     onViewClick : function(view, doFocus, el, e)
14033     {
14034         var index = this.view.getSelectedIndexes()[0];
14035         
14036         var r = this.store.getAt(index);
14037         
14038         if(this.tickable){
14039             
14040             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14041                 return;
14042             }
14043             
14044             var rm = false;
14045             var _this = this;
14046             
14047             Roo.each(this.tickItems, function(v,k){
14048                 
14049                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14050                     Roo.log(v);
14051                     _this.tickItems.splice(k, 1);
14052                     
14053                     if(typeof(e) == 'undefined' && view == false){
14054                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14055                     }
14056                     
14057                     rm = true;
14058                     return;
14059                 }
14060             });
14061             
14062             if(rm){
14063                 return;
14064             }
14065             
14066             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14067                 this.tickItems.push(r.data);
14068             }
14069             
14070             if(typeof(e) == 'undefined' && view == false){
14071                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14072             }
14073                     
14074             return;
14075         }
14076         
14077         if(r){
14078             this.onSelect(r, index);
14079         }
14080         if(doFocus !== false && !this.blockFocus){
14081             this.inputEl().focus();
14082         }
14083     },
14084
14085     // private
14086     restrictHeight : function(){
14087         //this.innerList.dom.style.height = '';
14088         //var inner = this.innerList.dom;
14089         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14090         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14091         //this.list.beginUpdate();
14092         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14093         this.list.alignTo(this.inputEl(), this.listAlign);
14094         this.list.alignTo(this.inputEl(), this.listAlign);
14095         //this.list.endUpdate();
14096     },
14097
14098     // private
14099     onEmptyResults : function(){
14100         
14101         if(this.tickable && this.editable){
14102             this.hasFocus = false;
14103             this.restrictHeight();
14104             return;
14105         }
14106         
14107         this.collapse();
14108     },
14109
14110     /**
14111      * Returns true if the dropdown list is expanded, else false.
14112      */
14113     isExpanded : function(){
14114         return this.list.isVisible();
14115     },
14116
14117     /**
14118      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14119      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14120      * @param {String} value The data value of the item to select
14121      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14122      * selected item if it is not currently in view (defaults to true)
14123      * @return {Boolean} True if the value matched an item in the list, else false
14124      */
14125     selectByValue : function(v, scrollIntoView){
14126         if(v !== undefined && v !== null){
14127             var r = this.findRecord(this.valueField || this.displayField, v);
14128             if(r){
14129                 this.select(this.store.indexOf(r), scrollIntoView);
14130                 return true;
14131             }
14132         }
14133         return false;
14134     },
14135
14136     /**
14137      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14138      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14139      * @param {Number} index The zero-based index of the list item to select
14140      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14141      * selected item if it is not currently in view (defaults to true)
14142      */
14143     select : function(index, scrollIntoView){
14144         this.selectedIndex = index;
14145         this.view.select(index);
14146         if(scrollIntoView !== false){
14147             var el = this.view.getNode(index);
14148             /*
14149              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14150              */
14151             if(el){
14152                 this.list.scrollChildIntoView(el, false);
14153             }
14154         }
14155     },
14156
14157     // private
14158     selectNext : function(){
14159         var ct = this.store.getCount();
14160         if(ct > 0){
14161             if(this.selectedIndex == -1){
14162                 this.select(0);
14163             }else if(this.selectedIndex < ct-1){
14164                 this.select(this.selectedIndex+1);
14165             }
14166         }
14167     },
14168
14169     // private
14170     selectPrev : function(){
14171         var ct = this.store.getCount();
14172         if(ct > 0){
14173             if(this.selectedIndex == -1){
14174                 this.select(0);
14175             }else if(this.selectedIndex != 0){
14176                 this.select(this.selectedIndex-1);
14177             }
14178         }
14179     },
14180
14181     // private
14182     onKeyUp : function(e){
14183         if(this.editable !== false && !e.isSpecialKey()){
14184             this.lastKey = e.getKey();
14185             this.dqTask.delay(this.queryDelay);
14186         }
14187     },
14188
14189     // private
14190     validateBlur : function(){
14191         return !this.list || !this.list.isVisible();   
14192     },
14193
14194     // private
14195     initQuery : function(){
14196         
14197         var v = this.getRawValue();
14198         
14199         if(this.tickable && this.editable){
14200             v = this.tickableInputEl().getValue();
14201         }
14202         
14203         this.doQuery(v);
14204     },
14205
14206     // private
14207     doForce : function(){
14208         if(this.inputEl().dom.value.length > 0){
14209             this.inputEl().dom.value =
14210                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14211              
14212         }
14213     },
14214
14215     /**
14216      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14217      * query allowing the query action to be canceled if needed.
14218      * @param {String} query The SQL query to execute
14219      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14220      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14221      * saved in the current store (defaults to false)
14222      */
14223     doQuery : function(q, forceAll){
14224         
14225         if(q === undefined || q === null){
14226             q = '';
14227         }
14228         var qe = {
14229             query: q,
14230             forceAll: forceAll,
14231             combo: this,
14232             cancel:false
14233         };
14234         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14235             return false;
14236         }
14237         q = qe.query;
14238         
14239         forceAll = qe.forceAll;
14240         if(forceAll === true || (q.length >= this.minChars)){
14241             
14242             this.hasQuery = true;
14243             
14244             if(this.lastQuery != q || this.alwaysQuery){
14245                 this.lastQuery = q;
14246                 if(this.mode == 'local'){
14247                     this.selectedIndex = -1;
14248                     if(forceAll){
14249                         this.store.clearFilter();
14250                     }else{
14251                         
14252                         if(this.specialFilter){
14253                             this.fireEvent('specialfilter', this);
14254                             this.onLoad();
14255                             return;
14256                         }
14257                         
14258                         this.store.filter(this.displayField, q);
14259                     }
14260                     
14261                     this.store.fireEvent("datachanged", this.store);
14262                     
14263                     this.onLoad();
14264                     
14265                     
14266                 }else{
14267                     
14268                     this.store.baseParams[this.queryParam] = q;
14269                     
14270                     var options = {params : this.getParams(q)};
14271                     
14272                     if(this.loadNext){
14273                         options.add = true;
14274                         options.params.start = this.page * this.pageSize;
14275                     }
14276                     
14277                     this.store.load(options);
14278                     
14279                     /*
14280                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14281                      *  we should expand the list on onLoad
14282                      *  so command out it
14283                      */
14284 //                    this.expand();
14285                 }
14286             }else{
14287                 this.selectedIndex = -1;
14288                 this.onLoad();   
14289             }
14290         }
14291         
14292         this.loadNext = false;
14293     },
14294     
14295     // private
14296     getParams : function(q){
14297         var p = {};
14298         //p[this.queryParam] = q;
14299         
14300         if(this.pageSize){
14301             p.start = 0;
14302             p.limit = this.pageSize;
14303         }
14304         return p;
14305     },
14306
14307     /**
14308      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14309      */
14310     collapse : function(){
14311         if(!this.isExpanded()){
14312             return;
14313         }
14314         
14315         this.list.hide();
14316         
14317         this.hasFocus = false;
14318         
14319         if(this.tickable){
14320             this.okBtn.hide();
14321             this.cancelBtn.hide();
14322             this.trigger.show();
14323             
14324             if(this.editable){
14325                 this.tickableInputEl().dom.value = '';
14326                 this.tickableInputEl().blur();
14327             }
14328             
14329         }
14330         
14331         Roo.get(document).un('mousedown', this.collapseIf, this);
14332         Roo.get(document).un('mousewheel', this.collapseIf, this);
14333         if (!this.editable) {
14334             Roo.get(document).un('keydown', this.listKeyPress, this);
14335         }
14336         this.fireEvent('collapse', this);
14337         
14338         this.validate();
14339     },
14340
14341     // private
14342     collapseIf : function(e){
14343         var in_combo  = e.within(this.el);
14344         var in_list =  e.within(this.list);
14345         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14346         
14347         if (in_combo || in_list || is_list) {
14348             //e.stopPropagation();
14349             return;
14350         }
14351         
14352         if(this.tickable){
14353             this.onTickableFooterButtonClick(e, false, false);
14354         }
14355
14356         this.collapse();
14357         
14358     },
14359
14360     /**
14361      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14362      */
14363     expand : function(){
14364        
14365         if(this.isExpanded() || !this.hasFocus){
14366             return;
14367         }
14368         
14369         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14370         this.list.setWidth(lw);
14371         
14372         Roo.log('expand');
14373         
14374         this.list.show();
14375         
14376         this.restrictHeight();
14377         
14378         if(this.tickable){
14379             
14380             this.tickItems = Roo.apply([], this.item);
14381             
14382             this.okBtn.show();
14383             this.cancelBtn.show();
14384             this.trigger.hide();
14385             
14386             if(this.editable){
14387                 this.tickableInputEl().focus();
14388             }
14389             
14390         }
14391         
14392         Roo.get(document).on('mousedown', this.collapseIf, this);
14393         Roo.get(document).on('mousewheel', this.collapseIf, this);
14394         if (!this.editable) {
14395             Roo.get(document).on('keydown', this.listKeyPress, this);
14396         }
14397         
14398         this.fireEvent('expand', this);
14399     },
14400
14401     // private
14402     // Implements the default empty TriggerField.onTriggerClick function
14403     onTriggerClick : function(e)
14404     {
14405         Roo.log('trigger click');
14406         
14407         if(this.disabled || !this.triggerList){
14408             return;
14409         }
14410         
14411         this.page = 0;
14412         this.loadNext = false;
14413         
14414         if(this.isExpanded()){
14415             this.collapse();
14416             if (!this.blockFocus) {
14417                 this.inputEl().focus();
14418             }
14419             
14420         }else {
14421             this.hasFocus = true;
14422             if(this.triggerAction == 'all') {
14423                 this.doQuery(this.allQuery, true);
14424             } else {
14425                 this.doQuery(this.getRawValue());
14426             }
14427             if (!this.blockFocus) {
14428                 this.inputEl().focus();
14429             }
14430         }
14431     },
14432     
14433     onTickableTriggerClick : function(e)
14434     {
14435         if(this.disabled){
14436             return;
14437         }
14438         
14439         this.page = 0;
14440         this.loadNext = false;
14441         this.hasFocus = true;
14442         
14443         if(this.triggerAction == 'all') {
14444             this.doQuery(this.allQuery, true);
14445         } else {
14446             this.doQuery(this.getRawValue());
14447         }
14448     },
14449     
14450     onSearchFieldClick : function(e)
14451     {
14452         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14453             this.onTickableFooterButtonClick(e, false, false);
14454             return;
14455         }
14456         
14457         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14458             return;
14459         }
14460         
14461         this.page = 0;
14462         this.loadNext = false;
14463         this.hasFocus = true;
14464         
14465         if(this.triggerAction == 'all') {
14466             this.doQuery(this.allQuery, true);
14467         } else {
14468             this.doQuery(this.getRawValue());
14469         }
14470     },
14471     
14472     listKeyPress : function(e)
14473     {
14474         //Roo.log('listkeypress');
14475         // scroll to first matching element based on key pres..
14476         if (e.isSpecialKey()) {
14477             return false;
14478         }
14479         var k = String.fromCharCode(e.getKey()).toUpperCase();
14480         //Roo.log(k);
14481         var match  = false;
14482         var csel = this.view.getSelectedNodes();
14483         var cselitem = false;
14484         if (csel.length) {
14485             var ix = this.view.indexOf(csel[0]);
14486             cselitem  = this.store.getAt(ix);
14487             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14488                 cselitem = false;
14489             }
14490             
14491         }
14492         
14493         this.store.each(function(v) { 
14494             if (cselitem) {
14495                 // start at existing selection.
14496                 if (cselitem.id == v.id) {
14497                     cselitem = false;
14498                 }
14499                 return true;
14500             }
14501                 
14502             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14503                 match = this.store.indexOf(v);
14504                 return false;
14505             }
14506             return true;
14507         }, this);
14508         
14509         if (match === false) {
14510             return true; // no more action?
14511         }
14512         // scroll to?
14513         this.view.select(match);
14514         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14515         sn.scrollIntoView(sn.dom.parentNode, false);
14516     },
14517     
14518     onViewScroll : function(e, t){
14519         
14520         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){
14521             return;
14522         }
14523         
14524         this.hasQuery = true;
14525         
14526         this.loading = this.list.select('.loading', true).first();
14527         
14528         if(this.loading === null){
14529             this.list.createChild({
14530                 tag: 'div',
14531                 cls: 'loading roo-select2-more-results roo-select2-active',
14532                 html: 'Loading more results...'
14533             });
14534             
14535             this.loading = this.list.select('.loading', true).first();
14536             
14537             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14538             
14539             this.loading.hide();
14540         }
14541         
14542         this.loading.show();
14543         
14544         var _combo = this;
14545         
14546         this.page++;
14547         this.loadNext = true;
14548         
14549         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14550         
14551         return;
14552     },
14553     
14554     addItem : function(o)
14555     {   
14556         var dv = ''; // display value
14557         
14558         if (this.displayField) {
14559             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14560         } else {
14561             // this is an error condition!!!
14562             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14563         }
14564         
14565         if(!dv.length){
14566             return;
14567         }
14568         
14569         var choice = this.choices.createChild({
14570             tag: 'li',
14571             cls: 'roo-select2-search-choice',
14572             cn: [
14573                 {
14574                     tag: 'div',
14575                     html: dv
14576                 },
14577                 {
14578                     tag: 'a',
14579                     href: '#',
14580                     cls: 'roo-select2-search-choice-close fa fa-times',
14581                     tabindex: '-1'
14582                 }
14583             ]
14584             
14585         }, this.searchField);
14586         
14587         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14588         
14589         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14590         
14591         this.item.push(o);
14592         
14593         this.lastData = o;
14594         
14595         this.syncValue();
14596         
14597         this.inputEl().dom.value = '';
14598         
14599         this.validate();
14600     },
14601     
14602     onRemoveItem : function(e, _self, o)
14603     {
14604         e.preventDefault();
14605         
14606         this.lastItem = Roo.apply([], this.item);
14607         
14608         var index = this.item.indexOf(o.data) * 1;
14609         
14610         if( index < 0){
14611             Roo.log('not this item?!');
14612             return;
14613         }
14614         
14615         this.item.splice(index, 1);
14616         o.item.remove();
14617         
14618         this.syncValue();
14619         
14620         this.fireEvent('remove', this, e);
14621         
14622         this.validate();
14623         
14624     },
14625     
14626     syncValue : function()
14627     {
14628         if(!this.item.length){
14629             this.clearValue();
14630             return;
14631         }
14632             
14633         var value = [];
14634         var _this = this;
14635         Roo.each(this.item, function(i){
14636             if(_this.valueField){
14637                 value.push(i[_this.valueField]);
14638                 return;
14639             }
14640
14641             value.push(i);
14642         });
14643
14644         this.value = value.join(',');
14645
14646         if(this.hiddenField){
14647             this.hiddenField.dom.value = this.value;
14648         }
14649         
14650         this.store.fireEvent("datachanged", this.store);
14651         
14652         this.validate();
14653     },
14654     
14655     clearItem : function()
14656     {
14657         if(!this.multiple){
14658             return;
14659         }
14660         
14661         this.item = [];
14662         
14663         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14664            c.remove();
14665         });
14666         
14667         this.syncValue();
14668         
14669         this.validate();
14670         
14671         if(this.tickable && !Roo.isTouch){
14672             this.view.refresh();
14673         }
14674     },
14675     
14676     inputEl: function ()
14677     {
14678         if(Roo.isIOS && this.useNativeIOS){
14679             return this.el.select('select.roo-ios-select', true).first();
14680         }
14681         
14682         if(Roo.isTouch && this.mobileTouchView){
14683             return this.el.select('input.form-control',true).first();
14684         }
14685         
14686         if(this.tickable){
14687             return this.searchField;
14688         }
14689         
14690         return this.el.select('input.form-control',true).first();
14691     },
14692     
14693     onTickableFooterButtonClick : function(e, btn, el)
14694     {
14695         e.preventDefault();
14696         
14697         this.lastItem = Roo.apply([], this.item);
14698         
14699         if(btn && btn.name == 'cancel'){
14700             this.tickItems = Roo.apply([], this.item);
14701             this.collapse();
14702             return;
14703         }
14704         
14705         this.clearItem();
14706         
14707         var _this = this;
14708         
14709         Roo.each(this.tickItems, function(o){
14710             _this.addItem(o);
14711         });
14712         
14713         this.collapse();
14714         
14715     },
14716     
14717     validate : function()
14718     {
14719         if(this.getVisibilityEl().hasClass('hidden')){
14720             return true;
14721         }
14722         
14723         var v = this.getRawValue();
14724         
14725         if(this.multiple){
14726             v = this.getValue();
14727         }
14728         
14729         if(this.disabled || this.allowBlank || v.length){
14730             this.markValid();
14731             return true;
14732         }
14733         
14734         this.markInvalid();
14735         return false;
14736     },
14737     
14738     tickableInputEl : function()
14739     {
14740         if(!this.tickable || !this.editable){
14741             return this.inputEl();
14742         }
14743         
14744         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14745     },
14746     
14747     
14748     getAutoCreateTouchView : function()
14749     {
14750         var id = Roo.id();
14751         
14752         var cfg = {
14753             cls: 'form-group' //input-group
14754         };
14755         
14756         var input =  {
14757             tag: 'input',
14758             id : id,
14759             type : this.inputType,
14760             cls : 'form-control x-combo-noedit',
14761             autocomplete: 'new-password',
14762             placeholder : this.placeholder || '',
14763             readonly : true
14764         };
14765         
14766         if (this.name) {
14767             input.name = this.name;
14768         }
14769         
14770         if (this.size) {
14771             input.cls += ' input-' + this.size;
14772         }
14773         
14774         if (this.disabled) {
14775             input.disabled = true;
14776         }
14777         
14778         var inputblock = {
14779             cls : '',
14780             cn : [
14781                 input
14782             ]
14783         };
14784         
14785         if(this.before){
14786             inputblock.cls += ' input-group';
14787             
14788             inputblock.cn.unshift({
14789                 tag :'span',
14790                 cls : 'input-group-addon',
14791                 html : this.before
14792             });
14793         }
14794         
14795         if(this.removable && !this.multiple){
14796             inputblock.cls += ' roo-removable';
14797             
14798             inputblock.cn.push({
14799                 tag: 'button',
14800                 html : 'x',
14801                 cls : 'roo-combo-removable-btn close'
14802             });
14803         }
14804
14805         if(this.hasFeedback && !this.allowBlank){
14806             
14807             inputblock.cls += ' has-feedback';
14808             
14809             inputblock.cn.push({
14810                 tag: 'span',
14811                 cls: 'glyphicon form-control-feedback'
14812             });
14813             
14814         }
14815         
14816         if (this.after) {
14817             
14818             inputblock.cls += (this.before) ? '' : ' input-group';
14819             
14820             inputblock.cn.push({
14821                 tag :'span',
14822                 cls : 'input-group-addon',
14823                 html : this.after
14824             });
14825         }
14826
14827         var box = {
14828             tag: 'div',
14829             cn: [
14830                 {
14831                     tag: 'input',
14832                     type : 'hidden',
14833                     cls: 'form-hidden-field'
14834                 },
14835                 inputblock
14836             ]
14837             
14838         };
14839         
14840         if(this.multiple){
14841             box = {
14842                 tag: 'div',
14843                 cn: [
14844                     {
14845                         tag: 'input',
14846                         type : 'hidden',
14847                         cls: 'form-hidden-field'
14848                     },
14849                     {
14850                         tag: 'ul',
14851                         cls: 'roo-select2-choices',
14852                         cn:[
14853                             {
14854                                 tag: 'li',
14855                                 cls: 'roo-select2-search-field',
14856                                 cn: [
14857
14858                                     inputblock
14859                                 ]
14860                             }
14861                         ]
14862                     }
14863                 ]
14864             }
14865         };
14866         
14867         var combobox = {
14868             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14869             cn: [
14870                 box
14871             ]
14872         };
14873         
14874         if(!this.multiple && this.showToggleBtn){
14875             
14876             var caret = {
14877                         tag: 'span',
14878                         cls: 'caret'
14879             };
14880             
14881             if (this.caret != false) {
14882                 caret = {
14883                      tag: 'i',
14884                      cls: 'fa fa-' + this.caret
14885                 };
14886                 
14887             }
14888             
14889             combobox.cn.push({
14890                 tag :'span',
14891                 cls : 'input-group-addon btn dropdown-toggle',
14892                 cn : [
14893                     caret,
14894                     {
14895                         tag: 'span',
14896                         cls: 'combobox-clear',
14897                         cn  : [
14898                             {
14899                                 tag : 'i',
14900                                 cls: 'icon-remove'
14901                             }
14902                         ]
14903                     }
14904                 ]
14905
14906             })
14907         }
14908         
14909         if(this.multiple){
14910             combobox.cls += ' roo-select2-container-multi';
14911         }
14912         
14913         var align = this.labelAlign || this.parentLabelAlign();
14914         
14915         if (align ==='left' && this.fieldLabel.length) {
14916
14917             cfg.cn = [
14918                 {
14919                    tag : 'i',
14920                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14921                    tooltip : 'This field is required'
14922                 },
14923                 {
14924                     tag: 'label',
14925                     cls : 'control-label',
14926                     html : this.fieldLabel
14927
14928                 },
14929                 {
14930                     cls : '', 
14931                     cn: [
14932                         combobox
14933                     ]
14934                 }
14935             ];
14936             
14937             var labelCfg = cfg.cn[1];
14938             var contentCfg = cfg.cn[2];
14939             
14940
14941             if(this.indicatorpos == 'right'){
14942                 cfg.cn = [
14943                     {
14944                         tag: 'label',
14945                         'for' :  id,
14946                         cls : 'control-label',
14947                         cn : [
14948                             {
14949                                 tag : 'span',
14950                                 html : this.fieldLabel
14951                             },
14952                             {
14953                                 tag : 'i',
14954                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14955                                 tooltip : 'This field is required'
14956                             }
14957                         ]
14958                     },
14959                     {
14960                         cls : "",
14961                         cn: [
14962                             combobox
14963                         ]
14964                     }
14965
14966                 ];
14967                 
14968                 labelCfg = cfg.cn[0];
14969                 contentCfg = cfg.cn[1];
14970             }
14971             
14972            
14973             
14974             if(this.labelWidth > 12){
14975                 labelCfg.style = "width: " + this.labelWidth + 'px';
14976             }
14977             
14978             if(this.labelWidth < 13 && this.labelmd == 0){
14979                 this.labelmd = this.labelWidth;
14980             }
14981             
14982             if(this.labellg > 0){
14983                 labelCfg.cls += ' col-lg-' + this.labellg;
14984                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14985             }
14986             
14987             if(this.labelmd > 0){
14988                 labelCfg.cls += ' col-md-' + this.labelmd;
14989                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14990             }
14991             
14992             if(this.labelsm > 0){
14993                 labelCfg.cls += ' col-sm-' + this.labelsm;
14994                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14995             }
14996             
14997             if(this.labelxs > 0){
14998                 labelCfg.cls += ' col-xs-' + this.labelxs;
14999                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15000             }
15001                 
15002                 
15003         } else if ( this.fieldLabel.length) {
15004             cfg.cn = [
15005                 {
15006                    tag : 'i',
15007                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15008                    tooltip : 'This field is required'
15009                 },
15010                 {
15011                     tag: 'label',
15012                     cls : 'control-label',
15013                     html : this.fieldLabel
15014
15015                 },
15016                 {
15017                     cls : '', 
15018                     cn: [
15019                         combobox
15020                     ]
15021                 }
15022             ];
15023             
15024             if(this.indicatorpos == 'right'){
15025                 cfg.cn = [
15026                     {
15027                         tag: 'label',
15028                         cls : 'control-label',
15029                         html : this.fieldLabel,
15030                         cn : [
15031                             {
15032                                tag : 'i',
15033                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15034                                tooltip : 'This field is required'
15035                             }
15036                         ]
15037                     },
15038                     {
15039                         cls : '', 
15040                         cn: [
15041                             combobox
15042                         ]
15043                     }
15044                 ];
15045             }
15046         } else {
15047             cfg.cn = combobox;    
15048         }
15049         
15050         
15051         var settings = this;
15052         
15053         ['xs','sm','md','lg'].map(function(size){
15054             if (settings[size]) {
15055                 cfg.cls += ' col-' + size + '-' + settings[size];
15056             }
15057         });
15058         
15059         return cfg;
15060     },
15061     
15062     initTouchView : function()
15063     {
15064         this.renderTouchView();
15065         
15066         this.touchViewEl.on('scroll', function(){
15067             this.el.dom.scrollTop = 0;
15068         }, this);
15069         
15070         this.originalValue = this.getValue();
15071         
15072         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15073         
15074         this.inputEl().on("click", this.showTouchView, this);
15075         if (this.triggerEl) {
15076             this.triggerEl.on("click", this.showTouchView, this);
15077         }
15078         
15079         
15080         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15081         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15082         
15083         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15084         
15085         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15086         this.store.on('load', this.onTouchViewLoad, this);
15087         this.store.on('loadexception', this.onTouchViewLoadException, this);
15088         
15089         if(this.hiddenName){
15090             
15091             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15092             
15093             this.hiddenField.dom.value =
15094                 this.hiddenValue !== undefined ? this.hiddenValue :
15095                 this.value !== undefined ? this.value : '';
15096         
15097             this.el.dom.removeAttribute('name');
15098             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15099         }
15100         
15101         if(this.multiple){
15102             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15103             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15104         }
15105         
15106         if(this.removable && !this.multiple){
15107             var close = this.closeTriggerEl();
15108             if(close){
15109                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15110                 close.on('click', this.removeBtnClick, this, close);
15111             }
15112         }
15113         /*
15114          * fix the bug in Safari iOS8
15115          */
15116         this.inputEl().on("focus", function(e){
15117             document.activeElement.blur();
15118         }, this);
15119         
15120         return;
15121         
15122         
15123     },
15124     
15125     renderTouchView : function()
15126     {
15127         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15128         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15129         
15130         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15131         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15132         
15133         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15134         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15135         this.touchViewBodyEl.setStyle('overflow', 'auto');
15136         
15137         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15138         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15139         
15140         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15141         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15142         
15143     },
15144     
15145     showTouchView : function()
15146     {
15147         if(this.disabled){
15148             return;
15149         }
15150         
15151         this.touchViewHeaderEl.hide();
15152
15153         if(this.modalTitle.length){
15154             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15155             this.touchViewHeaderEl.show();
15156         }
15157
15158         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15159         this.touchViewEl.show();
15160
15161         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15162         
15163         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15164         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15165
15166         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15167
15168         if(this.modalTitle.length){
15169             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15170         }
15171         
15172         this.touchViewBodyEl.setHeight(bodyHeight);
15173
15174         if(this.animate){
15175             var _this = this;
15176             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15177         }else{
15178             this.touchViewEl.addClass('in');
15179         }
15180
15181         this.doTouchViewQuery();
15182         
15183     },
15184     
15185     hideTouchView : function()
15186     {
15187         this.touchViewEl.removeClass('in');
15188
15189         if(this.animate){
15190             var _this = this;
15191             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15192         }else{
15193             this.touchViewEl.setStyle('display', 'none');
15194         }
15195         
15196     },
15197     
15198     setTouchViewValue : function()
15199     {
15200         if(this.multiple){
15201             this.clearItem();
15202         
15203             var _this = this;
15204
15205             Roo.each(this.tickItems, function(o){
15206                 this.addItem(o);
15207             }, this);
15208         }
15209         
15210         this.hideTouchView();
15211     },
15212     
15213     doTouchViewQuery : function()
15214     {
15215         var qe = {
15216             query: '',
15217             forceAll: true,
15218             combo: this,
15219             cancel:false
15220         };
15221         
15222         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15223             return false;
15224         }
15225         
15226         if(!this.alwaysQuery || this.mode == 'local'){
15227             this.onTouchViewLoad();
15228             return;
15229         }
15230         
15231         this.store.load();
15232     },
15233     
15234     onTouchViewBeforeLoad : function(combo,opts)
15235     {
15236         return;
15237     },
15238
15239     // private
15240     onTouchViewLoad : function()
15241     {
15242         if(this.store.getCount() < 1){
15243             this.onTouchViewEmptyResults();
15244             return;
15245         }
15246         
15247         this.clearTouchView();
15248         
15249         var rawValue = this.getRawValue();
15250         
15251         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15252         
15253         this.tickItems = [];
15254         
15255         this.store.data.each(function(d, rowIndex){
15256             var row = this.touchViewListGroup.createChild(template);
15257             
15258             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15259                 row.addClass(d.data.cls);
15260             }
15261             
15262             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15263                 var cfg = {
15264                     data : d.data,
15265                     html : d.data[this.displayField]
15266                 };
15267                 
15268                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15269                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15270                 }
15271             }
15272             row.removeClass('selected');
15273             if(!this.multiple && this.valueField &&
15274                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15275             {
15276                 // radio buttons..
15277                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15278                 row.addClass('selected');
15279             }
15280             
15281             if(this.multiple && this.valueField &&
15282                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15283             {
15284                 
15285                 // checkboxes...
15286                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15287                 this.tickItems.push(d.data);
15288             }
15289             
15290             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15291             
15292         }, this);
15293         
15294         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15295         
15296         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15297
15298         if(this.modalTitle.length){
15299             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15300         }
15301
15302         var listHeight = this.touchViewListGroup.getHeight();
15303         
15304         var _this = this;
15305         
15306         if(firstChecked && listHeight > bodyHeight){
15307             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15308         }
15309         
15310     },
15311     
15312     onTouchViewLoadException : function()
15313     {
15314         this.hideTouchView();
15315     },
15316     
15317     onTouchViewEmptyResults : function()
15318     {
15319         this.clearTouchView();
15320         
15321         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15322         
15323         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15324         
15325     },
15326     
15327     clearTouchView : function()
15328     {
15329         this.touchViewListGroup.dom.innerHTML = '';
15330     },
15331     
15332     onTouchViewClick : function(e, el, o)
15333     {
15334         e.preventDefault();
15335         
15336         var row = o.row;
15337         var rowIndex = o.rowIndex;
15338         
15339         var r = this.store.getAt(rowIndex);
15340         
15341         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15342             
15343             if(!this.multiple){
15344                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15345                     c.dom.removeAttribute('checked');
15346                 }, this);
15347
15348                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15349
15350                 this.setFromData(r.data);
15351
15352                 var close = this.closeTriggerEl();
15353
15354                 if(close){
15355                     close.show();
15356                 }
15357
15358                 this.hideTouchView();
15359
15360                 this.fireEvent('select', this, r, rowIndex);
15361
15362                 return;
15363             }
15364
15365             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15366                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15367                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15368                 return;
15369             }
15370
15371             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15372             this.addItem(r.data);
15373             this.tickItems.push(r.data);
15374         }
15375     },
15376     
15377     getAutoCreateNativeIOS : function()
15378     {
15379         var cfg = {
15380             cls: 'form-group' //input-group,
15381         };
15382         
15383         var combobox =  {
15384             tag: 'select',
15385             cls : 'roo-ios-select'
15386         };
15387         
15388         if (this.name) {
15389             combobox.name = this.name;
15390         }
15391         
15392         if (this.disabled) {
15393             combobox.disabled = true;
15394         }
15395         
15396         var settings = this;
15397         
15398         ['xs','sm','md','lg'].map(function(size){
15399             if (settings[size]) {
15400                 cfg.cls += ' col-' + size + '-' + settings[size];
15401             }
15402         });
15403         
15404         cfg.cn = combobox;
15405         
15406         return cfg;
15407         
15408     },
15409     
15410     initIOSView : function()
15411     {
15412         this.store.on('load', this.onIOSViewLoad, this);
15413         
15414         return;
15415     },
15416     
15417     onIOSViewLoad : function()
15418     {
15419         if(this.store.getCount() < 1){
15420             return;
15421         }
15422         
15423         this.clearIOSView();
15424         
15425         if(this.allowBlank) {
15426             
15427             var default_text = '-- SELECT --';
15428             
15429             if(this.placeholder.length){
15430                 default_text = this.placeholder;
15431             }
15432             
15433             if(this.emptyTitle.length){
15434                 default_text += ' - ' + this.emptyTitle + ' -';
15435             }
15436             
15437             var opt = this.inputEl().createChild({
15438                 tag: 'option',
15439                 value : 0,
15440                 html : default_text
15441             });
15442             
15443             var o = {};
15444             o[this.valueField] = 0;
15445             o[this.displayField] = default_text;
15446             
15447             this.ios_options.push({
15448                 data : o,
15449                 el : opt
15450             });
15451             
15452         }
15453         
15454         this.store.data.each(function(d, rowIndex){
15455             
15456             var html = '';
15457             
15458             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15459                 html = d.data[this.displayField];
15460             }
15461             
15462             var value = '';
15463             
15464             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15465                 value = d.data[this.valueField];
15466             }
15467             
15468             var option = {
15469                 tag: 'option',
15470                 value : value,
15471                 html : html
15472             };
15473             
15474             if(this.value == d.data[this.valueField]){
15475                 option['selected'] = true;
15476             }
15477             
15478             var opt = this.inputEl().createChild(option);
15479             
15480             this.ios_options.push({
15481                 data : d.data,
15482                 el : opt
15483             });
15484             
15485         }, this);
15486         
15487         this.inputEl().on('change', function(){
15488            this.fireEvent('select', this);
15489         }, this);
15490         
15491     },
15492     
15493     clearIOSView: function()
15494     {
15495         this.inputEl().dom.innerHTML = '';
15496         
15497         this.ios_options = [];
15498     },
15499     
15500     setIOSValue: function(v)
15501     {
15502         this.value = v;
15503         
15504         if(!this.ios_options){
15505             return;
15506         }
15507         
15508         Roo.each(this.ios_options, function(opts){
15509            
15510            opts.el.dom.removeAttribute('selected');
15511            
15512            if(opts.data[this.valueField] != v){
15513                return;
15514            }
15515            
15516            opts.el.dom.setAttribute('selected', true);
15517            
15518         }, this);
15519     }
15520
15521     /** 
15522     * @cfg {Boolean} grow 
15523     * @hide 
15524     */
15525     /** 
15526     * @cfg {Number} growMin 
15527     * @hide 
15528     */
15529     /** 
15530     * @cfg {Number} growMax 
15531     * @hide 
15532     */
15533     /**
15534      * @hide
15535      * @method autoSize
15536      */
15537 });
15538
15539 Roo.apply(Roo.bootstrap.ComboBox,  {
15540     
15541     header : {
15542         tag: 'div',
15543         cls: 'modal-header',
15544         cn: [
15545             {
15546                 tag: 'h4',
15547                 cls: 'modal-title'
15548             }
15549         ]
15550     },
15551     
15552     body : {
15553         tag: 'div',
15554         cls: 'modal-body',
15555         cn: [
15556             {
15557                 tag: 'ul',
15558                 cls: 'list-group'
15559             }
15560         ]
15561     },
15562     
15563     listItemRadio : {
15564         tag: 'li',
15565         cls: 'list-group-item',
15566         cn: [
15567             {
15568                 tag: 'span',
15569                 cls: 'roo-combobox-list-group-item-value'
15570             },
15571             {
15572                 tag: 'div',
15573                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15574                 cn: [
15575                     {
15576                         tag: 'input',
15577                         type: 'radio'
15578                     },
15579                     {
15580                         tag: 'label'
15581                     }
15582                 ]
15583             }
15584         ]
15585     },
15586     
15587     listItemCheckbox : {
15588         tag: 'li',
15589         cls: 'list-group-item',
15590         cn: [
15591             {
15592                 tag: 'span',
15593                 cls: 'roo-combobox-list-group-item-value'
15594             },
15595             {
15596                 tag: 'div',
15597                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15598                 cn: [
15599                     {
15600                         tag: 'input',
15601                         type: 'checkbox'
15602                     },
15603                     {
15604                         tag: 'label'
15605                     }
15606                 ]
15607             }
15608         ]
15609     },
15610     
15611     emptyResult : {
15612         tag: 'div',
15613         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15614     },
15615     
15616     footer : {
15617         tag: 'div',
15618         cls: 'modal-footer',
15619         cn: [
15620             {
15621                 tag: 'div',
15622                 cls: 'row',
15623                 cn: [
15624                     {
15625                         tag: 'div',
15626                         cls: 'col-xs-6 text-left',
15627                         cn: {
15628                             tag: 'button',
15629                             cls: 'btn btn-danger roo-touch-view-cancel',
15630                             html: 'Cancel'
15631                         }
15632                     },
15633                     {
15634                         tag: 'div',
15635                         cls: 'col-xs-6 text-right',
15636                         cn: {
15637                             tag: 'button',
15638                             cls: 'btn btn-success roo-touch-view-ok',
15639                             html: 'OK'
15640                         }
15641                     }
15642                 ]
15643             }
15644         ]
15645         
15646     }
15647 });
15648
15649 Roo.apply(Roo.bootstrap.ComboBox,  {
15650     
15651     touchViewTemplate : {
15652         tag: 'div',
15653         cls: 'modal fade roo-combobox-touch-view',
15654         cn: [
15655             {
15656                 tag: 'div',
15657                 cls: 'modal-dialog',
15658                 style : 'position:fixed', // we have to fix position....
15659                 cn: [
15660                     {
15661                         tag: 'div',
15662                         cls: 'modal-content',
15663                         cn: [
15664                             Roo.bootstrap.ComboBox.header,
15665                             Roo.bootstrap.ComboBox.body,
15666                             Roo.bootstrap.ComboBox.footer
15667                         ]
15668                     }
15669                 ]
15670             }
15671         ]
15672     }
15673 });/*
15674  * Based on:
15675  * Ext JS Library 1.1.1
15676  * Copyright(c) 2006-2007, Ext JS, LLC.
15677  *
15678  * Originally Released Under LGPL - original licence link has changed is not relivant.
15679  *
15680  * Fork - LGPL
15681  * <script type="text/javascript">
15682  */
15683
15684 /**
15685  * @class Roo.View
15686  * @extends Roo.util.Observable
15687  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15688  * This class also supports single and multi selection modes. <br>
15689  * Create a data model bound view:
15690  <pre><code>
15691  var store = new Roo.data.Store(...);
15692
15693  var view = new Roo.View({
15694     el : "my-element",
15695     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15696  
15697     singleSelect: true,
15698     selectedClass: "ydataview-selected",
15699     store: store
15700  });
15701
15702  // listen for node click?
15703  view.on("click", function(vw, index, node, e){
15704  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15705  });
15706
15707  // load XML data
15708  dataModel.load("foobar.xml");
15709  </code></pre>
15710  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15711  * <br><br>
15712  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15713  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15714  * 
15715  * Note: old style constructor is still suported (container, template, config)
15716  * 
15717  * @constructor
15718  * Create a new View
15719  * @param {Object} config The config object
15720  * 
15721  */
15722 Roo.View = function(config, depreciated_tpl, depreciated_config){
15723     
15724     this.parent = false;
15725     
15726     if (typeof(depreciated_tpl) == 'undefined') {
15727         // new way.. - universal constructor.
15728         Roo.apply(this, config);
15729         this.el  = Roo.get(this.el);
15730     } else {
15731         // old format..
15732         this.el  = Roo.get(config);
15733         this.tpl = depreciated_tpl;
15734         Roo.apply(this, depreciated_config);
15735     }
15736     this.wrapEl  = this.el.wrap().wrap();
15737     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15738     
15739     
15740     if(typeof(this.tpl) == "string"){
15741         this.tpl = new Roo.Template(this.tpl);
15742     } else {
15743         // support xtype ctors..
15744         this.tpl = new Roo.factory(this.tpl, Roo);
15745     }
15746     
15747     
15748     this.tpl.compile();
15749     
15750     /** @private */
15751     this.addEvents({
15752         /**
15753          * @event beforeclick
15754          * Fires before a click is processed. Returns false to cancel the default action.
15755          * @param {Roo.View} this
15756          * @param {Number} index The index of the target node
15757          * @param {HTMLElement} node The target node
15758          * @param {Roo.EventObject} e The raw event object
15759          */
15760             "beforeclick" : true,
15761         /**
15762          * @event click
15763          * Fires when a template node is clicked.
15764          * @param {Roo.View} this
15765          * @param {Number} index The index of the target node
15766          * @param {HTMLElement} node The target node
15767          * @param {Roo.EventObject} e The raw event object
15768          */
15769             "click" : true,
15770         /**
15771          * @event dblclick
15772          * Fires when a template node is double clicked.
15773          * @param {Roo.View} this
15774          * @param {Number} index The index of the target node
15775          * @param {HTMLElement} node The target node
15776          * @param {Roo.EventObject} e The raw event object
15777          */
15778             "dblclick" : true,
15779         /**
15780          * @event contextmenu
15781          * Fires when a template node is right clicked.
15782          * @param {Roo.View} this
15783          * @param {Number} index The index of the target node
15784          * @param {HTMLElement} node The target node
15785          * @param {Roo.EventObject} e The raw event object
15786          */
15787             "contextmenu" : true,
15788         /**
15789          * @event selectionchange
15790          * Fires when the selected nodes change.
15791          * @param {Roo.View} this
15792          * @param {Array} selections Array of the selected nodes
15793          */
15794             "selectionchange" : true,
15795     
15796         /**
15797          * @event beforeselect
15798          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15799          * @param {Roo.View} this
15800          * @param {HTMLElement} node The node to be selected
15801          * @param {Array} selections Array of currently selected nodes
15802          */
15803             "beforeselect" : true,
15804         /**
15805          * @event preparedata
15806          * Fires on every row to render, to allow you to change the data.
15807          * @param {Roo.View} this
15808          * @param {Object} data to be rendered (change this)
15809          */
15810           "preparedata" : true
15811           
15812           
15813         });
15814
15815
15816
15817     this.el.on({
15818         "click": this.onClick,
15819         "dblclick": this.onDblClick,
15820         "contextmenu": this.onContextMenu,
15821         scope:this
15822     });
15823
15824     this.selections = [];
15825     this.nodes = [];
15826     this.cmp = new Roo.CompositeElementLite([]);
15827     if(this.store){
15828         this.store = Roo.factory(this.store, Roo.data);
15829         this.setStore(this.store, true);
15830     }
15831     
15832     if ( this.footer && this.footer.xtype) {
15833            
15834          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15835         
15836         this.footer.dataSource = this.store;
15837         this.footer.container = fctr;
15838         this.footer = Roo.factory(this.footer, Roo);
15839         fctr.insertFirst(this.el);
15840         
15841         // this is a bit insane - as the paging toolbar seems to detach the el..
15842 //        dom.parentNode.parentNode.parentNode
15843          // they get detached?
15844     }
15845     
15846     
15847     Roo.View.superclass.constructor.call(this);
15848     
15849     
15850 };
15851
15852 Roo.extend(Roo.View, Roo.util.Observable, {
15853     
15854      /**
15855      * @cfg {Roo.data.Store} store Data store to load data from.
15856      */
15857     store : false,
15858     
15859     /**
15860      * @cfg {String|Roo.Element} el The container element.
15861      */
15862     el : '',
15863     
15864     /**
15865      * @cfg {String|Roo.Template} tpl The template used by this View 
15866      */
15867     tpl : false,
15868     /**
15869      * @cfg {String} dataName the named area of the template to use as the data area
15870      *                          Works with domtemplates roo-name="name"
15871      */
15872     dataName: false,
15873     /**
15874      * @cfg {String} selectedClass The css class to add to selected nodes
15875      */
15876     selectedClass : "x-view-selected",
15877      /**
15878      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15879      */
15880     emptyText : "",
15881     
15882     /**
15883      * @cfg {String} text to display on mask (default Loading)
15884      */
15885     mask : false,
15886     /**
15887      * @cfg {Boolean} multiSelect Allow multiple selection
15888      */
15889     multiSelect : false,
15890     /**
15891      * @cfg {Boolean} singleSelect Allow single selection
15892      */
15893     singleSelect:  false,
15894     
15895     /**
15896      * @cfg {Boolean} toggleSelect - selecting 
15897      */
15898     toggleSelect : false,
15899     
15900     /**
15901      * @cfg {Boolean} tickable - selecting 
15902      */
15903     tickable : false,
15904     
15905     /**
15906      * Returns the element this view is bound to.
15907      * @return {Roo.Element}
15908      */
15909     getEl : function(){
15910         return this.wrapEl;
15911     },
15912     
15913     
15914
15915     /**
15916      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15917      */
15918     refresh : function(){
15919         //Roo.log('refresh');
15920         var t = this.tpl;
15921         
15922         // if we are using something like 'domtemplate', then
15923         // the what gets used is:
15924         // t.applySubtemplate(NAME, data, wrapping data..)
15925         // the outer template then get' applied with
15926         //     the store 'extra data'
15927         // and the body get's added to the
15928         //      roo-name="data" node?
15929         //      <span class='roo-tpl-{name}'></span> ?????
15930         
15931         
15932         
15933         this.clearSelections();
15934         this.el.update("");
15935         var html = [];
15936         var records = this.store.getRange();
15937         if(records.length < 1) {
15938             
15939             // is this valid??  = should it render a template??
15940             
15941             this.el.update(this.emptyText);
15942             return;
15943         }
15944         var el = this.el;
15945         if (this.dataName) {
15946             this.el.update(t.apply(this.store.meta)); //????
15947             el = this.el.child('.roo-tpl-' + this.dataName);
15948         }
15949         
15950         for(var i = 0, len = records.length; i < len; i++){
15951             var data = this.prepareData(records[i].data, i, records[i]);
15952             this.fireEvent("preparedata", this, data, i, records[i]);
15953             
15954             var d = Roo.apply({}, data);
15955             
15956             if(this.tickable){
15957                 Roo.apply(d, {'roo-id' : Roo.id()});
15958                 
15959                 var _this = this;
15960             
15961                 Roo.each(this.parent.item, function(item){
15962                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15963                         return;
15964                     }
15965                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15966                 });
15967             }
15968             
15969             html[html.length] = Roo.util.Format.trim(
15970                 this.dataName ?
15971                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15972                     t.apply(d)
15973             );
15974         }
15975         
15976         
15977         
15978         el.update(html.join(""));
15979         this.nodes = el.dom.childNodes;
15980         this.updateIndexes(0);
15981     },
15982     
15983
15984     /**
15985      * Function to override to reformat the data that is sent to
15986      * the template for each node.
15987      * DEPRICATED - use the preparedata event handler.
15988      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15989      * a JSON object for an UpdateManager bound view).
15990      */
15991     prepareData : function(data, index, record)
15992     {
15993         this.fireEvent("preparedata", this, data, index, record);
15994         return data;
15995     },
15996
15997     onUpdate : function(ds, record){
15998         // Roo.log('on update');   
15999         this.clearSelections();
16000         var index = this.store.indexOf(record);
16001         var n = this.nodes[index];
16002         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16003         n.parentNode.removeChild(n);
16004         this.updateIndexes(index, index);
16005     },
16006
16007     
16008     
16009 // --------- FIXME     
16010     onAdd : function(ds, records, index)
16011     {
16012         //Roo.log(['on Add', ds, records, index] );        
16013         this.clearSelections();
16014         if(this.nodes.length == 0){
16015             this.refresh();
16016             return;
16017         }
16018         var n = this.nodes[index];
16019         for(var i = 0, len = records.length; i < len; i++){
16020             var d = this.prepareData(records[i].data, i, records[i]);
16021             if(n){
16022                 this.tpl.insertBefore(n, d);
16023             }else{
16024                 
16025                 this.tpl.append(this.el, d);
16026             }
16027         }
16028         this.updateIndexes(index);
16029     },
16030
16031     onRemove : function(ds, record, index){
16032        // Roo.log('onRemove');
16033         this.clearSelections();
16034         var el = this.dataName  ?
16035             this.el.child('.roo-tpl-' + this.dataName) :
16036             this.el; 
16037         
16038         el.dom.removeChild(this.nodes[index]);
16039         this.updateIndexes(index);
16040     },
16041
16042     /**
16043      * Refresh an individual node.
16044      * @param {Number} index
16045      */
16046     refreshNode : function(index){
16047         this.onUpdate(this.store, this.store.getAt(index));
16048     },
16049
16050     updateIndexes : function(startIndex, endIndex){
16051         var ns = this.nodes;
16052         startIndex = startIndex || 0;
16053         endIndex = endIndex || ns.length - 1;
16054         for(var i = startIndex; i <= endIndex; i++){
16055             ns[i].nodeIndex = i;
16056         }
16057     },
16058
16059     /**
16060      * Changes the data store this view uses and refresh the view.
16061      * @param {Store} store
16062      */
16063     setStore : function(store, initial){
16064         if(!initial && this.store){
16065             this.store.un("datachanged", this.refresh);
16066             this.store.un("add", this.onAdd);
16067             this.store.un("remove", this.onRemove);
16068             this.store.un("update", this.onUpdate);
16069             this.store.un("clear", this.refresh);
16070             this.store.un("beforeload", this.onBeforeLoad);
16071             this.store.un("load", this.onLoad);
16072             this.store.un("loadexception", this.onLoad);
16073         }
16074         if(store){
16075           
16076             store.on("datachanged", this.refresh, this);
16077             store.on("add", this.onAdd, this);
16078             store.on("remove", this.onRemove, this);
16079             store.on("update", this.onUpdate, this);
16080             store.on("clear", this.refresh, this);
16081             store.on("beforeload", this.onBeforeLoad, this);
16082             store.on("load", this.onLoad, this);
16083             store.on("loadexception", this.onLoad, this);
16084         }
16085         
16086         if(store){
16087             this.refresh();
16088         }
16089     },
16090     /**
16091      * onbeforeLoad - masks the loading area.
16092      *
16093      */
16094     onBeforeLoad : function(store,opts)
16095     {
16096          //Roo.log('onBeforeLoad');   
16097         if (!opts.add) {
16098             this.el.update("");
16099         }
16100         this.el.mask(this.mask ? this.mask : "Loading" ); 
16101     },
16102     onLoad : function ()
16103     {
16104         this.el.unmask();
16105     },
16106     
16107
16108     /**
16109      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16110      * @param {HTMLElement} node
16111      * @return {HTMLElement} The template node
16112      */
16113     findItemFromChild : function(node){
16114         var el = this.dataName  ?
16115             this.el.child('.roo-tpl-' + this.dataName,true) :
16116             this.el.dom; 
16117         
16118         if(!node || node.parentNode == el){
16119                     return node;
16120             }
16121             var p = node.parentNode;
16122             while(p && p != el){
16123             if(p.parentNode == el){
16124                 return p;
16125             }
16126             p = p.parentNode;
16127         }
16128             return null;
16129     },
16130
16131     /** @ignore */
16132     onClick : function(e){
16133         var item = this.findItemFromChild(e.getTarget());
16134         if(item){
16135             var index = this.indexOf(item);
16136             if(this.onItemClick(item, index, e) !== false){
16137                 this.fireEvent("click", this, index, item, e);
16138             }
16139         }else{
16140             this.clearSelections();
16141         }
16142     },
16143
16144     /** @ignore */
16145     onContextMenu : function(e){
16146         var item = this.findItemFromChild(e.getTarget());
16147         if(item){
16148             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16149         }
16150     },
16151
16152     /** @ignore */
16153     onDblClick : function(e){
16154         var item = this.findItemFromChild(e.getTarget());
16155         if(item){
16156             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16157         }
16158     },
16159
16160     onItemClick : function(item, index, e)
16161     {
16162         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16163             return false;
16164         }
16165         if (this.toggleSelect) {
16166             var m = this.isSelected(item) ? 'unselect' : 'select';
16167             //Roo.log(m);
16168             var _t = this;
16169             _t[m](item, true, false);
16170             return true;
16171         }
16172         if(this.multiSelect || this.singleSelect){
16173             if(this.multiSelect && e.shiftKey && this.lastSelection){
16174                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16175             }else{
16176                 this.select(item, this.multiSelect && e.ctrlKey);
16177                 this.lastSelection = item;
16178             }
16179             
16180             if(!this.tickable){
16181                 e.preventDefault();
16182             }
16183             
16184         }
16185         return true;
16186     },
16187
16188     /**
16189      * Get the number of selected nodes.
16190      * @return {Number}
16191      */
16192     getSelectionCount : function(){
16193         return this.selections.length;
16194     },
16195
16196     /**
16197      * Get the currently selected nodes.
16198      * @return {Array} An array of HTMLElements
16199      */
16200     getSelectedNodes : function(){
16201         return this.selections;
16202     },
16203
16204     /**
16205      * Get the indexes of the selected nodes.
16206      * @return {Array}
16207      */
16208     getSelectedIndexes : function(){
16209         var indexes = [], s = this.selections;
16210         for(var i = 0, len = s.length; i < len; i++){
16211             indexes.push(s[i].nodeIndex);
16212         }
16213         return indexes;
16214     },
16215
16216     /**
16217      * Clear all selections
16218      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16219      */
16220     clearSelections : function(suppressEvent){
16221         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16222             this.cmp.elements = this.selections;
16223             this.cmp.removeClass(this.selectedClass);
16224             this.selections = [];
16225             if(!suppressEvent){
16226                 this.fireEvent("selectionchange", this, this.selections);
16227             }
16228         }
16229     },
16230
16231     /**
16232      * Returns true if the passed node is selected
16233      * @param {HTMLElement/Number} node The node or node index
16234      * @return {Boolean}
16235      */
16236     isSelected : function(node){
16237         var s = this.selections;
16238         if(s.length < 1){
16239             return false;
16240         }
16241         node = this.getNode(node);
16242         return s.indexOf(node) !== -1;
16243     },
16244
16245     /**
16246      * Selects nodes.
16247      * @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
16248      * @param {Boolean} keepExisting (optional) true to keep existing selections
16249      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16250      */
16251     select : function(nodeInfo, keepExisting, suppressEvent){
16252         if(nodeInfo instanceof Array){
16253             if(!keepExisting){
16254                 this.clearSelections(true);
16255             }
16256             for(var i = 0, len = nodeInfo.length; i < len; i++){
16257                 this.select(nodeInfo[i], true, true);
16258             }
16259             return;
16260         } 
16261         var node = this.getNode(nodeInfo);
16262         if(!node || this.isSelected(node)){
16263             return; // already selected.
16264         }
16265         if(!keepExisting){
16266             this.clearSelections(true);
16267         }
16268         
16269         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16270             Roo.fly(node).addClass(this.selectedClass);
16271             this.selections.push(node);
16272             if(!suppressEvent){
16273                 this.fireEvent("selectionchange", this, this.selections);
16274             }
16275         }
16276         
16277         
16278     },
16279       /**
16280      * Unselects nodes.
16281      * @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
16282      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16283      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16284      */
16285     unselect : function(nodeInfo, keepExisting, suppressEvent)
16286     {
16287         if(nodeInfo instanceof Array){
16288             Roo.each(this.selections, function(s) {
16289                 this.unselect(s, nodeInfo);
16290             }, this);
16291             return;
16292         }
16293         var node = this.getNode(nodeInfo);
16294         if(!node || !this.isSelected(node)){
16295             //Roo.log("not selected");
16296             return; // not selected.
16297         }
16298         // fireevent???
16299         var ns = [];
16300         Roo.each(this.selections, function(s) {
16301             if (s == node ) {
16302                 Roo.fly(node).removeClass(this.selectedClass);
16303
16304                 return;
16305             }
16306             ns.push(s);
16307         },this);
16308         
16309         this.selections= ns;
16310         this.fireEvent("selectionchange", this, this.selections);
16311     },
16312
16313     /**
16314      * Gets a template node.
16315      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16316      * @return {HTMLElement} The node or null if it wasn't found
16317      */
16318     getNode : function(nodeInfo){
16319         if(typeof nodeInfo == "string"){
16320             return document.getElementById(nodeInfo);
16321         }else if(typeof nodeInfo == "number"){
16322             return this.nodes[nodeInfo];
16323         }
16324         return nodeInfo;
16325     },
16326
16327     /**
16328      * Gets a range template nodes.
16329      * @param {Number} startIndex
16330      * @param {Number} endIndex
16331      * @return {Array} An array of nodes
16332      */
16333     getNodes : function(start, end){
16334         var ns = this.nodes;
16335         start = start || 0;
16336         end = typeof end == "undefined" ? ns.length - 1 : end;
16337         var nodes = [];
16338         if(start <= end){
16339             for(var i = start; i <= end; i++){
16340                 nodes.push(ns[i]);
16341             }
16342         } else{
16343             for(var i = start; i >= end; i--){
16344                 nodes.push(ns[i]);
16345             }
16346         }
16347         return nodes;
16348     },
16349
16350     /**
16351      * Finds the index of the passed node
16352      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16353      * @return {Number} The index of the node or -1
16354      */
16355     indexOf : function(node){
16356         node = this.getNode(node);
16357         if(typeof node.nodeIndex == "number"){
16358             return node.nodeIndex;
16359         }
16360         var ns = this.nodes;
16361         for(var i = 0, len = ns.length; i < len; i++){
16362             if(ns[i] == node){
16363                 return i;
16364             }
16365         }
16366         return -1;
16367     }
16368 });
16369 /*
16370  * - LGPL
16371  *
16372  * based on jquery fullcalendar
16373  * 
16374  */
16375
16376 Roo.bootstrap = Roo.bootstrap || {};
16377 /**
16378  * @class Roo.bootstrap.Calendar
16379  * @extends Roo.bootstrap.Component
16380  * Bootstrap Calendar class
16381  * @cfg {Boolean} loadMask (true|false) default false
16382  * @cfg {Object} header generate the user specific header of the calendar, default false
16383
16384  * @constructor
16385  * Create a new Container
16386  * @param {Object} config The config object
16387  */
16388
16389
16390
16391 Roo.bootstrap.Calendar = function(config){
16392     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16393      this.addEvents({
16394         /**
16395              * @event select
16396              * Fires when a date is selected
16397              * @param {DatePicker} this
16398              * @param {Date} date The selected date
16399              */
16400         'select': true,
16401         /**
16402              * @event monthchange
16403              * Fires when the displayed month changes 
16404              * @param {DatePicker} this
16405              * @param {Date} date The selected month
16406              */
16407         'monthchange': true,
16408         /**
16409              * @event evententer
16410              * Fires when mouse over an event
16411              * @param {Calendar} this
16412              * @param {event} Event
16413              */
16414         'evententer': true,
16415         /**
16416              * @event eventleave
16417              * Fires when the mouse leaves an
16418              * @param {Calendar} this
16419              * @param {event}
16420              */
16421         'eventleave': true,
16422         /**
16423              * @event eventclick
16424              * Fires when the mouse click an
16425              * @param {Calendar} this
16426              * @param {event}
16427              */
16428         'eventclick': true
16429         
16430     });
16431
16432 };
16433
16434 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16435     
16436      /**
16437      * @cfg {Number} startDay
16438      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16439      */
16440     startDay : 0,
16441     
16442     loadMask : false,
16443     
16444     header : false,
16445       
16446     getAutoCreate : function(){
16447         
16448         
16449         var fc_button = function(name, corner, style, content ) {
16450             return Roo.apply({},{
16451                 tag : 'span',
16452                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16453                          (corner.length ?
16454                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16455                             ''
16456                         ),
16457                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16458                 unselectable: 'on'
16459             });
16460         };
16461         
16462         var header = {};
16463         
16464         if(!this.header){
16465             header = {
16466                 tag : 'table',
16467                 cls : 'fc-header',
16468                 style : 'width:100%',
16469                 cn : [
16470                     {
16471                         tag: 'tr',
16472                         cn : [
16473                             {
16474                                 tag : 'td',
16475                                 cls : 'fc-header-left',
16476                                 cn : [
16477                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16478                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16479                                     { tag: 'span', cls: 'fc-header-space' },
16480                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16481
16482
16483                                 ]
16484                             },
16485
16486                             {
16487                                 tag : 'td',
16488                                 cls : 'fc-header-center',
16489                                 cn : [
16490                                     {
16491                                         tag: 'span',
16492                                         cls: 'fc-header-title',
16493                                         cn : {
16494                                             tag: 'H2',
16495                                             html : 'month / year'
16496                                         }
16497                                     }
16498
16499                                 ]
16500                             },
16501                             {
16502                                 tag : 'td',
16503                                 cls : 'fc-header-right',
16504                                 cn : [
16505                               /*      fc_button('month', 'left', '', 'month' ),
16506                                     fc_button('week', '', '', 'week' ),
16507                                     fc_button('day', 'right', '', 'day' )
16508                                 */    
16509
16510                                 ]
16511                             }
16512
16513                         ]
16514                     }
16515                 ]
16516             };
16517         }
16518         
16519         header = this.header;
16520         
16521        
16522         var cal_heads = function() {
16523             var ret = [];
16524             // fixme - handle this.
16525             
16526             for (var i =0; i < Date.dayNames.length; i++) {
16527                 var d = Date.dayNames[i];
16528                 ret.push({
16529                     tag: 'th',
16530                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16531                     html : d.substring(0,3)
16532                 });
16533                 
16534             }
16535             ret[0].cls += ' fc-first';
16536             ret[6].cls += ' fc-last';
16537             return ret;
16538         };
16539         var cal_cell = function(n) {
16540             return  {
16541                 tag: 'td',
16542                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16543                 cn : [
16544                     {
16545                         cn : [
16546                             {
16547                                 cls: 'fc-day-number',
16548                                 html: 'D'
16549                             },
16550                             {
16551                                 cls: 'fc-day-content',
16552                              
16553                                 cn : [
16554                                      {
16555                                         style: 'position: relative;' // height: 17px;
16556                                     }
16557                                 ]
16558                             }
16559                             
16560                             
16561                         ]
16562                     }
16563                 ]
16564                 
16565             }
16566         };
16567         var cal_rows = function() {
16568             
16569             var ret = [];
16570             for (var r = 0; r < 6; r++) {
16571                 var row= {
16572                     tag : 'tr',
16573                     cls : 'fc-week',
16574                     cn : []
16575                 };
16576                 
16577                 for (var i =0; i < Date.dayNames.length; i++) {
16578                     var d = Date.dayNames[i];
16579                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16580
16581                 }
16582                 row.cn[0].cls+=' fc-first';
16583                 row.cn[0].cn[0].style = 'min-height:90px';
16584                 row.cn[6].cls+=' fc-last';
16585                 ret.push(row);
16586                 
16587             }
16588             ret[0].cls += ' fc-first';
16589             ret[4].cls += ' fc-prev-last';
16590             ret[5].cls += ' fc-last';
16591             return ret;
16592             
16593         };
16594         
16595         var cal_table = {
16596             tag: 'table',
16597             cls: 'fc-border-separate',
16598             style : 'width:100%',
16599             cellspacing  : 0,
16600             cn : [
16601                 { 
16602                     tag: 'thead',
16603                     cn : [
16604                         { 
16605                             tag: 'tr',
16606                             cls : 'fc-first fc-last',
16607                             cn : cal_heads()
16608                         }
16609                     ]
16610                 },
16611                 { 
16612                     tag: 'tbody',
16613                     cn : cal_rows()
16614                 }
16615                   
16616             ]
16617         };
16618          
16619          var cfg = {
16620             cls : 'fc fc-ltr',
16621             cn : [
16622                 header,
16623                 {
16624                     cls : 'fc-content',
16625                     style : "position: relative;",
16626                     cn : [
16627                         {
16628                             cls : 'fc-view fc-view-month fc-grid',
16629                             style : 'position: relative',
16630                             unselectable : 'on',
16631                             cn : [
16632                                 {
16633                                     cls : 'fc-event-container',
16634                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16635                                 },
16636                                 cal_table
16637                             ]
16638                         }
16639                     ]
16640     
16641                 }
16642            ] 
16643             
16644         };
16645         
16646          
16647         
16648         return cfg;
16649     },
16650     
16651     
16652     initEvents : function()
16653     {
16654         if(!this.store){
16655             throw "can not find store for calendar";
16656         }
16657         
16658         var mark = {
16659             tag: "div",
16660             cls:"x-dlg-mask",
16661             style: "text-align:center",
16662             cn: [
16663                 {
16664                     tag: "div",
16665                     style: "background-color:white;width:50%;margin:250 auto",
16666                     cn: [
16667                         {
16668                             tag: "img",
16669                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16670                         },
16671                         {
16672                             tag: "span",
16673                             html: "Loading"
16674                         }
16675                         
16676                     ]
16677                 }
16678             ]
16679         };
16680         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16681         
16682         var size = this.el.select('.fc-content', true).first().getSize();
16683         this.maskEl.setSize(size.width, size.height);
16684         this.maskEl.enableDisplayMode("block");
16685         if(!this.loadMask){
16686             this.maskEl.hide();
16687         }
16688         
16689         this.store = Roo.factory(this.store, Roo.data);
16690         this.store.on('load', this.onLoad, this);
16691         this.store.on('beforeload', this.onBeforeLoad, this);
16692         
16693         this.resize();
16694         
16695         this.cells = this.el.select('.fc-day',true);
16696         //Roo.log(this.cells);
16697         this.textNodes = this.el.query('.fc-day-number');
16698         this.cells.addClassOnOver('fc-state-hover');
16699         
16700         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16701         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16702         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16703         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16704         
16705         this.on('monthchange', this.onMonthChange, this);
16706         
16707         this.update(new Date().clearTime());
16708     },
16709     
16710     resize : function() {
16711         var sz  = this.el.getSize();
16712         
16713         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16714         this.el.select('.fc-day-content div',true).setHeight(34);
16715     },
16716     
16717     
16718     // private
16719     showPrevMonth : function(e){
16720         this.update(this.activeDate.add("mo", -1));
16721     },
16722     showToday : function(e){
16723         this.update(new Date().clearTime());
16724     },
16725     // private
16726     showNextMonth : function(e){
16727         this.update(this.activeDate.add("mo", 1));
16728     },
16729
16730     // private
16731     showPrevYear : function(){
16732         this.update(this.activeDate.add("y", -1));
16733     },
16734
16735     // private
16736     showNextYear : function(){
16737         this.update(this.activeDate.add("y", 1));
16738     },
16739
16740     
16741    // private
16742     update : function(date)
16743     {
16744         var vd = this.activeDate;
16745         this.activeDate = date;
16746 //        if(vd && this.el){
16747 //            var t = date.getTime();
16748 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16749 //                Roo.log('using add remove');
16750 //                
16751 //                this.fireEvent('monthchange', this, date);
16752 //                
16753 //                this.cells.removeClass("fc-state-highlight");
16754 //                this.cells.each(function(c){
16755 //                   if(c.dateValue == t){
16756 //                       c.addClass("fc-state-highlight");
16757 //                       setTimeout(function(){
16758 //                            try{c.dom.firstChild.focus();}catch(e){}
16759 //                       }, 50);
16760 //                       return false;
16761 //                   }
16762 //                   return true;
16763 //                });
16764 //                return;
16765 //            }
16766 //        }
16767         
16768         var days = date.getDaysInMonth();
16769         
16770         var firstOfMonth = date.getFirstDateOfMonth();
16771         var startingPos = firstOfMonth.getDay()-this.startDay;
16772         
16773         if(startingPos < this.startDay){
16774             startingPos += 7;
16775         }
16776         
16777         var pm = date.add(Date.MONTH, -1);
16778         var prevStart = pm.getDaysInMonth()-startingPos;
16779 //        
16780         this.cells = this.el.select('.fc-day',true);
16781         this.textNodes = this.el.query('.fc-day-number');
16782         this.cells.addClassOnOver('fc-state-hover');
16783         
16784         var cells = this.cells.elements;
16785         var textEls = this.textNodes;
16786         
16787         Roo.each(cells, function(cell){
16788             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16789         });
16790         
16791         days += startingPos;
16792
16793         // convert everything to numbers so it's fast
16794         var day = 86400000;
16795         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16796         //Roo.log(d);
16797         //Roo.log(pm);
16798         //Roo.log(prevStart);
16799         
16800         var today = new Date().clearTime().getTime();
16801         var sel = date.clearTime().getTime();
16802         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16803         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16804         var ddMatch = this.disabledDatesRE;
16805         var ddText = this.disabledDatesText;
16806         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16807         var ddaysText = this.disabledDaysText;
16808         var format = this.format;
16809         
16810         var setCellClass = function(cal, cell){
16811             cell.row = 0;
16812             cell.events = [];
16813             cell.more = [];
16814             //Roo.log('set Cell Class');
16815             cell.title = "";
16816             var t = d.getTime();
16817             
16818             //Roo.log(d);
16819             
16820             cell.dateValue = t;
16821             if(t == today){
16822                 cell.className += " fc-today";
16823                 cell.className += " fc-state-highlight";
16824                 cell.title = cal.todayText;
16825             }
16826             if(t == sel){
16827                 // disable highlight in other month..
16828                 //cell.className += " fc-state-highlight";
16829                 
16830             }
16831             // disabling
16832             if(t < min) {
16833                 cell.className = " fc-state-disabled";
16834                 cell.title = cal.minText;
16835                 return;
16836             }
16837             if(t > max) {
16838                 cell.className = " fc-state-disabled";
16839                 cell.title = cal.maxText;
16840                 return;
16841             }
16842             if(ddays){
16843                 if(ddays.indexOf(d.getDay()) != -1){
16844                     cell.title = ddaysText;
16845                     cell.className = " fc-state-disabled";
16846                 }
16847             }
16848             if(ddMatch && format){
16849                 var fvalue = d.dateFormat(format);
16850                 if(ddMatch.test(fvalue)){
16851                     cell.title = ddText.replace("%0", fvalue);
16852                     cell.className = " fc-state-disabled";
16853                 }
16854             }
16855             
16856             if (!cell.initialClassName) {
16857                 cell.initialClassName = cell.dom.className;
16858             }
16859             
16860             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16861         };
16862
16863         var i = 0;
16864         
16865         for(; i < startingPos; i++) {
16866             textEls[i].innerHTML = (++prevStart);
16867             d.setDate(d.getDate()+1);
16868             
16869             cells[i].className = "fc-past fc-other-month";
16870             setCellClass(this, cells[i]);
16871         }
16872         
16873         var intDay = 0;
16874         
16875         for(; i < days; i++){
16876             intDay = i - startingPos + 1;
16877             textEls[i].innerHTML = (intDay);
16878             d.setDate(d.getDate()+1);
16879             
16880             cells[i].className = ''; // "x-date-active";
16881             setCellClass(this, cells[i]);
16882         }
16883         var extraDays = 0;
16884         
16885         for(; i < 42; i++) {
16886             textEls[i].innerHTML = (++extraDays);
16887             d.setDate(d.getDate()+1);
16888             
16889             cells[i].className = "fc-future fc-other-month";
16890             setCellClass(this, cells[i]);
16891         }
16892         
16893         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16894         
16895         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16896         
16897         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16898         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16899         
16900         if(totalRows != 6){
16901             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16902             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16903         }
16904         
16905         this.fireEvent('monthchange', this, date);
16906         
16907         
16908         /*
16909         if(!this.internalRender){
16910             var main = this.el.dom.firstChild;
16911             var w = main.offsetWidth;
16912             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16913             Roo.fly(main).setWidth(w);
16914             this.internalRender = true;
16915             // opera does not respect the auto grow header center column
16916             // then, after it gets a width opera refuses to recalculate
16917             // without a second pass
16918             if(Roo.isOpera && !this.secondPass){
16919                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16920                 this.secondPass = true;
16921                 this.update.defer(10, this, [date]);
16922             }
16923         }
16924         */
16925         
16926     },
16927     
16928     findCell : function(dt) {
16929         dt = dt.clearTime().getTime();
16930         var ret = false;
16931         this.cells.each(function(c){
16932             //Roo.log("check " +c.dateValue + '?=' + dt);
16933             if(c.dateValue == dt){
16934                 ret = c;
16935                 return false;
16936             }
16937             return true;
16938         });
16939         
16940         return ret;
16941     },
16942     
16943     findCells : function(ev) {
16944         var s = ev.start.clone().clearTime().getTime();
16945        // Roo.log(s);
16946         var e= ev.end.clone().clearTime().getTime();
16947        // Roo.log(e);
16948         var ret = [];
16949         this.cells.each(function(c){
16950              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16951             
16952             if(c.dateValue > e){
16953                 return ;
16954             }
16955             if(c.dateValue < s){
16956                 return ;
16957             }
16958             ret.push(c);
16959         });
16960         
16961         return ret;    
16962     },
16963     
16964 //    findBestRow: function(cells)
16965 //    {
16966 //        var ret = 0;
16967 //        
16968 //        for (var i =0 ; i < cells.length;i++) {
16969 //            ret  = Math.max(cells[i].rows || 0,ret);
16970 //        }
16971 //        return ret;
16972 //        
16973 //    },
16974     
16975     
16976     addItem : function(ev)
16977     {
16978         // look for vertical location slot in
16979         var cells = this.findCells(ev);
16980         
16981 //        ev.row = this.findBestRow(cells);
16982         
16983         // work out the location.
16984         
16985         var crow = false;
16986         var rows = [];
16987         for(var i =0; i < cells.length; i++) {
16988             
16989             cells[i].row = cells[0].row;
16990             
16991             if(i == 0){
16992                 cells[i].row = cells[i].row + 1;
16993             }
16994             
16995             if (!crow) {
16996                 crow = {
16997                     start : cells[i],
16998                     end :  cells[i]
16999                 };
17000                 continue;
17001             }
17002             if (crow.start.getY() == cells[i].getY()) {
17003                 // on same row.
17004                 crow.end = cells[i];
17005                 continue;
17006             }
17007             // different row.
17008             rows.push(crow);
17009             crow = {
17010                 start: cells[i],
17011                 end : cells[i]
17012             };
17013             
17014         }
17015         
17016         rows.push(crow);
17017         ev.els = [];
17018         ev.rows = rows;
17019         ev.cells = cells;
17020         
17021         cells[0].events.push(ev);
17022         
17023         this.calevents.push(ev);
17024     },
17025     
17026     clearEvents: function() {
17027         
17028         if(!this.calevents){
17029             return;
17030         }
17031         
17032         Roo.each(this.cells.elements, function(c){
17033             c.row = 0;
17034             c.events = [];
17035             c.more = [];
17036         });
17037         
17038         Roo.each(this.calevents, function(e) {
17039             Roo.each(e.els, function(el) {
17040                 el.un('mouseenter' ,this.onEventEnter, this);
17041                 el.un('mouseleave' ,this.onEventLeave, this);
17042                 el.remove();
17043             },this);
17044         },this);
17045         
17046         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17047             e.remove();
17048         });
17049         
17050     },
17051     
17052     renderEvents: function()
17053     {   
17054         var _this = this;
17055         
17056         this.cells.each(function(c) {
17057             
17058             if(c.row < 5){
17059                 return;
17060             }
17061             
17062             var ev = c.events;
17063             
17064             var r = 4;
17065             if(c.row != c.events.length){
17066                 r = 4 - (4 - (c.row - c.events.length));
17067             }
17068             
17069             c.events = ev.slice(0, r);
17070             c.more = ev.slice(r);
17071             
17072             if(c.more.length && c.more.length == 1){
17073                 c.events.push(c.more.pop());
17074             }
17075             
17076             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17077             
17078         });
17079             
17080         this.cells.each(function(c) {
17081             
17082             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17083             
17084             
17085             for (var e = 0; e < c.events.length; e++){
17086                 var ev = c.events[e];
17087                 var rows = ev.rows;
17088                 
17089                 for(var i = 0; i < rows.length; i++) {
17090                 
17091                     // how many rows should it span..
17092
17093                     var  cfg = {
17094                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17095                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17096
17097                         unselectable : "on",
17098                         cn : [
17099                             {
17100                                 cls: 'fc-event-inner',
17101                                 cn : [
17102     //                                {
17103     //                                  tag:'span',
17104     //                                  cls: 'fc-event-time',
17105     //                                  html : cells.length > 1 ? '' : ev.time
17106     //                                },
17107                                     {
17108                                       tag:'span',
17109                                       cls: 'fc-event-title',
17110                                       html : String.format('{0}', ev.title)
17111                                     }
17112
17113
17114                                 ]
17115                             },
17116                             {
17117                                 cls: 'ui-resizable-handle ui-resizable-e',
17118                                 html : '&nbsp;&nbsp;&nbsp'
17119                             }
17120
17121                         ]
17122                     };
17123
17124                     if (i == 0) {
17125                         cfg.cls += ' fc-event-start';
17126                     }
17127                     if ((i+1) == rows.length) {
17128                         cfg.cls += ' fc-event-end';
17129                     }
17130
17131                     var ctr = _this.el.select('.fc-event-container',true).first();
17132                     var cg = ctr.createChild(cfg);
17133
17134                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17135                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17136
17137                     var r = (c.more.length) ? 1 : 0;
17138                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17139                     cg.setWidth(ebox.right - sbox.x -2);
17140
17141                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17142                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17143                     cg.on('click', _this.onEventClick, _this, ev);
17144
17145                     ev.els.push(cg);
17146                     
17147                 }
17148                 
17149             }
17150             
17151             
17152             if(c.more.length){
17153                 var  cfg = {
17154                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17155                     style : 'position: absolute',
17156                     unselectable : "on",
17157                     cn : [
17158                         {
17159                             cls: 'fc-event-inner',
17160                             cn : [
17161                                 {
17162                                   tag:'span',
17163                                   cls: 'fc-event-title',
17164                                   html : 'More'
17165                                 }
17166
17167
17168                             ]
17169                         },
17170                         {
17171                             cls: 'ui-resizable-handle ui-resizable-e',
17172                             html : '&nbsp;&nbsp;&nbsp'
17173                         }
17174
17175                     ]
17176                 };
17177
17178                 var ctr = _this.el.select('.fc-event-container',true).first();
17179                 var cg = ctr.createChild(cfg);
17180
17181                 var sbox = c.select('.fc-day-content',true).first().getBox();
17182                 var ebox = c.select('.fc-day-content',true).first().getBox();
17183                 //Roo.log(cg);
17184                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17185                 cg.setWidth(ebox.right - sbox.x -2);
17186
17187                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17188                 
17189             }
17190             
17191         });
17192         
17193         
17194         
17195     },
17196     
17197     onEventEnter: function (e, el,event,d) {
17198         this.fireEvent('evententer', this, el, event);
17199     },
17200     
17201     onEventLeave: function (e, el,event,d) {
17202         this.fireEvent('eventleave', this, el, event);
17203     },
17204     
17205     onEventClick: function (e, el,event,d) {
17206         this.fireEvent('eventclick', this, el, event);
17207     },
17208     
17209     onMonthChange: function () {
17210         this.store.load();
17211     },
17212     
17213     onMoreEventClick: function(e, el, more)
17214     {
17215         var _this = this;
17216         
17217         this.calpopover.placement = 'right';
17218         this.calpopover.setTitle('More');
17219         
17220         this.calpopover.setContent('');
17221         
17222         var ctr = this.calpopover.el.select('.popover-content', true).first();
17223         
17224         Roo.each(more, function(m){
17225             var cfg = {
17226                 cls : 'fc-event-hori fc-event-draggable',
17227                 html : m.title
17228             };
17229             var cg = ctr.createChild(cfg);
17230             
17231             cg.on('click', _this.onEventClick, _this, m);
17232         });
17233         
17234         this.calpopover.show(el);
17235         
17236         
17237     },
17238     
17239     onLoad: function () 
17240     {   
17241         this.calevents = [];
17242         var cal = this;
17243         
17244         if(this.store.getCount() > 0){
17245             this.store.data.each(function(d){
17246                cal.addItem({
17247                     id : d.data.id,
17248                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17249                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17250                     time : d.data.start_time,
17251                     title : d.data.title,
17252                     description : d.data.description,
17253                     venue : d.data.venue
17254                 });
17255             });
17256         }
17257         
17258         this.renderEvents();
17259         
17260         if(this.calevents.length && this.loadMask){
17261             this.maskEl.hide();
17262         }
17263     },
17264     
17265     onBeforeLoad: function()
17266     {
17267         this.clearEvents();
17268         if(this.loadMask){
17269             this.maskEl.show();
17270         }
17271     }
17272 });
17273
17274  
17275  /*
17276  * - LGPL
17277  *
17278  * element
17279  * 
17280  */
17281
17282 /**
17283  * @class Roo.bootstrap.Popover
17284  * @extends Roo.bootstrap.Component
17285  * Bootstrap Popover class
17286  * @cfg {String} html contents of the popover   (or false to use children..)
17287  * @cfg {String} title of popover (or false to hide)
17288  * @cfg {String} placement how it is placed
17289  * @cfg {String} trigger click || hover (or false to trigger manually)
17290  * @cfg {String} over what (parent or false to trigger manually.)
17291  * @cfg {Number} delay - delay before showing
17292  
17293  * @constructor
17294  * Create a new Popover
17295  * @param {Object} config The config object
17296  */
17297
17298 Roo.bootstrap.Popover = function(config){
17299     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17300     
17301     this.addEvents({
17302         // raw events
17303          /**
17304          * @event show
17305          * After the popover show
17306          * 
17307          * @param {Roo.bootstrap.Popover} this
17308          */
17309         "show" : true,
17310         /**
17311          * @event hide
17312          * After the popover hide
17313          * 
17314          * @param {Roo.bootstrap.Popover} this
17315          */
17316         "hide" : true
17317     });
17318 };
17319
17320 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17321     
17322     title: 'Fill in a title',
17323     html: false,
17324     
17325     placement : 'right',
17326     trigger : 'hover', // hover
17327     
17328     delay : 0,
17329     
17330     over: 'parent',
17331     
17332     can_build_overlaid : false,
17333     
17334     getChildContainer : function()
17335     {
17336         return this.el.select('.popover-content',true).first();
17337     },
17338     
17339     getAutoCreate : function(){
17340          
17341         var cfg = {
17342            cls : 'popover roo-dynamic',
17343            style: 'display:block',
17344            cn : [
17345                 {
17346                     cls : 'arrow'
17347                 },
17348                 {
17349                     cls : 'popover-inner',
17350                     cn : [
17351                         {
17352                             tag: 'h3',
17353                             cls: 'popover-title',
17354                             html : this.title
17355                         },
17356                         {
17357                             cls : 'popover-content',
17358                             html : this.html
17359                         }
17360                     ]
17361                     
17362                 }
17363            ]
17364         };
17365         
17366         return cfg;
17367     },
17368     setTitle: function(str)
17369     {
17370         this.title = str;
17371         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17372     },
17373     setContent: function(str)
17374     {
17375         this.html = str;
17376         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17377     },
17378     // as it get's added to the bottom of the page.
17379     onRender : function(ct, position)
17380     {
17381         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17382         if(!this.el){
17383             var cfg = Roo.apply({},  this.getAutoCreate());
17384             cfg.id = Roo.id();
17385             
17386             if (this.cls) {
17387                 cfg.cls += ' ' + this.cls;
17388             }
17389             if (this.style) {
17390                 cfg.style = this.style;
17391             }
17392             //Roo.log("adding to ");
17393             this.el = Roo.get(document.body).createChild(cfg, position);
17394 //            Roo.log(this.el);
17395         }
17396         this.initEvents();
17397     },
17398     
17399     initEvents : function()
17400     {
17401         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17402         this.el.enableDisplayMode('block');
17403         this.el.hide();
17404         if (this.over === false) {
17405             return; 
17406         }
17407         if (this.triggers === false) {
17408             return;
17409         }
17410         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17411         var triggers = this.trigger ? this.trigger.split(' ') : [];
17412         Roo.each(triggers, function(trigger) {
17413         
17414             if (trigger == 'click') {
17415                 on_el.on('click', this.toggle, this);
17416             } else if (trigger != 'manual') {
17417                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17418                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17419       
17420                 on_el.on(eventIn  ,this.enter, this);
17421                 on_el.on(eventOut, this.leave, this);
17422             }
17423         }, this);
17424         
17425     },
17426     
17427     
17428     // private
17429     timeout : null,
17430     hoverState : null,
17431     
17432     toggle : function () {
17433         this.hoverState == 'in' ? this.leave() : this.enter();
17434     },
17435     
17436     enter : function () {
17437         
17438         clearTimeout(this.timeout);
17439     
17440         this.hoverState = 'in';
17441     
17442         if (!this.delay || !this.delay.show) {
17443             this.show();
17444             return;
17445         }
17446         var _t = this;
17447         this.timeout = setTimeout(function () {
17448             if (_t.hoverState == 'in') {
17449                 _t.show();
17450             }
17451         }, this.delay.show)
17452     },
17453     
17454     leave : function() {
17455         clearTimeout(this.timeout);
17456     
17457         this.hoverState = 'out';
17458     
17459         if (!this.delay || !this.delay.hide) {
17460             this.hide();
17461             return;
17462         }
17463         var _t = this;
17464         this.timeout = setTimeout(function () {
17465             if (_t.hoverState == 'out') {
17466                 _t.hide();
17467             }
17468         }, this.delay.hide)
17469     },
17470     
17471     show : function (on_el)
17472     {
17473         if (!on_el) {
17474             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17475         }
17476         
17477         // set content.
17478         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17479         if (this.html !== false) {
17480             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17481         }
17482         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17483         if (!this.title.length) {
17484             this.el.select('.popover-title',true).hide();
17485         }
17486         
17487         var placement = typeof this.placement == 'function' ?
17488             this.placement.call(this, this.el, on_el) :
17489             this.placement;
17490             
17491         var autoToken = /\s?auto?\s?/i;
17492         var autoPlace = autoToken.test(placement);
17493         if (autoPlace) {
17494             placement = placement.replace(autoToken, '') || 'top';
17495         }
17496         
17497         //this.el.detach()
17498         //this.el.setXY([0,0]);
17499         this.el.show();
17500         this.el.dom.style.display='block';
17501         this.el.addClass(placement);
17502         
17503         //this.el.appendTo(on_el);
17504         
17505         var p = this.getPosition();
17506         var box = this.el.getBox();
17507         
17508         if (autoPlace) {
17509             // fixme..
17510         }
17511         var align = Roo.bootstrap.Popover.alignment[placement];
17512         
17513 //        Roo.log(align);
17514         this.el.alignTo(on_el, align[0],align[1]);
17515         //var arrow = this.el.select('.arrow',true).first();
17516         //arrow.set(align[2], 
17517         
17518         this.el.addClass('in');
17519         
17520         
17521         if (this.el.hasClass('fade')) {
17522             // fade it?
17523         }
17524         
17525         this.hoverState = 'in';
17526         
17527         this.fireEvent('show', this);
17528         
17529     },
17530     hide : function()
17531     {
17532         this.el.setXY([0,0]);
17533         this.el.removeClass('in');
17534         this.el.hide();
17535         this.hoverState = null;
17536         
17537         this.fireEvent('hide', this);
17538     }
17539     
17540 });
17541
17542 Roo.bootstrap.Popover.alignment = {
17543     'left' : ['r-l', [-10,0], 'right'],
17544     'right' : ['l-r', [10,0], 'left'],
17545     'bottom' : ['t-b', [0,10], 'top'],
17546     'top' : [ 'b-t', [0,-10], 'bottom']
17547 };
17548
17549  /*
17550  * - LGPL
17551  *
17552  * Progress
17553  * 
17554  */
17555
17556 /**
17557  * @class Roo.bootstrap.Progress
17558  * @extends Roo.bootstrap.Component
17559  * Bootstrap Progress class
17560  * @cfg {Boolean} striped striped of the progress bar
17561  * @cfg {Boolean} active animated of the progress bar
17562  * 
17563  * 
17564  * @constructor
17565  * Create a new Progress
17566  * @param {Object} config The config object
17567  */
17568
17569 Roo.bootstrap.Progress = function(config){
17570     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17571 };
17572
17573 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17574     
17575     striped : false,
17576     active: false,
17577     
17578     getAutoCreate : function(){
17579         var cfg = {
17580             tag: 'div',
17581             cls: 'progress'
17582         };
17583         
17584         
17585         if(this.striped){
17586             cfg.cls += ' progress-striped';
17587         }
17588       
17589         if(this.active){
17590             cfg.cls += ' active';
17591         }
17592         
17593         
17594         return cfg;
17595     }
17596    
17597 });
17598
17599  
17600
17601  /*
17602  * - LGPL
17603  *
17604  * ProgressBar
17605  * 
17606  */
17607
17608 /**
17609  * @class Roo.bootstrap.ProgressBar
17610  * @extends Roo.bootstrap.Component
17611  * Bootstrap ProgressBar class
17612  * @cfg {Number} aria_valuenow aria-value now
17613  * @cfg {Number} aria_valuemin aria-value min
17614  * @cfg {Number} aria_valuemax aria-value max
17615  * @cfg {String} label label for the progress bar
17616  * @cfg {String} panel (success | info | warning | danger )
17617  * @cfg {String} role role of the progress bar
17618  * @cfg {String} sr_only text
17619  * 
17620  * 
17621  * @constructor
17622  * Create a new ProgressBar
17623  * @param {Object} config The config object
17624  */
17625
17626 Roo.bootstrap.ProgressBar = function(config){
17627     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17628 };
17629
17630 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17631     
17632     aria_valuenow : 0,
17633     aria_valuemin : 0,
17634     aria_valuemax : 100,
17635     label : false,
17636     panel : false,
17637     role : false,
17638     sr_only: false,
17639     
17640     getAutoCreate : function()
17641     {
17642         
17643         var cfg = {
17644             tag: 'div',
17645             cls: 'progress-bar',
17646             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17647         };
17648         
17649         if(this.sr_only){
17650             cfg.cn = {
17651                 tag: 'span',
17652                 cls: 'sr-only',
17653                 html: this.sr_only
17654             }
17655         }
17656         
17657         if(this.role){
17658             cfg.role = this.role;
17659         }
17660         
17661         if(this.aria_valuenow){
17662             cfg['aria-valuenow'] = this.aria_valuenow;
17663         }
17664         
17665         if(this.aria_valuemin){
17666             cfg['aria-valuemin'] = this.aria_valuemin;
17667         }
17668         
17669         if(this.aria_valuemax){
17670             cfg['aria-valuemax'] = this.aria_valuemax;
17671         }
17672         
17673         if(this.label && !this.sr_only){
17674             cfg.html = this.label;
17675         }
17676         
17677         if(this.panel){
17678             cfg.cls += ' progress-bar-' + this.panel;
17679         }
17680         
17681         return cfg;
17682     },
17683     
17684     update : function(aria_valuenow)
17685     {
17686         this.aria_valuenow = aria_valuenow;
17687         
17688         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17689     }
17690    
17691 });
17692
17693  
17694
17695  /*
17696  * - LGPL
17697  *
17698  * column
17699  * 
17700  */
17701
17702 /**
17703  * @class Roo.bootstrap.TabGroup
17704  * @extends Roo.bootstrap.Column
17705  * Bootstrap Column class
17706  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17707  * @cfg {Boolean} carousel true to make the group behave like a carousel
17708  * @cfg {Boolean} bullets show bullets for the panels
17709  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17710  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17711  * @cfg {Boolean} showarrow (true|false) show arrow default true
17712  * 
17713  * @constructor
17714  * Create a new TabGroup
17715  * @param {Object} config The config object
17716  */
17717
17718 Roo.bootstrap.TabGroup = function(config){
17719     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17720     if (!this.navId) {
17721         this.navId = Roo.id();
17722     }
17723     this.tabs = [];
17724     Roo.bootstrap.TabGroup.register(this);
17725     
17726 };
17727
17728 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17729     
17730     carousel : false,
17731     transition : false,
17732     bullets : 0,
17733     timer : 0,
17734     autoslide : false,
17735     slideFn : false,
17736     slideOnTouch : false,
17737     showarrow : true,
17738     
17739     getAutoCreate : function()
17740     {
17741         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17742         
17743         cfg.cls += ' tab-content';
17744         
17745         if (this.carousel) {
17746             cfg.cls += ' carousel slide';
17747             
17748             cfg.cn = [{
17749                cls : 'carousel-inner',
17750                cn : []
17751             }];
17752         
17753             if(this.bullets  && !Roo.isTouch){
17754                 
17755                 var bullets = {
17756                     cls : 'carousel-bullets',
17757                     cn : []
17758                 };
17759                
17760                 if(this.bullets_cls){
17761                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17762                 }
17763                 
17764                 bullets.cn.push({
17765                     cls : 'clear'
17766                 });
17767                 
17768                 cfg.cn[0].cn.push(bullets);
17769             }
17770             
17771             if(this.showarrow){
17772                 cfg.cn[0].cn.push({
17773                     tag : 'div',
17774                     class : 'carousel-arrow',
17775                     cn : [
17776                         {
17777                             tag : 'div',
17778                             class : 'carousel-prev',
17779                             cn : [
17780                                 {
17781                                     tag : 'i',
17782                                     class : 'fa fa-chevron-left'
17783                                 }
17784                             ]
17785                         },
17786                         {
17787                             tag : 'div',
17788                             class : 'carousel-next',
17789                             cn : [
17790                                 {
17791                                     tag : 'i',
17792                                     class : 'fa fa-chevron-right'
17793                                 }
17794                             ]
17795                         }
17796                     ]
17797                 });
17798             }
17799             
17800         }
17801         
17802         return cfg;
17803     },
17804     
17805     initEvents:  function()
17806     {
17807 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17808 //            this.el.on("touchstart", this.onTouchStart, this);
17809 //        }
17810         
17811         if(this.autoslide){
17812             var _this = this;
17813             
17814             this.slideFn = window.setInterval(function() {
17815                 _this.showPanelNext();
17816             }, this.timer);
17817         }
17818         
17819         if(this.showarrow){
17820             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17821             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17822         }
17823         
17824         
17825     },
17826     
17827 //    onTouchStart : function(e, el, o)
17828 //    {
17829 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17830 //            return;
17831 //        }
17832 //        
17833 //        this.showPanelNext();
17834 //    },
17835     
17836     
17837     getChildContainer : function()
17838     {
17839         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17840     },
17841     
17842     /**
17843     * register a Navigation item
17844     * @param {Roo.bootstrap.NavItem} the navitem to add
17845     */
17846     register : function(item)
17847     {
17848         this.tabs.push( item);
17849         item.navId = this.navId; // not really needed..
17850         this.addBullet();
17851     
17852     },
17853     
17854     getActivePanel : function()
17855     {
17856         var r = false;
17857         Roo.each(this.tabs, function(t) {
17858             if (t.active) {
17859                 r = t;
17860                 return false;
17861             }
17862             return null;
17863         });
17864         return r;
17865         
17866     },
17867     getPanelByName : function(n)
17868     {
17869         var r = false;
17870         Roo.each(this.tabs, function(t) {
17871             if (t.tabId == n) {
17872                 r = t;
17873                 return false;
17874             }
17875             return null;
17876         });
17877         return r;
17878     },
17879     indexOfPanel : function(p)
17880     {
17881         var r = false;
17882         Roo.each(this.tabs, function(t,i) {
17883             if (t.tabId == p.tabId) {
17884                 r = i;
17885                 return false;
17886             }
17887             return null;
17888         });
17889         return r;
17890     },
17891     /**
17892      * show a specific panel
17893      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17894      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17895      */
17896     showPanel : function (pan)
17897     {
17898         if(this.transition || typeof(pan) == 'undefined'){
17899             Roo.log("waiting for the transitionend");
17900             return;
17901         }
17902         
17903         if (typeof(pan) == 'number') {
17904             pan = this.tabs[pan];
17905         }
17906         
17907         if (typeof(pan) == 'string') {
17908             pan = this.getPanelByName(pan);
17909         }
17910         
17911         var cur = this.getActivePanel();
17912         
17913         if(!pan || !cur){
17914             Roo.log('pan or acitve pan is undefined');
17915             return false;
17916         }
17917         
17918         if (pan.tabId == this.getActivePanel().tabId) {
17919             return true;
17920         }
17921         
17922         if (false === cur.fireEvent('beforedeactivate')) {
17923             return false;
17924         }
17925         
17926         if(this.bullets > 0 && !Roo.isTouch){
17927             this.setActiveBullet(this.indexOfPanel(pan));
17928         }
17929         
17930         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17931             
17932             this.transition = true;
17933             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17934             var lr = dir == 'next' ? 'left' : 'right';
17935             pan.el.addClass(dir); // or prev
17936             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17937             cur.el.addClass(lr); // or right
17938             pan.el.addClass(lr);
17939             
17940             var _this = this;
17941             cur.el.on('transitionend', function() {
17942                 Roo.log("trans end?");
17943                 
17944                 pan.el.removeClass([lr,dir]);
17945                 pan.setActive(true);
17946                 
17947                 cur.el.removeClass([lr]);
17948                 cur.setActive(false);
17949                 
17950                 _this.transition = false;
17951                 
17952             }, this, { single:  true } );
17953             
17954             return true;
17955         }
17956         
17957         cur.setActive(false);
17958         pan.setActive(true);
17959         
17960         return true;
17961         
17962     },
17963     showPanelNext : function()
17964     {
17965         var i = this.indexOfPanel(this.getActivePanel());
17966         
17967         if (i >= this.tabs.length - 1 && !this.autoslide) {
17968             return;
17969         }
17970         
17971         if (i >= this.tabs.length - 1 && this.autoslide) {
17972             i = -1;
17973         }
17974         
17975         this.showPanel(this.tabs[i+1]);
17976     },
17977     
17978     showPanelPrev : function()
17979     {
17980         var i = this.indexOfPanel(this.getActivePanel());
17981         
17982         if (i  < 1 && !this.autoslide) {
17983             return;
17984         }
17985         
17986         if (i < 1 && this.autoslide) {
17987             i = this.tabs.length;
17988         }
17989         
17990         this.showPanel(this.tabs[i-1]);
17991     },
17992     
17993     
17994     addBullet: function()
17995     {
17996         if(!this.bullets || Roo.isTouch){
17997             return;
17998         }
17999         var ctr = this.el.select('.carousel-bullets',true).first();
18000         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18001         var bullet = ctr.createChild({
18002             cls : 'bullet bullet-' + i
18003         },ctr.dom.lastChild);
18004         
18005         
18006         var _this = this;
18007         
18008         bullet.on('click', (function(e, el, o, ii, t){
18009
18010             e.preventDefault();
18011
18012             this.showPanel(ii);
18013
18014             if(this.autoslide && this.slideFn){
18015                 clearInterval(this.slideFn);
18016                 this.slideFn = window.setInterval(function() {
18017                     _this.showPanelNext();
18018                 }, this.timer);
18019             }
18020
18021         }).createDelegate(this, [i, bullet], true));
18022                 
18023         
18024     },
18025      
18026     setActiveBullet : function(i)
18027     {
18028         if(Roo.isTouch){
18029             return;
18030         }
18031         
18032         Roo.each(this.el.select('.bullet', true).elements, function(el){
18033             el.removeClass('selected');
18034         });
18035
18036         var bullet = this.el.select('.bullet-' + i, true).first();
18037         
18038         if(!bullet){
18039             return;
18040         }
18041         
18042         bullet.addClass('selected');
18043     }
18044     
18045     
18046   
18047 });
18048
18049  
18050
18051  
18052  
18053 Roo.apply(Roo.bootstrap.TabGroup, {
18054     
18055     groups: {},
18056      /**
18057     * register a Navigation Group
18058     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18059     */
18060     register : function(navgrp)
18061     {
18062         this.groups[navgrp.navId] = navgrp;
18063         
18064     },
18065     /**
18066     * fetch a Navigation Group based on the navigation ID
18067     * if one does not exist , it will get created.
18068     * @param {string} the navgroup to add
18069     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18070     */
18071     get: function(navId) {
18072         if (typeof(this.groups[navId]) == 'undefined') {
18073             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18074         }
18075         return this.groups[navId] ;
18076     }
18077     
18078     
18079     
18080 });
18081
18082  /*
18083  * - LGPL
18084  *
18085  * TabPanel
18086  * 
18087  */
18088
18089 /**
18090  * @class Roo.bootstrap.TabPanel
18091  * @extends Roo.bootstrap.Component
18092  * Bootstrap TabPanel class
18093  * @cfg {Boolean} active panel active
18094  * @cfg {String} html panel content
18095  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18096  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18097  * @cfg {String} href click to link..
18098  * 
18099  * 
18100  * @constructor
18101  * Create a new TabPanel
18102  * @param {Object} config The config object
18103  */
18104
18105 Roo.bootstrap.TabPanel = function(config){
18106     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18107     this.addEvents({
18108         /**
18109              * @event changed
18110              * Fires when the active status changes
18111              * @param {Roo.bootstrap.TabPanel} this
18112              * @param {Boolean} state the new state
18113             
18114          */
18115         'changed': true,
18116         /**
18117              * @event beforedeactivate
18118              * Fires before a tab is de-activated - can be used to do validation on a form.
18119              * @param {Roo.bootstrap.TabPanel} this
18120              * @return {Boolean} false if there is an error
18121             
18122          */
18123         'beforedeactivate': true
18124      });
18125     
18126     this.tabId = this.tabId || Roo.id();
18127   
18128 };
18129
18130 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18131     
18132     active: false,
18133     html: false,
18134     tabId: false,
18135     navId : false,
18136     href : '',
18137     
18138     getAutoCreate : function(){
18139         var cfg = {
18140             tag: 'div',
18141             // item is needed for carousel - not sure if it has any effect otherwise
18142             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18143             html: this.html || ''
18144         };
18145         
18146         if(this.active){
18147             cfg.cls += ' active';
18148         }
18149         
18150         if(this.tabId){
18151             cfg.tabId = this.tabId;
18152         }
18153         
18154         
18155         return cfg;
18156     },
18157     
18158     initEvents:  function()
18159     {
18160         var p = this.parent();
18161         
18162         this.navId = this.navId || p.navId;
18163         
18164         if (typeof(this.navId) != 'undefined') {
18165             // not really needed.. but just in case.. parent should be a NavGroup.
18166             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18167             
18168             tg.register(this);
18169             
18170             var i = tg.tabs.length - 1;
18171             
18172             if(this.active && tg.bullets > 0 && i < tg.bullets){
18173                 tg.setActiveBullet(i);
18174             }
18175         }
18176         
18177         this.el.on('click', this.onClick, this);
18178         
18179         if(Roo.isTouch){
18180             this.el.on("touchstart", this.onTouchStart, this);
18181             this.el.on("touchmove", this.onTouchMove, this);
18182             this.el.on("touchend", this.onTouchEnd, this);
18183         }
18184         
18185     },
18186     
18187     onRender : function(ct, position)
18188     {
18189         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18190     },
18191     
18192     setActive : function(state)
18193     {
18194         Roo.log("panel - set active " + this.tabId + "=" + state);
18195         
18196         this.active = state;
18197         if (!state) {
18198             this.el.removeClass('active');
18199             
18200         } else  if (!this.el.hasClass('active')) {
18201             this.el.addClass('active');
18202         }
18203         
18204         this.fireEvent('changed', this, state);
18205     },
18206     
18207     onClick : function(e)
18208     {
18209         e.preventDefault();
18210         
18211         if(!this.href.length){
18212             return;
18213         }
18214         
18215         window.location.href = this.href;
18216     },
18217     
18218     startX : 0,
18219     startY : 0,
18220     endX : 0,
18221     endY : 0,
18222     swiping : false,
18223     
18224     onTouchStart : function(e)
18225     {
18226         this.swiping = false;
18227         
18228         this.startX = e.browserEvent.touches[0].clientX;
18229         this.startY = e.browserEvent.touches[0].clientY;
18230     },
18231     
18232     onTouchMove : function(e)
18233     {
18234         this.swiping = true;
18235         
18236         this.endX = e.browserEvent.touches[0].clientX;
18237         this.endY = e.browserEvent.touches[0].clientY;
18238     },
18239     
18240     onTouchEnd : function(e)
18241     {
18242         if(!this.swiping){
18243             this.onClick(e);
18244             return;
18245         }
18246         
18247         var tabGroup = this.parent();
18248         
18249         if(this.endX > this.startX){ // swiping right
18250             tabGroup.showPanelPrev();
18251             return;
18252         }
18253         
18254         if(this.startX > this.endX){ // swiping left
18255             tabGroup.showPanelNext();
18256             return;
18257         }
18258     }
18259     
18260     
18261 });
18262  
18263
18264  
18265
18266  /*
18267  * - LGPL
18268  *
18269  * DateField
18270  * 
18271  */
18272
18273 /**
18274  * @class Roo.bootstrap.DateField
18275  * @extends Roo.bootstrap.Input
18276  * Bootstrap DateField class
18277  * @cfg {Number} weekStart default 0
18278  * @cfg {String} viewMode default empty, (months|years)
18279  * @cfg {String} minViewMode default empty, (months|years)
18280  * @cfg {Number} startDate default -Infinity
18281  * @cfg {Number} endDate default Infinity
18282  * @cfg {Boolean} todayHighlight default false
18283  * @cfg {Boolean} todayBtn default false
18284  * @cfg {Boolean} calendarWeeks default false
18285  * @cfg {Object} daysOfWeekDisabled default empty
18286  * @cfg {Boolean} singleMode default false (true | false)
18287  * 
18288  * @cfg {Boolean} keyboardNavigation default true
18289  * @cfg {String} language default en
18290  * 
18291  * @constructor
18292  * Create a new DateField
18293  * @param {Object} config The config object
18294  */
18295
18296 Roo.bootstrap.DateField = function(config){
18297     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18298      this.addEvents({
18299             /**
18300              * @event show
18301              * Fires when this field show.
18302              * @param {Roo.bootstrap.DateField} this
18303              * @param {Mixed} date The date value
18304              */
18305             show : true,
18306             /**
18307              * @event show
18308              * Fires when this field hide.
18309              * @param {Roo.bootstrap.DateField} this
18310              * @param {Mixed} date The date value
18311              */
18312             hide : true,
18313             /**
18314              * @event select
18315              * Fires when select a date.
18316              * @param {Roo.bootstrap.DateField} this
18317              * @param {Mixed} date The date value
18318              */
18319             select : true,
18320             /**
18321              * @event beforeselect
18322              * Fires when before select a date.
18323              * @param {Roo.bootstrap.DateField} this
18324              * @param {Mixed} date The date value
18325              */
18326             beforeselect : true
18327         });
18328 };
18329
18330 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18331     
18332     /**
18333      * @cfg {String} format
18334      * The default date format string which can be overriden for localization support.  The format must be
18335      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18336      */
18337     format : "m/d/y",
18338     /**
18339      * @cfg {String} altFormats
18340      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18341      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18342      */
18343     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18344     
18345     weekStart : 0,
18346     
18347     viewMode : '',
18348     
18349     minViewMode : '',
18350     
18351     todayHighlight : false,
18352     
18353     todayBtn: false,
18354     
18355     language: 'en',
18356     
18357     keyboardNavigation: true,
18358     
18359     calendarWeeks: false,
18360     
18361     startDate: -Infinity,
18362     
18363     endDate: Infinity,
18364     
18365     daysOfWeekDisabled: [],
18366     
18367     _events: [],
18368     
18369     singleMode : false,
18370     
18371     UTCDate: function()
18372     {
18373         return new Date(Date.UTC.apply(Date, arguments));
18374     },
18375     
18376     UTCToday: function()
18377     {
18378         var today = new Date();
18379         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18380     },
18381     
18382     getDate: function() {
18383             var d = this.getUTCDate();
18384             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18385     },
18386     
18387     getUTCDate: function() {
18388             return this.date;
18389     },
18390     
18391     setDate: function(d) {
18392             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18393     },
18394     
18395     setUTCDate: function(d) {
18396             this.date = d;
18397             this.setValue(this.formatDate(this.date));
18398     },
18399         
18400     onRender: function(ct, position)
18401     {
18402         
18403         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18404         
18405         this.language = this.language || 'en';
18406         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18407         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18408         
18409         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18410         this.format = this.format || 'm/d/y';
18411         this.isInline = false;
18412         this.isInput = true;
18413         this.component = this.el.select('.add-on', true).first() || false;
18414         this.component = (this.component && this.component.length === 0) ? false : this.component;
18415         this.hasInput = this.component && this.inputEl().length;
18416         
18417         if (typeof(this.minViewMode === 'string')) {
18418             switch (this.minViewMode) {
18419                 case 'months':
18420                     this.minViewMode = 1;
18421                     break;
18422                 case 'years':
18423                     this.minViewMode = 2;
18424                     break;
18425                 default:
18426                     this.minViewMode = 0;
18427                     break;
18428             }
18429         }
18430         
18431         if (typeof(this.viewMode === 'string')) {
18432             switch (this.viewMode) {
18433                 case 'months':
18434                     this.viewMode = 1;
18435                     break;
18436                 case 'years':
18437                     this.viewMode = 2;
18438                     break;
18439                 default:
18440                     this.viewMode = 0;
18441                     break;
18442             }
18443         }
18444                 
18445         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18446         
18447 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18448         
18449         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18450         
18451         this.picker().on('mousedown', this.onMousedown, this);
18452         this.picker().on('click', this.onClick, this);
18453         
18454         this.picker().addClass('datepicker-dropdown');
18455         
18456         this.startViewMode = this.viewMode;
18457         
18458         if(this.singleMode){
18459             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18460                 v.setVisibilityMode(Roo.Element.DISPLAY);
18461                 v.hide();
18462             });
18463             
18464             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18465                 v.setStyle('width', '189px');
18466             });
18467         }
18468         
18469         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18470             if(!this.calendarWeeks){
18471                 v.remove();
18472                 return;
18473             }
18474             
18475             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18476             v.attr('colspan', function(i, val){
18477                 return parseInt(val) + 1;
18478             });
18479         });
18480                         
18481         
18482         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18483         
18484         this.setStartDate(this.startDate);
18485         this.setEndDate(this.endDate);
18486         
18487         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18488         
18489         this.fillDow();
18490         this.fillMonths();
18491         this.update();
18492         this.showMode();
18493         
18494         if(this.isInline) {
18495             this.show();
18496         }
18497     },
18498     
18499     picker : function()
18500     {
18501         return this.pickerEl;
18502 //        return this.el.select('.datepicker', true).first();
18503     },
18504     
18505     fillDow: function()
18506     {
18507         var dowCnt = this.weekStart;
18508         
18509         var dow = {
18510             tag: 'tr',
18511             cn: [
18512                 
18513             ]
18514         };
18515         
18516         if(this.calendarWeeks){
18517             dow.cn.push({
18518                 tag: 'th',
18519                 cls: 'cw',
18520                 html: '&nbsp;'
18521             })
18522         }
18523         
18524         while (dowCnt < this.weekStart + 7) {
18525             dow.cn.push({
18526                 tag: 'th',
18527                 cls: 'dow',
18528                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18529             });
18530         }
18531         
18532         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18533     },
18534     
18535     fillMonths: function()
18536     {    
18537         var i = 0;
18538         var months = this.picker().select('>.datepicker-months td', true).first();
18539         
18540         months.dom.innerHTML = '';
18541         
18542         while (i < 12) {
18543             var month = {
18544                 tag: 'span',
18545                 cls: 'month',
18546                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18547             };
18548             
18549             months.createChild(month);
18550         }
18551         
18552     },
18553     
18554     update: function()
18555     {
18556         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;
18557         
18558         if (this.date < this.startDate) {
18559             this.viewDate = new Date(this.startDate);
18560         } else if (this.date > this.endDate) {
18561             this.viewDate = new Date(this.endDate);
18562         } else {
18563             this.viewDate = new Date(this.date);
18564         }
18565         
18566         this.fill();
18567     },
18568     
18569     fill: function() 
18570     {
18571         var d = new Date(this.viewDate),
18572                 year = d.getUTCFullYear(),
18573                 month = d.getUTCMonth(),
18574                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18575                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18576                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18577                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18578                 currentDate = this.date && this.date.valueOf(),
18579                 today = this.UTCToday();
18580         
18581         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18582         
18583 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18584         
18585 //        this.picker.select('>tfoot th.today').
18586 //                                              .text(dates[this.language].today)
18587 //                                              .toggle(this.todayBtn !== false);
18588     
18589         this.updateNavArrows();
18590         this.fillMonths();
18591                                                 
18592         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18593         
18594         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18595          
18596         prevMonth.setUTCDate(day);
18597         
18598         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18599         
18600         var nextMonth = new Date(prevMonth);
18601         
18602         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18603         
18604         nextMonth = nextMonth.valueOf();
18605         
18606         var fillMonths = false;
18607         
18608         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18609         
18610         while(prevMonth.valueOf() < nextMonth) {
18611             var clsName = '';
18612             
18613             if (prevMonth.getUTCDay() === this.weekStart) {
18614                 if(fillMonths){
18615                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18616                 }
18617                     
18618                 fillMonths = {
18619                     tag: 'tr',
18620                     cn: []
18621                 };
18622                 
18623                 if(this.calendarWeeks){
18624                     // ISO 8601: First week contains first thursday.
18625                     // ISO also states week starts on Monday, but we can be more abstract here.
18626                     var
18627                     // Start of current week: based on weekstart/current date
18628                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18629                     // Thursday of this week
18630                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18631                     // First Thursday of year, year from thursday
18632                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18633                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18634                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18635                     
18636                     fillMonths.cn.push({
18637                         tag: 'td',
18638                         cls: 'cw',
18639                         html: calWeek
18640                     });
18641                 }
18642             }
18643             
18644             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18645                 clsName += ' old';
18646             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18647                 clsName += ' new';
18648             }
18649             if (this.todayHighlight &&
18650                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18651                 prevMonth.getUTCMonth() == today.getMonth() &&
18652                 prevMonth.getUTCDate() == today.getDate()) {
18653                 clsName += ' today';
18654             }
18655             
18656             if (currentDate && prevMonth.valueOf() === currentDate) {
18657                 clsName += ' active';
18658             }
18659             
18660             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18661                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18662                     clsName += ' disabled';
18663             }
18664             
18665             fillMonths.cn.push({
18666                 tag: 'td',
18667                 cls: 'day ' + clsName,
18668                 html: prevMonth.getDate()
18669             });
18670             
18671             prevMonth.setDate(prevMonth.getDate()+1);
18672         }
18673           
18674         var currentYear = this.date && this.date.getUTCFullYear();
18675         var currentMonth = this.date && this.date.getUTCMonth();
18676         
18677         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18678         
18679         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18680             v.removeClass('active');
18681             
18682             if(currentYear === year && k === currentMonth){
18683                 v.addClass('active');
18684             }
18685             
18686             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18687                 v.addClass('disabled');
18688             }
18689             
18690         });
18691         
18692         
18693         year = parseInt(year/10, 10) * 10;
18694         
18695         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18696         
18697         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18698         
18699         year -= 1;
18700         for (var i = -1; i < 11; i++) {
18701             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18702                 tag: 'span',
18703                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18704                 html: year
18705             });
18706             
18707             year += 1;
18708         }
18709     },
18710     
18711     showMode: function(dir) 
18712     {
18713         if (dir) {
18714             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18715         }
18716         
18717         Roo.each(this.picker().select('>div',true).elements, function(v){
18718             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18719             v.hide();
18720         });
18721         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18722     },
18723     
18724     place: function()
18725     {
18726         if(this.isInline) {
18727             return;
18728         }
18729         
18730         this.picker().removeClass(['bottom', 'top']);
18731         
18732         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18733             /*
18734              * place to the top of element!
18735              *
18736              */
18737             
18738             this.picker().addClass('top');
18739             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18740             
18741             return;
18742         }
18743         
18744         this.picker().addClass('bottom');
18745         
18746         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18747     },
18748     
18749     parseDate : function(value)
18750     {
18751         if(!value || value instanceof Date){
18752             return value;
18753         }
18754         var v = Date.parseDate(value, this.format);
18755         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18756             v = Date.parseDate(value, 'Y-m-d');
18757         }
18758         if(!v && this.altFormats){
18759             if(!this.altFormatsArray){
18760                 this.altFormatsArray = this.altFormats.split("|");
18761             }
18762             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18763                 v = Date.parseDate(value, this.altFormatsArray[i]);
18764             }
18765         }
18766         return v;
18767     },
18768     
18769     formatDate : function(date, fmt)
18770     {   
18771         return (!date || !(date instanceof Date)) ?
18772         date : date.dateFormat(fmt || this.format);
18773     },
18774     
18775     onFocus : function()
18776     {
18777         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18778         this.show();
18779     },
18780     
18781     onBlur : function()
18782     {
18783         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18784         
18785         var d = this.inputEl().getValue();
18786         
18787         this.setValue(d);
18788                 
18789         this.hide();
18790     },
18791     
18792     show : function()
18793     {
18794         this.picker().show();
18795         this.update();
18796         this.place();
18797         
18798         this.fireEvent('show', this, this.date);
18799     },
18800     
18801     hide : function()
18802     {
18803         if(this.isInline) {
18804             return;
18805         }
18806         this.picker().hide();
18807         this.viewMode = this.startViewMode;
18808         this.showMode();
18809         
18810         this.fireEvent('hide', this, this.date);
18811         
18812     },
18813     
18814     onMousedown: function(e)
18815     {
18816         e.stopPropagation();
18817         e.preventDefault();
18818     },
18819     
18820     keyup: function(e)
18821     {
18822         Roo.bootstrap.DateField.superclass.keyup.call(this);
18823         this.update();
18824     },
18825
18826     setValue: function(v)
18827     {
18828         if(this.fireEvent('beforeselect', this, v) !== false){
18829             var d = new Date(this.parseDate(v) ).clearTime();
18830         
18831             if(isNaN(d.getTime())){
18832                 this.date = this.viewDate = '';
18833                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18834                 return;
18835             }
18836
18837             v = this.formatDate(d);
18838
18839             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18840
18841             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18842
18843             this.update();
18844
18845             this.fireEvent('select', this, this.date);
18846         }
18847     },
18848     
18849     getValue: function()
18850     {
18851         return this.formatDate(this.date);
18852     },
18853     
18854     fireKey: function(e)
18855     {
18856         if (!this.picker().isVisible()){
18857             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18858                 this.show();
18859             }
18860             return;
18861         }
18862         
18863         var dateChanged = false,
18864         dir, day, month,
18865         newDate, newViewDate;
18866         
18867         switch(e.keyCode){
18868             case 27: // escape
18869                 this.hide();
18870                 e.preventDefault();
18871                 break;
18872             case 37: // left
18873             case 39: // right
18874                 if (!this.keyboardNavigation) {
18875                     break;
18876                 }
18877                 dir = e.keyCode == 37 ? -1 : 1;
18878                 
18879                 if (e.ctrlKey){
18880                     newDate = this.moveYear(this.date, dir);
18881                     newViewDate = this.moveYear(this.viewDate, dir);
18882                 } else if (e.shiftKey){
18883                     newDate = this.moveMonth(this.date, dir);
18884                     newViewDate = this.moveMonth(this.viewDate, dir);
18885                 } else {
18886                     newDate = new Date(this.date);
18887                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18888                     newViewDate = new Date(this.viewDate);
18889                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18890                 }
18891                 if (this.dateWithinRange(newDate)){
18892                     this.date = newDate;
18893                     this.viewDate = newViewDate;
18894                     this.setValue(this.formatDate(this.date));
18895 //                    this.update();
18896                     e.preventDefault();
18897                     dateChanged = true;
18898                 }
18899                 break;
18900             case 38: // up
18901             case 40: // down
18902                 if (!this.keyboardNavigation) {
18903                     break;
18904                 }
18905                 dir = e.keyCode == 38 ? -1 : 1;
18906                 if (e.ctrlKey){
18907                     newDate = this.moveYear(this.date, dir);
18908                     newViewDate = this.moveYear(this.viewDate, dir);
18909                 } else if (e.shiftKey){
18910                     newDate = this.moveMonth(this.date, dir);
18911                     newViewDate = this.moveMonth(this.viewDate, dir);
18912                 } else {
18913                     newDate = new Date(this.date);
18914                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18915                     newViewDate = new Date(this.viewDate);
18916                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18917                 }
18918                 if (this.dateWithinRange(newDate)){
18919                     this.date = newDate;
18920                     this.viewDate = newViewDate;
18921                     this.setValue(this.formatDate(this.date));
18922 //                    this.update();
18923                     e.preventDefault();
18924                     dateChanged = true;
18925                 }
18926                 break;
18927             case 13: // enter
18928                 this.setValue(this.formatDate(this.date));
18929                 this.hide();
18930                 e.preventDefault();
18931                 break;
18932             case 9: // tab
18933                 this.setValue(this.formatDate(this.date));
18934                 this.hide();
18935                 break;
18936             case 16: // shift
18937             case 17: // ctrl
18938             case 18: // alt
18939                 break;
18940             default :
18941                 this.hide();
18942                 
18943         }
18944     },
18945     
18946     
18947     onClick: function(e) 
18948     {
18949         e.stopPropagation();
18950         e.preventDefault();
18951         
18952         var target = e.getTarget();
18953         
18954         if(target.nodeName.toLowerCase() === 'i'){
18955             target = Roo.get(target).dom.parentNode;
18956         }
18957         
18958         var nodeName = target.nodeName;
18959         var className = target.className;
18960         var html = target.innerHTML;
18961         //Roo.log(nodeName);
18962         
18963         switch(nodeName.toLowerCase()) {
18964             case 'th':
18965                 switch(className) {
18966                     case 'switch':
18967                         this.showMode(1);
18968                         break;
18969                     case 'prev':
18970                     case 'next':
18971                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18972                         switch(this.viewMode){
18973                                 case 0:
18974                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18975                                         break;
18976                                 case 1:
18977                                 case 2:
18978                                         this.viewDate = this.moveYear(this.viewDate, dir);
18979                                         break;
18980                         }
18981                         this.fill();
18982                         break;
18983                     case 'today':
18984                         var date = new Date();
18985                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18986 //                        this.fill()
18987                         this.setValue(this.formatDate(this.date));
18988                         
18989                         this.hide();
18990                         break;
18991                 }
18992                 break;
18993             case 'span':
18994                 if (className.indexOf('disabled') < 0) {
18995                     this.viewDate.setUTCDate(1);
18996                     if (className.indexOf('month') > -1) {
18997                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18998                     } else {
18999                         var year = parseInt(html, 10) || 0;
19000                         this.viewDate.setUTCFullYear(year);
19001                         
19002                     }
19003                     
19004                     if(this.singleMode){
19005                         this.setValue(this.formatDate(this.viewDate));
19006                         this.hide();
19007                         return;
19008                     }
19009                     
19010                     this.showMode(-1);
19011                     this.fill();
19012                 }
19013                 break;
19014                 
19015             case 'td':
19016                 //Roo.log(className);
19017                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19018                     var day = parseInt(html, 10) || 1;
19019                     var year = this.viewDate.getUTCFullYear(),
19020                         month = this.viewDate.getUTCMonth();
19021
19022                     if (className.indexOf('old') > -1) {
19023                         if(month === 0 ){
19024                             month = 11;
19025                             year -= 1;
19026                         }else{
19027                             month -= 1;
19028                         }
19029                     } else if (className.indexOf('new') > -1) {
19030                         if (month == 11) {
19031                             month = 0;
19032                             year += 1;
19033                         } else {
19034                             month += 1;
19035                         }
19036                     }
19037                     //Roo.log([year,month,day]);
19038                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19039                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19040 //                    this.fill();
19041                     //Roo.log(this.formatDate(this.date));
19042                     this.setValue(this.formatDate(this.date));
19043                     this.hide();
19044                 }
19045                 break;
19046         }
19047     },
19048     
19049     setStartDate: function(startDate)
19050     {
19051         this.startDate = startDate || -Infinity;
19052         if (this.startDate !== -Infinity) {
19053             this.startDate = this.parseDate(this.startDate);
19054         }
19055         this.update();
19056         this.updateNavArrows();
19057     },
19058
19059     setEndDate: function(endDate)
19060     {
19061         this.endDate = endDate || Infinity;
19062         if (this.endDate !== Infinity) {
19063             this.endDate = this.parseDate(this.endDate);
19064         }
19065         this.update();
19066         this.updateNavArrows();
19067     },
19068     
19069     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19070     {
19071         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19072         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19073             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19074         }
19075         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19076             return parseInt(d, 10);
19077         });
19078         this.update();
19079         this.updateNavArrows();
19080     },
19081     
19082     updateNavArrows: function() 
19083     {
19084         if(this.singleMode){
19085             return;
19086         }
19087         
19088         var d = new Date(this.viewDate),
19089         year = d.getUTCFullYear(),
19090         month = d.getUTCMonth();
19091         
19092         Roo.each(this.picker().select('.prev', true).elements, function(v){
19093             v.show();
19094             switch (this.viewMode) {
19095                 case 0:
19096
19097                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19098                         v.hide();
19099                     }
19100                     break;
19101                 case 1:
19102                 case 2:
19103                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19104                         v.hide();
19105                     }
19106                     break;
19107             }
19108         });
19109         
19110         Roo.each(this.picker().select('.next', true).elements, function(v){
19111             v.show();
19112             switch (this.viewMode) {
19113                 case 0:
19114
19115                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19116                         v.hide();
19117                     }
19118                     break;
19119                 case 1:
19120                 case 2:
19121                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19122                         v.hide();
19123                     }
19124                     break;
19125             }
19126         })
19127     },
19128     
19129     moveMonth: function(date, dir)
19130     {
19131         if (!dir) {
19132             return date;
19133         }
19134         var new_date = new Date(date.valueOf()),
19135         day = new_date.getUTCDate(),
19136         month = new_date.getUTCMonth(),
19137         mag = Math.abs(dir),
19138         new_month, test;
19139         dir = dir > 0 ? 1 : -1;
19140         if (mag == 1){
19141             test = dir == -1
19142             // If going back one month, make sure month is not current month
19143             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19144             ? function(){
19145                 return new_date.getUTCMonth() == month;
19146             }
19147             // If going forward one month, make sure month is as expected
19148             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19149             : function(){
19150                 return new_date.getUTCMonth() != new_month;
19151             };
19152             new_month = month + dir;
19153             new_date.setUTCMonth(new_month);
19154             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19155             if (new_month < 0 || new_month > 11) {
19156                 new_month = (new_month + 12) % 12;
19157             }
19158         } else {
19159             // For magnitudes >1, move one month at a time...
19160             for (var i=0; i<mag; i++) {
19161                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19162                 new_date = this.moveMonth(new_date, dir);
19163             }
19164             // ...then reset the day, keeping it in the new month
19165             new_month = new_date.getUTCMonth();
19166             new_date.setUTCDate(day);
19167             test = function(){
19168                 return new_month != new_date.getUTCMonth();
19169             };
19170         }
19171         // Common date-resetting loop -- if date is beyond end of month, make it
19172         // end of month
19173         while (test()){
19174             new_date.setUTCDate(--day);
19175             new_date.setUTCMonth(new_month);
19176         }
19177         return new_date;
19178     },
19179
19180     moveYear: function(date, dir)
19181     {
19182         return this.moveMonth(date, dir*12);
19183     },
19184
19185     dateWithinRange: function(date)
19186     {
19187         return date >= this.startDate && date <= this.endDate;
19188     },
19189
19190     
19191     remove: function() 
19192     {
19193         this.picker().remove();
19194     },
19195     
19196     validateValue : function(value)
19197     {
19198         if(this.getVisibilityEl().hasClass('hidden')){
19199             return true;
19200         }
19201         
19202         if(value.length < 1)  {
19203             if(this.allowBlank){
19204                 return true;
19205             }
19206             return false;
19207         }
19208         
19209         if(value.length < this.minLength){
19210             return false;
19211         }
19212         if(value.length > this.maxLength){
19213             return false;
19214         }
19215         if(this.vtype){
19216             var vt = Roo.form.VTypes;
19217             if(!vt[this.vtype](value, this)){
19218                 return false;
19219             }
19220         }
19221         if(typeof this.validator == "function"){
19222             var msg = this.validator(value);
19223             if(msg !== true){
19224                 return false;
19225             }
19226         }
19227         
19228         if(this.regex && !this.regex.test(value)){
19229             return false;
19230         }
19231         
19232         if(typeof(this.parseDate(value)) == 'undefined'){
19233             return false;
19234         }
19235         
19236         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19237             return false;
19238         }      
19239         
19240         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19241             return false;
19242         } 
19243         
19244         
19245         return true;
19246     },
19247     
19248     setVisible : function(visible)
19249     {
19250         if(!this.getEl()){
19251             return;
19252         }
19253         
19254         this.getEl().removeClass('hidden');
19255         
19256         if(visible){
19257             return;
19258         }
19259         
19260         this.getEl().addClass('hidden');
19261     }
19262    
19263 });
19264
19265 Roo.apply(Roo.bootstrap.DateField,  {
19266     
19267     head : {
19268         tag: 'thead',
19269         cn: [
19270         {
19271             tag: 'tr',
19272             cn: [
19273             {
19274                 tag: 'th',
19275                 cls: 'prev',
19276                 html: '<i class="fa fa-arrow-left"/>'
19277             },
19278             {
19279                 tag: 'th',
19280                 cls: 'switch',
19281                 colspan: '5'
19282             },
19283             {
19284                 tag: 'th',
19285                 cls: 'next',
19286                 html: '<i class="fa fa-arrow-right"/>'
19287             }
19288
19289             ]
19290         }
19291         ]
19292     },
19293     
19294     content : {
19295         tag: 'tbody',
19296         cn: [
19297         {
19298             tag: 'tr',
19299             cn: [
19300             {
19301                 tag: 'td',
19302                 colspan: '7'
19303             }
19304             ]
19305         }
19306         ]
19307     },
19308     
19309     footer : {
19310         tag: 'tfoot',
19311         cn: [
19312         {
19313             tag: 'tr',
19314             cn: [
19315             {
19316                 tag: 'th',
19317                 colspan: '7',
19318                 cls: 'today'
19319             }
19320                     
19321             ]
19322         }
19323         ]
19324     },
19325     
19326     dates:{
19327         en: {
19328             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19329             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19330             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19331             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19332             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19333             today: "Today"
19334         }
19335     },
19336     
19337     modes: [
19338     {
19339         clsName: 'days',
19340         navFnc: 'Month',
19341         navStep: 1
19342     },
19343     {
19344         clsName: 'months',
19345         navFnc: 'FullYear',
19346         navStep: 1
19347     },
19348     {
19349         clsName: 'years',
19350         navFnc: 'FullYear',
19351         navStep: 10
19352     }]
19353 });
19354
19355 Roo.apply(Roo.bootstrap.DateField,  {
19356   
19357     template : {
19358         tag: 'div',
19359         cls: 'datepicker dropdown-menu roo-dynamic',
19360         cn: [
19361         {
19362             tag: 'div',
19363             cls: 'datepicker-days',
19364             cn: [
19365             {
19366                 tag: 'table',
19367                 cls: 'table-condensed',
19368                 cn:[
19369                 Roo.bootstrap.DateField.head,
19370                 {
19371                     tag: 'tbody'
19372                 },
19373                 Roo.bootstrap.DateField.footer
19374                 ]
19375             }
19376             ]
19377         },
19378         {
19379             tag: 'div',
19380             cls: 'datepicker-months',
19381             cn: [
19382             {
19383                 tag: 'table',
19384                 cls: 'table-condensed',
19385                 cn:[
19386                 Roo.bootstrap.DateField.head,
19387                 Roo.bootstrap.DateField.content,
19388                 Roo.bootstrap.DateField.footer
19389                 ]
19390             }
19391             ]
19392         },
19393         {
19394             tag: 'div',
19395             cls: 'datepicker-years',
19396             cn: [
19397             {
19398                 tag: 'table',
19399                 cls: 'table-condensed',
19400                 cn:[
19401                 Roo.bootstrap.DateField.head,
19402                 Roo.bootstrap.DateField.content,
19403                 Roo.bootstrap.DateField.footer
19404                 ]
19405             }
19406             ]
19407         }
19408         ]
19409     }
19410 });
19411
19412  
19413
19414  /*
19415  * - LGPL
19416  *
19417  * TimeField
19418  * 
19419  */
19420
19421 /**
19422  * @class Roo.bootstrap.TimeField
19423  * @extends Roo.bootstrap.Input
19424  * Bootstrap DateField class
19425  * 
19426  * 
19427  * @constructor
19428  * Create a new TimeField
19429  * @param {Object} config The config object
19430  */
19431
19432 Roo.bootstrap.TimeField = function(config){
19433     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19434     this.addEvents({
19435             /**
19436              * @event show
19437              * Fires when this field show.
19438              * @param {Roo.bootstrap.DateField} thisthis
19439              * @param {Mixed} date The date value
19440              */
19441             show : true,
19442             /**
19443              * @event show
19444              * Fires when this field hide.
19445              * @param {Roo.bootstrap.DateField} this
19446              * @param {Mixed} date The date value
19447              */
19448             hide : true,
19449             /**
19450              * @event select
19451              * Fires when select a date.
19452              * @param {Roo.bootstrap.DateField} this
19453              * @param {Mixed} date The date value
19454              */
19455             select : true
19456         });
19457 };
19458
19459 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19460     
19461     /**
19462      * @cfg {String} format
19463      * The default time format string which can be overriden for localization support.  The format must be
19464      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19465      */
19466     format : "H:i",
19467        
19468     onRender: function(ct, position)
19469     {
19470         
19471         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19472                 
19473         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19474         
19475         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19476         
19477         this.pop = this.picker().select('>.datepicker-time',true).first();
19478         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19479         
19480         this.picker().on('mousedown', this.onMousedown, this);
19481         this.picker().on('click', this.onClick, this);
19482         
19483         this.picker().addClass('datepicker-dropdown');
19484     
19485         this.fillTime();
19486         this.update();
19487             
19488         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19489         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19490         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19491         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19492         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19493         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19494
19495     },
19496     
19497     fireKey: function(e){
19498         if (!this.picker().isVisible()){
19499             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19500                 this.show();
19501             }
19502             return;
19503         }
19504
19505         e.preventDefault();
19506         
19507         switch(e.keyCode){
19508             case 27: // escape
19509                 this.hide();
19510                 break;
19511             case 37: // left
19512             case 39: // right
19513                 this.onTogglePeriod();
19514                 break;
19515             case 38: // up
19516                 this.onIncrementMinutes();
19517                 break;
19518             case 40: // down
19519                 this.onDecrementMinutes();
19520                 break;
19521             case 13: // enter
19522             case 9: // tab
19523                 this.setTime();
19524                 break;
19525         }
19526     },
19527     
19528     onClick: function(e) {
19529         e.stopPropagation();
19530         e.preventDefault();
19531     },
19532     
19533     picker : function()
19534     {
19535         return this.el.select('.datepicker', true).first();
19536     },
19537     
19538     fillTime: function()
19539     {    
19540         var time = this.pop.select('tbody', true).first();
19541         
19542         time.dom.innerHTML = '';
19543         
19544         time.createChild({
19545             tag: 'tr',
19546             cn: [
19547                 {
19548                     tag: 'td',
19549                     cn: [
19550                         {
19551                             tag: 'a',
19552                             href: '#',
19553                             cls: 'btn',
19554                             cn: [
19555                                 {
19556                                     tag: 'span',
19557                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19558                                 }
19559                             ]
19560                         } 
19561                     ]
19562                 },
19563                 {
19564                     tag: 'td',
19565                     cls: 'separator'
19566                 },
19567                 {
19568                     tag: 'td',
19569                     cn: [
19570                         {
19571                             tag: 'a',
19572                             href: '#',
19573                             cls: 'btn',
19574                             cn: [
19575                                 {
19576                                     tag: 'span',
19577                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19578                                 }
19579                             ]
19580                         }
19581                     ]
19582                 },
19583                 {
19584                     tag: 'td',
19585                     cls: 'separator'
19586                 }
19587             ]
19588         });
19589         
19590         time.createChild({
19591             tag: 'tr',
19592             cn: [
19593                 {
19594                     tag: 'td',
19595                     cn: [
19596                         {
19597                             tag: 'span',
19598                             cls: 'timepicker-hour',
19599                             html: '00'
19600                         }  
19601                     ]
19602                 },
19603                 {
19604                     tag: 'td',
19605                     cls: 'separator',
19606                     html: ':'
19607                 },
19608                 {
19609                     tag: 'td',
19610                     cn: [
19611                         {
19612                             tag: 'span',
19613                             cls: 'timepicker-minute',
19614                             html: '00'
19615                         }  
19616                     ]
19617                 },
19618                 {
19619                     tag: 'td',
19620                     cls: 'separator'
19621                 },
19622                 {
19623                     tag: 'td',
19624                     cn: [
19625                         {
19626                             tag: 'button',
19627                             type: 'button',
19628                             cls: 'btn btn-primary period',
19629                             html: 'AM'
19630                             
19631                         }
19632                     ]
19633                 }
19634             ]
19635         });
19636         
19637         time.createChild({
19638             tag: 'tr',
19639             cn: [
19640                 {
19641                     tag: 'td',
19642                     cn: [
19643                         {
19644                             tag: 'a',
19645                             href: '#',
19646                             cls: 'btn',
19647                             cn: [
19648                                 {
19649                                     tag: 'span',
19650                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19651                                 }
19652                             ]
19653                         }
19654                     ]
19655                 },
19656                 {
19657                     tag: 'td',
19658                     cls: 'separator'
19659                 },
19660                 {
19661                     tag: 'td',
19662                     cn: [
19663                         {
19664                             tag: 'a',
19665                             href: '#',
19666                             cls: 'btn',
19667                             cn: [
19668                                 {
19669                                     tag: 'span',
19670                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19671                                 }
19672                             ]
19673                         }
19674                     ]
19675                 },
19676                 {
19677                     tag: 'td',
19678                     cls: 'separator'
19679                 }
19680             ]
19681         });
19682         
19683     },
19684     
19685     update: function()
19686     {
19687         
19688         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19689         
19690         this.fill();
19691     },
19692     
19693     fill: function() 
19694     {
19695         var hours = this.time.getHours();
19696         var minutes = this.time.getMinutes();
19697         var period = 'AM';
19698         
19699         if(hours > 11){
19700             period = 'PM';
19701         }
19702         
19703         if(hours == 0){
19704             hours = 12;
19705         }
19706         
19707         
19708         if(hours > 12){
19709             hours = hours - 12;
19710         }
19711         
19712         if(hours < 10){
19713             hours = '0' + hours;
19714         }
19715         
19716         if(minutes < 10){
19717             minutes = '0' + minutes;
19718         }
19719         
19720         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19721         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19722         this.pop.select('button', true).first().dom.innerHTML = period;
19723         
19724     },
19725     
19726     place: function()
19727     {   
19728         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19729         
19730         var cls = ['bottom'];
19731         
19732         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19733             cls.pop();
19734             cls.push('top');
19735         }
19736         
19737         cls.push('right');
19738         
19739         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19740             cls.pop();
19741             cls.push('left');
19742         }
19743         
19744         this.picker().addClass(cls.join('-'));
19745         
19746         var _this = this;
19747         
19748         Roo.each(cls, function(c){
19749             if(c == 'bottom'){
19750                 _this.picker().setTop(_this.inputEl().getHeight());
19751                 return;
19752             }
19753             if(c == 'top'){
19754                 _this.picker().setTop(0 - _this.picker().getHeight());
19755                 return;
19756             }
19757             
19758             if(c == 'left'){
19759                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19760                 return;
19761             }
19762             if(c == 'right'){
19763                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19764                 return;
19765             }
19766         });
19767         
19768     },
19769   
19770     onFocus : function()
19771     {
19772         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19773         this.show();
19774     },
19775     
19776     onBlur : function()
19777     {
19778         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19779         this.hide();
19780     },
19781     
19782     show : function()
19783     {
19784         this.picker().show();
19785         this.pop.show();
19786         this.update();
19787         this.place();
19788         
19789         this.fireEvent('show', this, this.date);
19790     },
19791     
19792     hide : function()
19793     {
19794         this.picker().hide();
19795         this.pop.hide();
19796         
19797         this.fireEvent('hide', this, this.date);
19798     },
19799     
19800     setTime : function()
19801     {
19802         this.hide();
19803         this.setValue(this.time.format(this.format));
19804         
19805         this.fireEvent('select', this, this.date);
19806         
19807         
19808     },
19809     
19810     onMousedown: function(e){
19811         e.stopPropagation();
19812         e.preventDefault();
19813     },
19814     
19815     onIncrementHours: function()
19816     {
19817         Roo.log('onIncrementHours');
19818         this.time = this.time.add(Date.HOUR, 1);
19819         this.update();
19820         
19821     },
19822     
19823     onDecrementHours: function()
19824     {
19825         Roo.log('onDecrementHours');
19826         this.time = this.time.add(Date.HOUR, -1);
19827         this.update();
19828     },
19829     
19830     onIncrementMinutes: function()
19831     {
19832         Roo.log('onIncrementMinutes');
19833         this.time = this.time.add(Date.MINUTE, 1);
19834         this.update();
19835     },
19836     
19837     onDecrementMinutes: function()
19838     {
19839         Roo.log('onDecrementMinutes');
19840         this.time = this.time.add(Date.MINUTE, -1);
19841         this.update();
19842     },
19843     
19844     onTogglePeriod: function()
19845     {
19846         Roo.log('onTogglePeriod');
19847         this.time = this.time.add(Date.HOUR, 12);
19848         this.update();
19849     }
19850     
19851    
19852 });
19853
19854 Roo.apply(Roo.bootstrap.TimeField,  {
19855     
19856     content : {
19857         tag: 'tbody',
19858         cn: [
19859             {
19860                 tag: 'tr',
19861                 cn: [
19862                 {
19863                     tag: 'td',
19864                     colspan: '7'
19865                 }
19866                 ]
19867             }
19868         ]
19869     },
19870     
19871     footer : {
19872         tag: 'tfoot',
19873         cn: [
19874             {
19875                 tag: 'tr',
19876                 cn: [
19877                 {
19878                     tag: 'th',
19879                     colspan: '7',
19880                     cls: '',
19881                     cn: [
19882                         {
19883                             tag: 'button',
19884                             cls: 'btn btn-info ok',
19885                             html: 'OK'
19886                         }
19887                     ]
19888                 }
19889
19890                 ]
19891             }
19892         ]
19893     }
19894 });
19895
19896 Roo.apply(Roo.bootstrap.TimeField,  {
19897   
19898     template : {
19899         tag: 'div',
19900         cls: 'datepicker dropdown-menu',
19901         cn: [
19902             {
19903                 tag: 'div',
19904                 cls: 'datepicker-time',
19905                 cn: [
19906                 {
19907                     tag: 'table',
19908                     cls: 'table-condensed',
19909                     cn:[
19910                     Roo.bootstrap.TimeField.content,
19911                     Roo.bootstrap.TimeField.footer
19912                     ]
19913                 }
19914                 ]
19915             }
19916         ]
19917     }
19918 });
19919
19920  
19921
19922  /*
19923  * - LGPL
19924  *
19925  * MonthField
19926  * 
19927  */
19928
19929 /**
19930  * @class Roo.bootstrap.MonthField
19931  * @extends Roo.bootstrap.Input
19932  * Bootstrap MonthField class
19933  * 
19934  * @cfg {String} language default en
19935  * 
19936  * @constructor
19937  * Create a new MonthField
19938  * @param {Object} config The config object
19939  */
19940
19941 Roo.bootstrap.MonthField = function(config){
19942     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19943     
19944     this.addEvents({
19945         /**
19946          * @event show
19947          * Fires when this field show.
19948          * @param {Roo.bootstrap.MonthField} this
19949          * @param {Mixed} date The date value
19950          */
19951         show : true,
19952         /**
19953          * @event show
19954          * Fires when this field hide.
19955          * @param {Roo.bootstrap.MonthField} this
19956          * @param {Mixed} date The date value
19957          */
19958         hide : true,
19959         /**
19960          * @event select
19961          * Fires when select a date.
19962          * @param {Roo.bootstrap.MonthField} this
19963          * @param {String} oldvalue The old value
19964          * @param {String} newvalue The new value
19965          */
19966         select : true
19967     });
19968 };
19969
19970 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19971     
19972     onRender: function(ct, position)
19973     {
19974         
19975         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19976         
19977         this.language = this.language || 'en';
19978         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19979         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19980         
19981         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19982         this.isInline = false;
19983         this.isInput = true;
19984         this.component = this.el.select('.add-on', true).first() || false;
19985         this.component = (this.component && this.component.length === 0) ? false : this.component;
19986         this.hasInput = this.component && this.inputEL().length;
19987         
19988         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19989         
19990         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19991         
19992         this.picker().on('mousedown', this.onMousedown, this);
19993         this.picker().on('click', this.onClick, this);
19994         
19995         this.picker().addClass('datepicker-dropdown');
19996         
19997         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19998             v.setStyle('width', '189px');
19999         });
20000         
20001         this.fillMonths();
20002         
20003         this.update();
20004         
20005         if(this.isInline) {
20006             this.show();
20007         }
20008         
20009     },
20010     
20011     setValue: function(v, suppressEvent)
20012     {   
20013         var o = this.getValue();
20014         
20015         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20016         
20017         this.update();
20018
20019         if(suppressEvent !== true){
20020             this.fireEvent('select', this, o, v);
20021         }
20022         
20023     },
20024     
20025     getValue: function()
20026     {
20027         return this.value;
20028     },
20029     
20030     onClick: function(e) 
20031     {
20032         e.stopPropagation();
20033         e.preventDefault();
20034         
20035         var target = e.getTarget();
20036         
20037         if(target.nodeName.toLowerCase() === 'i'){
20038             target = Roo.get(target).dom.parentNode;
20039         }
20040         
20041         var nodeName = target.nodeName;
20042         var className = target.className;
20043         var html = target.innerHTML;
20044         
20045         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20046             return;
20047         }
20048         
20049         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20050         
20051         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20052         
20053         this.hide();
20054                         
20055     },
20056     
20057     picker : function()
20058     {
20059         return this.pickerEl;
20060     },
20061     
20062     fillMonths: function()
20063     {    
20064         var i = 0;
20065         var months = this.picker().select('>.datepicker-months td', true).first();
20066         
20067         months.dom.innerHTML = '';
20068         
20069         while (i < 12) {
20070             var month = {
20071                 tag: 'span',
20072                 cls: 'month',
20073                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20074             };
20075             
20076             months.createChild(month);
20077         }
20078         
20079     },
20080     
20081     update: function()
20082     {
20083         var _this = this;
20084         
20085         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20086             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20087         }
20088         
20089         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20090             e.removeClass('active');
20091             
20092             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20093                 e.addClass('active');
20094             }
20095         })
20096     },
20097     
20098     place: function()
20099     {
20100         if(this.isInline) {
20101             return;
20102         }
20103         
20104         this.picker().removeClass(['bottom', 'top']);
20105         
20106         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20107             /*
20108              * place to the top of element!
20109              *
20110              */
20111             
20112             this.picker().addClass('top');
20113             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20114             
20115             return;
20116         }
20117         
20118         this.picker().addClass('bottom');
20119         
20120         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20121     },
20122     
20123     onFocus : function()
20124     {
20125         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20126         this.show();
20127     },
20128     
20129     onBlur : function()
20130     {
20131         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20132         
20133         var d = this.inputEl().getValue();
20134         
20135         this.setValue(d);
20136                 
20137         this.hide();
20138     },
20139     
20140     show : function()
20141     {
20142         this.picker().show();
20143         this.picker().select('>.datepicker-months', true).first().show();
20144         this.update();
20145         this.place();
20146         
20147         this.fireEvent('show', this, this.date);
20148     },
20149     
20150     hide : function()
20151     {
20152         if(this.isInline) {
20153             return;
20154         }
20155         this.picker().hide();
20156         this.fireEvent('hide', this, this.date);
20157         
20158     },
20159     
20160     onMousedown: function(e)
20161     {
20162         e.stopPropagation();
20163         e.preventDefault();
20164     },
20165     
20166     keyup: function(e)
20167     {
20168         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20169         this.update();
20170     },
20171
20172     fireKey: function(e)
20173     {
20174         if (!this.picker().isVisible()){
20175             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20176                 this.show();
20177             }
20178             return;
20179         }
20180         
20181         var dir;
20182         
20183         switch(e.keyCode){
20184             case 27: // escape
20185                 this.hide();
20186                 e.preventDefault();
20187                 break;
20188             case 37: // left
20189             case 39: // right
20190                 dir = e.keyCode == 37 ? -1 : 1;
20191                 
20192                 this.vIndex = this.vIndex + dir;
20193                 
20194                 if(this.vIndex < 0){
20195                     this.vIndex = 0;
20196                 }
20197                 
20198                 if(this.vIndex > 11){
20199                     this.vIndex = 11;
20200                 }
20201                 
20202                 if(isNaN(this.vIndex)){
20203                     this.vIndex = 0;
20204                 }
20205                 
20206                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20207                 
20208                 break;
20209             case 38: // up
20210             case 40: // down
20211                 
20212                 dir = e.keyCode == 38 ? -1 : 1;
20213                 
20214                 this.vIndex = this.vIndex + dir * 4;
20215                 
20216                 if(this.vIndex < 0){
20217                     this.vIndex = 0;
20218                 }
20219                 
20220                 if(this.vIndex > 11){
20221                     this.vIndex = 11;
20222                 }
20223                 
20224                 if(isNaN(this.vIndex)){
20225                     this.vIndex = 0;
20226                 }
20227                 
20228                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20229                 break;
20230                 
20231             case 13: // enter
20232                 
20233                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20234                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20235                 }
20236                 
20237                 this.hide();
20238                 e.preventDefault();
20239                 break;
20240             case 9: // tab
20241                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20242                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20243                 }
20244                 this.hide();
20245                 break;
20246             case 16: // shift
20247             case 17: // ctrl
20248             case 18: // alt
20249                 break;
20250             default :
20251                 this.hide();
20252                 
20253         }
20254     },
20255     
20256     remove: function() 
20257     {
20258         this.picker().remove();
20259     }
20260    
20261 });
20262
20263 Roo.apply(Roo.bootstrap.MonthField,  {
20264     
20265     content : {
20266         tag: 'tbody',
20267         cn: [
20268         {
20269             tag: 'tr',
20270             cn: [
20271             {
20272                 tag: 'td',
20273                 colspan: '7'
20274             }
20275             ]
20276         }
20277         ]
20278     },
20279     
20280     dates:{
20281         en: {
20282             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20283             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20284         }
20285     }
20286 });
20287
20288 Roo.apply(Roo.bootstrap.MonthField,  {
20289   
20290     template : {
20291         tag: 'div',
20292         cls: 'datepicker dropdown-menu roo-dynamic',
20293         cn: [
20294             {
20295                 tag: 'div',
20296                 cls: 'datepicker-months',
20297                 cn: [
20298                 {
20299                     tag: 'table',
20300                     cls: 'table-condensed',
20301                     cn:[
20302                         Roo.bootstrap.DateField.content
20303                     ]
20304                 }
20305                 ]
20306             }
20307         ]
20308     }
20309 });
20310
20311  
20312
20313  
20314  /*
20315  * - LGPL
20316  *
20317  * CheckBox
20318  * 
20319  */
20320
20321 /**
20322  * @class Roo.bootstrap.CheckBox
20323  * @extends Roo.bootstrap.Input
20324  * Bootstrap CheckBox class
20325  * 
20326  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20327  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20328  * @cfg {String} boxLabel The text that appears beside the checkbox
20329  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20330  * @cfg {Boolean} checked initnal the element
20331  * @cfg {Boolean} inline inline the element (default false)
20332  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20333  * @cfg {String} tooltip label tooltip
20334  * 
20335  * @constructor
20336  * Create a new CheckBox
20337  * @param {Object} config The config object
20338  */
20339
20340 Roo.bootstrap.CheckBox = function(config){
20341     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20342    
20343     this.addEvents({
20344         /**
20345         * @event check
20346         * Fires when the element is checked or unchecked.
20347         * @param {Roo.bootstrap.CheckBox} this This input
20348         * @param {Boolean} checked The new checked value
20349         */
20350        check : true,
20351        /**
20352         * @event click
20353         * Fires when the element is click.
20354         * @param {Roo.bootstrap.CheckBox} this This input
20355         */
20356        click : true
20357     });
20358     
20359 };
20360
20361 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20362   
20363     inputType: 'checkbox',
20364     inputValue: 1,
20365     valueOff: 0,
20366     boxLabel: false,
20367     checked: false,
20368     weight : false,
20369     inline: false,
20370     tooltip : '',
20371     
20372     getAutoCreate : function()
20373     {
20374         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20375         
20376         var id = Roo.id();
20377         
20378         var cfg = {};
20379         
20380         cfg.cls = 'form-group ' + this.inputType; //input-group
20381         
20382         if(this.inline){
20383             cfg.cls += ' ' + this.inputType + '-inline';
20384         }
20385         
20386         var input =  {
20387             tag: 'input',
20388             id : id,
20389             type : this.inputType,
20390             value : this.inputValue,
20391             cls : 'roo-' + this.inputType, //'form-box',
20392             placeholder : this.placeholder || ''
20393             
20394         };
20395         
20396         if(this.inputType != 'radio'){
20397             var hidden =  {
20398                 tag: 'input',
20399                 type : 'hidden',
20400                 cls : 'roo-hidden-value',
20401                 value : this.checked ? this.inputValue : this.valueOff
20402             };
20403         }
20404         
20405             
20406         if (this.weight) { // Validity check?
20407             cfg.cls += " " + this.inputType + "-" + this.weight;
20408         }
20409         
20410         if (this.disabled) {
20411             input.disabled=true;
20412         }
20413         
20414         if(this.checked){
20415             input.checked = this.checked;
20416         }
20417         
20418         if (this.name) {
20419             
20420             input.name = this.name;
20421             
20422             if(this.inputType != 'radio'){
20423                 hidden.name = this.name;
20424                 input.name = '_hidden_' + this.name;
20425             }
20426         }
20427         
20428         if (this.size) {
20429             input.cls += ' input-' + this.size;
20430         }
20431         
20432         var settings=this;
20433         
20434         ['xs','sm','md','lg'].map(function(size){
20435             if (settings[size]) {
20436                 cfg.cls += ' col-' + size + '-' + settings[size];
20437             }
20438         });
20439         
20440         var inputblock = input;
20441          
20442         if (this.before || this.after) {
20443             
20444             inputblock = {
20445                 cls : 'input-group',
20446                 cn :  [] 
20447             };
20448             
20449             if (this.before) {
20450                 inputblock.cn.push({
20451                     tag :'span',
20452                     cls : 'input-group-addon',
20453                     html : this.before
20454                 });
20455             }
20456             
20457             inputblock.cn.push(input);
20458             
20459             if(this.inputType != 'radio'){
20460                 inputblock.cn.push(hidden);
20461             }
20462             
20463             if (this.after) {
20464                 inputblock.cn.push({
20465                     tag :'span',
20466                     cls : 'input-group-addon',
20467                     html : this.after
20468                 });
20469             }
20470             
20471         }
20472         
20473         if (align ==='left' && this.fieldLabel.length) {
20474 //                Roo.log("left and has label");
20475             cfg.cn = [
20476                 {
20477                     tag: 'label',
20478                     'for' :  id,
20479                     cls : 'control-label',
20480                     html : this.fieldLabel
20481                 },
20482                 {
20483                     cls : "", 
20484                     cn: [
20485                         inputblock
20486                     ]
20487                 }
20488             ];
20489             
20490             if(this.labelWidth > 12){
20491                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20492             }
20493             
20494             if(this.labelWidth < 13 && this.labelmd == 0){
20495                 this.labelmd = this.labelWidth;
20496             }
20497             
20498             if(this.labellg > 0){
20499                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20500                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20501             }
20502             
20503             if(this.labelmd > 0){
20504                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20505                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20506             }
20507             
20508             if(this.labelsm > 0){
20509                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20510                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20511             }
20512             
20513             if(this.labelxs > 0){
20514                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20515                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20516             }
20517             
20518         } else if ( this.fieldLabel.length) {
20519 //                Roo.log(" label");
20520                 cfg.cn = [
20521                    
20522                     {
20523                         tag: this.boxLabel ? 'span' : 'label',
20524                         'for': id,
20525                         cls: 'control-label box-input-label',
20526                         //cls : 'input-group-addon',
20527                         html : this.fieldLabel
20528                     },
20529                     
20530                     inputblock
20531                     
20532                 ];
20533
20534         } else {
20535             
20536 //                Roo.log(" no label && no align");
20537                 cfg.cn = [  inputblock ] ;
20538                 
20539                 
20540         }
20541         
20542         if(this.boxLabel){
20543              var boxLabelCfg = {
20544                 tag: 'label',
20545                 //'for': id, // box label is handled by onclick - so no for...
20546                 cls: 'box-label',
20547                 html: this.boxLabel
20548             };
20549             
20550             if(this.tooltip){
20551                 boxLabelCfg.tooltip = this.tooltip;
20552             }
20553              
20554             cfg.cn.push(boxLabelCfg);
20555         }
20556         
20557         if(this.inputType != 'radio'){
20558             cfg.cn.push(hidden);
20559         }
20560         
20561         return cfg;
20562         
20563     },
20564     
20565     /**
20566      * return the real input element.
20567      */
20568     inputEl: function ()
20569     {
20570         return this.el.select('input.roo-' + this.inputType,true).first();
20571     },
20572     hiddenEl: function ()
20573     {
20574         return this.el.select('input.roo-hidden-value',true).first();
20575     },
20576     
20577     labelEl: function()
20578     {
20579         return this.el.select('label.control-label',true).first();
20580     },
20581     /* depricated... */
20582     
20583     label: function()
20584     {
20585         return this.labelEl();
20586     },
20587     
20588     boxLabelEl: function()
20589     {
20590         return this.el.select('label.box-label',true).first();
20591     },
20592     
20593     initEvents : function()
20594     {
20595 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20596         
20597         this.inputEl().on('click', this.onClick,  this);
20598         
20599         if (this.boxLabel) { 
20600             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20601         }
20602         
20603         this.startValue = this.getValue();
20604         
20605         if(this.groupId){
20606             Roo.bootstrap.CheckBox.register(this);
20607         }
20608     },
20609     
20610     onClick : function(e)
20611     {   
20612         if(this.fireEvent('click', this, e) !== false){
20613             this.setChecked(!this.checked);
20614         }
20615         
20616     },
20617     
20618     setChecked : function(state,suppressEvent)
20619     {
20620         this.startValue = this.getValue();
20621
20622         if(this.inputType == 'radio'){
20623             
20624             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20625                 e.dom.checked = false;
20626             });
20627             
20628             this.inputEl().dom.checked = true;
20629             
20630             this.inputEl().dom.value = this.inputValue;
20631             
20632             if(suppressEvent !== true){
20633                 this.fireEvent('check', this, true);
20634             }
20635             
20636             this.validate();
20637             
20638             return;
20639         }
20640         
20641         this.checked = state;
20642         
20643         this.inputEl().dom.checked = state;
20644         
20645         
20646         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20647         
20648         if(suppressEvent !== true){
20649             this.fireEvent('check', this, state);
20650         }
20651         
20652         this.validate();
20653     },
20654     
20655     getValue : function()
20656     {
20657         if(this.inputType == 'radio'){
20658             return this.getGroupValue();
20659         }
20660         
20661         return this.hiddenEl().dom.value;
20662         
20663     },
20664     
20665     getGroupValue : function()
20666     {
20667         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20668             return '';
20669         }
20670         
20671         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20672     },
20673     
20674     setValue : function(v,suppressEvent)
20675     {
20676         if(this.inputType == 'radio'){
20677             this.setGroupValue(v, suppressEvent);
20678             return;
20679         }
20680         
20681         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20682         
20683         this.validate();
20684     },
20685     
20686     setGroupValue : function(v, suppressEvent)
20687     {
20688         this.startValue = this.getValue();
20689         
20690         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20691             e.dom.checked = false;
20692             
20693             if(e.dom.value == v){
20694                 e.dom.checked = true;
20695             }
20696         });
20697         
20698         if(suppressEvent !== true){
20699             this.fireEvent('check', this, true);
20700         }
20701
20702         this.validate();
20703         
20704         return;
20705     },
20706     
20707     validate : function()
20708     {
20709         if(this.getVisibilityEl().hasClass('hidden')){
20710             return true;
20711         }
20712         
20713         if(
20714                 this.disabled || 
20715                 (this.inputType == 'radio' && this.validateRadio()) ||
20716                 (this.inputType == 'checkbox' && this.validateCheckbox())
20717         ){
20718             this.markValid();
20719             return true;
20720         }
20721         
20722         this.markInvalid();
20723         return false;
20724     },
20725     
20726     validateRadio : function()
20727     {
20728         if(this.getVisibilityEl().hasClass('hidden')){
20729             return true;
20730         }
20731         
20732         if(this.allowBlank){
20733             return true;
20734         }
20735         
20736         var valid = false;
20737         
20738         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20739             if(!e.dom.checked){
20740                 return;
20741             }
20742             
20743             valid = true;
20744             
20745             return false;
20746         });
20747         
20748         return valid;
20749     },
20750     
20751     validateCheckbox : function()
20752     {
20753         if(!this.groupId){
20754             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20755             //return (this.getValue() == this.inputValue) ? true : false;
20756         }
20757         
20758         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20759         
20760         if(!group){
20761             return false;
20762         }
20763         
20764         var r = false;
20765         
20766         for(var i in group){
20767             if(group[i].el.isVisible(true)){
20768                 r = false;
20769                 break;
20770             }
20771             
20772             r = true;
20773         }
20774         
20775         for(var i in group){
20776             if(r){
20777                 break;
20778             }
20779             
20780             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20781         }
20782         
20783         return r;
20784     },
20785     
20786     /**
20787      * Mark this field as valid
20788      */
20789     markValid : function()
20790     {
20791         var _this = this;
20792         
20793         this.fireEvent('valid', this);
20794         
20795         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20796         
20797         if(this.groupId){
20798             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20799         }
20800         
20801         if(label){
20802             label.markValid();
20803         }
20804
20805         if(this.inputType == 'radio'){
20806             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20807                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20808                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20809             });
20810             
20811             return;
20812         }
20813
20814         if(!this.groupId){
20815             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20816             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20817             return;
20818         }
20819         
20820         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20821         
20822         if(!group){
20823             return;
20824         }
20825         
20826         for(var i in group){
20827             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20828             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20829         }
20830     },
20831     
20832      /**
20833      * Mark this field as invalid
20834      * @param {String} msg The validation message
20835      */
20836     markInvalid : function(msg)
20837     {
20838         if(this.allowBlank){
20839             return;
20840         }
20841         
20842         var _this = this;
20843         
20844         this.fireEvent('invalid', this, msg);
20845         
20846         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20847         
20848         if(this.groupId){
20849             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20850         }
20851         
20852         if(label){
20853             label.markInvalid();
20854         }
20855             
20856         if(this.inputType == 'radio'){
20857             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20858                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20859                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20860             });
20861             
20862             return;
20863         }
20864         
20865         if(!this.groupId){
20866             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20867             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20868             return;
20869         }
20870         
20871         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20872         
20873         if(!group){
20874             return;
20875         }
20876         
20877         for(var i in group){
20878             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20879             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20880         }
20881         
20882     },
20883     
20884     clearInvalid : function()
20885     {
20886         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20887         
20888         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20889         
20890         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20891         
20892         if (label && label.iconEl) {
20893             label.iconEl.removeClass(label.validClass);
20894             label.iconEl.removeClass(label.invalidClass);
20895         }
20896     },
20897     
20898     disable : function()
20899     {
20900         if(this.inputType != 'radio'){
20901             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20902             return;
20903         }
20904         
20905         var _this = this;
20906         
20907         if(this.rendered){
20908             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20909                 _this.getActionEl().addClass(this.disabledClass);
20910                 e.dom.disabled = true;
20911             });
20912         }
20913         
20914         this.disabled = true;
20915         this.fireEvent("disable", this);
20916         return this;
20917     },
20918
20919     enable : function()
20920     {
20921         if(this.inputType != 'radio'){
20922             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20923             return;
20924         }
20925         
20926         var _this = this;
20927         
20928         if(this.rendered){
20929             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20930                 _this.getActionEl().removeClass(this.disabledClass);
20931                 e.dom.disabled = false;
20932             });
20933         }
20934         
20935         this.disabled = false;
20936         this.fireEvent("enable", this);
20937         return this;
20938     },
20939     
20940     setBoxLabel : function(v)
20941     {
20942         this.boxLabel = v;
20943         
20944         if(this.rendered){
20945             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20946         }
20947     }
20948
20949 });
20950
20951 Roo.apply(Roo.bootstrap.CheckBox, {
20952     
20953     groups: {},
20954     
20955      /**
20956     * register a CheckBox Group
20957     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20958     */
20959     register : function(checkbox)
20960     {
20961         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20962             this.groups[checkbox.groupId] = {};
20963         }
20964         
20965         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20966             return;
20967         }
20968         
20969         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20970         
20971     },
20972     /**
20973     * fetch a CheckBox Group based on the group ID
20974     * @param {string} the group ID
20975     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20976     */
20977     get: function(groupId) {
20978         if (typeof(this.groups[groupId]) == 'undefined') {
20979             return false;
20980         }
20981         
20982         return this.groups[groupId] ;
20983     }
20984     
20985     
20986 });
20987 /*
20988  * - LGPL
20989  *
20990  * RadioItem
20991  * 
20992  */
20993
20994 /**
20995  * @class Roo.bootstrap.Radio
20996  * @extends Roo.bootstrap.Component
20997  * Bootstrap Radio class
20998  * @cfg {String} boxLabel - the label associated
20999  * @cfg {String} value - the value of radio
21000  * 
21001  * @constructor
21002  * Create a new Radio
21003  * @param {Object} config The config object
21004  */
21005 Roo.bootstrap.Radio = function(config){
21006     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21007     
21008 };
21009
21010 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21011     
21012     boxLabel : '',
21013     
21014     value : '',
21015     
21016     getAutoCreate : function()
21017     {
21018         var cfg = {
21019             tag : 'div',
21020             cls : 'form-group radio',
21021             cn : [
21022                 {
21023                     tag : 'label',
21024                     cls : 'box-label',
21025                     html : this.boxLabel
21026                 }
21027             ]
21028         };
21029         
21030         return cfg;
21031     },
21032     
21033     initEvents : function() 
21034     {
21035         this.parent().register(this);
21036         
21037         this.el.on('click', this.onClick, this);
21038         
21039     },
21040     
21041     onClick : function(e)
21042     {
21043         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21044             this.setChecked(true);
21045         }
21046     },
21047     
21048     setChecked : function(state, suppressEvent)
21049     {
21050         this.parent().setValue(this.value, suppressEvent);
21051         
21052     },
21053     
21054     setBoxLabel : function(v)
21055     {
21056         this.boxLabel = v;
21057         
21058         if(this.rendered){
21059             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21060         }
21061     }
21062     
21063 });
21064  
21065
21066  /*
21067  * - LGPL
21068  *
21069  * Input
21070  * 
21071  */
21072
21073 /**
21074  * @class Roo.bootstrap.SecurePass
21075  * @extends Roo.bootstrap.Input
21076  * Bootstrap SecurePass class
21077  *
21078  * 
21079  * @constructor
21080  * Create a new SecurePass
21081  * @param {Object} config The config object
21082  */
21083  
21084 Roo.bootstrap.SecurePass = function (config) {
21085     // these go here, so the translation tool can replace them..
21086     this.errors = {
21087         PwdEmpty: "Please type a password, and then retype it to confirm.",
21088         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21089         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21090         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21091         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21092         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21093         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21094         TooWeak: "Your password is Too Weak."
21095     },
21096     this.meterLabel = "Password strength:";
21097     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21098     this.meterClass = [
21099         "roo-password-meter-tooweak", 
21100         "roo-password-meter-weak", 
21101         "roo-password-meter-medium", 
21102         "roo-password-meter-strong", 
21103         "roo-password-meter-grey"
21104     ];
21105     
21106     this.errors = {};
21107     
21108     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21109 }
21110
21111 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21112     /**
21113      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21114      * {
21115      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21116      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21117      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21118      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21119      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21120      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21121      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21122      * })
21123      */
21124     // private
21125     
21126     meterWidth: 300,
21127     errorMsg :'',    
21128     errors: false,
21129     imageRoot: '/',
21130     /**
21131      * @cfg {String/Object} Label for the strength meter (defaults to
21132      * 'Password strength:')
21133      */
21134     // private
21135     meterLabel: '',
21136     /**
21137      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21138      * ['Weak', 'Medium', 'Strong'])
21139      */
21140     // private    
21141     pwdStrengths: false,    
21142     // private
21143     strength: 0,
21144     // private
21145     _lastPwd: null,
21146     // private
21147     kCapitalLetter: 0,
21148     kSmallLetter: 1,
21149     kDigit: 2,
21150     kPunctuation: 3,
21151     
21152     insecure: false,
21153     // private
21154     initEvents: function ()
21155     {
21156         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21157
21158         if (this.el.is('input[type=password]') && Roo.isSafari) {
21159             this.el.on('keydown', this.SafariOnKeyDown, this);
21160         }
21161
21162         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21163     },
21164     // private
21165     onRender: function (ct, position)
21166     {
21167         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21168         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21169         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21170
21171         this.trigger.createChild({
21172                    cn: [
21173                     {
21174                     //id: 'PwdMeter',
21175                     tag: 'div',
21176                     cls: 'roo-password-meter-grey col-xs-12',
21177                     style: {
21178                         //width: 0,
21179                         //width: this.meterWidth + 'px'                                                
21180                         }
21181                     },
21182                     {                            
21183                          cls: 'roo-password-meter-text'                          
21184                     }
21185                 ]            
21186         });
21187
21188          
21189         if (this.hideTrigger) {
21190             this.trigger.setDisplayed(false);
21191         }
21192         this.setSize(this.width || '', this.height || '');
21193     },
21194     // private
21195     onDestroy: function ()
21196     {
21197         if (this.trigger) {
21198             this.trigger.removeAllListeners();
21199             this.trigger.remove();
21200         }
21201         if (this.wrap) {
21202             this.wrap.remove();
21203         }
21204         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21205     },
21206     // private
21207     checkStrength: function ()
21208     {
21209         var pwd = this.inputEl().getValue();
21210         if (pwd == this._lastPwd) {
21211             return;
21212         }
21213
21214         var strength;
21215         if (this.ClientSideStrongPassword(pwd)) {
21216             strength = 3;
21217         } else if (this.ClientSideMediumPassword(pwd)) {
21218             strength = 2;
21219         } else if (this.ClientSideWeakPassword(pwd)) {
21220             strength = 1;
21221         } else {
21222             strength = 0;
21223         }
21224         
21225         Roo.log('strength1: ' + strength);
21226         
21227         //var pm = this.trigger.child('div/div/div').dom;
21228         var pm = this.trigger.child('div/div');
21229         pm.removeClass(this.meterClass);
21230         pm.addClass(this.meterClass[strength]);
21231                 
21232         
21233         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21234                 
21235         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21236         
21237         this._lastPwd = pwd;
21238     },
21239     reset: function ()
21240     {
21241         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21242         
21243         this._lastPwd = '';
21244         
21245         var pm = this.trigger.child('div/div');
21246         pm.removeClass(this.meterClass);
21247         pm.addClass('roo-password-meter-grey');        
21248         
21249         
21250         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21251         
21252         pt.innerHTML = '';
21253         this.inputEl().dom.type='password';
21254     },
21255     // private
21256     validateValue: function (value)
21257     {
21258         
21259         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21260             return false;
21261         }
21262         if (value.length == 0) {
21263             if (this.allowBlank) {
21264                 this.clearInvalid();
21265                 return true;
21266             }
21267
21268             this.markInvalid(this.errors.PwdEmpty);
21269             this.errorMsg = this.errors.PwdEmpty;
21270             return false;
21271         }
21272         
21273         if(this.insecure){
21274             return true;
21275         }
21276         
21277         if ('[\x21-\x7e]*'.match(value)) {
21278             this.markInvalid(this.errors.PwdBadChar);
21279             this.errorMsg = this.errors.PwdBadChar;
21280             return false;
21281         }
21282         if (value.length < 6) {
21283             this.markInvalid(this.errors.PwdShort);
21284             this.errorMsg = this.errors.PwdShort;
21285             return false;
21286         }
21287         if (value.length > 16) {
21288             this.markInvalid(this.errors.PwdLong);
21289             this.errorMsg = this.errors.PwdLong;
21290             return false;
21291         }
21292         var strength;
21293         if (this.ClientSideStrongPassword(value)) {
21294             strength = 3;
21295         } else if (this.ClientSideMediumPassword(value)) {
21296             strength = 2;
21297         } else if (this.ClientSideWeakPassword(value)) {
21298             strength = 1;
21299         } else {
21300             strength = 0;
21301         }
21302
21303         
21304         if (strength < 2) {
21305             //this.markInvalid(this.errors.TooWeak);
21306             this.errorMsg = this.errors.TooWeak;
21307             //return false;
21308         }
21309         
21310         
21311         console.log('strength2: ' + strength);
21312         
21313         //var pm = this.trigger.child('div/div/div').dom;
21314         
21315         var pm = this.trigger.child('div/div');
21316         pm.removeClass(this.meterClass);
21317         pm.addClass(this.meterClass[strength]);
21318                 
21319         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21320                 
21321         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21322         
21323         this.errorMsg = ''; 
21324         return true;
21325     },
21326     // private
21327     CharacterSetChecks: function (type)
21328     {
21329         this.type = type;
21330         this.fResult = false;
21331     },
21332     // private
21333     isctype: function (character, type)
21334     {
21335         switch (type) {  
21336             case this.kCapitalLetter:
21337                 if (character >= 'A' && character <= 'Z') {
21338                     return true;
21339                 }
21340                 break;
21341             
21342             case this.kSmallLetter:
21343                 if (character >= 'a' && character <= 'z') {
21344                     return true;
21345                 }
21346                 break;
21347             
21348             case this.kDigit:
21349                 if (character >= '0' && character <= '9') {
21350                     return true;
21351                 }
21352                 break;
21353             
21354             case this.kPunctuation:
21355                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21356                     return true;
21357                 }
21358                 break;
21359             
21360             default:
21361                 return false;
21362         }
21363
21364     },
21365     // private
21366     IsLongEnough: function (pwd, size)
21367     {
21368         return !(pwd == null || isNaN(size) || pwd.length < size);
21369     },
21370     // private
21371     SpansEnoughCharacterSets: function (word, nb)
21372     {
21373         if (!this.IsLongEnough(word, nb))
21374         {
21375             return false;
21376         }
21377
21378         var characterSetChecks = new Array(
21379             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21380             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21381         );
21382         
21383         for (var index = 0; index < word.length; ++index) {
21384             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21385                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21386                     characterSetChecks[nCharSet].fResult = true;
21387                     break;
21388                 }
21389             }
21390         }
21391
21392         var nCharSets = 0;
21393         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21394             if (characterSetChecks[nCharSet].fResult) {
21395                 ++nCharSets;
21396             }
21397         }
21398
21399         if (nCharSets < nb) {
21400             return false;
21401         }
21402         return true;
21403     },
21404     // private
21405     ClientSideStrongPassword: function (pwd)
21406     {
21407         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21408     },
21409     // private
21410     ClientSideMediumPassword: function (pwd)
21411     {
21412         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21413     },
21414     // private
21415     ClientSideWeakPassword: function (pwd)
21416     {
21417         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21418     }
21419           
21420 })//<script type="text/javascript">
21421
21422 /*
21423  * Based  Ext JS Library 1.1.1
21424  * Copyright(c) 2006-2007, Ext JS, LLC.
21425  * LGPL
21426  *
21427  */
21428  
21429 /**
21430  * @class Roo.HtmlEditorCore
21431  * @extends Roo.Component
21432  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21433  *
21434  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21435  */
21436
21437 Roo.HtmlEditorCore = function(config){
21438     
21439     
21440     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21441     
21442     
21443     this.addEvents({
21444         /**
21445          * @event initialize
21446          * Fires when the editor is fully initialized (including the iframe)
21447          * @param {Roo.HtmlEditorCore} this
21448          */
21449         initialize: true,
21450         /**
21451          * @event activate
21452          * Fires when the editor is first receives the focus. Any insertion must wait
21453          * until after this event.
21454          * @param {Roo.HtmlEditorCore} this
21455          */
21456         activate: true,
21457          /**
21458          * @event beforesync
21459          * Fires before the textarea is updated with content from the editor iframe. Return false
21460          * to cancel the sync.
21461          * @param {Roo.HtmlEditorCore} this
21462          * @param {String} html
21463          */
21464         beforesync: true,
21465          /**
21466          * @event beforepush
21467          * Fires before the iframe editor is updated with content from the textarea. Return false
21468          * to cancel the push.
21469          * @param {Roo.HtmlEditorCore} this
21470          * @param {String} html
21471          */
21472         beforepush: true,
21473          /**
21474          * @event sync
21475          * Fires when the textarea is updated with content from the editor iframe.
21476          * @param {Roo.HtmlEditorCore} this
21477          * @param {String} html
21478          */
21479         sync: true,
21480          /**
21481          * @event push
21482          * Fires when the iframe editor is updated with content from the textarea.
21483          * @param {Roo.HtmlEditorCore} this
21484          * @param {String} html
21485          */
21486         push: true,
21487         
21488         /**
21489          * @event editorevent
21490          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21491          * @param {Roo.HtmlEditorCore} this
21492          */
21493         editorevent: true
21494         
21495     });
21496     
21497     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21498     
21499     // defaults : white / black...
21500     this.applyBlacklists();
21501     
21502     
21503     
21504 };
21505
21506
21507 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21508
21509
21510      /**
21511      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21512      */
21513     
21514     owner : false,
21515     
21516      /**
21517      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21518      *                        Roo.resizable.
21519      */
21520     resizable : false,
21521      /**
21522      * @cfg {Number} height (in pixels)
21523      */   
21524     height: 300,
21525    /**
21526      * @cfg {Number} width (in pixels)
21527      */   
21528     width: 500,
21529     
21530     /**
21531      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21532      * 
21533      */
21534     stylesheets: false,
21535     
21536     // id of frame..
21537     frameId: false,
21538     
21539     // private properties
21540     validationEvent : false,
21541     deferHeight: true,
21542     initialized : false,
21543     activated : false,
21544     sourceEditMode : false,
21545     onFocus : Roo.emptyFn,
21546     iframePad:3,
21547     hideMode:'offsets',
21548     
21549     clearUp: true,
21550     
21551     // blacklist + whitelisted elements..
21552     black: false,
21553     white: false,
21554      
21555     bodyCls : '',
21556
21557     /**
21558      * Protected method that will not generally be called directly. It
21559      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21560      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21561      */
21562     getDocMarkup : function(){
21563         // body styles..
21564         var st = '';
21565         
21566         // inherit styels from page...?? 
21567         if (this.stylesheets === false) {
21568             
21569             Roo.get(document.head).select('style').each(function(node) {
21570                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21571             });
21572             
21573             Roo.get(document.head).select('link').each(function(node) { 
21574                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21575             });
21576             
21577         } else if (!this.stylesheets.length) {
21578                 // simple..
21579                 st = '<style type="text/css">' +
21580                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21581                    '</style>';
21582         } else { 
21583             st = '<style type="text/css">' +
21584                     this.stylesheets +
21585                 '</style>';
21586         }
21587         
21588         st +=  '<style type="text/css">' +
21589             'IMG { cursor: pointer } ' +
21590         '</style>';
21591
21592         var cls = 'roo-htmleditor-body';
21593         
21594         if(this.bodyCls.length){
21595             cls += ' ' + this.bodyCls;
21596         }
21597         
21598         return '<html><head>' + st  +
21599             //<style type="text/css">' +
21600             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21601             //'</style>' +
21602             ' </head><body class="' +  cls + '"></body></html>';
21603     },
21604
21605     // private
21606     onRender : function(ct, position)
21607     {
21608         var _t = this;
21609         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21610         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21611         
21612         
21613         this.el.dom.style.border = '0 none';
21614         this.el.dom.setAttribute('tabIndex', -1);
21615         this.el.addClass('x-hidden hide');
21616         
21617         
21618         
21619         if(Roo.isIE){ // fix IE 1px bogus margin
21620             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21621         }
21622        
21623         
21624         this.frameId = Roo.id();
21625         
21626          
21627         
21628         var iframe = this.owner.wrap.createChild({
21629             tag: 'iframe',
21630             cls: 'form-control', // bootstrap..
21631             id: this.frameId,
21632             name: this.frameId,
21633             frameBorder : 'no',
21634             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21635         }, this.el
21636         );
21637         
21638         
21639         this.iframe = iframe.dom;
21640
21641          this.assignDocWin();
21642         
21643         this.doc.designMode = 'on';
21644        
21645         this.doc.open();
21646         this.doc.write(this.getDocMarkup());
21647         this.doc.close();
21648
21649         
21650         var task = { // must defer to wait for browser to be ready
21651             run : function(){
21652                 //console.log("run task?" + this.doc.readyState);
21653                 this.assignDocWin();
21654                 if(this.doc.body || this.doc.readyState == 'complete'){
21655                     try {
21656                         this.doc.designMode="on";
21657                     } catch (e) {
21658                         return;
21659                     }
21660                     Roo.TaskMgr.stop(task);
21661                     this.initEditor.defer(10, this);
21662                 }
21663             },
21664             interval : 10,
21665             duration: 10000,
21666             scope: this
21667         };
21668         Roo.TaskMgr.start(task);
21669
21670     },
21671
21672     // private
21673     onResize : function(w, h)
21674     {
21675          Roo.log('resize: ' +w + ',' + h );
21676         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21677         if(!this.iframe){
21678             return;
21679         }
21680         if(typeof w == 'number'){
21681             
21682             this.iframe.style.width = w + 'px';
21683         }
21684         if(typeof h == 'number'){
21685             
21686             this.iframe.style.height = h + 'px';
21687             if(this.doc){
21688                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21689             }
21690         }
21691         
21692     },
21693
21694     /**
21695      * Toggles the editor between standard and source edit mode.
21696      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21697      */
21698     toggleSourceEdit : function(sourceEditMode){
21699         
21700         this.sourceEditMode = sourceEditMode === true;
21701         
21702         if(this.sourceEditMode){
21703  
21704             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21705             
21706         }else{
21707             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21708             //this.iframe.className = '';
21709             this.deferFocus();
21710         }
21711         //this.setSize(this.owner.wrap.getSize());
21712         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21713     },
21714
21715     
21716   
21717
21718     /**
21719      * Protected method that will not generally be called directly. If you need/want
21720      * custom HTML cleanup, this is the method you should override.
21721      * @param {String} html The HTML to be cleaned
21722      * return {String} The cleaned HTML
21723      */
21724     cleanHtml : function(html){
21725         html = String(html);
21726         if(html.length > 5){
21727             if(Roo.isSafari){ // strip safari nonsense
21728                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21729             }
21730         }
21731         if(html == '&nbsp;'){
21732             html = '';
21733         }
21734         return html;
21735     },
21736
21737     /**
21738      * HTML Editor -> Textarea
21739      * Protected method that will not generally be called directly. Syncs the contents
21740      * of the editor iframe with the textarea.
21741      */
21742     syncValue : function(){
21743         if(this.initialized){
21744             var bd = (this.doc.body || this.doc.documentElement);
21745             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21746             var html = bd.innerHTML;
21747             if(Roo.isSafari){
21748                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21749                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21750                 if(m && m[1]){
21751                     html = '<div style="'+m[0]+'">' + html + '</div>';
21752                 }
21753             }
21754             html = this.cleanHtml(html);
21755             // fix up the special chars.. normaly like back quotes in word...
21756             // however we do not want to do this with chinese..
21757             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21758                 var cc = b.charCodeAt();
21759                 if (
21760                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21761                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21762                     (cc >= 0xf900 && cc < 0xfb00 )
21763                 ) {
21764                         return b;
21765                 }
21766                 return "&#"+cc+";" 
21767             });
21768             if(this.owner.fireEvent('beforesync', this, html) !== false){
21769                 this.el.dom.value = html;
21770                 this.owner.fireEvent('sync', this, html);
21771             }
21772         }
21773     },
21774
21775     /**
21776      * Protected method that will not generally be called directly. Pushes the value of the textarea
21777      * into the iframe editor.
21778      */
21779     pushValue : function(){
21780         if(this.initialized){
21781             var v = this.el.dom.value.trim();
21782             
21783 //            if(v.length < 1){
21784 //                v = '&#160;';
21785 //            }
21786             
21787             if(this.owner.fireEvent('beforepush', this, v) !== false){
21788                 var d = (this.doc.body || this.doc.documentElement);
21789                 d.innerHTML = v;
21790                 this.cleanUpPaste();
21791                 this.el.dom.value = d.innerHTML;
21792                 this.owner.fireEvent('push', this, v);
21793             }
21794         }
21795     },
21796
21797     // private
21798     deferFocus : function(){
21799         this.focus.defer(10, this);
21800     },
21801
21802     // doc'ed in Field
21803     focus : function(){
21804         if(this.win && !this.sourceEditMode){
21805             this.win.focus();
21806         }else{
21807             this.el.focus();
21808         }
21809     },
21810     
21811     assignDocWin: function()
21812     {
21813         var iframe = this.iframe;
21814         
21815          if(Roo.isIE){
21816             this.doc = iframe.contentWindow.document;
21817             this.win = iframe.contentWindow;
21818         } else {
21819 //            if (!Roo.get(this.frameId)) {
21820 //                return;
21821 //            }
21822 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21823 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21824             
21825             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21826                 return;
21827             }
21828             
21829             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21830             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21831         }
21832     },
21833     
21834     // private
21835     initEditor : function(){
21836         //console.log("INIT EDITOR");
21837         this.assignDocWin();
21838         
21839         
21840         
21841         this.doc.designMode="on";
21842         this.doc.open();
21843         this.doc.write(this.getDocMarkup());
21844         this.doc.close();
21845         
21846         var dbody = (this.doc.body || this.doc.documentElement);
21847         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21848         // this copies styles from the containing element into thsi one..
21849         // not sure why we need all of this..
21850         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21851         
21852         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21853         //ss['background-attachment'] = 'fixed'; // w3c
21854         dbody.bgProperties = 'fixed'; // ie
21855         //Roo.DomHelper.applyStyles(dbody, ss);
21856         Roo.EventManager.on(this.doc, {
21857             //'mousedown': this.onEditorEvent,
21858             'mouseup': this.onEditorEvent,
21859             'dblclick': this.onEditorEvent,
21860             'click': this.onEditorEvent,
21861             'keyup': this.onEditorEvent,
21862             buffer:100,
21863             scope: this
21864         });
21865         if(Roo.isGecko){
21866             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21867         }
21868         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21869             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21870         }
21871         this.initialized = true;
21872
21873         this.owner.fireEvent('initialize', this);
21874         this.pushValue();
21875     },
21876
21877     // private
21878     onDestroy : function(){
21879         
21880         
21881         
21882         if(this.rendered){
21883             
21884             //for (var i =0; i < this.toolbars.length;i++) {
21885             //    // fixme - ask toolbars for heights?
21886             //    this.toolbars[i].onDestroy();
21887            // }
21888             
21889             //this.wrap.dom.innerHTML = '';
21890             //this.wrap.remove();
21891         }
21892     },
21893
21894     // private
21895     onFirstFocus : function(){
21896         
21897         this.assignDocWin();
21898         
21899         
21900         this.activated = true;
21901          
21902     
21903         if(Roo.isGecko){ // prevent silly gecko errors
21904             this.win.focus();
21905             var s = this.win.getSelection();
21906             if(!s.focusNode || s.focusNode.nodeType != 3){
21907                 var r = s.getRangeAt(0);
21908                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21909                 r.collapse(true);
21910                 this.deferFocus();
21911             }
21912             try{
21913                 this.execCmd('useCSS', true);
21914                 this.execCmd('styleWithCSS', false);
21915             }catch(e){}
21916         }
21917         this.owner.fireEvent('activate', this);
21918     },
21919
21920     // private
21921     adjustFont: function(btn){
21922         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21923         //if(Roo.isSafari){ // safari
21924         //    adjust *= 2;
21925        // }
21926         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21927         if(Roo.isSafari){ // safari
21928             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21929             v =  (v < 10) ? 10 : v;
21930             v =  (v > 48) ? 48 : v;
21931             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21932             
21933         }
21934         
21935         
21936         v = Math.max(1, v+adjust);
21937         
21938         this.execCmd('FontSize', v  );
21939     },
21940
21941     onEditorEvent : function(e)
21942     {
21943         this.owner.fireEvent('editorevent', this, e);
21944       //  this.updateToolbar();
21945         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21946     },
21947
21948     insertTag : function(tg)
21949     {
21950         // could be a bit smarter... -> wrap the current selected tRoo..
21951         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21952             
21953             range = this.createRange(this.getSelection());
21954             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21955             wrappingNode.appendChild(range.extractContents());
21956             range.insertNode(wrappingNode);
21957
21958             return;
21959             
21960             
21961             
21962         }
21963         this.execCmd("formatblock",   tg);
21964         
21965     },
21966     
21967     insertText : function(txt)
21968     {
21969         
21970         
21971         var range = this.createRange();
21972         range.deleteContents();
21973                //alert(Sender.getAttribute('label'));
21974                
21975         range.insertNode(this.doc.createTextNode(txt));
21976     } ,
21977     
21978      
21979
21980     /**
21981      * Executes a Midas editor command on the editor document and performs necessary focus and
21982      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21983      * @param {String} cmd The Midas command
21984      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21985      */
21986     relayCmd : function(cmd, value){
21987         this.win.focus();
21988         this.execCmd(cmd, value);
21989         this.owner.fireEvent('editorevent', this);
21990         //this.updateToolbar();
21991         this.owner.deferFocus();
21992     },
21993
21994     /**
21995      * Executes a Midas editor command directly on the editor document.
21996      * For visual commands, you should use {@link #relayCmd} instead.
21997      * <b>This should only be called after the editor is initialized.</b>
21998      * @param {String} cmd The Midas command
21999      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22000      */
22001     execCmd : function(cmd, value){
22002         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22003         this.syncValue();
22004     },
22005  
22006  
22007    
22008     /**
22009      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22010      * to insert tRoo.
22011      * @param {String} text | dom node.. 
22012      */
22013     insertAtCursor : function(text)
22014     {
22015         
22016         if(!this.activated){
22017             return;
22018         }
22019         /*
22020         if(Roo.isIE){
22021             this.win.focus();
22022             var r = this.doc.selection.createRange();
22023             if(r){
22024                 r.collapse(true);
22025                 r.pasteHTML(text);
22026                 this.syncValue();
22027                 this.deferFocus();
22028             
22029             }
22030             return;
22031         }
22032         */
22033         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22034             this.win.focus();
22035             
22036             
22037             // from jquery ui (MIT licenced)
22038             var range, node;
22039             var win = this.win;
22040             
22041             if (win.getSelection && win.getSelection().getRangeAt) {
22042                 range = win.getSelection().getRangeAt(0);
22043                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22044                 range.insertNode(node);
22045             } else if (win.document.selection && win.document.selection.createRange) {
22046                 // no firefox support
22047                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22048                 win.document.selection.createRange().pasteHTML(txt);
22049             } else {
22050                 // no firefox support
22051                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22052                 this.execCmd('InsertHTML', txt);
22053             } 
22054             
22055             this.syncValue();
22056             
22057             this.deferFocus();
22058         }
22059     },
22060  // private
22061     mozKeyPress : function(e){
22062         if(e.ctrlKey){
22063             var c = e.getCharCode(), cmd;
22064           
22065             if(c > 0){
22066                 c = String.fromCharCode(c).toLowerCase();
22067                 switch(c){
22068                     case 'b':
22069                         cmd = 'bold';
22070                         break;
22071                     case 'i':
22072                         cmd = 'italic';
22073                         break;
22074                     
22075                     case 'u':
22076                         cmd = 'underline';
22077                         break;
22078                     
22079                     case 'v':
22080                         this.cleanUpPaste.defer(100, this);
22081                         return;
22082                         
22083                 }
22084                 if(cmd){
22085                     this.win.focus();
22086                     this.execCmd(cmd);
22087                     this.deferFocus();
22088                     e.preventDefault();
22089                 }
22090                 
22091             }
22092         }
22093     },
22094
22095     // private
22096     fixKeys : function(){ // load time branching for fastest keydown performance
22097         if(Roo.isIE){
22098             return function(e){
22099                 var k = e.getKey(), r;
22100                 if(k == e.TAB){
22101                     e.stopEvent();
22102                     r = this.doc.selection.createRange();
22103                     if(r){
22104                         r.collapse(true);
22105                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22106                         this.deferFocus();
22107                     }
22108                     return;
22109                 }
22110                 
22111                 if(k == e.ENTER){
22112                     r = this.doc.selection.createRange();
22113                     if(r){
22114                         var target = r.parentElement();
22115                         if(!target || target.tagName.toLowerCase() != 'li'){
22116                             e.stopEvent();
22117                             r.pasteHTML('<br />');
22118                             r.collapse(false);
22119                             r.select();
22120                         }
22121                     }
22122                 }
22123                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22124                     this.cleanUpPaste.defer(100, this);
22125                     return;
22126                 }
22127                 
22128                 
22129             };
22130         }else if(Roo.isOpera){
22131             return function(e){
22132                 var k = e.getKey();
22133                 if(k == e.TAB){
22134                     e.stopEvent();
22135                     this.win.focus();
22136                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22137                     this.deferFocus();
22138                 }
22139                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22140                     this.cleanUpPaste.defer(100, this);
22141                     return;
22142                 }
22143                 
22144             };
22145         }else if(Roo.isSafari){
22146             return function(e){
22147                 var k = e.getKey();
22148                 
22149                 if(k == e.TAB){
22150                     e.stopEvent();
22151                     this.execCmd('InsertText','\t');
22152                     this.deferFocus();
22153                     return;
22154                 }
22155                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22156                     this.cleanUpPaste.defer(100, this);
22157                     return;
22158                 }
22159                 
22160              };
22161         }
22162     }(),
22163     
22164     getAllAncestors: function()
22165     {
22166         var p = this.getSelectedNode();
22167         var a = [];
22168         if (!p) {
22169             a.push(p); // push blank onto stack..
22170             p = this.getParentElement();
22171         }
22172         
22173         
22174         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22175             a.push(p);
22176             p = p.parentNode;
22177         }
22178         a.push(this.doc.body);
22179         return a;
22180     },
22181     lastSel : false,
22182     lastSelNode : false,
22183     
22184     
22185     getSelection : function() 
22186     {
22187         this.assignDocWin();
22188         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22189     },
22190     
22191     getSelectedNode: function() 
22192     {
22193         // this may only work on Gecko!!!
22194         
22195         // should we cache this!!!!
22196         
22197         
22198         
22199          
22200         var range = this.createRange(this.getSelection()).cloneRange();
22201         
22202         if (Roo.isIE) {
22203             var parent = range.parentElement();
22204             while (true) {
22205                 var testRange = range.duplicate();
22206                 testRange.moveToElementText(parent);
22207                 if (testRange.inRange(range)) {
22208                     break;
22209                 }
22210                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22211                     break;
22212                 }
22213                 parent = parent.parentElement;
22214             }
22215             return parent;
22216         }
22217         
22218         // is ancestor a text element.
22219         var ac =  range.commonAncestorContainer;
22220         if (ac.nodeType == 3) {
22221             ac = ac.parentNode;
22222         }
22223         
22224         var ar = ac.childNodes;
22225          
22226         var nodes = [];
22227         var other_nodes = [];
22228         var has_other_nodes = false;
22229         for (var i=0;i<ar.length;i++) {
22230             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22231                 continue;
22232             }
22233             // fullly contained node.
22234             
22235             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22236                 nodes.push(ar[i]);
22237                 continue;
22238             }
22239             
22240             // probably selected..
22241             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22242                 other_nodes.push(ar[i]);
22243                 continue;
22244             }
22245             // outer..
22246             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22247                 continue;
22248             }
22249             
22250             
22251             has_other_nodes = true;
22252         }
22253         if (!nodes.length && other_nodes.length) {
22254             nodes= other_nodes;
22255         }
22256         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22257             return false;
22258         }
22259         
22260         return nodes[0];
22261     },
22262     createRange: function(sel)
22263     {
22264         // this has strange effects when using with 
22265         // top toolbar - not sure if it's a great idea.
22266         //this.editor.contentWindow.focus();
22267         if (typeof sel != "undefined") {
22268             try {
22269                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22270             } catch(e) {
22271                 return this.doc.createRange();
22272             }
22273         } else {
22274             return this.doc.createRange();
22275         }
22276     },
22277     getParentElement: function()
22278     {
22279         
22280         this.assignDocWin();
22281         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22282         
22283         var range = this.createRange(sel);
22284          
22285         try {
22286             var p = range.commonAncestorContainer;
22287             while (p.nodeType == 3) { // text node
22288                 p = p.parentNode;
22289             }
22290             return p;
22291         } catch (e) {
22292             return null;
22293         }
22294     
22295     },
22296     /***
22297      *
22298      * Range intersection.. the hard stuff...
22299      *  '-1' = before
22300      *  '0' = hits..
22301      *  '1' = after.
22302      *         [ -- selected range --- ]
22303      *   [fail]                        [fail]
22304      *
22305      *    basically..
22306      *      if end is before start or  hits it. fail.
22307      *      if start is after end or hits it fail.
22308      *
22309      *   if either hits (but other is outside. - then it's not 
22310      *   
22311      *    
22312      **/
22313     
22314     
22315     // @see http://www.thismuchiknow.co.uk/?p=64.
22316     rangeIntersectsNode : function(range, node)
22317     {
22318         var nodeRange = node.ownerDocument.createRange();
22319         try {
22320             nodeRange.selectNode(node);
22321         } catch (e) {
22322             nodeRange.selectNodeContents(node);
22323         }
22324     
22325         var rangeStartRange = range.cloneRange();
22326         rangeStartRange.collapse(true);
22327     
22328         var rangeEndRange = range.cloneRange();
22329         rangeEndRange.collapse(false);
22330     
22331         var nodeStartRange = nodeRange.cloneRange();
22332         nodeStartRange.collapse(true);
22333     
22334         var nodeEndRange = nodeRange.cloneRange();
22335         nodeEndRange.collapse(false);
22336     
22337         return rangeStartRange.compareBoundaryPoints(
22338                  Range.START_TO_START, nodeEndRange) == -1 &&
22339                rangeEndRange.compareBoundaryPoints(
22340                  Range.START_TO_START, nodeStartRange) == 1;
22341         
22342          
22343     },
22344     rangeCompareNode : function(range, node)
22345     {
22346         var nodeRange = node.ownerDocument.createRange();
22347         try {
22348             nodeRange.selectNode(node);
22349         } catch (e) {
22350             nodeRange.selectNodeContents(node);
22351         }
22352         
22353         
22354         range.collapse(true);
22355     
22356         nodeRange.collapse(true);
22357      
22358         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22359         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22360          
22361         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22362         
22363         var nodeIsBefore   =  ss == 1;
22364         var nodeIsAfter    = ee == -1;
22365         
22366         if (nodeIsBefore && nodeIsAfter) {
22367             return 0; // outer
22368         }
22369         if (!nodeIsBefore && nodeIsAfter) {
22370             return 1; //right trailed.
22371         }
22372         
22373         if (nodeIsBefore && !nodeIsAfter) {
22374             return 2;  // left trailed.
22375         }
22376         // fully contined.
22377         return 3;
22378     },
22379
22380     // private? - in a new class?
22381     cleanUpPaste :  function()
22382     {
22383         // cleans up the whole document..
22384         Roo.log('cleanuppaste');
22385         
22386         this.cleanUpChildren(this.doc.body);
22387         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22388         if (clean != this.doc.body.innerHTML) {
22389             this.doc.body.innerHTML = clean;
22390         }
22391         
22392     },
22393     
22394     cleanWordChars : function(input) {// change the chars to hex code
22395         var he = Roo.HtmlEditorCore;
22396         
22397         var output = input;
22398         Roo.each(he.swapCodes, function(sw) { 
22399             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22400             
22401             output = output.replace(swapper, sw[1]);
22402         });
22403         
22404         return output;
22405     },
22406     
22407     
22408     cleanUpChildren : function (n)
22409     {
22410         if (!n.childNodes.length) {
22411             return;
22412         }
22413         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22414            this.cleanUpChild(n.childNodes[i]);
22415         }
22416     },
22417     
22418     
22419         
22420     
22421     cleanUpChild : function (node)
22422     {
22423         var ed = this;
22424         //console.log(node);
22425         if (node.nodeName == "#text") {
22426             // clean up silly Windows -- stuff?
22427             return; 
22428         }
22429         if (node.nodeName == "#comment") {
22430             node.parentNode.removeChild(node);
22431             // clean up silly Windows -- stuff?
22432             return; 
22433         }
22434         var lcname = node.tagName.toLowerCase();
22435         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22436         // whitelist of tags..
22437         
22438         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22439             // remove node.
22440             node.parentNode.removeChild(node);
22441             return;
22442             
22443         }
22444         
22445         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22446         
22447         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22448         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22449         
22450         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22451         //    remove_keep_children = true;
22452         //}
22453         
22454         if (remove_keep_children) {
22455             this.cleanUpChildren(node);
22456             // inserts everything just before this node...
22457             while (node.childNodes.length) {
22458                 var cn = node.childNodes[0];
22459                 node.removeChild(cn);
22460                 node.parentNode.insertBefore(cn, node);
22461             }
22462             node.parentNode.removeChild(node);
22463             return;
22464         }
22465         
22466         if (!node.attributes || !node.attributes.length) {
22467             this.cleanUpChildren(node);
22468             return;
22469         }
22470         
22471         function cleanAttr(n,v)
22472         {
22473             
22474             if (v.match(/^\./) || v.match(/^\//)) {
22475                 return;
22476             }
22477             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22478                 return;
22479             }
22480             if (v.match(/^#/)) {
22481                 return;
22482             }
22483 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22484             node.removeAttribute(n);
22485             
22486         }
22487         
22488         var cwhite = this.cwhite;
22489         var cblack = this.cblack;
22490             
22491         function cleanStyle(n,v)
22492         {
22493             if (v.match(/expression/)) { //XSS?? should we even bother..
22494                 node.removeAttribute(n);
22495                 return;
22496             }
22497             
22498             var parts = v.split(/;/);
22499             var clean = [];
22500             
22501             Roo.each(parts, function(p) {
22502                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22503                 if (!p.length) {
22504                     return true;
22505                 }
22506                 var l = p.split(':').shift().replace(/\s+/g,'');
22507                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22508                 
22509                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22510 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22511                     //node.removeAttribute(n);
22512                     return true;
22513                 }
22514                 //Roo.log()
22515                 // only allow 'c whitelisted system attributes'
22516                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22517 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22518                     //node.removeAttribute(n);
22519                     return true;
22520                 }
22521                 
22522                 
22523                  
22524                 
22525                 clean.push(p);
22526                 return true;
22527             });
22528             if (clean.length) { 
22529                 node.setAttribute(n, clean.join(';'));
22530             } else {
22531                 node.removeAttribute(n);
22532             }
22533             
22534         }
22535         
22536         
22537         for (var i = node.attributes.length-1; i > -1 ; i--) {
22538             var a = node.attributes[i];
22539             //console.log(a);
22540             
22541             if (a.name.toLowerCase().substr(0,2)=='on')  {
22542                 node.removeAttribute(a.name);
22543                 continue;
22544             }
22545             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22546                 node.removeAttribute(a.name);
22547                 continue;
22548             }
22549             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22550                 cleanAttr(a.name,a.value); // fixme..
22551                 continue;
22552             }
22553             if (a.name == 'style') {
22554                 cleanStyle(a.name,a.value);
22555                 continue;
22556             }
22557             /// clean up MS crap..
22558             // tecnically this should be a list of valid class'es..
22559             
22560             
22561             if (a.name == 'class') {
22562                 if (a.value.match(/^Mso/)) {
22563                     node.className = '';
22564                 }
22565                 
22566                 if (a.value.match(/^body$/)) {
22567                     node.className = '';
22568                 }
22569                 continue;
22570             }
22571             
22572             // style cleanup!?
22573             // class cleanup?
22574             
22575         }
22576         
22577         
22578         this.cleanUpChildren(node);
22579         
22580         
22581     },
22582     
22583     /**
22584      * Clean up MS wordisms...
22585      */
22586     cleanWord : function(node)
22587     {
22588         
22589         
22590         if (!node) {
22591             this.cleanWord(this.doc.body);
22592             return;
22593         }
22594         if (node.nodeName == "#text") {
22595             // clean up silly Windows -- stuff?
22596             return; 
22597         }
22598         if (node.nodeName == "#comment") {
22599             node.parentNode.removeChild(node);
22600             // clean up silly Windows -- stuff?
22601             return; 
22602         }
22603         
22604         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22605             node.parentNode.removeChild(node);
22606             return;
22607         }
22608         
22609         // remove - but keep children..
22610         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22611             while (node.childNodes.length) {
22612                 var cn = node.childNodes[0];
22613                 node.removeChild(cn);
22614                 node.parentNode.insertBefore(cn, node);
22615             }
22616             node.parentNode.removeChild(node);
22617             this.iterateChildren(node, this.cleanWord);
22618             return;
22619         }
22620         // clean styles
22621         if (node.className.length) {
22622             
22623             var cn = node.className.split(/\W+/);
22624             var cna = [];
22625             Roo.each(cn, function(cls) {
22626                 if (cls.match(/Mso[a-zA-Z]+/)) {
22627                     return;
22628                 }
22629                 cna.push(cls);
22630             });
22631             node.className = cna.length ? cna.join(' ') : '';
22632             if (!cna.length) {
22633                 node.removeAttribute("class");
22634             }
22635         }
22636         
22637         if (node.hasAttribute("lang")) {
22638             node.removeAttribute("lang");
22639         }
22640         
22641         if (node.hasAttribute("style")) {
22642             
22643             var styles = node.getAttribute("style").split(";");
22644             var nstyle = [];
22645             Roo.each(styles, function(s) {
22646                 if (!s.match(/:/)) {
22647                     return;
22648                 }
22649                 var kv = s.split(":");
22650                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22651                     return;
22652                 }
22653                 // what ever is left... we allow.
22654                 nstyle.push(s);
22655             });
22656             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22657             if (!nstyle.length) {
22658                 node.removeAttribute('style');
22659             }
22660         }
22661         this.iterateChildren(node, this.cleanWord);
22662         
22663         
22664         
22665     },
22666     /**
22667      * iterateChildren of a Node, calling fn each time, using this as the scole..
22668      * @param {DomNode} node node to iterate children of.
22669      * @param {Function} fn method of this class to call on each item.
22670      */
22671     iterateChildren : function(node, fn)
22672     {
22673         if (!node.childNodes.length) {
22674                 return;
22675         }
22676         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22677            fn.call(this, node.childNodes[i])
22678         }
22679     },
22680     
22681     
22682     /**
22683      * cleanTableWidths.
22684      *
22685      * Quite often pasting from word etc.. results in tables with column and widths.
22686      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22687      *
22688      */
22689     cleanTableWidths : function(node)
22690     {
22691          
22692          
22693         if (!node) {
22694             this.cleanTableWidths(this.doc.body);
22695             return;
22696         }
22697         
22698         // ignore list...
22699         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22700             return; 
22701         }
22702         Roo.log(node.tagName);
22703         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22704             this.iterateChildren(node, this.cleanTableWidths);
22705             return;
22706         }
22707         if (node.hasAttribute('width')) {
22708             node.removeAttribute('width');
22709         }
22710         
22711          
22712         if (node.hasAttribute("style")) {
22713             // pretty basic...
22714             
22715             var styles = node.getAttribute("style").split(";");
22716             var nstyle = [];
22717             Roo.each(styles, function(s) {
22718                 if (!s.match(/:/)) {
22719                     return;
22720                 }
22721                 var kv = s.split(":");
22722                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22723                     return;
22724                 }
22725                 // what ever is left... we allow.
22726                 nstyle.push(s);
22727             });
22728             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22729             if (!nstyle.length) {
22730                 node.removeAttribute('style');
22731             }
22732         }
22733         
22734         this.iterateChildren(node, this.cleanTableWidths);
22735         
22736         
22737     },
22738     
22739     
22740     
22741     
22742     domToHTML : function(currentElement, depth, nopadtext) {
22743         
22744         depth = depth || 0;
22745         nopadtext = nopadtext || false;
22746     
22747         if (!currentElement) {
22748             return this.domToHTML(this.doc.body);
22749         }
22750         
22751         //Roo.log(currentElement);
22752         var j;
22753         var allText = false;
22754         var nodeName = currentElement.nodeName;
22755         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22756         
22757         if  (nodeName == '#text') {
22758             
22759             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22760         }
22761         
22762         
22763         var ret = '';
22764         if (nodeName != 'BODY') {
22765              
22766             var i = 0;
22767             // Prints the node tagName, such as <A>, <IMG>, etc
22768             if (tagName) {
22769                 var attr = [];
22770                 for(i = 0; i < currentElement.attributes.length;i++) {
22771                     // quoting?
22772                     var aname = currentElement.attributes.item(i).name;
22773                     if (!currentElement.attributes.item(i).value.length) {
22774                         continue;
22775                     }
22776                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22777                 }
22778                 
22779                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22780             } 
22781             else {
22782                 
22783                 // eack
22784             }
22785         } else {
22786             tagName = false;
22787         }
22788         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22789             return ret;
22790         }
22791         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22792             nopadtext = true;
22793         }
22794         
22795         
22796         // Traverse the tree
22797         i = 0;
22798         var currentElementChild = currentElement.childNodes.item(i);
22799         var allText = true;
22800         var innerHTML  = '';
22801         lastnode = '';
22802         while (currentElementChild) {
22803             // Formatting code (indent the tree so it looks nice on the screen)
22804             var nopad = nopadtext;
22805             if (lastnode == 'SPAN') {
22806                 nopad  = true;
22807             }
22808             // text
22809             if  (currentElementChild.nodeName == '#text') {
22810                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22811                 toadd = nopadtext ? toadd : toadd.trim();
22812                 if (!nopad && toadd.length > 80) {
22813                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22814                 }
22815                 innerHTML  += toadd;
22816                 
22817                 i++;
22818                 currentElementChild = currentElement.childNodes.item(i);
22819                 lastNode = '';
22820                 continue;
22821             }
22822             allText = false;
22823             
22824             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22825                 
22826             // Recursively traverse the tree structure of the child node
22827             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22828             lastnode = currentElementChild.nodeName;
22829             i++;
22830             currentElementChild=currentElement.childNodes.item(i);
22831         }
22832         
22833         ret += innerHTML;
22834         
22835         if (!allText) {
22836                 // The remaining code is mostly for formatting the tree
22837             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22838         }
22839         
22840         
22841         if (tagName) {
22842             ret+= "</"+tagName+">";
22843         }
22844         return ret;
22845         
22846     },
22847         
22848     applyBlacklists : function()
22849     {
22850         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22851         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22852         
22853         this.white = [];
22854         this.black = [];
22855         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22856             if (b.indexOf(tag) > -1) {
22857                 return;
22858             }
22859             this.white.push(tag);
22860             
22861         }, this);
22862         
22863         Roo.each(w, function(tag) {
22864             if (b.indexOf(tag) > -1) {
22865                 return;
22866             }
22867             if (this.white.indexOf(tag) > -1) {
22868                 return;
22869             }
22870             this.white.push(tag);
22871             
22872         }, this);
22873         
22874         
22875         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22876             if (w.indexOf(tag) > -1) {
22877                 return;
22878             }
22879             this.black.push(tag);
22880             
22881         }, this);
22882         
22883         Roo.each(b, function(tag) {
22884             if (w.indexOf(tag) > -1) {
22885                 return;
22886             }
22887             if (this.black.indexOf(tag) > -1) {
22888                 return;
22889             }
22890             this.black.push(tag);
22891             
22892         }, this);
22893         
22894         
22895         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22896         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22897         
22898         this.cwhite = [];
22899         this.cblack = [];
22900         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22901             if (b.indexOf(tag) > -1) {
22902                 return;
22903             }
22904             this.cwhite.push(tag);
22905             
22906         }, this);
22907         
22908         Roo.each(w, function(tag) {
22909             if (b.indexOf(tag) > -1) {
22910                 return;
22911             }
22912             if (this.cwhite.indexOf(tag) > -1) {
22913                 return;
22914             }
22915             this.cwhite.push(tag);
22916             
22917         }, this);
22918         
22919         
22920         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22921             if (w.indexOf(tag) > -1) {
22922                 return;
22923             }
22924             this.cblack.push(tag);
22925             
22926         }, this);
22927         
22928         Roo.each(b, function(tag) {
22929             if (w.indexOf(tag) > -1) {
22930                 return;
22931             }
22932             if (this.cblack.indexOf(tag) > -1) {
22933                 return;
22934             }
22935             this.cblack.push(tag);
22936             
22937         }, this);
22938     },
22939     
22940     setStylesheets : function(stylesheets)
22941     {
22942         if(typeof(stylesheets) == 'string'){
22943             Roo.get(this.iframe.contentDocument.head).createChild({
22944                 tag : 'link',
22945                 rel : 'stylesheet',
22946                 type : 'text/css',
22947                 href : stylesheets
22948             });
22949             
22950             return;
22951         }
22952         var _this = this;
22953      
22954         Roo.each(stylesheets, function(s) {
22955             if(!s.length){
22956                 return;
22957             }
22958             
22959             Roo.get(_this.iframe.contentDocument.head).createChild({
22960                 tag : 'link',
22961                 rel : 'stylesheet',
22962                 type : 'text/css',
22963                 href : s
22964             });
22965         });
22966
22967         
22968     },
22969     
22970     removeStylesheets : function()
22971     {
22972         var _this = this;
22973         
22974         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22975             s.remove();
22976         });
22977     },
22978     
22979     setStyle : function(style)
22980     {
22981         Roo.get(this.iframe.contentDocument.head).createChild({
22982             tag : 'style',
22983             type : 'text/css',
22984             html : style
22985         });
22986
22987         return;
22988     }
22989     
22990     // hide stuff that is not compatible
22991     /**
22992      * @event blur
22993      * @hide
22994      */
22995     /**
22996      * @event change
22997      * @hide
22998      */
22999     /**
23000      * @event focus
23001      * @hide
23002      */
23003     /**
23004      * @event specialkey
23005      * @hide
23006      */
23007     /**
23008      * @cfg {String} fieldClass @hide
23009      */
23010     /**
23011      * @cfg {String} focusClass @hide
23012      */
23013     /**
23014      * @cfg {String} autoCreate @hide
23015      */
23016     /**
23017      * @cfg {String} inputType @hide
23018      */
23019     /**
23020      * @cfg {String} invalidClass @hide
23021      */
23022     /**
23023      * @cfg {String} invalidText @hide
23024      */
23025     /**
23026      * @cfg {String} msgFx @hide
23027      */
23028     /**
23029      * @cfg {String} validateOnBlur @hide
23030      */
23031 });
23032
23033 Roo.HtmlEditorCore.white = [
23034         'area', 'br', 'img', 'input', 'hr', 'wbr',
23035         
23036        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23037        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23038        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23039        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23040        'table',   'ul',         'xmp', 
23041        
23042        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23043       'thead',   'tr', 
23044      
23045       'dir', 'menu', 'ol', 'ul', 'dl',
23046        
23047       'embed',  'object'
23048 ];
23049
23050
23051 Roo.HtmlEditorCore.black = [
23052     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23053         'applet', // 
23054         'base',   'basefont', 'bgsound', 'blink',  'body', 
23055         'frame',  'frameset', 'head',    'html',   'ilayer', 
23056         'iframe', 'layer',  'link',     'meta',    'object',   
23057         'script', 'style' ,'title',  'xml' // clean later..
23058 ];
23059 Roo.HtmlEditorCore.clean = [
23060     'script', 'style', 'title', 'xml'
23061 ];
23062 Roo.HtmlEditorCore.remove = [
23063     'font'
23064 ];
23065 // attributes..
23066
23067 Roo.HtmlEditorCore.ablack = [
23068     'on'
23069 ];
23070     
23071 Roo.HtmlEditorCore.aclean = [ 
23072     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23073 ];
23074
23075 // protocols..
23076 Roo.HtmlEditorCore.pwhite= [
23077         'http',  'https',  'mailto'
23078 ];
23079
23080 // white listed style attributes.
23081 Roo.HtmlEditorCore.cwhite= [
23082       //  'text-align', /// default is to allow most things..
23083       
23084          
23085 //        'font-size'//??
23086 ];
23087
23088 // black listed style attributes.
23089 Roo.HtmlEditorCore.cblack= [
23090       //  'font-size' -- this can be set by the project 
23091 ];
23092
23093
23094 Roo.HtmlEditorCore.swapCodes   =[ 
23095     [    8211, "--" ], 
23096     [    8212, "--" ], 
23097     [    8216,  "'" ],  
23098     [    8217, "'" ],  
23099     [    8220, '"' ],  
23100     [    8221, '"' ],  
23101     [    8226, "*" ],  
23102     [    8230, "..." ]
23103 ]; 
23104
23105     /*
23106  * - LGPL
23107  *
23108  * HtmlEditor
23109  * 
23110  */
23111
23112 /**
23113  * @class Roo.bootstrap.HtmlEditor
23114  * @extends Roo.bootstrap.TextArea
23115  * Bootstrap HtmlEditor class
23116
23117  * @constructor
23118  * Create a new HtmlEditor
23119  * @param {Object} config The config object
23120  */
23121
23122 Roo.bootstrap.HtmlEditor = function(config){
23123     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23124     if (!this.toolbars) {
23125         this.toolbars = [];
23126     }
23127     
23128     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23129     this.addEvents({
23130             /**
23131              * @event initialize
23132              * Fires when the editor is fully initialized (including the iframe)
23133              * @param {HtmlEditor} this
23134              */
23135             initialize: true,
23136             /**
23137              * @event activate
23138              * Fires when the editor is first receives the focus. Any insertion must wait
23139              * until after this event.
23140              * @param {HtmlEditor} this
23141              */
23142             activate: true,
23143              /**
23144              * @event beforesync
23145              * Fires before the textarea is updated with content from the editor iframe. Return false
23146              * to cancel the sync.
23147              * @param {HtmlEditor} this
23148              * @param {String} html
23149              */
23150             beforesync: true,
23151              /**
23152              * @event beforepush
23153              * Fires before the iframe editor is updated with content from the textarea. Return false
23154              * to cancel the push.
23155              * @param {HtmlEditor} this
23156              * @param {String} html
23157              */
23158             beforepush: true,
23159              /**
23160              * @event sync
23161              * Fires when the textarea is updated with content from the editor iframe.
23162              * @param {HtmlEditor} this
23163              * @param {String} html
23164              */
23165             sync: true,
23166              /**
23167              * @event push
23168              * Fires when the iframe editor is updated with content from the textarea.
23169              * @param {HtmlEditor} this
23170              * @param {String} html
23171              */
23172             push: true,
23173              /**
23174              * @event editmodechange
23175              * Fires when the editor switches edit modes
23176              * @param {HtmlEditor} this
23177              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23178              */
23179             editmodechange: true,
23180             /**
23181              * @event editorevent
23182              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23183              * @param {HtmlEditor} this
23184              */
23185             editorevent: true,
23186             /**
23187              * @event firstfocus
23188              * Fires when on first focus - needed by toolbars..
23189              * @param {HtmlEditor} this
23190              */
23191             firstfocus: true,
23192             /**
23193              * @event autosave
23194              * Auto save the htmlEditor value as a file into Events
23195              * @param {HtmlEditor} this
23196              */
23197             autosave: true,
23198             /**
23199              * @event savedpreview
23200              * preview the saved version of htmlEditor
23201              * @param {HtmlEditor} this
23202              */
23203             savedpreview: true
23204         });
23205 };
23206
23207
23208 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23209     
23210     
23211       /**
23212      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23213      */
23214     toolbars : false,
23215     
23216      /**
23217     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23218     */
23219     btns : [],
23220    
23221      /**
23222      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23223      *                        Roo.resizable.
23224      */
23225     resizable : false,
23226      /**
23227      * @cfg {Number} height (in pixels)
23228      */   
23229     height: 300,
23230    /**
23231      * @cfg {Number} width (in pixels)
23232      */   
23233     width: false,
23234     
23235     /**
23236      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23237      * 
23238      */
23239     stylesheets: false,
23240     
23241     // id of frame..
23242     frameId: false,
23243     
23244     // private properties
23245     validationEvent : false,
23246     deferHeight: true,
23247     initialized : false,
23248     activated : false,
23249     
23250     onFocus : Roo.emptyFn,
23251     iframePad:3,
23252     hideMode:'offsets',
23253     
23254     tbContainer : false,
23255     
23256     bodyCls : '',
23257     
23258     toolbarContainer :function() {
23259         return this.wrap.select('.x-html-editor-tb',true).first();
23260     },
23261
23262     /**
23263      * Protected method that will not generally be called directly. It
23264      * is called when the editor creates its toolbar. Override this method if you need to
23265      * add custom toolbar buttons.
23266      * @param {HtmlEditor} editor
23267      */
23268     createToolbar : function(){
23269         Roo.log('renewing');
23270         Roo.log("create toolbars");
23271         
23272         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23273         this.toolbars[0].render(this.toolbarContainer());
23274         
23275         return;
23276         
23277 //        if (!editor.toolbars || !editor.toolbars.length) {
23278 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23279 //        }
23280 //        
23281 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23282 //            editor.toolbars[i] = Roo.factory(
23283 //                    typeof(editor.toolbars[i]) == 'string' ?
23284 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23285 //                Roo.bootstrap.HtmlEditor);
23286 //            editor.toolbars[i].init(editor);
23287 //        }
23288     },
23289
23290      
23291     // private
23292     onRender : function(ct, position)
23293     {
23294        // Roo.log("Call onRender: " + this.xtype);
23295         var _t = this;
23296         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23297       
23298         this.wrap = this.inputEl().wrap({
23299             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23300         });
23301         
23302         this.editorcore.onRender(ct, position);
23303          
23304         if (this.resizable) {
23305             this.resizeEl = new Roo.Resizable(this.wrap, {
23306                 pinned : true,
23307                 wrap: true,
23308                 dynamic : true,
23309                 minHeight : this.height,
23310                 height: this.height,
23311                 handles : this.resizable,
23312                 width: this.width,
23313                 listeners : {
23314                     resize : function(r, w, h) {
23315                         _t.onResize(w,h); // -something
23316                     }
23317                 }
23318             });
23319             
23320         }
23321         this.createToolbar(this);
23322        
23323         
23324         if(!this.width && this.resizable){
23325             this.setSize(this.wrap.getSize());
23326         }
23327         if (this.resizeEl) {
23328             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23329             // should trigger onReize..
23330         }
23331         
23332     },
23333
23334     // private
23335     onResize : function(w, h)
23336     {
23337         Roo.log('resize: ' +w + ',' + h );
23338         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23339         var ew = false;
23340         var eh = false;
23341         
23342         if(this.inputEl() ){
23343             if(typeof w == 'number'){
23344                 var aw = w - this.wrap.getFrameWidth('lr');
23345                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23346                 ew = aw;
23347             }
23348             if(typeof h == 'number'){
23349                  var tbh = -11;  // fixme it needs to tool bar size!
23350                 for (var i =0; i < this.toolbars.length;i++) {
23351                     // fixme - ask toolbars for heights?
23352                     tbh += this.toolbars[i].el.getHeight();
23353                     //if (this.toolbars[i].footer) {
23354                     //    tbh += this.toolbars[i].footer.el.getHeight();
23355                     //}
23356                 }
23357               
23358                 
23359                 
23360                 
23361                 
23362                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23363                 ah -= 5; // knock a few pixes off for look..
23364                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23365                 var eh = ah;
23366             }
23367         }
23368         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23369         this.editorcore.onResize(ew,eh);
23370         
23371     },
23372
23373     /**
23374      * Toggles the editor between standard and source edit mode.
23375      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23376      */
23377     toggleSourceEdit : function(sourceEditMode)
23378     {
23379         this.editorcore.toggleSourceEdit(sourceEditMode);
23380         
23381         if(this.editorcore.sourceEditMode){
23382             Roo.log('editor - showing textarea');
23383             
23384 //            Roo.log('in');
23385 //            Roo.log(this.syncValue());
23386             this.syncValue();
23387             this.inputEl().removeClass(['hide', 'x-hidden']);
23388             this.inputEl().dom.removeAttribute('tabIndex');
23389             this.inputEl().focus();
23390         }else{
23391             Roo.log('editor - hiding textarea');
23392 //            Roo.log('out')
23393 //            Roo.log(this.pushValue()); 
23394             this.pushValue();
23395             
23396             this.inputEl().addClass(['hide', 'x-hidden']);
23397             this.inputEl().dom.setAttribute('tabIndex', -1);
23398             //this.deferFocus();
23399         }
23400          
23401         if(this.resizable){
23402             this.setSize(this.wrap.getSize());
23403         }
23404         
23405         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23406     },
23407  
23408     // private (for BoxComponent)
23409     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23410
23411     // private (for BoxComponent)
23412     getResizeEl : function(){
23413         return this.wrap;
23414     },
23415
23416     // private (for BoxComponent)
23417     getPositionEl : function(){
23418         return this.wrap;
23419     },
23420
23421     // private
23422     initEvents : function(){
23423         this.originalValue = this.getValue();
23424     },
23425
23426 //    /**
23427 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23428 //     * @method
23429 //     */
23430 //    markInvalid : Roo.emptyFn,
23431 //    /**
23432 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23433 //     * @method
23434 //     */
23435 //    clearInvalid : Roo.emptyFn,
23436
23437     setValue : function(v){
23438         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23439         this.editorcore.pushValue();
23440     },
23441
23442      
23443     // private
23444     deferFocus : function(){
23445         this.focus.defer(10, this);
23446     },
23447
23448     // doc'ed in Field
23449     focus : function(){
23450         this.editorcore.focus();
23451         
23452     },
23453       
23454
23455     // private
23456     onDestroy : function(){
23457         
23458         
23459         
23460         if(this.rendered){
23461             
23462             for (var i =0; i < this.toolbars.length;i++) {
23463                 // fixme - ask toolbars for heights?
23464                 this.toolbars[i].onDestroy();
23465             }
23466             
23467             this.wrap.dom.innerHTML = '';
23468             this.wrap.remove();
23469         }
23470     },
23471
23472     // private
23473     onFirstFocus : function(){
23474         //Roo.log("onFirstFocus");
23475         this.editorcore.onFirstFocus();
23476          for (var i =0; i < this.toolbars.length;i++) {
23477             this.toolbars[i].onFirstFocus();
23478         }
23479         
23480     },
23481     
23482     // private
23483     syncValue : function()
23484     {   
23485         this.editorcore.syncValue();
23486     },
23487     
23488     pushValue : function()
23489     {   
23490         this.editorcore.pushValue();
23491     }
23492      
23493     
23494     // hide stuff that is not compatible
23495     /**
23496      * @event blur
23497      * @hide
23498      */
23499     /**
23500      * @event change
23501      * @hide
23502      */
23503     /**
23504      * @event focus
23505      * @hide
23506      */
23507     /**
23508      * @event specialkey
23509      * @hide
23510      */
23511     /**
23512      * @cfg {String} fieldClass @hide
23513      */
23514     /**
23515      * @cfg {String} focusClass @hide
23516      */
23517     /**
23518      * @cfg {String} autoCreate @hide
23519      */
23520     /**
23521      * @cfg {String} inputType @hide
23522      */
23523     /**
23524      * @cfg {String} invalidClass @hide
23525      */
23526     /**
23527      * @cfg {String} invalidText @hide
23528      */
23529     /**
23530      * @cfg {String} msgFx @hide
23531      */
23532     /**
23533      * @cfg {String} validateOnBlur @hide
23534      */
23535 });
23536  
23537     
23538    
23539    
23540    
23541       
23542 Roo.namespace('Roo.bootstrap.htmleditor');
23543 /**
23544  * @class Roo.bootstrap.HtmlEditorToolbar1
23545  * Basic Toolbar
23546  * 
23547  * Usage:
23548  *
23549  new Roo.bootstrap.HtmlEditor({
23550     ....
23551     toolbars : [
23552         new Roo.bootstrap.HtmlEditorToolbar1({
23553             disable : { fonts: 1 , format: 1, ..., ... , ...],
23554             btns : [ .... ]
23555         })
23556     }
23557      
23558  * 
23559  * @cfg {Object} disable List of elements to disable..
23560  * @cfg {Array} btns List of additional buttons.
23561  * 
23562  * 
23563  * NEEDS Extra CSS? 
23564  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23565  */
23566  
23567 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23568 {
23569     
23570     Roo.apply(this, config);
23571     
23572     // default disabled, based on 'good practice'..
23573     this.disable = this.disable || {};
23574     Roo.applyIf(this.disable, {
23575         fontSize : true,
23576         colors : true,
23577         specialElements : true
23578     });
23579     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23580     
23581     this.editor = config.editor;
23582     this.editorcore = config.editor.editorcore;
23583     
23584     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23585     
23586     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23587     // dont call parent... till later.
23588 }
23589 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23590      
23591     bar : true,
23592     
23593     editor : false,
23594     editorcore : false,
23595     
23596     
23597     formats : [
23598         "p" ,  
23599         "h1","h2","h3","h4","h5","h6", 
23600         "pre", "code", 
23601         "abbr", "acronym", "address", "cite", "samp", "var",
23602         'div','span'
23603     ],
23604     
23605     onRender : function(ct, position)
23606     {
23607        // Roo.log("Call onRender: " + this.xtype);
23608         
23609        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23610        Roo.log(this.el);
23611        this.el.dom.style.marginBottom = '0';
23612        var _this = this;
23613        var editorcore = this.editorcore;
23614        var editor= this.editor;
23615        
23616        var children = [];
23617        var btn = function(id,cmd , toggle, handler, html){
23618        
23619             var  event = toggle ? 'toggle' : 'click';
23620        
23621             var a = {
23622                 size : 'sm',
23623                 xtype: 'Button',
23624                 xns: Roo.bootstrap,
23625                 glyphicon : id,
23626                 cmd : id || cmd,
23627                 enableToggle:toggle !== false,
23628                 html : html || '',
23629                 pressed : toggle ? false : null,
23630                 listeners : {}
23631             };
23632             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23633                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23634             };
23635             children.push(a);
23636             return a;
23637        }
23638        
23639     //    var cb_box = function...
23640         
23641         var style = {
23642                 xtype: 'Button',
23643                 size : 'sm',
23644                 xns: Roo.bootstrap,
23645                 glyphicon : 'font',
23646                 //html : 'submit'
23647                 menu : {
23648                     xtype: 'Menu',
23649                     xns: Roo.bootstrap,
23650                     items:  []
23651                 }
23652         };
23653         Roo.each(this.formats, function(f) {
23654             style.menu.items.push({
23655                 xtype :'MenuItem',
23656                 xns: Roo.bootstrap,
23657                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23658                 tagname : f,
23659                 listeners : {
23660                     click : function()
23661                     {
23662                         editorcore.insertTag(this.tagname);
23663                         editor.focus();
23664                     }
23665                 }
23666                 
23667             });
23668         });
23669         children.push(style);   
23670         
23671         btn('bold',false,true);
23672         btn('italic',false,true);
23673         btn('align-left', 'justifyleft',true);
23674         btn('align-center', 'justifycenter',true);
23675         btn('align-right' , 'justifyright',true);
23676         btn('link', false, false, function(btn) {
23677             //Roo.log("create link?");
23678             var url = prompt(this.createLinkText, this.defaultLinkValue);
23679             if(url && url != 'http:/'+'/'){
23680                 this.editorcore.relayCmd('createlink', url);
23681             }
23682         }),
23683         btn('list','insertunorderedlist',true);
23684         btn('pencil', false,true, function(btn){
23685                 Roo.log(this);
23686                 this.toggleSourceEdit(btn.pressed);
23687         });
23688         
23689         if (this.editor.btns.length > 0) {
23690             for (var i = 0; i<this.editor.btns.length; i++) {
23691                 children.push(this.editor.btns[i]);
23692             }
23693         }
23694         
23695         /*
23696         var cog = {
23697                 xtype: 'Button',
23698                 size : 'sm',
23699                 xns: Roo.bootstrap,
23700                 glyphicon : 'cog',
23701                 //html : 'submit'
23702                 menu : {
23703                     xtype: 'Menu',
23704                     xns: Roo.bootstrap,
23705                     items:  []
23706                 }
23707         };
23708         
23709         cog.menu.items.push({
23710             xtype :'MenuItem',
23711             xns: Roo.bootstrap,
23712             html : Clean styles,
23713             tagname : f,
23714             listeners : {
23715                 click : function()
23716                 {
23717                     editorcore.insertTag(this.tagname);
23718                     editor.focus();
23719                 }
23720             }
23721             
23722         });
23723        */
23724         
23725          
23726        this.xtype = 'NavSimplebar';
23727         
23728         for(var i=0;i< children.length;i++) {
23729             
23730             this.buttons.add(this.addxtypeChild(children[i]));
23731             
23732         }
23733         
23734         editor.on('editorevent', this.updateToolbar, this);
23735     },
23736     onBtnClick : function(id)
23737     {
23738        this.editorcore.relayCmd(id);
23739        this.editorcore.focus();
23740     },
23741     
23742     /**
23743      * Protected method that will not generally be called directly. It triggers
23744      * a toolbar update by reading the markup state of the current selection in the editor.
23745      */
23746     updateToolbar: function(){
23747
23748         if(!this.editorcore.activated){
23749             this.editor.onFirstFocus(); // is this neeed?
23750             return;
23751         }
23752
23753         var btns = this.buttons; 
23754         var doc = this.editorcore.doc;
23755         btns.get('bold').setActive(doc.queryCommandState('bold'));
23756         btns.get('italic').setActive(doc.queryCommandState('italic'));
23757         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23758         
23759         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23760         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23761         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23762         
23763         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23764         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23765          /*
23766         
23767         var ans = this.editorcore.getAllAncestors();
23768         if (this.formatCombo) {
23769             
23770             
23771             var store = this.formatCombo.store;
23772             this.formatCombo.setValue("");
23773             for (var i =0; i < ans.length;i++) {
23774                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23775                     // select it..
23776                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23777                     break;
23778                 }
23779             }
23780         }
23781         
23782         
23783         
23784         // hides menus... - so this cant be on a menu...
23785         Roo.bootstrap.MenuMgr.hideAll();
23786         */
23787         Roo.bootstrap.MenuMgr.hideAll();
23788         //this.editorsyncValue();
23789     },
23790     onFirstFocus: function() {
23791         this.buttons.each(function(item){
23792            item.enable();
23793         });
23794     },
23795     toggleSourceEdit : function(sourceEditMode){
23796         
23797           
23798         if(sourceEditMode){
23799             Roo.log("disabling buttons");
23800            this.buttons.each( function(item){
23801                 if(item.cmd != 'pencil'){
23802                     item.disable();
23803                 }
23804             });
23805           
23806         }else{
23807             Roo.log("enabling buttons");
23808             if(this.editorcore.initialized){
23809                 this.buttons.each( function(item){
23810                     item.enable();
23811                 });
23812             }
23813             
23814         }
23815         Roo.log("calling toggole on editor");
23816         // tell the editor that it's been pressed..
23817         this.editor.toggleSourceEdit(sourceEditMode);
23818        
23819     }
23820 });
23821
23822
23823
23824
23825
23826 /**
23827  * @class Roo.bootstrap.Table.AbstractSelectionModel
23828  * @extends Roo.util.Observable
23829  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23830  * implemented by descendant classes.  This class should not be directly instantiated.
23831  * @constructor
23832  */
23833 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23834     this.locked = false;
23835     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23836 };
23837
23838
23839 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23840     /** @ignore Called by the grid automatically. Do not call directly. */
23841     init : function(grid){
23842         this.grid = grid;
23843         this.initEvents();
23844     },
23845
23846     /**
23847      * Locks the selections.
23848      */
23849     lock : function(){
23850         this.locked = true;
23851     },
23852
23853     /**
23854      * Unlocks the selections.
23855      */
23856     unlock : function(){
23857         this.locked = false;
23858     },
23859
23860     /**
23861      * Returns true if the selections are locked.
23862      * @return {Boolean}
23863      */
23864     isLocked : function(){
23865         return this.locked;
23866     }
23867 });
23868 /**
23869  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23870  * @class Roo.bootstrap.Table.RowSelectionModel
23871  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23872  * It supports multiple selections and keyboard selection/navigation. 
23873  * @constructor
23874  * @param {Object} config
23875  */
23876
23877 Roo.bootstrap.Table.RowSelectionModel = function(config){
23878     Roo.apply(this, config);
23879     this.selections = new Roo.util.MixedCollection(false, function(o){
23880         return o.id;
23881     });
23882
23883     this.last = false;
23884     this.lastActive = false;
23885
23886     this.addEvents({
23887         /**
23888              * @event selectionchange
23889              * Fires when the selection changes
23890              * @param {SelectionModel} this
23891              */
23892             "selectionchange" : true,
23893         /**
23894              * @event afterselectionchange
23895              * Fires after the selection changes (eg. by key press or clicking)
23896              * @param {SelectionModel} this
23897              */
23898             "afterselectionchange" : true,
23899         /**
23900              * @event beforerowselect
23901              * Fires when a row is selected being selected, return false to cancel.
23902              * @param {SelectionModel} this
23903              * @param {Number} rowIndex The selected index
23904              * @param {Boolean} keepExisting False if other selections will be cleared
23905              */
23906             "beforerowselect" : true,
23907         /**
23908              * @event rowselect
23909              * Fires when a row is selected.
23910              * @param {SelectionModel} this
23911              * @param {Number} rowIndex The selected index
23912              * @param {Roo.data.Record} r The record
23913              */
23914             "rowselect" : true,
23915         /**
23916              * @event rowdeselect
23917              * Fires when a row is deselected.
23918              * @param {SelectionModel} this
23919              * @param {Number} rowIndex The selected index
23920              */
23921         "rowdeselect" : true
23922     });
23923     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23924     this.locked = false;
23925  };
23926
23927 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23928     /**
23929      * @cfg {Boolean} singleSelect
23930      * True to allow selection of only one row at a time (defaults to false)
23931      */
23932     singleSelect : false,
23933
23934     // private
23935     initEvents : function()
23936     {
23937
23938         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23939         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23940         //}else{ // allow click to work like normal
23941          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23942         //}
23943         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23944         this.grid.on("rowclick", this.handleMouseDown, this);
23945         
23946         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23947             "up" : function(e){
23948                 if(!e.shiftKey){
23949                     this.selectPrevious(e.shiftKey);
23950                 }else if(this.last !== false && this.lastActive !== false){
23951                     var last = this.last;
23952                     this.selectRange(this.last,  this.lastActive-1);
23953                     this.grid.getView().focusRow(this.lastActive);
23954                     if(last !== false){
23955                         this.last = last;
23956                     }
23957                 }else{
23958                     this.selectFirstRow();
23959                 }
23960                 this.fireEvent("afterselectionchange", this);
23961             },
23962             "down" : function(e){
23963                 if(!e.shiftKey){
23964                     this.selectNext(e.shiftKey);
23965                 }else if(this.last !== false && this.lastActive !== false){
23966                     var last = this.last;
23967                     this.selectRange(this.last,  this.lastActive+1);
23968                     this.grid.getView().focusRow(this.lastActive);
23969                     if(last !== false){
23970                         this.last = last;
23971                     }
23972                 }else{
23973                     this.selectFirstRow();
23974                 }
23975                 this.fireEvent("afterselectionchange", this);
23976             },
23977             scope: this
23978         });
23979         this.grid.store.on('load', function(){
23980             this.selections.clear();
23981         },this);
23982         /*
23983         var view = this.grid.view;
23984         view.on("refresh", this.onRefresh, this);
23985         view.on("rowupdated", this.onRowUpdated, this);
23986         view.on("rowremoved", this.onRemove, this);
23987         */
23988     },
23989
23990     // private
23991     onRefresh : function()
23992     {
23993         var ds = this.grid.store, i, v = this.grid.view;
23994         var s = this.selections;
23995         s.each(function(r){
23996             if((i = ds.indexOfId(r.id)) != -1){
23997                 v.onRowSelect(i);
23998             }else{
23999                 s.remove(r);
24000             }
24001         });
24002     },
24003
24004     // private
24005     onRemove : function(v, index, r){
24006         this.selections.remove(r);
24007     },
24008
24009     // private
24010     onRowUpdated : function(v, index, r){
24011         if(this.isSelected(r)){
24012             v.onRowSelect(index);
24013         }
24014     },
24015
24016     /**
24017      * Select records.
24018      * @param {Array} records The records to select
24019      * @param {Boolean} keepExisting (optional) True to keep existing selections
24020      */
24021     selectRecords : function(records, keepExisting)
24022     {
24023         if(!keepExisting){
24024             this.clearSelections();
24025         }
24026             var ds = this.grid.store;
24027         for(var i = 0, len = records.length; i < len; i++){
24028             this.selectRow(ds.indexOf(records[i]), true);
24029         }
24030     },
24031
24032     /**
24033      * Gets the number of selected rows.
24034      * @return {Number}
24035      */
24036     getCount : function(){
24037         return this.selections.length;
24038     },
24039
24040     /**
24041      * Selects the first row in the grid.
24042      */
24043     selectFirstRow : function(){
24044         this.selectRow(0);
24045     },
24046
24047     /**
24048      * Select the last row.
24049      * @param {Boolean} keepExisting (optional) True to keep existing selections
24050      */
24051     selectLastRow : function(keepExisting){
24052         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24053         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24054     },
24055
24056     /**
24057      * Selects the row immediately following the last selected row.
24058      * @param {Boolean} keepExisting (optional) True to keep existing selections
24059      */
24060     selectNext : function(keepExisting)
24061     {
24062             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24063             this.selectRow(this.last+1, keepExisting);
24064             this.grid.getView().focusRow(this.last);
24065         }
24066     },
24067
24068     /**
24069      * Selects the row that precedes the last selected row.
24070      * @param {Boolean} keepExisting (optional) True to keep existing selections
24071      */
24072     selectPrevious : function(keepExisting){
24073         if(this.last){
24074             this.selectRow(this.last-1, keepExisting);
24075             this.grid.getView().focusRow(this.last);
24076         }
24077     },
24078
24079     /**
24080      * Returns the selected records
24081      * @return {Array} Array of selected records
24082      */
24083     getSelections : function(){
24084         return [].concat(this.selections.items);
24085     },
24086
24087     /**
24088      * Returns the first selected record.
24089      * @return {Record}
24090      */
24091     getSelected : function(){
24092         return this.selections.itemAt(0);
24093     },
24094
24095
24096     /**
24097      * Clears all selections.
24098      */
24099     clearSelections : function(fast)
24100     {
24101         if(this.locked) {
24102             return;
24103         }
24104         if(fast !== true){
24105                 var ds = this.grid.store;
24106             var s = this.selections;
24107             s.each(function(r){
24108                 this.deselectRow(ds.indexOfId(r.id));
24109             }, this);
24110             s.clear();
24111         }else{
24112             this.selections.clear();
24113         }
24114         this.last = false;
24115     },
24116
24117
24118     /**
24119      * Selects all rows.
24120      */
24121     selectAll : function(){
24122         if(this.locked) {
24123             return;
24124         }
24125         this.selections.clear();
24126         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24127             this.selectRow(i, true);
24128         }
24129     },
24130
24131     /**
24132      * Returns True if there is a selection.
24133      * @return {Boolean}
24134      */
24135     hasSelection : function(){
24136         return this.selections.length > 0;
24137     },
24138
24139     /**
24140      * Returns True if the specified row is selected.
24141      * @param {Number/Record} record The record or index of the record to check
24142      * @return {Boolean}
24143      */
24144     isSelected : function(index){
24145             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24146         return (r && this.selections.key(r.id) ? true : false);
24147     },
24148
24149     /**
24150      * Returns True if the specified record id is selected.
24151      * @param {String} id The id of record to check
24152      * @return {Boolean}
24153      */
24154     isIdSelected : function(id){
24155         return (this.selections.key(id) ? true : false);
24156     },
24157
24158
24159     // private
24160     handleMouseDBClick : function(e, t){
24161         
24162     },
24163     // private
24164     handleMouseDown : function(e, t)
24165     {
24166             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24167         if(this.isLocked() || rowIndex < 0 ){
24168             return;
24169         };
24170         if(e.shiftKey && this.last !== false){
24171             var last = this.last;
24172             this.selectRange(last, rowIndex, e.ctrlKey);
24173             this.last = last; // reset the last
24174             t.focus();
24175     
24176         }else{
24177             var isSelected = this.isSelected(rowIndex);
24178             //Roo.log("select row:" + rowIndex);
24179             if(isSelected){
24180                 this.deselectRow(rowIndex);
24181             } else {
24182                         this.selectRow(rowIndex, true);
24183             }
24184     
24185             /*
24186                 if(e.button !== 0 && isSelected){
24187                 alert('rowIndex 2: ' + rowIndex);
24188                     view.focusRow(rowIndex);
24189                 }else if(e.ctrlKey && isSelected){
24190                     this.deselectRow(rowIndex);
24191                 }else if(!isSelected){
24192                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24193                     view.focusRow(rowIndex);
24194                 }
24195             */
24196         }
24197         this.fireEvent("afterselectionchange", this);
24198     },
24199     // private
24200     handleDragableRowClick :  function(grid, rowIndex, e) 
24201     {
24202         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24203             this.selectRow(rowIndex, false);
24204             grid.view.focusRow(rowIndex);
24205              this.fireEvent("afterselectionchange", this);
24206         }
24207     },
24208     
24209     /**
24210      * Selects multiple rows.
24211      * @param {Array} rows Array of the indexes of the row to select
24212      * @param {Boolean} keepExisting (optional) True to keep existing selections
24213      */
24214     selectRows : function(rows, keepExisting){
24215         if(!keepExisting){
24216             this.clearSelections();
24217         }
24218         for(var i = 0, len = rows.length; i < len; i++){
24219             this.selectRow(rows[i], true);
24220         }
24221     },
24222
24223     /**
24224      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24225      * @param {Number} startRow The index of the first row in the range
24226      * @param {Number} endRow The index of the last row in the range
24227      * @param {Boolean} keepExisting (optional) True to retain existing selections
24228      */
24229     selectRange : function(startRow, endRow, keepExisting){
24230         if(this.locked) {
24231             return;
24232         }
24233         if(!keepExisting){
24234             this.clearSelections();
24235         }
24236         if(startRow <= endRow){
24237             for(var i = startRow; i <= endRow; i++){
24238                 this.selectRow(i, true);
24239             }
24240         }else{
24241             for(var i = startRow; i >= endRow; i--){
24242                 this.selectRow(i, true);
24243             }
24244         }
24245     },
24246
24247     /**
24248      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24249      * @param {Number} startRow The index of the first row in the range
24250      * @param {Number} endRow The index of the last row in the range
24251      */
24252     deselectRange : function(startRow, endRow, preventViewNotify){
24253         if(this.locked) {
24254             return;
24255         }
24256         for(var i = startRow; i <= endRow; i++){
24257             this.deselectRow(i, preventViewNotify);
24258         }
24259     },
24260
24261     /**
24262      * Selects a row.
24263      * @param {Number} row The index of the row to select
24264      * @param {Boolean} keepExisting (optional) True to keep existing selections
24265      */
24266     selectRow : function(index, keepExisting, preventViewNotify)
24267     {
24268             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24269             return;
24270         }
24271         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24272             if(!keepExisting || this.singleSelect){
24273                 this.clearSelections();
24274             }
24275             
24276             var r = this.grid.store.getAt(index);
24277             //console.log('selectRow - record id :' + r.id);
24278             
24279             this.selections.add(r);
24280             this.last = this.lastActive = index;
24281             if(!preventViewNotify){
24282                 var proxy = new Roo.Element(
24283                                 this.grid.getRowDom(index)
24284                 );
24285                 proxy.addClass('bg-info info');
24286             }
24287             this.fireEvent("rowselect", this, index, r);
24288             this.fireEvent("selectionchange", this);
24289         }
24290     },
24291
24292     /**
24293      * Deselects a row.
24294      * @param {Number} row The index of the row to deselect
24295      */
24296     deselectRow : function(index, preventViewNotify)
24297     {
24298         if(this.locked) {
24299             return;
24300         }
24301         if(this.last == index){
24302             this.last = false;
24303         }
24304         if(this.lastActive == index){
24305             this.lastActive = false;
24306         }
24307         
24308         var r = this.grid.store.getAt(index);
24309         if (!r) {
24310             return;
24311         }
24312         
24313         this.selections.remove(r);
24314         //.console.log('deselectRow - record id :' + r.id);
24315         if(!preventViewNotify){
24316         
24317             var proxy = new Roo.Element(
24318                 this.grid.getRowDom(index)
24319             );
24320             proxy.removeClass('bg-info info');
24321         }
24322         this.fireEvent("rowdeselect", this, index);
24323         this.fireEvent("selectionchange", this);
24324     },
24325
24326     // private
24327     restoreLast : function(){
24328         if(this._last){
24329             this.last = this._last;
24330         }
24331     },
24332
24333     // private
24334     acceptsNav : function(row, col, cm){
24335         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24336     },
24337
24338     // private
24339     onEditorKey : function(field, e){
24340         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24341         if(k == e.TAB){
24342             e.stopEvent();
24343             ed.completeEdit();
24344             if(e.shiftKey){
24345                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24346             }else{
24347                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24348             }
24349         }else if(k == e.ENTER && !e.ctrlKey){
24350             e.stopEvent();
24351             ed.completeEdit();
24352             if(e.shiftKey){
24353                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24354             }else{
24355                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24356             }
24357         }else if(k == e.ESC){
24358             ed.cancelEdit();
24359         }
24360         if(newCell){
24361             g.startEditing(newCell[0], newCell[1]);
24362         }
24363     }
24364 });
24365 /*
24366  * Based on:
24367  * Ext JS Library 1.1.1
24368  * Copyright(c) 2006-2007, Ext JS, LLC.
24369  *
24370  * Originally Released Under LGPL - original licence link has changed is not relivant.
24371  *
24372  * Fork - LGPL
24373  * <script type="text/javascript">
24374  */
24375  
24376 /**
24377  * @class Roo.bootstrap.PagingToolbar
24378  * @extends Roo.bootstrap.NavSimplebar
24379  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24380  * @constructor
24381  * Create a new PagingToolbar
24382  * @param {Object} config The config object
24383  * @param {Roo.data.Store} store
24384  */
24385 Roo.bootstrap.PagingToolbar = function(config)
24386 {
24387     // old args format still supported... - xtype is prefered..
24388         // created from xtype...
24389     
24390     this.ds = config.dataSource;
24391     
24392     if (config.store && !this.ds) {
24393         this.store= Roo.factory(config.store, Roo.data);
24394         this.ds = this.store;
24395         this.ds.xmodule = this.xmodule || false;
24396     }
24397     
24398     this.toolbarItems = [];
24399     if (config.items) {
24400         this.toolbarItems = config.items;
24401     }
24402     
24403     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24404     
24405     this.cursor = 0;
24406     
24407     if (this.ds) { 
24408         this.bind(this.ds);
24409     }
24410     
24411     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24412     
24413 };
24414
24415 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24416     /**
24417      * @cfg {Roo.data.Store} dataSource
24418      * The underlying data store providing the paged data
24419      */
24420     /**
24421      * @cfg {String/HTMLElement/Element} container
24422      * container The id or element that will contain the toolbar
24423      */
24424     /**
24425      * @cfg {Boolean} displayInfo
24426      * True to display the displayMsg (defaults to false)
24427      */
24428     /**
24429      * @cfg {Number} pageSize
24430      * The number of records to display per page (defaults to 20)
24431      */
24432     pageSize: 20,
24433     /**
24434      * @cfg {String} displayMsg
24435      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24436      */
24437     displayMsg : 'Displaying {0} - {1} of {2}',
24438     /**
24439      * @cfg {String} emptyMsg
24440      * The message to display when no records are found (defaults to "No data to display")
24441      */
24442     emptyMsg : 'No data to display',
24443     /**
24444      * Customizable piece of the default paging text (defaults to "Page")
24445      * @type String
24446      */
24447     beforePageText : "Page",
24448     /**
24449      * Customizable piece of the default paging text (defaults to "of %0")
24450      * @type String
24451      */
24452     afterPageText : "of {0}",
24453     /**
24454      * Customizable piece of the default paging text (defaults to "First Page")
24455      * @type String
24456      */
24457     firstText : "First Page",
24458     /**
24459      * Customizable piece of the default paging text (defaults to "Previous Page")
24460      * @type String
24461      */
24462     prevText : "Previous Page",
24463     /**
24464      * Customizable piece of the default paging text (defaults to "Next Page")
24465      * @type String
24466      */
24467     nextText : "Next Page",
24468     /**
24469      * Customizable piece of the default paging text (defaults to "Last Page")
24470      * @type String
24471      */
24472     lastText : "Last Page",
24473     /**
24474      * Customizable piece of the default paging text (defaults to "Refresh")
24475      * @type String
24476      */
24477     refreshText : "Refresh",
24478
24479     buttons : false,
24480     // private
24481     onRender : function(ct, position) 
24482     {
24483         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24484         this.navgroup.parentId = this.id;
24485         this.navgroup.onRender(this.el, null);
24486         // add the buttons to the navgroup
24487         
24488         if(this.displayInfo){
24489             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24490             this.displayEl = this.el.select('.x-paging-info', true).first();
24491 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24492 //            this.displayEl = navel.el.select('span',true).first();
24493         }
24494         
24495         var _this = this;
24496         
24497         if(this.buttons){
24498             Roo.each(_this.buttons, function(e){ // this might need to use render????
24499                Roo.factory(e).onRender(_this.el, null);
24500             });
24501         }
24502             
24503         Roo.each(_this.toolbarItems, function(e) {
24504             _this.navgroup.addItem(e);
24505         });
24506         
24507         
24508         this.first = this.navgroup.addItem({
24509             tooltip: this.firstText,
24510             cls: "prev",
24511             icon : 'fa fa-backward',
24512             disabled: true,
24513             preventDefault: true,
24514             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24515         });
24516         
24517         this.prev =  this.navgroup.addItem({
24518             tooltip: this.prevText,
24519             cls: "prev",
24520             icon : 'fa fa-step-backward',
24521             disabled: true,
24522             preventDefault: true,
24523             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24524         });
24525     //this.addSeparator();
24526         
24527         
24528         var field = this.navgroup.addItem( {
24529             tagtype : 'span',
24530             cls : 'x-paging-position',
24531             
24532             html : this.beforePageText  +
24533                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24534                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24535          } ); //?? escaped?
24536         
24537         this.field = field.el.select('input', true).first();
24538         this.field.on("keydown", this.onPagingKeydown, this);
24539         this.field.on("focus", function(){this.dom.select();});
24540     
24541     
24542         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24543         //this.field.setHeight(18);
24544         //this.addSeparator();
24545         this.next = this.navgroup.addItem({
24546             tooltip: this.nextText,
24547             cls: "next",
24548             html : ' <i class="fa fa-step-forward">',
24549             disabled: true,
24550             preventDefault: true,
24551             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24552         });
24553         this.last = this.navgroup.addItem({
24554             tooltip: this.lastText,
24555             icon : 'fa fa-forward',
24556             cls: "next",
24557             disabled: true,
24558             preventDefault: true,
24559             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24560         });
24561     //this.addSeparator();
24562         this.loading = this.navgroup.addItem({
24563             tooltip: this.refreshText,
24564             icon: 'fa fa-refresh',
24565             preventDefault: true,
24566             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24567         });
24568         
24569     },
24570
24571     // private
24572     updateInfo : function(){
24573         if(this.displayEl){
24574             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24575             var msg = count == 0 ?
24576                 this.emptyMsg :
24577                 String.format(
24578                     this.displayMsg,
24579                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24580                 );
24581             this.displayEl.update(msg);
24582         }
24583     },
24584
24585     // private
24586     onLoad : function(ds, r, o)
24587     {
24588         this.cursor = o.params.start ? o.params.start : 0;
24589         
24590         var d = this.getPageData(),
24591             ap = d.activePage,
24592             ps = d.pages;
24593         
24594         
24595         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24596         this.field.dom.value = ap;
24597         this.first.setDisabled(ap == 1);
24598         this.prev.setDisabled(ap == 1);
24599         this.next.setDisabled(ap == ps);
24600         this.last.setDisabled(ap == ps);
24601         this.loading.enable();
24602         this.updateInfo();
24603     },
24604
24605     // private
24606     getPageData : function(){
24607         var total = this.ds.getTotalCount();
24608         return {
24609             total : total,
24610             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24611             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24612         };
24613     },
24614
24615     // private
24616     onLoadError : function(){
24617         this.loading.enable();
24618     },
24619
24620     // private
24621     onPagingKeydown : function(e){
24622         var k = e.getKey();
24623         var d = this.getPageData();
24624         if(k == e.RETURN){
24625             var v = this.field.dom.value, pageNum;
24626             if(!v || isNaN(pageNum = parseInt(v, 10))){
24627                 this.field.dom.value = d.activePage;
24628                 return;
24629             }
24630             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24631             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24632             e.stopEvent();
24633         }
24634         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))
24635         {
24636           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24637           this.field.dom.value = pageNum;
24638           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24639           e.stopEvent();
24640         }
24641         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24642         {
24643           var v = this.field.dom.value, pageNum; 
24644           var increment = (e.shiftKey) ? 10 : 1;
24645           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24646                 increment *= -1;
24647           }
24648           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24649             this.field.dom.value = d.activePage;
24650             return;
24651           }
24652           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24653           {
24654             this.field.dom.value = parseInt(v, 10) + increment;
24655             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24656             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24657           }
24658           e.stopEvent();
24659         }
24660     },
24661
24662     // private
24663     beforeLoad : function(){
24664         if(this.loading){
24665             this.loading.disable();
24666         }
24667     },
24668
24669     // private
24670     onClick : function(which){
24671         
24672         var ds = this.ds;
24673         if (!ds) {
24674             return;
24675         }
24676         
24677         switch(which){
24678             case "first":
24679                 ds.load({params:{start: 0, limit: this.pageSize}});
24680             break;
24681             case "prev":
24682                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24683             break;
24684             case "next":
24685                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24686             break;
24687             case "last":
24688                 var total = ds.getTotalCount();
24689                 var extra = total % this.pageSize;
24690                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24691                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24692             break;
24693             case "refresh":
24694                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24695             break;
24696         }
24697     },
24698
24699     /**
24700      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24701      * @param {Roo.data.Store} store The data store to unbind
24702      */
24703     unbind : function(ds){
24704         ds.un("beforeload", this.beforeLoad, this);
24705         ds.un("load", this.onLoad, this);
24706         ds.un("loadexception", this.onLoadError, this);
24707         ds.un("remove", this.updateInfo, this);
24708         ds.un("add", this.updateInfo, this);
24709         this.ds = undefined;
24710     },
24711
24712     /**
24713      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24714      * @param {Roo.data.Store} store The data store to bind
24715      */
24716     bind : function(ds){
24717         ds.on("beforeload", this.beforeLoad, this);
24718         ds.on("load", this.onLoad, this);
24719         ds.on("loadexception", this.onLoadError, this);
24720         ds.on("remove", this.updateInfo, this);
24721         ds.on("add", this.updateInfo, this);
24722         this.ds = ds;
24723     }
24724 });/*
24725  * - LGPL
24726  *
24727  * element
24728  * 
24729  */
24730
24731 /**
24732  * @class Roo.bootstrap.MessageBar
24733  * @extends Roo.bootstrap.Component
24734  * Bootstrap MessageBar class
24735  * @cfg {String} html contents of the MessageBar
24736  * @cfg {String} weight (info | success | warning | danger) default info
24737  * @cfg {String} beforeClass insert the bar before the given class
24738  * @cfg {Boolean} closable (true | false) default false
24739  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24740  * 
24741  * @constructor
24742  * Create a new Element
24743  * @param {Object} config The config object
24744  */
24745
24746 Roo.bootstrap.MessageBar = function(config){
24747     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24748 };
24749
24750 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24751     
24752     html: '',
24753     weight: 'info',
24754     closable: false,
24755     fixed: false,
24756     beforeClass: 'bootstrap-sticky-wrap',
24757     
24758     getAutoCreate : function(){
24759         
24760         var cfg = {
24761             tag: 'div',
24762             cls: 'alert alert-dismissable alert-' + this.weight,
24763             cn: [
24764                 {
24765                     tag: 'span',
24766                     cls: 'message',
24767                     html: this.html || ''
24768                 }
24769             ]
24770         };
24771         
24772         if(this.fixed){
24773             cfg.cls += ' alert-messages-fixed';
24774         }
24775         
24776         if(this.closable){
24777             cfg.cn.push({
24778                 tag: 'button',
24779                 cls: 'close',
24780                 html: 'x'
24781             });
24782         }
24783         
24784         return cfg;
24785     },
24786     
24787     onRender : function(ct, position)
24788     {
24789         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24790         
24791         if(!this.el){
24792             var cfg = Roo.apply({},  this.getAutoCreate());
24793             cfg.id = Roo.id();
24794             
24795             if (this.cls) {
24796                 cfg.cls += ' ' + this.cls;
24797             }
24798             if (this.style) {
24799                 cfg.style = this.style;
24800             }
24801             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24802             
24803             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24804         }
24805         
24806         this.el.select('>button.close').on('click', this.hide, this);
24807         
24808     },
24809     
24810     show : function()
24811     {
24812         if (!this.rendered) {
24813             this.render();
24814         }
24815         
24816         this.el.show();
24817         
24818         this.fireEvent('show', this);
24819         
24820     },
24821     
24822     hide : function()
24823     {
24824         if (!this.rendered) {
24825             this.render();
24826         }
24827         
24828         this.el.hide();
24829         
24830         this.fireEvent('hide', this);
24831     },
24832     
24833     update : function()
24834     {
24835 //        var e = this.el.dom.firstChild;
24836 //        
24837 //        if(this.closable){
24838 //            e = e.nextSibling;
24839 //        }
24840 //        
24841 //        e.data = this.html || '';
24842
24843         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24844     }
24845    
24846 });
24847
24848  
24849
24850      /*
24851  * - LGPL
24852  *
24853  * Graph
24854  * 
24855  */
24856
24857
24858 /**
24859  * @class Roo.bootstrap.Graph
24860  * @extends Roo.bootstrap.Component
24861  * Bootstrap Graph class
24862 > Prameters
24863  -sm {number} sm 4
24864  -md {number} md 5
24865  @cfg {String} graphtype  bar | vbar | pie
24866  @cfg {number} g_x coodinator | centre x (pie)
24867  @cfg {number} g_y coodinator | centre y (pie)
24868  @cfg {number} g_r radius (pie)
24869  @cfg {number} g_height height of the chart (respected by all elements in the set)
24870  @cfg {number} g_width width of the chart (respected by all elements in the set)
24871  @cfg {Object} title The title of the chart
24872     
24873  -{Array}  values
24874  -opts (object) options for the chart 
24875      o {
24876      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24877      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24878      o vgutter (number)
24879      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.
24880      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24881      o to
24882      o stretch (boolean)
24883      o }
24884  -opts (object) options for the pie
24885      o{
24886      o cut
24887      o startAngle (number)
24888      o endAngle (number)
24889      } 
24890  *
24891  * @constructor
24892  * Create a new Input
24893  * @param {Object} config The config object
24894  */
24895
24896 Roo.bootstrap.Graph = function(config){
24897     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24898     
24899     this.addEvents({
24900         // img events
24901         /**
24902          * @event click
24903          * The img click event for the img.
24904          * @param {Roo.EventObject} e
24905          */
24906         "click" : true
24907     });
24908 };
24909
24910 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24911     
24912     sm: 4,
24913     md: 5,
24914     graphtype: 'bar',
24915     g_height: 250,
24916     g_width: 400,
24917     g_x: 50,
24918     g_y: 50,
24919     g_r: 30,
24920     opts:{
24921         //g_colors: this.colors,
24922         g_type: 'soft',
24923         g_gutter: '20%'
24924
24925     },
24926     title : false,
24927
24928     getAutoCreate : function(){
24929         
24930         var cfg = {
24931             tag: 'div',
24932             html : null
24933         };
24934         
24935         
24936         return  cfg;
24937     },
24938
24939     onRender : function(ct,position){
24940         
24941         
24942         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24943         
24944         if (typeof(Raphael) == 'undefined') {
24945             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24946             return;
24947         }
24948         
24949         this.raphael = Raphael(this.el.dom);
24950         
24951                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24952                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24953                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24954                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24955                 /*
24956                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24957                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24958                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24959                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24960                 
24961                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24962                 r.barchart(330, 10, 300, 220, data1);
24963                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24964                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24965                 */
24966                 
24967                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24968                 // r.barchart(30, 30, 560, 250,  xdata, {
24969                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24970                 //     axis : "0 0 1 1",
24971                 //     axisxlabels :  xdata
24972                 //     //yvalues : cols,
24973                    
24974                 // });
24975 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24976 //        
24977 //        this.load(null,xdata,{
24978 //                axis : "0 0 1 1",
24979 //                axisxlabels :  xdata
24980 //                });
24981
24982     },
24983
24984     load : function(graphtype,xdata,opts)
24985     {
24986         this.raphael.clear();
24987         if(!graphtype) {
24988             graphtype = this.graphtype;
24989         }
24990         if(!opts){
24991             opts = this.opts;
24992         }
24993         var r = this.raphael,
24994             fin = function () {
24995                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24996             },
24997             fout = function () {
24998                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24999             },
25000             pfin = function() {
25001                 this.sector.stop();
25002                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25003
25004                 if (this.label) {
25005                     this.label[0].stop();
25006                     this.label[0].attr({ r: 7.5 });
25007                     this.label[1].attr({ "font-weight": 800 });
25008                 }
25009             },
25010             pfout = function() {
25011                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25012
25013                 if (this.label) {
25014                     this.label[0].animate({ r: 5 }, 500, "bounce");
25015                     this.label[1].attr({ "font-weight": 400 });
25016                 }
25017             };
25018
25019         switch(graphtype){
25020             case 'bar':
25021                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25022                 break;
25023             case 'hbar':
25024                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25025                 break;
25026             case 'pie':
25027 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25028 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25029 //            
25030                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25031                 
25032                 break;
25033
25034         }
25035         
25036         if(this.title){
25037             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25038         }
25039         
25040     },
25041     
25042     setTitle: function(o)
25043     {
25044         this.title = o;
25045     },
25046     
25047     initEvents: function() {
25048         
25049         if(!this.href){
25050             this.el.on('click', this.onClick, this);
25051         }
25052     },
25053     
25054     onClick : function(e)
25055     {
25056         Roo.log('img onclick');
25057         this.fireEvent('click', this, e);
25058     }
25059    
25060 });
25061
25062  
25063 /*
25064  * - LGPL
25065  *
25066  * numberBox
25067  * 
25068  */
25069 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25070
25071 /**
25072  * @class Roo.bootstrap.dash.NumberBox
25073  * @extends Roo.bootstrap.Component
25074  * Bootstrap NumberBox class
25075  * @cfg {String} headline Box headline
25076  * @cfg {String} content Box content
25077  * @cfg {String} icon Box icon
25078  * @cfg {String} footer Footer text
25079  * @cfg {String} fhref Footer href
25080  * 
25081  * @constructor
25082  * Create a new NumberBox
25083  * @param {Object} config The config object
25084  */
25085
25086
25087 Roo.bootstrap.dash.NumberBox = function(config){
25088     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25089     
25090 };
25091
25092 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25093     
25094     headline : '',
25095     content : '',
25096     icon : '',
25097     footer : '',
25098     fhref : '',
25099     ficon : '',
25100     
25101     getAutoCreate : function(){
25102         
25103         var cfg = {
25104             tag : 'div',
25105             cls : 'small-box ',
25106             cn : [
25107                 {
25108                     tag : 'div',
25109                     cls : 'inner',
25110                     cn :[
25111                         {
25112                             tag : 'h3',
25113                             cls : 'roo-headline',
25114                             html : this.headline
25115                         },
25116                         {
25117                             tag : 'p',
25118                             cls : 'roo-content',
25119                             html : this.content
25120                         }
25121                     ]
25122                 }
25123             ]
25124         };
25125         
25126         if(this.icon){
25127             cfg.cn.push({
25128                 tag : 'div',
25129                 cls : 'icon',
25130                 cn :[
25131                     {
25132                         tag : 'i',
25133                         cls : 'ion ' + this.icon
25134                     }
25135                 ]
25136             });
25137         }
25138         
25139         if(this.footer){
25140             var footer = {
25141                 tag : 'a',
25142                 cls : 'small-box-footer',
25143                 href : this.fhref || '#',
25144                 html : this.footer
25145             };
25146             
25147             cfg.cn.push(footer);
25148             
25149         }
25150         
25151         return  cfg;
25152     },
25153
25154     onRender : function(ct,position){
25155         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25156
25157
25158        
25159                 
25160     },
25161
25162     setHeadline: function (value)
25163     {
25164         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25165     },
25166     
25167     setFooter: function (value, href)
25168     {
25169         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25170         
25171         if(href){
25172             this.el.select('a.small-box-footer',true).first().attr('href', href);
25173         }
25174         
25175     },
25176
25177     setContent: function (value)
25178     {
25179         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25180     },
25181
25182     initEvents: function() 
25183     {   
25184         
25185     }
25186     
25187 });
25188
25189  
25190 /*
25191  * - LGPL
25192  *
25193  * TabBox
25194  * 
25195  */
25196 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25197
25198 /**
25199  * @class Roo.bootstrap.dash.TabBox
25200  * @extends Roo.bootstrap.Component
25201  * Bootstrap TabBox class
25202  * @cfg {String} title Title of the TabBox
25203  * @cfg {String} icon Icon of the TabBox
25204  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25205  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25206  * 
25207  * @constructor
25208  * Create a new TabBox
25209  * @param {Object} config The config object
25210  */
25211
25212
25213 Roo.bootstrap.dash.TabBox = function(config){
25214     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25215     this.addEvents({
25216         // raw events
25217         /**
25218          * @event addpane
25219          * When a pane is added
25220          * @param {Roo.bootstrap.dash.TabPane} pane
25221          */
25222         "addpane" : true,
25223         /**
25224          * @event activatepane
25225          * When a pane is activated
25226          * @param {Roo.bootstrap.dash.TabPane} pane
25227          */
25228         "activatepane" : true
25229         
25230          
25231     });
25232     
25233     this.panes = [];
25234 };
25235
25236 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25237
25238     title : '',
25239     icon : false,
25240     showtabs : true,
25241     tabScrollable : false,
25242     
25243     getChildContainer : function()
25244     {
25245         return this.el.select('.tab-content', true).first();
25246     },
25247     
25248     getAutoCreate : function(){
25249         
25250         var header = {
25251             tag: 'li',
25252             cls: 'pull-left header',
25253             html: this.title,
25254             cn : []
25255         };
25256         
25257         if(this.icon){
25258             header.cn.push({
25259                 tag: 'i',
25260                 cls: 'fa ' + this.icon
25261             });
25262         }
25263         
25264         var h = {
25265             tag: 'ul',
25266             cls: 'nav nav-tabs pull-right',
25267             cn: [
25268                 header
25269             ]
25270         };
25271         
25272         if(this.tabScrollable){
25273             h = {
25274                 tag: 'div',
25275                 cls: 'tab-header',
25276                 cn: [
25277                     {
25278                         tag: 'ul',
25279                         cls: 'nav nav-tabs pull-right',
25280                         cn: [
25281                             header
25282                         ]
25283                     }
25284                 ]
25285             };
25286         }
25287         
25288         var cfg = {
25289             tag: 'div',
25290             cls: 'nav-tabs-custom',
25291             cn: [
25292                 h,
25293                 {
25294                     tag: 'div',
25295                     cls: 'tab-content no-padding',
25296                     cn: []
25297                 }
25298             ]
25299         };
25300
25301         return  cfg;
25302     },
25303     initEvents : function()
25304     {
25305         //Roo.log('add add pane handler');
25306         this.on('addpane', this.onAddPane, this);
25307     },
25308      /**
25309      * Updates the box title
25310      * @param {String} html to set the title to.
25311      */
25312     setTitle : function(value)
25313     {
25314         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25315     },
25316     onAddPane : function(pane)
25317     {
25318         this.panes.push(pane);
25319         //Roo.log('addpane');
25320         //Roo.log(pane);
25321         // tabs are rendere left to right..
25322         if(!this.showtabs){
25323             return;
25324         }
25325         
25326         var ctr = this.el.select('.nav-tabs', true).first();
25327          
25328          
25329         var existing = ctr.select('.nav-tab',true);
25330         var qty = existing.getCount();;
25331         
25332         
25333         var tab = ctr.createChild({
25334             tag : 'li',
25335             cls : 'nav-tab' + (qty ? '' : ' active'),
25336             cn : [
25337                 {
25338                     tag : 'a',
25339                     href:'#',
25340                     html : pane.title
25341                 }
25342             ]
25343         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25344         pane.tab = tab;
25345         
25346         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25347         if (!qty) {
25348             pane.el.addClass('active');
25349         }
25350         
25351                 
25352     },
25353     onTabClick : function(ev,un,ob,pane)
25354     {
25355         //Roo.log('tab - prev default');
25356         ev.preventDefault();
25357         
25358         
25359         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25360         pane.tab.addClass('active');
25361         //Roo.log(pane.title);
25362         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25363         // technically we should have a deactivate event.. but maybe add later.
25364         // and it should not de-activate the selected tab...
25365         this.fireEvent('activatepane', pane);
25366         pane.el.addClass('active');
25367         pane.fireEvent('activate');
25368         
25369         
25370     },
25371     
25372     getActivePane : function()
25373     {
25374         var r = false;
25375         Roo.each(this.panes, function(p) {
25376             if(p.el.hasClass('active')){
25377                 r = p;
25378                 return false;
25379             }
25380             
25381             return;
25382         });
25383         
25384         return r;
25385     }
25386     
25387     
25388 });
25389
25390  
25391 /*
25392  * - LGPL
25393  *
25394  * Tab pane
25395  * 
25396  */
25397 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25398 /**
25399  * @class Roo.bootstrap.TabPane
25400  * @extends Roo.bootstrap.Component
25401  * Bootstrap TabPane class
25402  * @cfg {Boolean} active (false | true) Default false
25403  * @cfg {String} title title of panel
25404
25405  * 
25406  * @constructor
25407  * Create a new TabPane
25408  * @param {Object} config The config object
25409  */
25410
25411 Roo.bootstrap.dash.TabPane = function(config){
25412     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25413     
25414     this.addEvents({
25415         // raw events
25416         /**
25417          * @event activate
25418          * When a pane is activated
25419          * @param {Roo.bootstrap.dash.TabPane} pane
25420          */
25421         "activate" : true
25422          
25423     });
25424 };
25425
25426 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25427     
25428     active : false,
25429     title : '',
25430     
25431     // the tabBox that this is attached to.
25432     tab : false,
25433      
25434     getAutoCreate : function() 
25435     {
25436         var cfg = {
25437             tag: 'div',
25438             cls: 'tab-pane'
25439         };
25440         
25441         if(this.active){
25442             cfg.cls += ' active';
25443         }
25444         
25445         return cfg;
25446     },
25447     initEvents  : function()
25448     {
25449         //Roo.log('trigger add pane handler');
25450         this.parent().fireEvent('addpane', this)
25451     },
25452     
25453      /**
25454      * Updates the tab title 
25455      * @param {String} html to set the title to.
25456      */
25457     setTitle: function(str)
25458     {
25459         if (!this.tab) {
25460             return;
25461         }
25462         this.title = str;
25463         this.tab.select('a', true).first().dom.innerHTML = str;
25464         
25465     }
25466     
25467     
25468     
25469 });
25470
25471  
25472
25473
25474  /*
25475  * - LGPL
25476  *
25477  * menu
25478  * 
25479  */
25480 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25481
25482 /**
25483  * @class Roo.bootstrap.menu.Menu
25484  * @extends Roo.bootstrap.Component
25485  * Bootstrap Menu class - container for Menu
25486  * @cfg {String} html Text of the menu
25487  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25488  * @cfg {String} icon Font awesome icon
25489  * @cfg {String} pos Menu align to (top | bottom) default bottom
25490  * 
25491  * 
25492  * @constructor
25493  * Create a new Menu
25494  * @param {Object} config The config object
25495  */
25496
25497
25498 Roo.bootstrap.menu.Menu = function(config){
25499     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25500     
25501     this.addEvents({
25502         /**
25503          * @event beforeshow
25504          * Fires before this menu is displayed
25505          * @param {Roo.bootstrap.menu.Menu} this
25506          */
25507         beforeshow : true,
25508         /**
25509          * @event beforehide
25510          * Fires before this menu is hidden
25511          * @param {Roo.bootstrap.menu.Menu} this
25512          */
25513         beforehide : true,
25514         /**
25515          * @event show
25516          * Fires after this menu is displayed
25517          * @param {Roo.bootstrap.menu.Menu} this
25518          */
25519         show : true,
25520         /**
25521          * @event hide
25522          * Fires after this menu is hidden
25523          * @param {Roo.bootstrap.menu.Menu} this
25524          */
25525         hide : true,
25526         /**
25527          * @event click
25528          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25529          * @param {Roo.bootstrap.menu.Menu} this
25530          * @param {Roo.EventObject} e
25531          */
25532         click : true
25533     });
25534     
25535 };
25536
25537 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25538     
25539     submenu : false,
25540     html : '',
25541     weight : 'default',
25542     icon : false,
25543     pos : 'bottom',
25544     
25545     
25546     getChildContainer : function() {
25547         if(this.isSubMenu){
25548             return this.el;
25549         }
25550         
25551         return this.el.select('ul.dropdown-menu', true).first();  
25552     },
25553     
25554     getAutoCreate : function()
25555     {
25556         var text = [
25557             {
25558                 tag : 'span',
25559                 cls : 'roo-menu-text',
25560                 html : this.html
25561             }
25562         ];
25563         
25564         if(this.icon){
25565             text.unshift({
25566                 tag : 'i',
25567                 cls : 'fa ' + this.icon
25568             })
25569         }
25570         
25571         
25572         var cfg = {
25573             tag : 'div',
25574             cls : 'btn-group',
25575             cn : [
25576                 {
25577                     tag : 'button',
25578                     cls : 'dropdown-button btn btn-' + this.weight,
25579                     cn : text
25580                 },
25581                 {
25582                     tag : 'button',
25583                     cls : 'dropdown-toggle btn btn-' + this.weight,
25584                     cn : [
25585                         {
25586                             tag : 'span',
25587                             cls : 'caret'
25588                         }
25589                     ]
25590                 },
25591                 {
25592                     tag : 'ul',
25593                     cls : 'dropdown-menu'
25594                 }
25595             ]
25596             
25597         };
25598         
25599         if(this.pos == 'top'){
25600             cfg.cls += ' dropup';
25601         }
25602         
25603         if(this.isSubMenu){
25604             cfg = {
25605                 tag : 'ul',
25606                 cls : 'dropdown-menu'
25607             }
25608         }
25609         
25610         return cfg;
25611     },
25612     
25613     onRender : function(ct, position)
25614     {
25615         this.isSubMenu = ct.hasClass('dropdown-submenu');
25616         
25617         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25618     },
25619     
25620     initEvents : function() 
25621     {
25622         if(this.isSubMenu){
25623             return;
25624         }
25625         
25626         this.hidden = true;
25627         
25628         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25629         this.triggerEl.on('click', this.onTriggerPress, this);
25630         
25631         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25632         this.buttonEl.on('click', this.onClick, this);
25633         
25634     },
25635     
25636     list : function()
25637     {
25638         if(this.isSubMenu){
25639             return this.el;
25640         }
25641         
25642         return this.el.select('ul.dropdown-menu', true).first();
25643     },
25644     
25645     onClick : function(e)
25646     {
25647         this.fireEvent("click", this, e);
25648     },
25649     
25650     onTriggerPress  : function(e)
25651     {   
25652         if (this.isVisible()) {
25653             this.hide();
25654         } else {
25655             this.show();
25656         }
25657     },
25658     
25659     isVisible : function(){
25660         return !this.hidden;
25661     },
25662     
25663     show : function()
25664     {
25665         this.fireEvent("beforeshow", this);
25666         
25667         this.hidden = false;
25668         this.el.addClass('open');
25669         
25670         Roo.get(document).on("mouseup", this.onMouseUp, this);
25671         
25672         this.fireEvent("show", this);
25673         
25674         
25675     },
25676     
25677     hide : function()
25678     {
25679         this.fireEvent("beforehide", this);
25680         
25681         this.hidden = true;
25682         this.el.removeClass('open');
25683         
25684         Roo.get(document).un("mouseup", this.onMouseUp);
25685         
25686         this.fireEvent("hide", this);
25687     },
25688     
25689     onMouseUp : function()
25690     {
25691         this.hide();
25692     }
25693     
25694 });
25695
25696  
25697  /*
25698  * - LGPL
25699  *
25700  * menu item
25701  * 
25702  */
25703 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25704
25705 /**
25706  * @class Roo.bootstrap.menu.Item
25707  * @extends Roo.bootstrap.Component
25708  * Bootstrap MenuItem class
25709  * @cfg {Boolean} submenu (true | false) default false
25710  * @cfg {String} html text of the item
25711  * @cfg {String} href the link
25712  * @cfg {Boolean} disable (true | false) default false
25713  * @cfg {Boolean} preventDefault (true | false) default true
25714  * @cfg {String} icon Font awesome icon
25715  * @cfg {String} pos Submenu align to (left | right) default right 
25716  * 
25717  * 
25718  * @constructor
25719  * Create a new Item
25720  * @param {Object} config The config object
25721  */
25722
25723
25724 Roo.bootstrap.menu.Item = function(config){
25725     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25726     this.addEvents({
25727         /**
25728          * @event mouseover
25729          * Fires when the mouse is hovering over this menu
25730          * @param {Roo.bootstrap.menu.Item} this
25731          * @param {Roo.EventObject} e
25732          */
25733         mouseover : true,
25734         /**
25735          * @event mouseout
25736          * Fires when the mouse exits this menu
25737          * @param {Roo.bootstrap.menu.Item} this
25738          * @param {Roo.EventObject} e
25739          */
25740         mouseout : true,
25741         // raw events
25742         /**
25743          * @event click
25744          * The raw click event for the entire grid.
25745          * @param {Roo.EventObject} e
25746          */
25747         click : true
25748     });
25749 };
25750
25751 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25752     
25753     submenu : false,
25754     href : '',
25755     html : '',
25756     preventDefault: true,
25757     disable : false,
25758     icon : false,
25759     pos : 'right',
25760     
25761     getAutoCreate : function()
25762     {
25763         var text = [
25764             {
25765                 tag : 'span',
25766                 cls : 'roo-menu-item-text',
25767                 html : this.html
25768             }
25769         ];
25770         
25771         if(this.icon){
25772             text.unshift({
25773                 tag : 'i',
25774                 cls : 'fa ' + this.icon
25775             })
25776         }
25777         
25778         var cfg = {
25779             tag : 'li',
25780             cn : [
25781                 {
25782                     tag : 'a',
25783                     href : this.href || '#',
25784                     cn : text
25785                 }
25786             ]
25787         };
25788         
25789         if(this.disable){
25790             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25791         }
25792         
25793         if(this.submenu){
25794             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25795             
25796             if(this.pos == 'left'){
25797                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25798             }
25799         }
25800         
25801         return cfg;
25802     },
25803     
25804     initEvents : function() 
25805     {
25806         this.el.on('mouseover', this.onMouseOver, this);
25807         this.el.on('mouseout', this.onMouseOut, this);
25808         
25809         this.el.select('a', true).first().on('click', this.onClick, this);
25810         
25811     },
25812     
25813     onClick : function(e)
25814     {
25815         if(this.preventDefault){
25816             e.preventDefault();
25817         }
25818         
25819         this.fireEvent("click", this, e);
25820     },
25821     
25822     onMouseOver : function(e)
25823     {
25824         if(this.submenu && this.pos == 'left'){
25825             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25826         }
25827         
25828         this.fireEvent("mouseover", this, e);
25829     },
25830     
25831     onMouseOut : function(e)
25832     {
25833         this.fireEvent("mouseout", this, e);
25834     }
25835 });
25836
25837  
25838
25839  /*
25840  * - LGPL
25841  *
25842  * menu separator
25843  * 
25844  */
25845 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25846
25847 /**
25848  * @class Roo.bootstrap.menu.Separator
25849  * @extends Roo.bootstrap.Component
25850  * Bootstrap Separator class
25851  * 
25852  * @constructor
25853  * Create a new Separator
25854  * @param {Object} config The config object
25855  */
25856
25857
25858 Roo.bootstrap.menu.Separator = function(config){
25859     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25860 };
25861
25862 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25863     
25864     getAutoCreate : function(){
25865         var cfg = {
25866             tag : 'li',
25867             cls: 'divider'
25868         };
25869         
25870         return cfg;
25871     }
25872    
25873 });
25874
25875  
25876
25877  /*
25878  * - LGPL
25879  *
25880  * Tooltip
25881  * 
25882  */
25883
25884 /**
25885  * @class Roo.bootstrap.Tooltip
25886  * Bootstrap Tooltip class
25887  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25888  * to determine which dom element triggers the tooltip.
25889  * 
25890  * It needs to add support for additional attributes like tooltip-position
25891  * 
25892  * @constructor
25893  * Create a new Toolti
25894  * @param {Object} config The config object
25895  */
25896
25897 Roo.bootstrap.Tooltip = function(config){
25898     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25899     
25900     this.alignment = Roo.bootstrap.Tooltip.alignment;
25901     
25902     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25903         this.alignment = config.alignment;
25904     }
25905     
25906 };
25907
25908 Roo.apply(Roo.bootstrap.Tooltip, {
25909     /**
25910      * @function init initialize tooltip monitoring.
25911      * @static
25912      */
25913     currentEl : false,
25914     currentTip : false,
25915     currentRegion : false,
25916     
25917     //  init : delay?
25918     
25919     init : function()
25920     {
25921         Roo.get(document).on('mouseover', this.enter ,this);
25922         Roo.get(document).on('mouseout', this.leave, this);
25923          
25924         
25925         this.currentTip = new Roo.bootstrap.Tooltip();
25926     },
25927     
25928     enter : function(ev)
25929     {
25930         var dom = ev.getTarget();
25931         
25932         //Roo.log(['enter',dom]);
25933         var el = Roo.fly(dom);
25934         if (this.currentEl) {
25935             //Roo.log(dom);
25936             //Roo.log(this.currentEl);
25937             //Roo.log(this.currentEl.contains(dom));
25938             if (this.currentEl == el) {
25939                 return;
25940             }
25941             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25942                 return;
25943             }
25944
25945         }
25946         
25947         if (this.currentTip.el) {
25948             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25949         }    
25950         //Roo.log(ev);
25951         
25952         if(!el || el.dom == document){
25953             return;
25954         }
25955         
25956         var bindEl = el;
25957         
25958         // you can not look for children, as if el is the body.. then everythign is the child..
25959         if (!el.attr('tooltip')) { //
25960             if (!el.select("[tooltip]").elements.length) {
25961                 return;
25962             }
25963             // is the mouse over this child...?
25964             bindEl = el.select("[tooltip]").first();
25965             var xy = ev.getXY();
25966             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25967                 //Roo.log("not in region.");
25968                 return;
25969             }
25970             //Roo.log("child element over..");
25971             
25972         }
25973         this.currentEl = bindEl;
25974         this.currentTip.bind(bindEl);
25975         this.currentRegion = Roo.lib.Region.getRegion(dom);
25976         this.currentTip.enter();
25977         
25978     },
25979     leave : function(ev)
25980     {
25981         var dom = ev.getTarget();
25982         //Roo.log(['leave',dom]);
25983         if (!this.currentEl) {
25984             return;
25985         }
25986         
25987         
25988         if (dom != this.currentEl.dom) {
25989             return;
25990         }
25991         var xy = ev.getXY();
25992         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25993             return;
25994         }
25995         // only activate leave if mouse cursor is outside... bounding box..
25996         
25997         
25998         
25999         
26000         if (this.currentTip) {
26001             this.currentTip.leave();
26002         }
26003         //Roo.log('clear currentEl');
26004         this.currentEl = false;
26005         
26006         
26007     },
26008     alignment : {
26009         'left' : ['r-l', [-2,0], 'right'],
26010         'right' : ['l-r', [2,0], 'left'],
26011         'bottom' : ['t-b', [0,2], 'top'],
26012         'top' : [ 'b-t', [0,-2], 'bottom']
26013     }
26014     
26015 });
26016
26017
26018 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26019     
26020     
26021     bindEl : false,
26022     
26023     delay : null, // can be { show : 300 , hide: 500}
26024     
26025     timeout : null,
26026     
26027     hoverState : null, //???
26028     
26029     placement : 'bottom', 
26030     
26031     alignment : false,
26032     
26033     getAutoCreate : function(){
26034     
26035         var cfg = {
26036            cls : 'tooltip',
26037            role : 'tooltip',
26038            cn : [
26039                 {
26040                     cls : 'tooltip-arrow'
26041                 },
26042                 {
26043                     cls : 'tooltip-inner'
26044                 }
26045            ]
26046         };
26047         
26048         return cfg;
26049     },
26050     bind : function(el)
26051     {
26052         this.bindEl = el;
26053     },
26054       
26055     
26056     enter : function () {
26057        
26058         if (this.timeout != null) {
26059             clearTimeout(this.timeout);
26060         }
26061         
26062         this.hoverState = 'in';
26063          //Roo.log("enter - show");
26064         if (!this.delay || !this.delay.show) {
26065             this.show();
26066             return;
26067         }
26068         var _t = this;
26069         this.timeout = setTimeout(function () {
26070             if (_t.hoverState == 'in') {
26071                 _t.show();
26072             }
26073         }, this.delay.show);
26074     },
26075     leave : function()
26076     {
26077         clearTimeout(this.timeout);
26078     
26079         this.hoverState = 'out';
26080          if (!this.delay || !this.delay.hide) {
26081             this.hide();
26082             return;
26083         }
26084        
26085         var _t = this;
26086         this.timeout = setTimeout(function () {
26087             //Roo.log("leave - timeout");
26088             
26089             if (_t.hoverState == 'out') {
26090                 _t.hide();
26091                 Roo.bootstrap.Tooltip.currentEl = false;
26092             }
26093         }, delay);
26094     },
26095     
26096     show : function (msg)
26097     {
26098         if (!this.el) {
26099             this.render(document.body);
26100         }
26101         // set content.
26102         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26103         
26104         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26105         
26106         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26107         
26108         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26109         
26110         var placement = typeof this.placement == 'function' ?
26111             this.placement.call(this, this.el, on_el) :
26112             this.placement;
26113             
26114         var autoToken = /\s?auto?\s?/i;
26115         var autoPlace = autoToken.test(placement);
26116         if (autoPlace) {
26117             placement = placement.replace(autoToken, '') || 'top';
26118         }
26119         
26120         //this.el.detach()
26121         //this.el.setXY([0,0]);
26122         this.el.show();
26123         //this.el.dom.style.display='block';
26124         
26125         //this.el.appendTo(on_el);
26126         
26127         var p = this.getPosition();
26128         var box = this.el.getBox();
26129         
26130         if (autoPlace) {
26131             // fixme..
26132         }
26133         
26134         var align = this.alignment[placement];
26135         
26136         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26137         
26138         if(placement == 'top' || placement == 'bottom'){
26139             if(xy[0] < 0){
26140                 placement = 'right';
26141             }
26142             
26143             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26144                 placement = 'left';
26145             }
26146             
26147             var scroll = Roo.select('body', true).first().getScroll();
26148             
26149             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26150                 placement = 'top';
26151             }
26152             
26153         }
26154         
26155         this.el.alignTo(this.bindEl, align[0],align[1]);
26156         //var arrow = this.el.select('.arrow',true).first();
26157         //arrow.set(align[2], 
26158         
26159         this.el.addClass(placement);
26160         
26161         this.el.addClass('in fade');
26162         
26163         this.hoverState = null;
26164         
26165         if (this.el.hasClass('fade')) {
26166             // fade it?
26167         }
26168         
26169     },
26170     hide : function()
26171     {
26172          
26173         if (!this.el) {
26174             return;
26175         }
26176         //this.el.setXY([0,0]);
26177         this.el.removeClass('in');
26178         //this.el.hide();
26179         
26180     }
26181     
26182 });
26183  
26184
26185  /*
26186  * - LGPL
26187  *
26188  * Location Picker
26189  * 
26190  */
26191
26192 /**
26193  * @class Roo.bootstrap.LocationPicker
26194  * @extends Roo.bootstrap.Component
26195  * Bootstrap LocationPicker class
26196  * @cfg {Number} latitude Position when init default 0
26197  * @cfg {Number} longitude Position when init default 0
26198  * @cfg {Number} zoom default 15
26199  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26200  * @cfg {Boolean} mapTypeControl default false
26201  * @cfg {Boolean} disableDoubleClickZoom default false
26202  * @cfg {Boolean} scrollwheel default true
26203  * @cfg {Boolean} streetViewControl default false
26204  * @cfg {Number} radius default 0
26205  * @cfg {String} locationName
26206  * @cfg {Boolean} draggable default true
26207  * @cfg {Boolean} enableAutocomplete default false
26208  * @cfg {Boolean} enableReverseGeocode default true
26209  * @cfg {String} markerTitle
26210  * 
26211  * @constructor
26212  * Create a new LocationPicker
26213  * @param {Object} config The config object
26214  */
26215
26216
26217 Roo.bootstrap.LocationPicker = function(config){
26218     
26219     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26220     
26221     this.addEvents({
26222         /**
26223          * @event initial
26224          * Fires when the picker initialized.
26225          * @param {Roo.bootstrap.LocationPicker} this
26226          * @param {Google Location} location
26227          */
26228         initial : true,
26229         /**
26230          * @event positionchanged
26231          * Fires when the picker position changed.
26232          * @param {Roo.bootstrap.LocationPicker} this
26233          * @param {Google Location} location
26234          */
26235         positionchanged : true,
26236         /**
26237          * @event resize
26238          * Fires when the map resize.
26239          * @param {Roo.bootstrap.LocationPicker} this
26240          */
26241         resize : true,
26242         /**
26243          * @event show
26244          * Fires when the map show.
26245          * @param {Roo.bootstrap.LocationPicker} this
26246          */
26247         show : true,
26248         /**
26249          * @event hide
26250          * Fires when the map hide.
26251          * @param {Roo.bootstrap.LocationPicker} this
26252          */
26253         hide : true,
26254         /**
26255          * @event mapClick
26256          * Fires when click the map.
26257          * @param {Roo.bootstrap.LocationPicker} this
26258          * @param {Map event} e
26259          */
26260         mapClick : true,
26261         /**
26262          * @event mapRightClick
26263          * Fires when right click the map.
26264          * @param {Roo.bootstrap.LocationPicker} this
26265          * @param {Map event} e
26266          */
26267         mapRightClick : true,
26268         /**
26269          * @event markerClick
26270          * Fires when click the marker.
26271          * @param {Roo.bootstrap.LocationPicker} this
26272          * @param {Map event} e
26273          */
26274         markerClick : true,
26275         /**
26276          * @event markerRightClick
26277          * Fires when right click the marker.
26278          * @param {Roo.bootstrap.LocationPicker} this
26279          * @param {Map event} e
26280          */
26281         markerRightClick : true,
26282         /**
26283          * @event OverlayViewDraw
26284          * Fires when OverlayView Draw
26285          * @param {Roo.bootstrap.LocationPicker} this
26286          */
26287         OverlayViewDraw : true,
26288         /**
26289          * @event OverlayViewOnAdd
26290          * Fires when OverlayView Draw
26291          * @param {Roo.bootstrap.LocationPicker} this
26292          */
26293         OverlayViewOnAdd : true,
26294         /**
26295          * @event OverlayViewOnRemove
26296          * Fires when OverlayView Draw
26297          * @param {Roo.bootstrap.LocationPicker} this
26298          */
26299         OverlayViewOnRemove : true,
26300         /**
26301          * @event OverlayViewShow
26302          * Fires when OverlayView Draw
26303          * @param {Roo.bootstrap.LocationPicker} this
26304          * @param {Pixel} cpx
26305          */
26306         OverlayViewShow : true,
26307         /**
26308          * @event OverlayViewHide
26309          * Fires when OverlayView Draw
26310          * @param {Roo.bootstrap.LocationPicker} this
26311          */
26312         OverlayViewHide : true,
26313         /**
26314          * @event loadexception
26315          * Fires when load google lib failed.
26316          * @param {Roo.bootstrap.LocationPicker} this
26317          */
26318         loadexception : true
26319     });
26320         
26321 };
26322
26323 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26324     
26325     gMapContext: false,
26326     
26327     latitude: 0,
26328     longitude: 0,
26329     zoom: 15,
26330     mapTypeId: false,
26331     mapTypeControl: false,
26332     disableDoubleClickZoom: false,
26333     scrollwheel: true,
26334     streetViewControl: false,
26335     radius: 0,
26336     locationName: '',
26337     draggable: true,
26338     enableAutocomplete: false,
26339     enableReverseGeocode: true,
26340     markerTitle: '',
26341     
26342     getAutoCreate: function()
26343     {
26344
26345         var cfg = {
26346             tag: 'div',
26347             cls: 'roo-location-picker'
26348         };
26349         
26350         return cfg
26351     },
26352     
26353     initEvents: function(ct, position)
26354     {       
26355         if(!this.el.getWidth() || this.isApplied()){
26356             return;
26357         }
26358         
26359         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26360         
26361         this.initial();
26362     },
26363     
26364     initial: function()
26365     {
26366         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26367             this.fireEvent('loadexception', this);
26368             return;
26369         }
26370         
26371         if(!this.mapTypeId){
26372             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26373         }
26374         
26375         this.gMapContext = this.GMapContext();
26376         
26377         this.initOverlayView();
26378         
26379         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26380         
26381         var _this = this;
26382                 
26383         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26384             _this.setPosition(_this.gMapContext.marker.position);
26385         });
26386         
26387         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26388             _this.fireEvent('mapClick', this, event);
26389             
26390         });
26391
26392         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26393             _this.fireEvent('mapRightClick', this, event);
26394             
26395         });
26396         
26397         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26398             _this.fireEvent('markerClick', this, event);
26399             
26400         });
26401
26402         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26403             _this.fireEvent('markerRightClick', this, event);
26404             
26405         });
26406         
26407         this.setPosition(this.gMapContext.location);
26408         
26409         this.fireEvent('initial', this, this.gMapContext.location);
26410     },
26411     
26412     initOverlayView: function()
26413     {
26414         var _this = this;
26415         
26416         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26417             
26418             draw: function()
26419             {
26420                 _this.fireEvent('OverlayViewDraw', _this);
26421             },
26422             
26423             onAdd: function()
26424             {
26425                 _this.fireEvent('OverlayViewOnAdd', _this);
26426             },
26427             
26428             onRemove: function()
26429             {
26430                 _this.fireEvent('OverlayViewOnRemove', _this);
26431             },
26432             
26433             show: function(cpx)
26434             {
26435                 _this.fireEvent('OverlayViewShow', _this, cpx);
26436             },
26437             
26438             hide: function()
26439             {
26440                 _this.fireEvent('OverlayViewHide', _this);
26441             }
26442             
26443         });
26444     },
26445     
26446     fromLatLngToContainerPixel: function(event)
26447     {
26448         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26449     },
26450     
26451     isApplied: function() 
26452     {
26453         return this.getGmapContext() == false ? false : true;
26454     },
26455     
26456     getGmapContext: function() 
26457     {
26458         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26459     },
26460     
26461     GMapContext: function() 
26462     {
26463         var position = new google.maps.LatLng(this.latitude, this.longitude);
26464         
26465         var _map = new google.maps.Map(this.el.dom, {
26466             center: position,
26467             zoom: this.zoom,
26468             mapTypeId: this.mapTypeId,
26469             mapTypeControl: this.mapTypeControl,
26470             disableDoubleClickZoom: this.disableDoubleClickZoom,
26471             scrollwheel: this.scrollwheel,
26472             streetViewControl: this.streetViewControl,
26473             locationName: this.locationName,
26474             draggable: this.draggable,
26475             enableAutocomplete: this.enableAutocomplete,
26476             enableReverseGeocode: this.enableReverseGeocode
26477         });
26478         
26479         var _marker = new google.maps.Marker({
26480             position: position,
26481             map: _map,
26482             title: this.markerTitle,
26483             draggable: this.draggable
26484         });
26485         
26486         return {
26487             map: _map,
26488             marker: _marker,
26489             circle: null,
26490             location: position,
26491             radius: this.radius,
26492             locationName: this.locationName,
26493             addressComponents: {
26494                 formatted_address: null,
26495                 addressLine1: null,
26496                 addressLine2: null,
26497                 streetName: null,
26498                 streetNumber: null,
26499                 city: null,
26500                 district: null,
26501                 state: null,
26502                 stateOrProvince: null
26503             },
26504             settings: this,
26505             domContainer: this.el.dom,
26506             geodecoder: new google.maps.Geocoder()
26507         };
26508     },
26509     
26510     drawCircle: function(center, radius, options) 
26511     {
26512         if (this.gMapContext.circle != null) {
26513             this.gMapContext.circle.setMap(null);
26514         }
26515         if (radius > 0) {
26516             radius *= 1;
26517             options = Roo.apply({}, options, {
26518                 strokeColor: "#0000FF",
26519                 strokeOpacity: .35,
26520                 strokeWeight: 2,
26521                 fillColor: "#0000FF",
26522                 fillOpacity: .2
26523             });
26524             
26525             options.map = this.gMapContext.map;
26526             options.radius = radius;
26527             options.center = center;
26528             this.gMapContext.circle = new google.maps.Circle(options);
26529             return this.gMapContext.circle;
26530         }
26531         
26532         return null;
26533     },
26534     
26535     setPosition: function(location) 
26536     {
26537         this.gMapContext.location = location;
26538         this.gMapContext.marker.setPosition(location);
26539         this.gMapContext.map.panTo(location);
26540         this.drawCircle(location, this.gMapContext.radius, {});
26541         
26542         var _this = this;
26543         
26544         if (this.gMapContext.settings.enableReverseGeocode) {
26545             this.gMapContext.geodecoder.geocode({
26546                 latLng: this.gMapContext.location
26547             }, function(results, status) {
26548                 
26549                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26550                     _this.gMapContext.locationName = results[0].formatted_address;
26551                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26552                     
26553                     _this.fireEvent('positionchanged', this, location);
26554                 }
26555             });
26556             
26557             return;
26558         }
26559         
26560         this.fireEvent('positionchanged', this, location);
26561     },
26562     
26563     resize: function()
26564     {
26565         google.maps.event.trigger(this.gMapContext.map, "resize");
26566         
26567         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26568         
26569         this.fireEvent('resize', this);
26570     },
26571     
26572     setPositionByLatLng: function(latitude, longitude)
26573     {
26574         this.setPosition(new google.maps.LatLng(latitude, longitude));
26575     },
26576     
26577     getCurrentPosition: function() 
26578     {
26579         return {
26580             latitude: this.gMapContext.location.lat(),
26581             longitude: this.gMapContext.location.lng()
26582         };
26583     },
26584     
26585     getAddressName: function() 
26586     {
26587         return this.gMapContext.locationName;
26588     },
26589     
26590     getAddressComponents: function() 
26591     {
26592         return this.gMapContext.addressComponents;
26593     },
26594     
26595     address_component_from_google_geocode: function(address_components) 
26596     {
26597         var result = {};
26598         
26599         for (var i = 0; i < address_components.length; i++) {
26600             var component = address_components[i];
26601             if (component.types.indexOf("postal_code") >= 0) {
26602                 result.postalCode = component.short_name;
26603             } else if (component.types.indexOf("street_number") >= 0) {
26604                 result.streetNumber = component.short_name;
26605             } else if (component.types.indexOf("route") >= 0) {
26606                 result.streetName = component.short_name;
26607             } else if (component.types.indexOf("neighborhood") >= 0) {
26608                 result.city = component.short_name;
26609             } else if (component.types.indexOf("locality") >= 0) {
26610                 result.city = component.short_name;
26611             } else if (component.types.indexOf("sublocality") >= 0) {
26612                 result.district = component.short_name;
26613             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26614                 result.stateOrProvince = component.short_name;
26615             } else if (component.types.indexOf("country") >= 0) {
26616                 result.country = component.short_name;
26617             }
26618         }
26619         
26620         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26621         result.addressLine2 = "";
26622         return result;
26623     },
26624     
26625     setZoomLevel: function(zoom)
26626     {
26627         this.gMapContext.map.setZoom(zoom);
26628     },
26629     
26630     show: function()
26631     {
26632         if(!this.el){
26633             return;
26634         }
26635         
26636         this.el.show();
26637         
26638         this.resize();
26639         
26640         this.fireEvent('show', this);
26641     },
26642     
26643     hide: function()
26644     {
26645         if(!this.el){
26646             return;
26647         }
26648         
26649         this.el.hide();
26650         
26651         this.fireEvent('hide', this);
26652     }
26653     
26654 });
26655
26656 Roo.apply(Roo.bootstrap.LocationPicker, {
26657     
26658     OverlayView : function(map, options)
26659     {
26660         options = options || {};
26661         
26662         this.setMap(map);
26663     }
26664     
26665     
26666 });/*
26667  * - LGPL
26668  *
26669  * Alert
26670  * 
26671  */
26672
26673 /**
26674  * @class Roo.bootstrap.Alert
26675  * @extends Roo.bootstrap.Component
26676  * Bootstrap Alert class
26677  * @cfg {String} title The title of alert
26678  * @cfg {String} html The content of alert
26679  * @cfg {String} weight (  success | info | warning | danger )
26680  * @cfg {String} faicon font-awesomeicon
26681  * 
26682  * @constructor
26683  * Create a new alert
26684  * @param {Object} config The config object
26685  */
26686
26687
26688 Roo.bootstrap.Alert = function(config){
26689     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26690     
26691 };
26692
26693 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26694     
26695     title: '',
26696     html: '',
26697     weight: false,
26698     faicon: false,
26699     
26700     getAutoCreate : function()
26701     {
26702         
26703         var cfg = {
26704             tag : 'div',
26705             cls : 'alert',
26706             cn : [
26707                 {
26708                     tag : 'i',
26709                     cls : 'roo-alert-icon'
26710                     
26711                 },
26712                 {
26713                     tag : 'b',
26714                     cls : 'roo-alert-title',
26715                     html : this.title
26716                 },
26717                 {
26718                     tag : 'span',
26719                     cls : 'roo-alert-text',
26720                     html : this.html
26721                 }
26722             ]
26723         };
26724         
26725         if(this.faicon){
26726             cfg.cn[0].cls += ' fa ' + this.faicon;
26727         }
26728         
26729         if(this.weight){
26730             cfg.cls += ' alert-' + this.weight;
26731         }
26732         
26733         return cfg;
26734     },
26735     
26736     initEvents: function() 
26737     {
26738         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26739     },
26740     
26741     setTitle : function(str)
26742     {
26743         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26744     },
26745     
26746     setText : function(str)
26747     {
26748         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26749     },
26750     
26751     setWeight : function(weight)
26752     {
26753         if(this.weight){
26754             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26755         }
26756         
26757         this.weight = weight;
26758         
26759         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26760     },
26761     
26762     setIcon : function(icon)
26763     {
26764         if(this.faicon){
26765             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26766         }
26767         
26768         this.faicon = icon;
26769         
26770         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26771     },
26772     
26773     hide: function() 
26774     {
26775         this.el.hide();   
26776     },
26777     
26778     show: function() 
26779     {  
26780         this.el.show();   
26781     }
26782     
26783 });
26784
26785  
26786 /*
26787 * Licence: LGPL
26788 */
26789
26790 /**
26791  * @class Roo.bootstrap.UploadCropbox
26792  * @extends Roo.bootstrap.Component
26793  * Bootstrap UploadCropbox class
26794  * @cfg {String} emptyText show when image has been loaded
26795  * @cfg {String} rotateNotify show when image too small to rotate
26796  * @cfg {Number} errorTimeout default 3000
26797  * @cfg {Number} minWidth default 300
26798  * @cfg {Number} minHeight default 300
26799  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26800  * @cfg {Boolean} isDocument (true|false) default false
26801  * @cfg {String} url action url
26802  * @cfg {String} paramName default 'imageUpload'
26803  * @cfg {String} method default POST
26804  * @cfg {Boolean} loadMask (true|false) default true
26805  * @cfg {Boolean} loadingText default 'Loading...'
26806  * 
26807  * @constructor
26808  * Create a new UploadCropbox
26809  * @param {Object} config The config object
26810  */
26811
26812 Roo.bootstrap.UploadCropbox = function(config){
26813     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26814     
26815     this.addEvents({
26816         /**
26817          * @event beforeselectfile
26818          * Fire before select file
26819          * @param {Roo.bootstrap.UploadCropbox} this
26820          */
26821         "beforeselectfile" : true,
26822         /**
26823          * @event initial
26824          * Fire after initEvent
26825          * @param {Roo.bootstrap.UploadCropbox} this
26826          */
26827         "initial" : true,
26828         /**
26829          * @event crop
26830          * Fire after initEvent
26831          * @param {Roo.bootstrap.UploadCropbox} this
26832          * @param {String} data
26833          */
26834         "crop" : true,
26835         /**
26836          * @event prepare
26837          * Fire when preparing the file data
26838          * @param {Roo.bootstrap.UploadCropbox} this
26839          * @param {Object} file
26840          */
26841         "prepare" : true,
26842         /**
26843          * @event exception
26844          * Fire when get exception
26845          * @param {Roo.bootstrap.UploadCropbox} this
26846          * @param {XMLHttpRequest} xhr
26847          */
26848         "exception" : true,
26849         /**
26850          * @event beforeloadcanvas
26851          * Fire before load the canvas
26852          * @param {Roo.bootstrap.UploadCropbox} this
26853          * @param {String} src
26854          */
26855         "beforeloadcanvas" : true,
26856         /**
26857          * @event trash
26858          * Fire when trash image
26859          * @param {Roo.bootstrap.UploadCropbox} this
26860          */
26861         "trash" : true,
26862         /**
26863          * @event download
26864          * Fire when download the image
26865          * @param {Roo.bootstrap.UploadCropbox} this
26866          */
26867         "download" : true,
26868         /**
26869          * @event footerbuttonclick
26870          * Fire when footerbuttonclick
26871          * @param {Roo.bootstrap.UploadCropbox} this
26872          * @param {String} type
26873          */
26874         "footerbuttonclick" : true,
26875         /**
26876          * @event resize
26877          * Fire when resize
26878          * @param {Roo.bootstrap.UploadCropbox} this
26879          */
26880         "resize" : true,
26881         /**
26882          * @event rotate
26883          * Fire when rotate the image
26884          * @param {Roo.bootstrap.UploadCropbox} this
26885          * @param {String} pos
26886          */
26887         "rotate" : true,
26888         /**
26889          * @event inspect
26890          * Fire when inspect the file
26891          * @param {Roo.bootstrap.UploadCropbox} this
26892          * @param {Object} file
26893          */
26894         "inspect" : true,
26895         /**
26896          * @event upload
26897          * Fire when xhr upload the file
26898          * @param {Roo.bootstrap.UploadCropbox} this
26899          * @param {Object} data
26900          */
26901         "upload" : true,
26902         /**
26903          * @event arrange
26904          * Fire when arrange the file data
26905          * @param {Roo.bootstrap.UploadCropbox} this
26906          * @param {Object} formData
26907          */
26908         "arrange" : true
26909     });
26910     
26911     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26912 };
26913
26914 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26915     
26916     emptyText : 'Click to upload image',
26917     rotateNotify : 'Image is too small to rotate',
26918     errorTimeout : 3000,
26919     scale : 0,
26920     baseScale : 1,
26921     rotate : 0,
26922     dragable : false,
26923     pinching : false,
26924     mouseX : 0,
26925     mouseY : 0,
26926     cropData : false,
26927     minWidth : 300,
26928     minHeight : 300,
26929     file : false,
26930     exif : {},
26931     baseRotate : 1,
26932     cropType : 'image/jpeg',
26933     buttons : false,
26934     canvasLoaded : false,
26935     isDocument : false,
26936     method : 'POST',
26937     paramName : 'imageUpload',
26938     loadMask : true,
26939     loadingText : 'Loading...',
26940     maskEl : false,
26941     
26942     getAutoCreate : function()
26943     {
26944         var cfg = {
26945             tag : 'div',
26946             cls : 'roo-upload-cropbox',
26947             cn : [
26948                 {
26949                     tag : 'input',
26950                     cls : 'roo-upload-cropbox-selector',
26951                     type : 'file'
26952                 },
26953                 {
26954                     tag : 'div',
26955                     cls : 'roo-upload-cropbox-body',
26956                     style : 'cursor:pointer',
26957                     cn : [
26958                         {
26959                             tag : 'div',
26960                             cls : 'roo-upload-cropbox-preview'
26961                         },
26962                         {
26963                             tag : 'div',
26964                             cls : 'roo-upload-cropbox-thumb'
26965                         },
26966                         {
26967                             tag : 'div',
26968                             cls : 'roo-upload-cropbox-empty-notify',
26969                             html : this.emptyText
26970                         },
26971                         {
26972                             tag : 'div',
26973                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26974                             html : this.rotateNotify
26975                         }
26976                     ]
26977                 },
26978                 {
26979                     tag : 'div',
26980                     cls : 'roo-upload-cropbox-footer',
26981                     cn : {
26982                         tag : 'div',
26983                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26984                         cn : []
26985                     }
26986                 }
26987             ]
26988         };
26989         
26990         return cfg;
26991     },
26992     
26993     onRender : function(ct, position)
26994     {
26995         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26996         
26997         if (this.buttons.length) {
26998             
26999             Roo.each(this.buttons, function(bb) {
27000                 
27001                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27002                 
27003                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27004                 
27005             }, this);
27006         }
27007         
27008         if(this.loadMask){
27009             this.maskEl = this.el;
27010         }
27011     },
27012     
27013     initEvents : function()
27014     {
27015         this.urlAPI = (window.createObjectURL && window) || 
27016                                 (window.URL && URL.revokeObjectURL && URL) || 
27017                                 (window.webkitURL && webkitURL);
27018                         
27019         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27020         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27021         
27022         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27023         this.selectorEl.hide();
27024         
27025         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27026         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27027         
27028         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27029         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27030         this.thumbEl.hide();
27031         
27032         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27033         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27034         
27035         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27036         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27037         this.errorEl.hide();
27038         
27039         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27040         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27041         this.footerEl.hide();
27042         
27043         this.setThumbBoxSize();
27044         
27045         this.bind();
27046         
27047         this.resize();
27048         
27049         this.fireEvent('initial', this);
27050     },
27051
27052     bind : function()
27053     {
27054         var _this = this;
27055         
27056         window.addEventListener("resize", function() { _this.resize(); } );
27057         
27058         this.bodyEl.on('click', this.beforeSelectFile, this);
27059         
27060         if(Roo.isTouch){
27061             this.bodyEl.on('touchstart', this.onTouchStart, this);
27062             this.bodyEl.on('touchmove', this.onTouchMove, this);
27063             this.bodyEl.on('touchend', this.onTouchEnd, this);
27064         }
27065         
27066         if(!Roo.isTouch){
27067             this.bodyEl.on('mousedown', this.onMouseDown, this);
27068             this.bodyEl.on('mousemove', this.onMouseMove, this);
27069             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27070             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27071             Roo.get(document).on('mouseup', this.onMouseUp, this);
27072         }
27073         
27074         this.selectorEl.on('change', this.onFileSelected, this);
27075     },
27076     
27077     reset : function()
27078     {    
27079         this.scale = 0;
27080         this.baseScale = 1;
27081         this.rotate = 0;
27082         this.baseRotate = 1;
27083         this.dragable = false;
27084         this.pinching = false;
27085         this.mouseX = 0;
27086         this.mouseY = 0;
27087         this.cropData = false;
27088         this.notifyEl.dom.innerHTML = this.emptyText;
27089         
27090         this.selectorEl.dom.value = '';
27091         
27092     },
27093     
27094     resize : function()
27095     {
27096         if(this.fireEvent('resize', this) != false){
27097             this.setThumbBoxPosition();
27098             this.setCanvasPosition();
27099         }
27100     },
27101     
27102     onFooterButtonClick : function(e, el, o, type)
27103     {
27104         switch (type) {
27105             case 'rotate-left' :
27106                 this.onRotateLeft(e);
27107                 break;
27108             case 'rotate-right' :
27109                 this.onRotateRight(e);
27110                 break;
27111             case 'picture' :
27112                 this.beforeSelectFile(e);
27113                 break;
27114             case 'trash' :
27115                 this.trash(e);
27116                 break;
27117             case 'crop' :
27118                 this.crop(e);
27119                 break;
27120             case 'download' :
27121                 this.download(e);
27122                 break;
27123             default :
27124                 break;
27125         }
27126         
27127         this.fireEvent('footerbuttonclick', this, type);
27128     },
27129     
27130     beforeSelectFile : function(e)
27131     {
27132         e.preventDefault();
27133         
27134         if(this.fireEvent('beforeselectfile', this) != false){
27135             this.selectorEl.dom.click();
27136         }
27137     },
27138     
27139     onFileSelected : function(e)
27140     {
27141         e.preventDefault();
27142         
27143         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27144             return;
27145         }
27146         
27147         var file = this.selectorEl.dom.files[0];
27148         
27149         if(this.fireEvent('inspect', this, file) != false){
27150             this.prepare(file);
27151         }
27152         
27153     },
27154     
27155     trash : function(e)
27156     {
27157         this.fireEvent('trash', this);
27158     },
27159     
27160     download : function(e)
27161     {
27162         this.fireEvent('download', this);
27163     },
27164     
27165     loadCanvas : function(src)
27166     {   
27167         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27168             
27169             this.reset();
27170             
27171             this.imageEl = document.createElement('img');
27172             
27173             var _this = this;
27174             
27175             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27176             
27177             this.imageEl.src = src;
27178         }
27179     },
27180     
27181     onLoadCanvas : function()
27182     {   
27183         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27184         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27185         
27186         this.bodyEl.un('click', this.beforeSelectFile, this);
27187         
27188         this.notifyEl.hide();
27189         this.thumbEl.show();
27190         this.footerEl.show();
27191         
27192         this.baseRotateLevel();
27193         
27194         if(this.isDocument){
27195             this.setThumbBoxSize();
27196         }
27197         
27198         this.setThumbBoxPosition();
27199         
27200         this.baseScaleLevel();
27201         
27202         this.draw();
27203         
27204         this.resize();
27205         
27206         this.canvasLoaded = true;
27207         
27208         if(this.loadMask){
27209             this.maskEl.unmask();
27210         }
27211         
27212     },
27213     
27214     setCanvasPosition : function()
27215     {   
27216         if(!this.canvasEl){
27217             return;
27218         }
27219         
27220         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27221         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27222         
27223         this.previewEl.setLeft(pw);
27224         this.previewEl.setTop(ph);
27225         
27226     },
27227     
27228     onMouseDown : function(e)
27229     {   
27230         e.stopEvent();
27231         
27232         this.dragable = true;
27233         this.pinching = false;
27234         
27235         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27236             this.dragable = false;
27237             return;
27238         }
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     
27245     onMouseMove : function(e)
27246     {   
27247         e.stopEvent();
27248         
27249         if(!this.canvasLoaded){
27250             return;
27251         }
27252         
27253         if (!this.dragable){
27254             return;
27255         }
27256         
27257         var minX = Math.ceil(this.thumbEl.getLeft(true));
27258         var minY = Math.ceil(this.thumbEl.getTop(true));
27259         
27260         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27261         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27262         
27263         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27264         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27265         
27266         x = x - this.mouseX;
27267         y = y - this.mouseY;
27268         
27269         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27270         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27271         
27272         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27273         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27274         
27275         this.previewEl.setLeft(bgX);
27276         this.previewEl.setTop(bgY);
27277         
27278         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27279         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27280     },
27281     
27282     onMouseUp : function(e)
27283     {   
27284         e.stopEvent();
27285         
27286         this.dragable = false;
27287     },
27288     
27289     onMouseWheel : function(e)
27290     {   
27291         e.stopEvent();
27292         
27293         this.startScale = this.scale;
27294         
27295         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27296         
27297         if(!this.zoomable()){
27298             this.scale = this.startScale;
27299             return;
27300         }
27301         
27302         this.draw();
27303         
27304         return;
27305     },
27306     
27307     zoomable : function()
27308     {
27309         var minScale = this.thumbEl.getWidth() / this.minWidth;
27310         
27311         if(this.minWidth < this.minHeight){
27312             minScale = this.thumbEl.getHeight() / this.minHeight;
27313         }
27314         
27315         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27316         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27317         
27318         if(
27319                 this.isDocument &&
27320                 (this.rotate == 0 || this.rotate == 180) && 
27321                 (
27322                     width > this.imageEl.OriginWidth || 
27323                     height > this.imageEl.OriginHeight ||
27324                     (width < this.minWidth && height < this.minHeight)
27325                 )
27326         ){
27327             return false;
27328         }
27329         
27330         if(
27331                 this.isDocument &&
27332                 (this.rotate == 90 || this.rotate == 270) && 
27333                 (
27334                     width > this.imageEl.OriginWidth || 
27335                     height > this.imageEl.OriginHeight ||
27336                     (width < this.minHeight && height < this.minWidth)
27337                 )
27338         ){
27339             return false;
27340         }
27341         
27342         if(
27343                 !this.isDocument &&
27344                 (this.rotate == 0 || this.rotate == 180) && 
27345                 (
27346                     width < this.minWidth || 
27347                     width > this.imageEl.OriginWidth || 
27348                     height < this.minHeight || 
27349                     height > this.imageEl.OriginHeight
27350                 )
27351         ){
27352             return false;
27353         }
27354         
27355         if(
27356                 !this.isDocument &&
27357                 (this.rotate == 90 || this.rotate == 270) && 
27358                 (
27359                     width < this.minHeight || 
27360                     width > this.imageEl.OriginWidth || 
27361                     height < this.minWidth || 
27362                     height > this.imageEl.OriginHeight
27363                 )
27364         ){
27365             return false;
27366         }
27367         
27368         return true;
27369         
27370     },
27371     
27372     onRotateLeft : function(e)
27373     {   
27374         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27375             
27376             var minScale = this.thumbEl.getWidth() / this.minWidth;
27377             
27378             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27379             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27380             
27381             this.startScale = this.scale;
27382             
27383             while (this.getScaleLevel() < minScale){
27384             
27385                 this.scale = this.scale + 1;
27386                 
27387                 if(!this.zoomable()){
27388                     break;
27389                 }
27390                 
27391                 if(
27392                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27393                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27394                 ){
27395                     continue;
27396                 }
27397                 
27398                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27399
27400                 this.draw();
27401                 
27402                 return;
27403             }
27404             
27405             this.scale = this.startScale;
27406             
27407             this.onRotateFail();
27408             
27409             return false;
27410         }
27411         
27412         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27413
27414         if(this.isDocument){
27415             this.setThumbBoxSize();
27416             this.setThumbBoxPosition();
27417             this.setCanvasPosition();
27418         }
27419         
27420         this.draw();
27421         
27422         this.fireEvent('rotate', this, 'left');
27423         
27424     },
27425     
27426     onRotateRight : function(e)
27427     {
27428         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27429             
27430             var minScale = this.thumbEl.getWidth() / this.minWidth;
27431         
27432             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27433             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27434             
27435             this.startScale = this.scale;
27436             
27437             while (this.getScaleLevel() < minScale){
27438             
27439                 this.scale = this.scale + 1;
27440                 
27441                 if(!this.zoomable()){
27442                     break;
27443                 }
27444                 
27445                 if(
27446                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27447                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27448                 ){
27449                     continue;
27450                 }
27451                 
27452                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27453
27454                 this.draw();
27455                 
27456                 return;
27457             }
27458             
27459             this.scale = this.startScale;
27460             
27461             this.onRotateFail();
27462             
27463             return false;
27464         }
27465         
27466         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27467
27468         if(this.isDocument){
27469             this.setThumbBoxSize();
27470             this.setThumbBoxPosition();
27471             this.setCanvasPosition();
27472         }
27473         
27474         this.draw();
27475         
27476         this.fireEvent('rotate', this, 'right');
27477     },
27478     
27479     onRotateFail : function()
27480     {
27481         this.errorEl.show(true);
27482         
27483         var _this = this;
27484         
27485         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27486     },
27487     
27488     draw : function()
27489     {
27490         this.previewEl.dom.innerHTML = '';
27491         
27492         var canvasEl = document.createElement("canvas");
27493         
27494         var contextEl = canvasEl.getContext("2d");
27495         
27496         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27497         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27498         var center = this.imageEl.OriginWidth / 2;
27499         
27500         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27501             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27502             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27503             center = this.imageEl.OriginHeight / 2;
27504         }
27505         
27506         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27507         
27508         contextEl.translate(center, center);
27509         contextEl.rotate(this.rotate * Math.PI / 180);
27510
27511         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27512         
27513         this.canvasEl = document.createElement("canvas");
27514         
27515         this.contextEl = this.canvasEl.getContext("2d");
27516         
27517         switch (this.rotate) {
27518             case 0 :
27519                 
27520                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27521                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27522                 
27523                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27524                 
27525                 break;
27526             case 90 : 
27527                 
27528                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27529                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27530                 
27531                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27532                     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);
27533                     break;
27534                 }
27535                 
27536                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27537                 
27538                 break;
27539             case 180 :
27540                 
27541                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27542                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27543                 
27544                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27545                     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);
27546                     break;
27547                 }
27548                 
27549                 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);
27550                 
27551                 break;
27552             case 270 :
27553                 
27554                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27555                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27556         
27557                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27558                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27559                     break;
27560                 }
27561                 
27562                 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);
27563                 
27564                 break;
27565             default : 
27566                 break;
27567         }
27568         
27569         this.previewEl.appendChild(this.canvasEl);
27570         
27571         this.setCanvasPosition();
27572     },
27573     
27574     crop : function()
27575     {
27576         if(!this.canvasLoaded){
27577             return;
27578         }
27579         
27580         var imageCanvas = document.createElement("canvas");
27581         
27582         var imageContext = imageCanvas.getContext("2d");
27583         
27584         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27585         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27586         
27587         var center = imageCanvas.width / 2;
27588         
27589         imageContext.translate(center, center);
27590         
27591         imageContext.rotate(this.rotate * Math.PI / 180);
27592         
27593         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27594         
27595         var canvas = document.createElement("canvas");
27596         
27597         var context = canvas.getContext("2d");
27598                 
27599         canvas.width = this.minWidth;
27600         canvas.height = this.minHeight;
27601
27602         switch (this.rotate) {
27603             case 0 :
27604                 
27605                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27606                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27607                 
27608                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27609                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27610                 
27611                 var targetWidth = this.minWidth - 2 * x;
27612                 var targetHeight = this.minHeight - 2 * y;
27613                 
27614                 var scale = 1;
27615                 
27616                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27617                     scale = targetWidth / width;
27618                 }
27619                 
27620                 if(x > 0 && y == 0){
27621                     scale = targetHeight / height;
27622                 }
27623                 
27624                 if(x > 0 && y > 0){
27625                     scale = targetWidth / width;
27626                     
27627                     if(width < height){
27628                         scale = targetHeight / height;
27629                     }
27630                 }
27631                 
27632                 context.scale(scale, scale);
27633                 
27634                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27635                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27636
27637                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27638                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27639
27640                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27641                 
27642                 break;
27643             case 90 : 
27644                 
27645                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27646                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27647                 
27648                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27649                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27650                 
27651                 var targetWidth = this.minWidth - 2 * x;
27652                 var targetHeight = this.minHeight - 2 * y;
27653                 
27654                 var scale = 1;
27655                 
27656                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27657                     scale = targetWidth / width;
27658                 }
27659                 
27660                 if(x > 0 && y == 0){
27661                     scale = targetHeight / height;
27662                 }
27663                 
27664                 if(x > 0 && y > 0){
27665                     scale = targetWidth / width;
27666                     
27667                     if(width < height){
27668                         scale = targetHeight / height;
27669                     }
27670                 }
27671                 
27672                 context.scale(scale, scale);
27673                 
27674                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27675                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27676
27677                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27678                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27679                 
27680                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27681                 
27682                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27683                 
27684                 break;
27685             case 180 :
27686                 
27687                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27688                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27689                 
27690                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27691                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27692                 
27693                 var targetWidth = this.minWidth - 2 * x;
27694                 var targetHeight = this.minHeight - 2 * y;
27695                 
27696                 var scale = 1;
27697                 
27698                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27699                     scale = targetWidth / width;
27700                 }
27701                 
27702                 if(x > 0 && y == 0){
27703                     scale = targetHeight / height;
27704                 }
27705                 
27706                 if(x > 0 && y > 0){
27707                     scale = targetWidth / width;
27708                     
27709                     if(width < height){
27710                         scale = targetHeight / height;
27711                     }
27712                 }
27713                 
27714                 context.scale(scale, scale);
27715                 
27716                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27717                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27718
27719                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27720                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27721
27722                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27723                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27724                 
27725                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27726                 
27727                 break;
27728             case 270 :
27729                 
27730                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27731                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27732                 
27733                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27734                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27735                 
27736                 var targetWidth = this.minWidth - 2 * x;
27737                 var targetHeight = this.minHeight - 2 * y;
27738                 
27739                 var scale = 1;
27740                 
27741                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27742                     scale = targetWidth / width;
27743                 }
27744                 
27745                 if(x > 0 && y == 0){
27746                     scale = targetHeight / height;
27747                 }
27748                 
27749                 if(x > 0 && y > 0){
27750                     scale = targetWidth / width;
27751                     
27752                     if(width < height){
27753                         scale = targetHeight / height;
27754                     }
27755                 }
27756                 
27757                 context.scale(scale, scale);
27758                 
27759                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27760                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27761
27762                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27763                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27764                 
27765                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27766                 
27767                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27768                 
27769                 break;
27770             default : 
27771                 break;
27772         }
27773         
27774         this.cropData = canvas.toDataURL(this.cropType);
27775         
27776         if(this.fireEvent('crop', this, this.cropData) !== false){
27777             this.process(this.file, this.cropData);
27778         }
27779         
27780         return;
27781         
27782     },
27783     
27784     setThumbBoxSize : function()
27785     {
27786         var width, height;
27787         
27788         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27789             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27790             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27791             
27792             this.minWidth = width;
27793             this.minHeight = height;
27794             
27795             if(this.rotate == 90 || this.rotate == 270){
27796                 this.minWidth = height;
27797                 this.minHeight = width;
27798             }
27799         }
27800         
27801         height = 300;
27802         width = Math.ceil(this.minWidth * height / this.minHeight);
27803         
27804         if(this.minWidth > this.minHeight){
27805             width = 300;
27806             height = Math.ceil(this.minHeight * width / this.minWidth);
27807         }
27808         
27809         this.thumbEl.setStyle({
27810             width : width + 'px',
27811             height : height + 'px'
27812         });
27813
27814         return;
27815             
27816     },
27817     
27818     setThumbBoxPosition : function()
27819     {
27820         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27821         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27822         
27823         this.thumbEl.setLeft(x);
27824         this.thumbEl.setTop(y);
27825         
27826     },
27827     
27828     baseRotateLevel : function()
27829     {
27830         this.baseRotate = 1;
27831         
27832         if(
27833                 typeof(this.exif) != 'undefined' &&
27834                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27835                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27836         ){
27837             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27838         }
27839         
27840         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27841         
27842     },
27843     
27844     baseScaleLevel : function()
27845     {
27846         var width, height;
27847         
27848         if(this.isDocument){
27849             
27850             if(this.baseRotate == 6 || this.baseRotate == 8){
27851             
27852                 height = this.thumbEl.getHeight();
27853                 this.baseScale = height / this.imageEl.OriginWidth;
27854
27855                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27856                     width = this.thumbEl.getWidth();
27857                     this.baseScale = width / this.imageEl.OriginHeight;
27858                 }
27859
27860                 return;
27861             }
27862
27863             height = this.thumbEl.getHeight();
27864             this.baseScale = height / this.imageEl.OriginHeight;
27865
27866             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27867                 width = this.thumbEl.getWidth();
27868                 this.baseScale = width / this.imageEl.OriginWidth;
27869             }
27870
27871             return;
27872         }
27873         
27874         if(this.baseRotate == 6 || this.baseRotate == 8){
27875             
27876             width = this.thumbEl.getHeight();
27877             this.baseScale = width / this.imageEl.OriginHeight;
27878             
27879             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27880                 height = this.thumbEl.getWidth();
27881                 this.baseScale = height / this.imageEl.OriginHeight;
27882             }
27883             
27884             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27885                 height = this.thumbEl.getWidth();
27886                 this.baseScale = height / this.imageEl.OriginHeight;
27887                 
27888                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27889                     width = this.thumbEl.getHeight();
27890                     this.baseScale = width / this.imageEl.OriginWidth;
27891                 }
27892             }
27893             
27894             return;
27895         }
27896         
27897         width = this.thumbEl.getWidth();
27898         this.baseScale = width / this.imageEl.OriginWidth;
27899         
27900         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27901             height = this.thumbEl.getHeight();
27902             this.baseScale = height / this.imageEl.OriginHeight;
27903         }
27904         
27905         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27906             
27907             height = this.thumbEl.getHeight();
27908             this.baseScale = height / this.imageEl.OriginHeight;
27909             
27910             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27911                 width = this.thumbEl.getWidth();
27912                 this.baseScale = width / this.imageEl.OriginWidth;
27913             }
27914             
27915         }
27916         
27917         return;
27918     },
27919     
27920     getScaleLevel : function()
27921     {
27922         return this.baseScale * Math.pow(1.1, this.scale);
27923     },
27924     
27925     onTouchStart : function(e)
27926     {
27927         if(!this.canvasLoaded){
27928             this.beforeSelectFile(e);
27929             return;
27930         }
27931         
27932         var touches = e.browserEvent.touches;
27933         
27934         if(!touches){
27935             return;
27936         }
27937         
27938         if(touches.length == 1){
27939             this.onMouseDown(e);
27940             return;
27941         }
27942         
27943         if(touches.length != 2){
27944             return;
27945         }
27946         
27947         var coords = [];
27948         
27949         for(var i = 0, finger; finger = touches[i]; i++){
27950             coords.push(finger.pageX, finger.pageY);
27951         }
27952         
27953         var x = Math.pow(coords[0] - coords[2], 2);
27954         var y = Math.pow(coords[1] - coords[3], 2);
27955         
27956         this.startDistance = Math.sqrt(x + y);
27957         
27958         this.startScale = this.scale;
27959         
27960         this.pinching = true;
27961         this.dragable = false;
27962         
27963     },
27964     
27965     onTouchMove : function(e)
27966     {
27967         if(!this.pinching && !this.dragable){
27968             return;
27969         }
27970         
27971         var touches = e.browserEvent.touches;
27972         
27973         if(!touches){
27974             return;
27975         }
27976         
27977         if(this.dragable){
27978             this.onMouseMove(e);
27979             return;
27980         }
27981         
27982         var coords = [];
27983         
27984         for(var i = 0, finger; finger = touches[i]; i++){
27985             coords.push(finger.pageX, finger.pageY);
27986         }
27987         
27988         var x = Math.pow(coords[0] - coords[2], 2);
27989         var y = Math.pow(coords[1] - coords[3], 2);
27990         
27991         this.endDistance = Math.sqrt(x + y);
27992         
27993         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27994         
27995         if(!this.zoomable()){
27996             this.scale = this.startScale;
27997             return;
27998         }
27999         
28000         this.draw();
28001         
28002     },
28003     
28004     onTouchEnd : function(e)
28005     {
28006         this.pinching = false;
28007         this.dragable = false;
28008         
28009     },
28010     
28011     process : function(file, crop)
28012     {
28013         if(this.loadMask){
28014             this.maskEl.mask(this.loadingText);
28015         }
28016         
28017         this.xhr = new XMLHttpRequest();
28018         
28019         file.xhr = this.xhr;
28020
28021         this.xhr.open(this.method, this.url, true);
28022         
28023         var headers = {
28024             "Accept": "application/json",
28025             "Cache-Control": "no-cache",
28026             "X-Requested-With": "XMLHttpRequest"
28027         };
28028         
28029         for (var headerName in headers) {
28030             var headerValue = headers[headerName];
28031             if (headerValue) {
28032                 this.xhr.setRequestHeader(headerName, headerValue);
28033             }
28034         }
28035         
28036         var _this = this;
28037         
28038         this.xhr.onload = function()
28039         {
28040             _this.xhrOnLoad(_this.xhr);
28041         }
28042         
28043         this.xhr.onerror = function()
28044         {
28045             _this.xhrOnError(_this.xhr);
28046         }
28047         
28048         var formData = new FormData();
28049
28050         formData.append('returnHTML', 'NO');
28051         
28052         if(crop){
28053             formData.append('crop', crop);
28054         }
28055         
28056         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28057             formData.append(this.paramName, file, file.name);
28058         }
28059         
28060         if(typeof(file.filename) != 'undefined'){
28061             formData.append('filename', file.filename);
28062         }
28063         
28064         if(typeof(file.mimetype) != 'undefined'){
28065             formData.append('mimetype', file.mimetype);
28066         }
28067         
28068         if(this.fireEvent('arrange', this, formData) != false){
28069             this.xhr.send(formData);
28070         };
28071     },
28072     
28073     xhrOnLoad : function(xhr)
28074     {
28075         if(this.loadMask){
28076             this.maskEl.unmask();
28077         }
28078         
28079         if (xhr.readyState !== 4) {
28080             this.fireEvent('exception', this, xhr);
28081             return;
28082         }
28083
28084         var response = Roo.decode(xhr.responseText);
28085         
28086         if(!response.success){
28087             this.fireEvent('exception', this, xhr);
28088             return;
28089         }
28090         
28091         var response = Roo.decode(xhr.responseText);
28092         
28093         this.fireEvent('upload', this, response);
28094         
28095     },
28096     
28097     xhrOnError : function()
28098     {
28099         if(this.loadMask){
28100             this.maskEl.unmask();
28101         }
28102         
28103         Roo.log('xhr on error');
28104         
28105         var response = Roo.decode(xhr.responseText);
28106           
28107         Roo.log(response);
28108         
28109     },
28110     
28111     prepare : function(file)
28112     {   
28113         if(this.loadMask){
28114             this.maskEl.mask(this.loadingText);
28115         }
28116         
28117         this.file = false;
28118         this.exif = {};
28119         
28120         if(typeof(file) === 'string'){
28121             this.loadCanvas(file);
28122             return;
28123         }
28124         
28125         if(!file || !this.urlAPI){
28126             return;
28127         }
28128         
28129         this.file = file;
28130         this.cropType = file.type;
28131         
28132         var _this = this;
28133         
28134         if(this.fireEvent('prepare', this, this.file) != false){
28135             
28136             var reader = new FileReader();
28137             
28138             reader.onload = function (e) {
28139                 if (e.target.error) {
28140                     Roo.log(e.target.error);
28141                     return;
28142                 }
28143                 
28144                 var buffer = e.target.result,
28145                     dataView = new DataView(buffer),
28146                     offset = 2,
28147                     maxOffset = dataView.byteLength - 4,
28148                     markerBytes,
28149                     markerLength;
28150                 
28151                 if (dataView.getUint16(0) === 0xffd8) {
28152                     while (offset < maxOffset) {
28153                         markerBytes = dataView.getUint16(offset);
28154                         
28155                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28156                             markerLength = dataView.getUint16(offset + 2) + 2;
28157                             if (offset + markerLength > dataView.byteLength) {
28158                                 Roo.log('Invalid meta data: Invalid segment size.');
28159                                 break;
28160                             }
28161                             
28162                             if(markerBytes == 0xffe1){
28163                                 _this.parseExifData(
28164                                     dataView,
28165                                     offset,
28166                                     markerLength
28167                                 );
28168                             }
28169                             
28170                             offset += markerLength;
28171                             
28172                             continue;
28173                         }
28174                         
28175                         break;
28176                     }
28177                     
28178                 }
28179                 
28180                 var url = _this.urlAPI.createObjectURL(_this.file);
28181                 
28182                 _this.loadCanvas(url);
28183                 
28184                 return;
28185             }
28186             
28187             reader.readAsArrayBuffer(this.file);
28188             
28189         }
28190         
28191     },
28192     
28193     parseExifData : function(dataView, offset, length)
28194     {
28195         var tiffOffset = offset + 10,
28196             littleEndian,
28197             dirOffset;
28198     
28199         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28200             // No Exif data, might be XMP data instead
28201             return;
28202         }
28203         
28204         // Check for the ASCII code for "Exif" (0x45786966):
28205         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28206             // No Exif data, might be XMP data instead
28207             return;
28208         }
28209         if (tiffOffset + 8 > dataView.byteLength) {
28210             Roo.log('Invalid Exif data: Invalid segment size.');
28211             return;
28212         }
28213         // Check for the two null bytes:
28214         if (dataView.getUint16(offset + 8) !== 0x0000) {
28215             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28216             return;
28217         }
28218         // Check the byte alignment:
28219         switch (dataView.getUint16(tiffOffset)) {
28220         case 0x4949:
28221             littleEndian = true;
28222             break;
28223         case 0x4D4D:
28224             littleEndian = false;
28225             break;
28226         default:
28227             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28228             return;
28229         }
28230         // Check for the TIFF tag marker (0x002A):
28231         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28232             Roo.log('Invalid Exif data: Missing TIFF marker.');
28233             return;
28234         }
28235         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28236         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28237         
28238         this.parseExifTags(
28239             dataView,
28240             tiffOffset,
28241             tiffOffset + dirOffset,
28242             littleEndian
28243         );
28244     },
28245     
28246     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28247     {
28248         var tagsNumber,
28249             dirEndOffset,
28250             i;
28251         if (dirOffset + 6 > dataView.byteLength) {
28252             Roo.log('Invalid Exif data: Invalid directory offset.');
28253             return;
28254         }
28255         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28256         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28257         if (dirEndOffset + 4 > dataView.byteLength) {
28258             Roo.log('Invalid Exif data: Invalid directory size.');
28259             return;
28260         }
28261         for (i = 0; i < tagsNumber; i += 1) {
28262             this.parseExifTag(
28263                 dataView,
28264                 tiffOffset,
28265                 dirOffset + 2 + 12 * i, // tag offset
28266                 littleEndian
28267             );
28268         }
28269         // Return the offset to the next directory:
28270         return dataView.getUint32(dirEndOffset, littleEndian);
28271     },
28272     
28273     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28274     {
28275         var tag = dataView.getUint16(offset, littleEndian);
28276         
28277         this.exif[tag] = this.getExifValue(
28278             dataView,
28279             tiffOffset,
28280             offset,
28281             dataView.getUint16(offset + 2, littleEndian), // tag type
28282             dataView.getUint32(offset + 4, littleEndian), // tag length
28283             littleEndian
28284         );
28285     },
28286     
28287     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28288     {
28289         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28290             tagSize,
28291             dataOffset,
28292             values,
28293             i,
28294             str,
28295             c;
28296     
28297         if (!tagType) {
28298             Roo.log('Invalid Exif data: Invalid tag type.');
28299             return;
28300         }
28301         
28302         tagSize = tagType.size * length;
28303         // Determine if the value is contained in the dataOffset bytes,
28304         // or if the value at the dataOffset is a pointer to the actual data:
28305         dataOffset = tagSize > 4 ?
28306                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28307         if (dataOffset + tagSize > dataView.byteLength) {
28308             Roo.log('Invalid Exif data: Invalid data offset.');
28309             return;
28310         }
28311         if (length === 1) {
28312             return tagType.getValue(dataView, dataOffset, littleEndian);
28313         }
28314         values = [];
28315         for (i = 0; i < length; i += 1) {
28316             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28317         }
28318         
28319         if (tagType.ascii) {
28320             str = '';
28321             // Concatenate the chars:
28322             for (i = 0; i < values.length; i += 1) {
28323                 c = values[i];
28324                 // Ignore the terminating NULL byte(s):
28325                 if (c === '\u0000') {
28326                     break;
28327                 }
28328                 str += c;
28329             }
28330             return str;
28331         }
28332         return values;
28333     }
28334     
28335 });
28336
28337 Roo.apply(Roo.bootstrap.UploadCropbox, {
28338     tags : {
28339         'Orientation': 0x0112
28340     },
28341     
28342     Orientation: {
28343             1: 0, //'top-left',
28344 //            2: 'top-right',
28345             3: 180, //'bottom-right',
28346 //            4: 'bottom-left',
28347 //            5: 'left-top',
28348             6: 90, //'right-top',
28349 //            7: 'right-bottom',
28350             8: 270 //'left-bottom'
28351     },
28352     
28353     exifTagTypes : {
28354         // byte, 8-bit unsigned int:
28355         1: {
28356             getValue: function (dataView, dataOffset) {
28357                 return dataView.getUint8(dataOffset);
28358             },
28359             size: 1
28360         },
28361         // ascii, 8-bit byte:
28362         2: {
28363             getValue: function (dataView, dataOffset) {
28364                 return String.fromCharCode(dataView.getUint8(dataOffset));
28365             },
28366             size: 1,
28367             ascii: true
28368         },
28369         // short, 16 bit int:
28370         3: {
28371             getValue: function (dataView, dataOffset, littleEndian) {
28372                 return dataView.getUint16(dataOffset, littleEndian);
28373             },
28374             size: 2
28375         },
28376         // long, 32 bit int:
28377         4: {
28378             getValue: function (dataView, dataOffset, littleEndian) {
28379                 return dataView.getUint32(dataOffset, littleEndian);
28380             },
28381             size: 4
28382         },
28383         // rational = two long values, first is numerator, second is denominator:
28384         5: {
28385             getValue: function (dataView, dataOffset, littleEndian) {
28386                 return dataView.getUint32(dataOffset, littleEndian) /
28387                     dataView.getUint32(dataOffset + 4, littleEndian);
28388             },
28389             size: 8
28390         },
28391         // slong, 32 bit signed int:
28392         9: {
28393             getValue: function (dataView, dataOffset, littleEndian) {
28394                 return dataView.getInt32(dataOffset, littleEndian);
28395             },
28396             size: 4
28397         },
28398         // srational, two slongs, first is numerator, second is denominator:
28399         10: {
28400             getValue: function (dataView, dataOffset, littleEndian) {
28401                 return dataView.getInt32(dataOffset, littleEndian) /
28402                     dataView.getInt32(dataOffset + 4, littleEndian);
28403             },
28404             size: 8
28405         }
28406     },
28407     
28408     footer : {
28409         STANDARD : [
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-picture',
28425                 action : 'picture',
28426                 cn : [
28427                     {
28428                         tag : 'button',
28429                         cls : 'btn btn-default',
28430                         html : '<i class="fa fa-picture-o"></i>'
28431                     }
28432                 ]
28433             },
28434             {
28435                 tag : 'div',
28436                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28437                 action : 'rotate-right',
28438                 cn : [
28439                     {
28440                         tag : 'button',
28441                         cls : 'btn btn-default',
28442                         html : '<i class="fa fa-repeat"></i>'
28443                     }
28444                 ]
28445             }
28446         ],
28447         DOCUMENT : [
28448             {
28449                 tag : 'div',
28450                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28451                 action : 'rotate-left',
28452                 cn : [
28453                     {
28454                         tag : 'button',
28455                         cls : 'btn btn-default',
28456                         html : '<i class="fa fa-undo"></i>'
28457                     }
28458                 ]
28459             },
28460             {
28461                 tag : 'div',
28462                 cls : 'btn-group roo-upload-cropbox-download',
28463                 action : 'download',
28464                 cn : [
28465                     {
28466                         tag : 'button',
28467                         cls : 'btn btn-default',
28468                         html : '<i class="fa fa-download"></i>'
28469                     }
28470                 ]
28471             },
28472             {
28473                 tag : 'div',
28474                 cls : 'btn-group roo-upload-cropbox-crop',
28475                 action : 'crop',
28476                 cn : [
28477                     {
28478                         tag : 'button',
28479                         cls : 'btn btn-default',
28480                         html : '<i class="fa fa-crop"></i>'
28481                     }
28482                 ]
28483             },
28484             {
28485                 tag : 'div',
28486                 cls : 'btn-group roo-upload-cropbox-trash',
28487                 action : 'trash',
28488                 cn : [
28489                     {
28490                         tag : 'button',
28491                         cls : 'btn btn-default',
28492                         html : '<i class="fa fa-trash"></i>'
28493                     }
28494                 ]
28495             },
28496             {
28497                 tag : 'div',
28498                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28499                 action : 'rotate-right',
28500                 cn : [
28501                     {
28502                         tag : 'button',
28503                         cls : 'btn btn-default',
28504                         html : '<i class="fa fa-repeat"></i>'
28505                     }
28506                 ]
28507             }
28508         ],
28509         ROTATOR : [
28510             {
28511                 tag : 'div',
28512                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28513                 action : 'rotate-left',
28514                 cn : [
28515                     {
28516                         tag : 'button',
28517                         cls : 'btn btn-default',
28518                         html : '<i class="fa fa-undo"></i>'
28519                     }
28520                 ]
28521             },
28522             {
28523                 tag : 'div',
28524                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28525                 action : 'rotate-right',
28526                 cn : [
28527                     {
28528                         tag : 'button',
28529                         cls : 'btn btn-default',
28530                         html : '<i class="fa fa-repeat"></i>'
28531                     }
28532                 ]
28533             }
28534         ]
28535     }
28536 });
28537
28538 /*
28539 * Licence: LGPL
28540 */
28541
28542 /**
28543  * @class Roo.bootstrap.DocumentManager
28544  * @extends Roo.bootstrap.Component
28545  * Bootstrap DocumentManager class
28546  * @cfg {String} paramName default 'imageUpload'
28547  * @cfg {String} toolTipName default 'filename'
28548  * @cfg {String} method default POST
28549  * @cfg {String} url action url
28550  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28551  * @cfg {Boolean} multiple multiple upload default true
28552  * @cfg {Number} thumbSize default 300
28553  * @cfg {String} fieldLabel
28554  * @cfg {Number} labelWidth default 4
28555  * @cfg {String} labelAlign (left|top) default left
28556  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28557 * @cfg {Number} labellg set the width of label (1-12)
28558  * @cfg {Number} labelmd set the width of label (1-12)
28559  * @cfg {Number} labelsm set the width of label (1-12)
28560  * @cfg {Number} labelxs set the width of label (1-12)
28561  * 
28562  * @constructor
28563  * Create a new DocumentManager
28564  * @param {Object} config The config object
28565  */
28566
28567 Roo.bootstrap.DocumentManager = function(config){
28568     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28569     
28570     this.files = [];
28571     this.delegates = [];
28572     
28573     this.addEvents({
28574         /**
28575          * @event initial
28576          * Fire when initial the DocumentManager
28577          * @param {Roo.bootstrap.DocumentManager} this
28578          */
28579         "initial" : true,
28580         /**
28581          * @event inspect
28582          * inspect selected file
28583          * @param {Roo.bootstrap.DocumentManager} this
28584          * @param {File} file
28585          */
28586         "inspect" : true,
28587         /**
28588          * @event exception
28589          * Fire when xhr load exception
28590          * @param {Roo.bootstrap.DocumentManager} this
28591          * @param {XMLHttpRequest} xhr
28592          */
28593         "exception" : true,
28594         /**
28595          * @event afterupload
28596          * Fire when xhr load exception
28597          * @param {Roo.bootstrap.DocumentManager} this
28598          * @param {XMLHttpRequest} xhr
28599          */
28600         "afterupload" : true,
28601         /**
28602          * @event prepare
28603          * prepare the form data
28604          * @param {Roo.bootstrap.DocumentManager} this
28605          * @param {Object} formData
28606          */
28607         "prepare" : true,
28608         /**
28609          * @event remove
28610          * Fire when remove the file
28611          * @param {Roo.bootstrap.DocumentManager} this
28612          * @param {Object} file
28613          */
28614         "remove" : true,
28615         /**
28616          * @event refresh
28617          * Fire after refresh the file
28618          * @param {Roo.bootstrap.DocumentManager} this
28619          */
28620         "refresh" : true,
28621         /**
28622          * @event click
28623          * Fire after click the image
28624          * @param {Roo.bootstrap.DocumentManager} this
28625          * @param {Object} file
28626          */
28627         "click" : true,
28628         /**
28629          * @event edit
28630          * Fire when upload a image and editable set to true
28631          * @param {Roo.bootstrap.DocumentManager} this
28632          * @param {Object} file
28633          */
28634         "edit" : true,
28635         /**
28636          * @event beforeselectfile
28637          * Fire before select file
28638          * @param {Roo.bootstrap.DocumentManager} this
28639          */
28640         "beforeselectfile" : true,
28641         /**
28642          * @event process
28643          * Fire before process file
28644          * @param {Roo.bootstrap.DocumentManager} this
28645          * @param {Object} file
28646          */
28647         "process" : true,
28648         /**
28649          * @event previewrendered
28650          * Fire when preview rendered
28651          * @param {Roo.bootstrap.DocumentManager} this
28652          * @param {Object} file
28653          */
28654         "previewrendered" : true
28655         
28656     });
28657 };
28658
28659 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28660     
28661     boxes : 0,
28662     inputName : '',
28663     thumbSize : 300,
28664     multiple : true,
28665     files : false,
28666     method : 'POST',
28667     url : '',
28668     paramName : 'imageUpload',
28669     toolTipName : 'filename',
28670     fieldLabel : '',
28671     labelWidth : 4,
28672     labelAlign : 'left',
28673     editable : true,
28674     delegates : false,
28675     xhr : false, 
28676     
28677     labellg : 0,
28678     labelmd : 0,
28679     labelsm : 0,
28680     labelxs : 0,
28681     
28682     getAutoCreate : function()
28683     {   
28684         var managerWidget = {
28685             tag : 'div',
28686             cls : 'roo-document-manager',
28687             cn : [
28688                 {
28689                     tag : 'input',
28690                     cls : 'roo-document-manager-selector',
28691                     type : 'file'
28692                 },
28693                 {
28694                     tag : 'div',
28695                     cls : 'roo-document-manager-uploader',
28696                     cn : [
28697                         {
28698                             tag : 'div',
28699                             cls : 'roo-document-manager-upload-btn',
28700                             html : '<i class="fa fa-plus"></i>'
28701                         }
28702                     ]
28703                     
28704                 }
28705             ]
28706         };
28707         
28708         var content = [
28709             {
28710                 tag : 'div',
28711                 cls : 'column col-md-12',
28712                 cn : managerWidget
28713             }
28714         ];
28715         
28716         if(this.fieldLabel.length){
28717             
28718             content = [
28719                 {
28720                     tag : 'div',
28721                     cls : 'column col-md-12',
28722                     html : this.fieldLabel
28723                 },
28724                 {
28725                     tag : 'div',
28726                     cls : 'column col-md-12',
28727                     cn : managerWidget
28728                 }
28729             ];
28730
28731             if(this.labelAlign == 'left'){
28732                 content = [
28733                     {
28734                         tag : 'div',
28735                         cls : 'column',
28736                         html : this.fieldLabel
28737                     },
28738                     {
28739                         tag : 'div',
28740                         cls : 'column',
28741                         cn : managerWidget
28742                     }
28743                 ];
28744                 
28745                 if(this.labelWidth > 12){
28746                     content[0].style = "width: " + this.labelWidth + 'px';
28747                 }
28748
28749                 if(this.labelWidth < 13 && this.labelmd == 0){
28750                     this.labelmd = this.labelWidth;
28751                 }
28752
28753                 if(this.labellg > 0){
28754                     content[0].cls += ' col-lg-' + this.labellg;
28755                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28756                 }
28757
28758                 if(this.labelmd > 0){
28759                     content[0].cls += ' col-md-' + this.labelmd;
28760                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28761                 }
28762
28763                 if(this.labelsm > 0){
28764                     content[0].cls += ' col-sm-' + this.labelsm;
28765                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28766                 }
28767
28768                 if(this.labelxs > 0){
28769                     content[0].cls += ' col-xs-' + this.labelxs;
28770                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28771                 }
28772                 
28773             }
28774         }
28775         
28776         var cfg = {
28777             tag : 'div',
28778             cls : 'row clearfix',
28779             cn : content
28780         };
28781         
28782         return cfg;
28783         
28784     },
28785     
28786     initEvents : function()
28787     {
28788         this.managerEl = this.el.select('.roo-document-manager', true).first();
28789         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28790         
28791         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28792         this.selectorEl.hide();
28793         
28794         if(this.multiple){
28795             this.selectorEl.attr('multiple', 'multiple');
28796         }
28797         
28798         this.selectorEl.on('change', this.onFileSelected, this);
28799         
28800         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28801         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28802         
28803         this.uploader.on('click', this.onUploaderClick, this);
28804         
28805         this.renderProgressDialog();
28806         
28807         var _this = this;
28808         
28809         window.addEventListener("resize", function() { _this.refresh(); } );
28810         
28811         this.fireEvent('initial', this);
28812     },
28813     
28814     renderProgressDialog : function()
28815     {
28816         var _this = this;
28817         
28818         this.progressDialog = new Roo.bootstrap.Modal({
28819             cls : 'roo-document-manager-progress-dialog',
28820             allow_close : false,
28821             title : '',
28822             buttons : [
28823                 {
28824                     name  :'cancel',
28825                     weight : 'danger',
28826                     html : 'Cancel'
28827                 }
28828             ], 
28829             listeners : { 
28830                 btnclick : function() {
28831                     _this.uploadCancel();
28832                     this.hide();
28833                 }
28834             }
28835         });
28836          
28837         this.progressDialog.render(Roo.get(document.body));
28838          
28839         this.progress = new Roo.bootstrap.Progress({
28840             cls : 'roo-document-manager-progress',
28841             active : true,
28842             striped : true
28843         });
28844         
28845         this.progress.render(this.progressDialog.getChildContainer());
28846         
28847         this.progressBar = new Roo.bootstrap.ProgressBar({
28848             cls : 'roo-document-manager-progress-bar',
28849             aria_valuenow : 0,
28850             aria_valuemin : 0,
28851             aria_valuemax : 12,
28852             panel : 'success'
28853         });
28854         
28855         this.progressBar.render(this.progress.getChildContainer());
28856     },
28857     
28858     onUploaderClick : function(e)
28859     {
28860         e.preventDefault();
28861      
28862         if(this.fireEvent('beforeselectfile', this) != false){
28863             this.selectorEl.dom.click();
28864         }
28865         
28866     },
28867     
28868     onFileSelected : function(e)
28869     {
28870         e.preventDefault();
28871         
28872         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28873             return;
28874         }
28875         
28876         Roo.each(this.selectorEl.dom.files, function(file){
28877             if(this.fireEvent('inspect', this, file) != false){
28878                 this.files.push(file);
28879             }
28880         }, this);
28881         
28882         this.queue();
28883         
28884     },
28885     
28886     queue : function()
28887     {
28888         this.selectorEl.dom.value = '';
28889         
28890         if(!this.files || !this.files.length){
28891             return;
28892         }
28893         
28894         if(this.boxes > 0 && this.files.length > this.boxes){
28895             this.files = this.files.slice(0, this.boxes);
28896         }
28897         
28898         this.uploader.show();
28899         
28900         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28901             this.uploader.hide();
28902         }
28903         
28904         var _this = this;
28905         
28906         var files = [];
28907         
28908         var docs = [];
28909         
28910         Roo.each(this.files, function(file){
28911             
28912             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28913                 var f = this.renderPreview(file);
28914                 files.push(f);
28915                 return;
28916             }
28917             
28918             if(file.type.indexOf('image') != -1){
28919                 this.delegates.push(
28920                     (function(){
28921                         _this.process(file);
28922                     }).createDelegate(this)
28923                 );
28924         
28925                 return;
28926             }
28927             
28928             docs.push(
28929                 (function(){
28930                     _this.process(file);
28931                 }).createDelegate(this)
28932             );
28933             
28934         }, this);
28935         
28936         this.files = files;
28937         
28938         this.delegates = this.delegates.concat(docs);
28939         
28940         if(!this.delegates.length){
28941             this.refresh();
28942             return;
28943         }
28944         
28945         this.progressBar.aria_valuemax = this.delegates.length;
28946         
28947         this.arrange();
28948         
28949         return;
28950     },
28951     
28952     arrange : function()
28953     {
28954         if(!this.delegates.length){
28955             this.progressDialog.hide();
28956             this.refresh();
28957             return;
28958         }
28959         
28960         var delegate = this.delegates.shift();
28961         
28962         this.progressDialog.show();
28963         
28964         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28965         
28966         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28967         
28968         delegate();
28969     },
28970     
28971     refresh : function()
28972     {
28973         this.uploader.show();
28974         
28975         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28976             this.uploader.hide();
28977         }
28978         
28979         Roo.isTouch ? this.closable(false) : this.closable(true);
28980         
28981         this.fireEvent('refresh', this);
28982     },
28983     
28984     onRemove : function(e, el, o)
28985     {
28986         e.preventDefault();
28987         
28988         this.fireEvent('remove', this, o);
28989         
28990     },
28991     
28992     remove : function(o)
28993     {
28994         var files = [];
28995         
28996         Roo.each(this.files, function(file){
28997             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28998                 files.push(file);
28999                 return;
29000             }
29001
29002             o.target.remove();
29003
29004         }, this);
29005         
29006         this.files = files;
29007         
29008         this.refresh();
29009     },
29010     
29011     clear : function()
29012     {
29013         Roo.each(this.files, function(file){
29014             if(!file.target){
29015                 return;
29016             }
29017             
29018             file.target.remove();
29019
29020         }, this);
29021         
29022         this.files = [];
29023         
29024         this.refresh();
29025     },
29026     
29027     onClick : function(e, el, o)
29028     {
29029         e.preventDefault();
29030         
29031         this.fireEvent('click', this, o);
29032         
29033     },
29034     
29035     closable : function(closable)
29036     {
29037         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29038             
29039             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29040             
29041             if(closable){
29042                 el.show();
29043                 return;
29044             }
29045             
29046             el.hide();
29047             
29048         }, this);
29049     },
29050     
29051     xhrOnLoad : function(xhr)
29052     {
29053         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29054             el.remove();
29055         }, this);
29056         
29057         if (xhr.readyState !== 4) {
29058             this.arrange();
29059             this.fireEvent('exception', this, xhr);
29060             return;
29061         }
29062
29063         var response = Roo.decode(xhr.responseText);
29064         
29065         if(!response.success){
29066             this.arrange();
29067             this.fireEvent('exception', this, xhr);
29068             return;
29069         }
29070         
29071         var file = this.renderPreview(response.data);
29072         
29073         this.files.push(file);
29074         
29075         this.arrange();
29076         
29077         this.fireEvent('afterupload', this, xhr);
29078         
29079     },
29080     
29081     xhrOnError : function(xhr)
29082     {
29083         Roo.log('xhr on error');
29084         
29085         var response = Roo.decode(xhr.responseText);
29086           
29087         Roo.log(response);
29088         
29089         this.arrange();
29090     },
29091     
29092     process : function(file)
29093     {
29094         if(this.fireEvent('process', this, file) !== false){
29095             if(this.editable && file.type.indexOf('image') != -1){
29096                 this.fireEvent('edit', this, file);
29097                 return;
29098             }
29099
29100             this.uploadStart(file, false);
29101
29102             return;
29103         }
29104         
29105     },
29106     
29107     uploadStart : function(file, crop)
29108     {
29109         this.xhr = new XMLHttpRequest();
29110         
29111         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29112             this.arrange();
29113             return;
29114         }
29115         
29116         file.xhr = this.xhr;
29117             
29118         this.managerEl.createChild({
29119             tag : 'div',
29120             cls : 'roo-document-manager-loading',
29121             cn : [
29122                 {
29123                     tag : 'div',
29124                     tooltip : file.name,
29125                     cls : 'roo-document-manager-thumb',
29126                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29127                 }
29128             ]
29129
29130         });
29131
29132         this.xhr.open(this.method, this.url, true);
29133         
29134         var headers = {
29135             "Accept": "application/json",
29136             "Cache-Control": "no-cache",
29137             "X-Requested-With": "XMLHttpRequest"
29138         };
29139         
29140         for (var headerName in headers) {
29141             var headerValue = headers[headerName];
29142             if (headerValue) {
29143                 this.xhr.setRequestHeader(headerName, headerValue);
29144             }
29145         }
29146         
29147         var _this = this;
29148         
29149         this.xhr.onload = function()
29150         {
29151             _this.xhrOnLoad(_this.xhr);
29152         }
29153         
29154         this.xhr.onerror = function()
29155         {
29156             _this.xhrOnError(_this.xhr);
29157         }
29158         
29159         var formData = new FormData();
29160
29161         formData.append('returnHTML', 'NO');
29162         
29163         if(crop){
29164             formData.append('crop', crop);
29165         }
29166         
29167         formData.append(this.paramName, file, file.name);
29168         
29169         var options = {
29170             file : file, 
29171             manually : false
29172         };
29173         
29174         if(this.fireEvent('prepare', this, formData, options) != false){
29175             
29176             if(options.manually){
29177                 return;
29178             }
29179             
29180             this.xhr.send(formData);
29181             return;
29182         };
29183         
29184         this.uploadCancel();
29185     },
29186     
29187     uploadCancel : function()
29188     {
29189         if (this.xhr) {
29190             this.xhr.abort();
29191         }
29192         
29193         this.delegates = [];
29194         
29195         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29196             el.remove();
29197         }, this);
29198         
29199         this.arrange();
29200     },
29201     
29202     renderPreview : function(file)
29203     {
29204         if(typeof(file.target) != 'undefined' && file.target){
29205             return file;
29206         }
29207         
29208         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29209         
29210         var previewEl = this.managerEl.createChild({
29211             tag : 'div',
29212             cls : 'roo-document-manager-preview',
29213             cn : [
29214                 {
29215                     tag : 'div',
29216                     tooltip : file[this.toolTipName],
29217                     cls : 'roo-document-manager-thumb',
29218                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29219                 },
29220                 {
29221                     tag : 'button',
29222                     cls : 'close',
29223                     html : '<i class="fa fa-times-circle"></i>'
29224                 }
29225             ]
29226         });
29227
29228         var close = previewEl.select('button.close', true).first();
29229
29230         close.on('click', this.onRemove, this, file);
29231
29232         file.target = previewEl;
29233
29234         var image = previewEl.select('img', true).first();
29235         
29236         var _this = this;
29237         
29238         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29239         
29240         image.on('click', this.onClick, this, file);
29241         
29242         this.fireEvent('previewrendered', this, file);
29243         
29244         return file;
29245         
29246     },
29247     
29248     onPreviewLoad : function(file, image)
29249     {
29250         if(typeof(file.target) == 'undefined' || !file.target){
29251             return;
29252         }
29253         
29254         var width = image.dom.naturalWidth || image.dom.width;
29255         var height = image.dom.naturalHeight || image.dom.height;
29256         
29257         if(width > height){
29258             file.target.addClass('wide');
29259             return;
29260         }
29261         
29262         file.target.addClass('tall');
29263         return;
29264         
29265     },
29266     
29267     uploadFromSource : function(file, crop)
29268     {
29269         this.xhr = new XMLHttpRequest();
29270         
29271         this.managerEl.createChild({
29272             tag : 'div',
29273             cls : 'roo-document-manager-loading',
29274             cn : [
29275                 {
29276                     tag : 'div',
29277                     tooltip : file.name,
29278                     cls : 'roo-document-manager-thumb',
29279                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29280                 }
29281             ]
29282
29283         });
29284
29285         this.xhr.open(this.method, this.url, true);
29286         
29287         var headers = {
29288             "Accept": "application/json",
29289             "Cache-Control": "no-cache",
29290             "X-Requested-With": "XMLHttpRequest"
29291         };
29292         
29293         for (var headerName in headers) {
29294             var headerValue = headers[headerName];
29295             if (headerValue) {
29296                 this.xhr.setRequestHeader(headerName, headerValue);
29297             }
29298         }
29299         
29300         var _this = this;
29301         
29302         this.xhr.onload = function()
29303         {
29304             _this.xhrOnLoad(_this.xhr);
29305         }
29306         
29307         this.xhr.onerror = function()
29308         {
29309             _this.xhrOnError(_this.xhr);
29310         }
29311         
29312         var formData = new FormData();
29313
29314         formData.append('returnHTML', 'NO');
29315         
29316         formData.append('crop', crop);
29317         
29318         if(typeof(file.filename) != 'undefined'){
29319             formData.append('filename', file.filename);
29320         }
29321         
29322         if(typeof(file.mimetype) != 'undefined'){
29323             formData.append('mimetype', file.mimetype);
29324         }
29325         
29326         Roo.log(formData);
29327         
29328         if(this.fireEvent('prepare', this, formData) != false){
29329             this.xhr.send(formData);
29330         };
29331     }
29332 });
29333
29334 /*
29335 * Licence: LGPL
29336 */
29337
29338 /**
29339  * @class Roo.bootstrap.DocumentViewer
29340  * @extends Roo.bootstrap.Component
29341  * Bootstrap DocumentViewer class
29342  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29343  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29344  * 
29345  * @constructor
29346  * Create a new DocumentViewer
29347  * @param {Object} config The config object
29348  */
29349
29350 Roo.bootstrap.DocumentViewer = function(config){
29351     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29352     
29353     this.addEvents({
29354         /**
29355          * @event initial
29356          * Fire after initEvent
29357          * @param {Roo.bootstrap.DocumentViewer} this
29358          */
29359         "initial" : true,
29360         /**
29361          * @event click
29362          * Fire after click
29363          * @param {Roo.bootstrap.DocumentViewer} this
29364          */
29365         "click" : true,
29366         /**
29367          * @event download
29368          * Fire after download button
29369          * @param {Roo.bootstrap.DocumentViewer} this
29370          */
29371         "download" : true,
29372         /**
29373          * @event trash
29374          * Fire after trash button
29375          * @param {Roo.bootstrap.DocumentViewer} this
29376          */
29377         "trash" : true
29378         
29379     });
29380 };
29381
29382 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29383     
29384     showDownload : true,
29385     
29386     showTrash : true,
29387     
29388     getAutoCreate : function()
29389     {
29390         var cfg = {
29391             tag : 'div',
29392             cls : 'roo-document-viewer',
29393             cn : [
29394                 {
29395                     tag : 'div',
29396                     cls : 'roo-document-viewer-body',
29397                     cn : [
29398                         {
29399                             tag : 'div',
29400                             cls : 'roo-document-viewer-thumb',
29401                             cn : [
29402                                 {
29403                                     tag : 'img',
29404                                     cls : 'roo-document-viewer-image'
29405                                 }
29406                             ]
29407                         }
29408                     ]
29409                 },
29410                 {
29411                     tag : 'div',
29412                     cls : 'roo-document-viewer-footer',
29413                     cn : {
29414                         tag : 'div',
29415                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29416                         cn : [
29417                             {
29418                                 tag : 'div',
29419                                 cls : 'btn-group roo-document-viewer-download',
29420                                 cn : [
29421                                     {
29422                                         tag : 'button',
29423                                         cls : 'btn btn-default',
29424                                         html : '<i class="fa fa-download"></i>'
29425                                     }
29426                                 ]
29427                             },
29428                             {
29429                                 tag : 'div',
29430                                 cls : 'btn-group roo-document-viewer-trash',
29431                                 cn : [
29432                                     {
29433                                         tag : 'button',
29434                                         cls : 'btn btn-default',
29435                                         html : '<i class="fa fa-trash"></i>'
29436                                     }
29437                                 ]
29438                             }
29439                         ]
29440                     }
29441                 }
29442             ]
29443         };
29444         
29445         return cfg;
29446     },
29447     
29448     initEvents : function()
29449     {
29450         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29451         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29452         
29453         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29454         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29455         
29456         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29457         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29458         
29459         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29460         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29461         
29462         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29463         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29464         
29465         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29466         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29467         
29468         this.bodyEl.on('click', this.onClick, this);
29469         this.downloadBtn.on('click', this.onDownload, this);
29470         this.trashBtn.on('click', this.onTrash, this);
29471         
29472         this.downloadBtn.hide();
29473         this.trashBtn.hide();
29474         
29475         if(this.showDownload){
29476             this.downloadBtn.show();
29477         }
29478         
29479         if(this.showTrash){
29480             this.trashBtn.show();
29481         }
29482         
29483         if(!this.showDownload && !this.showTrash) {
29484             this.footerEl.hide();
29485         }
29486         
29487     },
29488     
29489     initial : function()
29490     {
29491         this.fireEvent('initial', this);
29492         
29493     },
29494     
29495     onClick : function(e)
29496     {
29497         e.preventDefault();
29498         
29499         this.fireEvent('click', this);
29500     },
29501     
29502     onDownload : function(e)
29503     {
29504         e.preventDefault();
29505         
29506         this.fireEvent('download', this);
29507     },
29508     
29509     onTrash : function(e)
29510     {
29511         e.preventDefault();
29512         
29513         this.fireEvent('trash', this);
29514     }
29515     
29516 });
29517 /*
29518  * - LGPL
29519  *
29520  * nav progress bar
29521  * 
29522  */
29523
29524 /**
29525  * @class Roo.bootstrap.NavProgressBar
29526  * @extends Roo.bootstrap.Component
29527  * Bootstrap NavProgressBar class
29528  * 
29529  * @constructor
29530  * Create a new nav progress bar
29531  * @param {Object} config The config object
29532  */
29533
29534 Roo.bootstrap.NavProgressBar = function(config){
29535     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29536
29537     this.bullets = this.bullets || [];
29538    
29539 //    Roo.bootstrap.NavProgressBar.register(this);
29540      this.addEvents({
29541         /**
29542              * @event changed
29543              * Fires when the active item changes
29544              * @param {Roo.bootstrap.NavProgressBar} this
29545              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29546              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29547          */
29548         'changed': true
29549      });
29550     
29551 };
29552
29553 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29554     
29555     bullets : [],
29556     barItems : [],
29557     
29558     getAutoCreate : function()
29559     {
29560         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29561         
29562         cfg = {
29563             tag : 'div',
29564             cls : 'roo-navigation-bar-group',
29565             cn : [
29566                 {
29567                     tag : 'div',
29568                     cls : 'roo-navigation-top-bar'
29569                 },
29570                 {
29571                     tag : 'div',
29572                     cls : 'roo-navigation-bullets-bar',
29573                     cn : [
29574                         {
29575                             tag : 'ul',
29576                             cls : 'roo-navigation-bar'
29577                         }
29578                     ]
29579                 },
29580                 
29581                 {
29582                     tag : 'div',
29583                     cls : 'roo-navigation-bottom-bar'
29584                 }
29585             ]
29586             
29587         };
29588         
29589         return cfg;
29590         
29591     },
29592     
29593     initEvents: function() 
29594     {
29595         
29596     },
29597     
29598     onRender : function(ct, position) 
29599     {
29600         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29601         
29602         if(this.bullets.length){
29603             Roo.each(this.bullets, function(b){
29604                this.addItem(b);
29605             }, this);
29606         }
29607         
29608         this.format();
29609         
29610     },
29611     
29612     addItem : function(cfg)
29613     {
29614         var item = new Roo.bootstrap.NavProgressItem(cfg);
29615         
29616         item.parentId = this.id;
29617         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29618         
29619         if(cfg.html){
29620             var top = new Roo.bootstrap.Element({
29621                 tag : 'div',
29622                 cls : 'roo-navigation-bar-text'
29623             });
29624             
29625             var bottom = new Roo.bootstrap.Element({
29626                 tag : 'div',
29627                 cls : 'roo-navigation-bar-text'
29628             });
29629             
29630             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29631             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29632             
29633             var topText = new Roo.bootstrap.Element({
29634                 tag : 'span',
29635                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29636             });
29637             
29638             var bottomText = new Roo.bootstrap.Element({
29639                 tag : 'span',
29640                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29641             });
29642             
29643             topText.onRender(top.el, null);
29644             bottomText.onRender(bottom.el, null);
29645             
29646             item.topEl = top;
29647             item.bottomEl = bottom;
29648         }
29649         
29650         this.barItems.push(item);
29651         
29652         return item;
29653     },
29654     
29655     getActive : function()
29656     {
29657         var active = false;
29658         
29659         Roo.each(this.barItems, function(v){
29660             
29661             if (!v.isActive()) {
29662                 return;
29663             }
29664             
29665             active = v;
29666             return false;
29667             
29668         });
29669         
29670         return active;
29671     },
29672     
29673     setActiveItem : function(item)
29674     {
29675         var prev = false;
29676         
29677         Roo.each(this.barItems, function(v){
29678             if (v.rid == item.rid) {
29679                 return ;
29680             }
29681             
29682             if (v.isActive()) {
29683                 v.setActive(false);
29684                 prev = v;
29685             }
29686         });
29687
29688         item.setActive(true);
29689         
29690         this.fireEvent('changed', this, item, prev);
29691     },
29692     
29693     getBarItem: function(rid)
29694     {
29695         var ret = false;
29696         
29697         Roo.each(this.barItems, function(e) {
29698             if (e.rid != rid) {
29699                 return;
29700             }
29701             
29702             ret =  e;
29703             return false;
29704         });
29705         
29706         return ret;
29707     },
29708     
29709     indexOfItem : function(item)
29710     {
29711         var index = false;
29712         
29713         Roo.each(this.barItems, function(v, i){
29714             
29715             if (v.rid != item.rid) {
29716                 return;
29717             }
29718             
29719             index = i;
29720             return false
29721         });
29722         
29723         return index;
29724     },
29725     
29726     setActiveNext : function()
29727     {
29728         var i = this.indexOfItem(this.getActive());
29729         
29730         if (i > this.barItems.length) {
29731             return;
29732         }
29733         
29734         this.setActiveItem(this.barItems[i+1]);
29735     },
29736     
29737     setActivePrev : function()
29738     {
29739         var i = this.indexOfItem(this.getActive());
29740         
29741         if (i  < 1) {
29742             return;
29743         }
29744         
29745         this.setActiveItem(this.barItems[i-1]);
29746     },
29747     
29748     format : function()
29749     {
29750         if(!this.barItems.length){
29751             return;
29752         }
29753      
29754         var width = 100 / this.barItems.length;
29755         
29756         Roo.each(this.barItems, function(i){
29757             i.el.setStyle('width', width + '%');
29758             i.topEl.el.setStyle('width', width + '%');
29759             i.bottomEl.el.setStyle('width', width + '%');
29760         }, this);
29761         
29762     }
29763     
29764 });
29765 /*
29766  * - LGPL
29767  *
29768  * Nav Progress Item
29769  * 
29770  */
29771
29772 /**
29773  * @class Roo.bootstrap.NavProgressItem
29774  * @extends Roo.bootstrap.Component
29775  * Bootstrap NavProgressItem class
29776  * @cfg {String} rid the reference id
29777  * @cfg {Boolean} active (true|false) Is item active default false
29778  * @cfg {Boolean} disabled (true|false) Is item active default false
29779  * @cfg {String} html
29780  * @cfg {String} position (top|bottom) text position default bottom
29781  * @cfg {String} icon show icon instead of number
29782  * 
29783  * @constructor
29784  * Create a new NavProgressItem
29785  * @param {Object} config The config object
29786  */
29787 Roo.bootstrap.NavProgressItem = function(config){
29788     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29789     this.addEvents({
29790         // raw events
29791         /**
29792          * @event click
29793          * The raw click event for the entire grid.
29794          * @param {Roo.bootstrap.NavProgressItem} this
29795          * @param {Roo.EventObject} e
29796          */
29797         "click" : true
29798     });
29799    
29800 };
29801
29802 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29803     
29804     rid : '',
29805     active : false,
29806     disabled : false,
29807     html : '',
29808     position : 'bottom',
29809     icon : false,
29810     
29811     getAutoCreate : function()
29812     {
29813         var iconCls = 'roo-navigation-bar-item-icon';
29814         
29815         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29816         
29817         var cfg = {
29818             tag: 'li',
29819             cls: 'roo-navigation-bar-item',
29820             cn : [
29821                 {
29822                     tag : 'i',
29823                     cls : iconCls
29824                 }
29825             ]
29826         };
29827         
29828         if(this.active){
29829             cfg.cls += ' active';
29830         }
29831         if(this.disabled){
29832             cfg.cls += ' disabled';
29833         }
29834         
29835         return cfg;
29836     },
29837     
29838     disable : function()
29839     {
29840         this.setDisabled(true);
29841     },
29842     
29843     enable : function()
29844     {
29845         this.setDisabled(false);
29846     },
29847     
29848     initEvents: function() 
29849     {
29850         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29851         
29852         this.iconEl.on('click', this.onClick, this);
29853     },
29854     
29855     onClick : function(e)
29856     {
29857         e.preventDefault();
29858         
29859         if(this.disabled){
29860             return;
29861         }
29862         
29863         if(this.fireEvent('click', this, e) === false){
29864             return;
29865         };
29866         
29867         this.parent().setActiveItem(this);
29868     },
29869     
29870     isActive: function () 
29871     {
29872         return this.active;
29873     },
29874     
29875     setActive : function(state)
29876     {
29877         if(this.active == state){
29878             return;
29879         }
29880         
29881         this.active = state;
29882         
29883         if (state) {
29884             this.el.addClass('active');
29885             return;
29886         }
29887         
29888         this.el.removeClass('active');
29889         
29890         return;
29891     },
29892     
29893     setDisabled : function(state)
29894     {
29895         if(this.disabled == state){
29896             return;
29897         }
29898         
29899         this.disabled = state;
29900         
29901         if (state) {
29902             this.el.addClass('disabled');
29903             return;
29904         }
29905         
29906         this.el.removeClass('disabled');
29907     },
29908     
29909     tooltipEl : function()
29910     {
29911         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29912     }
29913 });
29914  
29915
29916  /*
29917  * - LGPL
29918  *
29919  * FieldLabel
29920  * 
29921  */
29922
29923 /**
29924  * @class Roo.bootstrap.FieldLabel
29925  * @extends Roo.bootstrap.Component
29926  * Bootstrap FieldLabel class
29927  * @cfg {String} html contents of the element
29928  * @cfg {String} tag tag of the element default label
29929  * @cfg {String} cls class of the element
29930  * @cfg {String} target label target 
29931  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29932  * @cfg {String} invalidClass default "text-warning"
29933  * @cfg {String} validClass default "text-success"
29934  * @cfg {String} iconTooltip default "This field is required"
29935  * @cfg {String} indicatorpos (left|right) default left
29936  * 
29937  * @constructor
29938  * Create a new FieldLabel
29939  * @param {Object} config The config object
29940  */
29941
29942 Roo.bootstrap.FieldLabel = function(config){
29943     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29944     
29945     this.addEvents({
29946             /**
29947              * @event invalid
29948              * Fires after the field has been marked as invalid.
29949              * @param {Roo.form.FieldLabel} this
29950              * @param {String} msg The validation message
29951              */
29952             invalid : true,
29953             /**
29954              * @event valid
29955              * Fires after the field has been validated with no errors.
29956              * @param {Roo.form.FieldLabel} this
29957              */
29958             valid : true
29959         });
29960 };
29961
29962 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29963     
29964     tag: 'label',
29965     cls: '',
29966     html: '',
29967     target: '',
29968     allowBlank : true,
29969     invalidClass : 'has-warning',
29970     validClass : 'has-success',
29971     iconTooltip : 'This field is required',
29972     indicatorpos : 'left',
29973     
29974     getAutoCreate : function(){
29975         
29976         var cfg = {
29977             tag : this.tag,
29978             cls : 'roo-bootstrap-field-label ' + this.cls,
29979             for : this.target,
29980             cn : [
29981                 {
29982                     tag : 'i',
29983                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29984                     tooltip : this.iconTooltip
29985                 },
29986                 {
29987                     tag : 'span',
29988                     html : this.html
29989                 }
29990             ] 
29991         };
29992         
29993         if(this.indicatorpos == 'right'){
29994             var cfg = {
29995                 tag : this.tag,
29996                 cls : 'roo-bootstrap-field-label ' + this.cls,
29997                 for : this.target,
29998                 cn : [
29999                     {
30000                         tag : 'span',
30001                         html : this.html
30002                     },
30003                     {
30004                         tag : 'i',
30005                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30006                         tooltip : this.iconTooltip
30007                     }
30008                 ] 
30009             };
30010         }
30011         
30012         return cfg;
30013     },
30014     
30015     initEvents: function() 
30016     {
30017         Roo.bootstrap.Element.superclass.initEvents.call(this);
30018         
30019         this.indicator = this.indicatorEl();
30020         
30021         if(this.indicator){
30022             this.indicator.removeClass('visible');
30023             this.indicator.addClass('invisible');
30024         }
30025         
30026         Roo.bootstrap.FieldLabel.register(this);
30027     },
30028     
30029     indicatorEl : function()
30030     {
30031         var indicator = this.el.select('i.roo-required-indicator',true).first();
30032         
30033         if(!indicator){
30034             return false;
30035         }
30036         
30037         return indicator;
30038         
30039     },
30040     
30041     /**
30042      * Mark this field as valid
30043      */
30044     markValid : function()
30045     {
30046         if(this.indicator){
30047             this.indicator.removeClass('visible');
30048             this.indicator.addClass('invisible');
30049         }
30050         
30051         this.el.removeClass(this.invalidClass);
30052         
30053         this.el.addClass(this.validClass);
30054         
30055         this.fireEvent('valid', this);
30056     },
30057     
30058     /**
30059      * Mark this field as invalid
30060      * @param {String} msg The validation message
30061      */
30062     markInvalid : function(msg)
30063     {
30064         if(this.indicator){
30065             this.indicator.removeClass('invisible');
30066             this.indicator.addClass('visible');
30067         }
30068         
30069         this.el.removeClass(this.validClass);
30070         
30071         this.el.addClass(this.invalidClass);
30072         
30073         this.fireEvent('invalid', this, msg);
30074     }
30075     
30076    
30077 });
30078
30079 Roo.apply(Roo.bootstrap.FieldLabel, {
30080     
30081     groups: {},
30082     
30083      /**
30084     * register a FieldLabel Group
30085     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30086     */
30087     register : function(label)
30088     {
30089         if(this.groups.hasOwnProperty(label.target)){
30090             return;
30091         }
30092      
30093         this.groups[label.target] = label;
30094         
30095     },
30096     /**
30097     * fetch a FieldLabel Group based on the target
30098     * @param {string} target
30099     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30100     */
30101     get: function(target) {
30102         if (typeof(this.groups[target]) == 'undefined') {
30103             return false;
30104         }
30105         
30106         return this.groups[target] ;
30107     }
30108 });
30109
30110  
30111
30112  /*
30113  * - LGPL
30114  *
30115  * page DateSplitField.
30116  * 
30117  */
30118
30119
30120 /**
30121  * @class Roo.bootstrap.DateSplitField
30122  * @extends Roo.bootstrap.Component
30123  * Bootstrap DateSplitField class
30124  * @cfg {string} fieldLabel - the label associated
30125  * @cfg {Number} labelWidth set the width of label (0-12)
30126  * @cfg {String} labelAlign (top|left)
30127  * @cfg {Boolean} dayAllowBlank (true|false) default false
30128  * @cfg {Boolean} monthAllowBlank (true|false) default false
30129  * @cfg {Boolean} yearAllowBlank (true|false) default false
30130  * @cfg {string} dayPlaceholder 
30131  * @cfg {string} monthPlaceholder
30132  * @cfg {string} yearPlaceholder
30133  * @cfg {string} dayFormat default 'd'
30134  * @cfg {string} monthFormat default 'm'
30135  * @cfg {string} yearFormat default 'Y'
30136  * @cfg {Number} labellg set the width of label (1-12)
30137  * @cfg {Number} labelmd set the width of label (1-12)
30138  * @cfg {Number} labelsm set the width of label (1-12)
30139  * @cfg {Number} labelxs set the width of label (1-12)
30140
30141  *     
30142  * @constructor
30143  * Create a new DateSplitField
30144  * @param {Object} config The config object
30145  */
30146
30147 Roo.bootstrap.DateSplitField = function(config){
30148     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30149     
30150     this.addEvents({
30151         // raw events
30152          /**
30153          * @event years
30154          * getting the data of years
30155          * @param {Roo.bootstrap.DateSplitField} this
30156          * @param {Object} years
30157          */
30158         "years" : true,
30159         /**
30160          * @event days
30161          * getting the data of days
30162          * @param {Roo.bootstrap.DateSplitField} this
30163          * @param {Object} days
30164          */
30165         "days" : true,
30166         /**
30167          * @event invalid
30168          * Fires after the field has been marked as invalid.
30169          * @param {Roo.form.Field} this
30170          * @param {String} msg The validation message
30171          */
30172         invalid : true,
30173        /**
30174          * @event valid
30175          * Fires after the field has been validated with no errors.
30176          * @param {Roo.form.Field} this
30177          */
30178         valid : true
30179     });
30180 };
30181
30182 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30183     
30184     fieldLabel : '',
30185     labelAlign : 'top',
30186     labelWidth : 3,
30187     dayAllowBlank : false,
30188     monthAllowBlank : false,
30189     yearAllowBlank : false,
30190     dayPlaceholder : '',
30191     monthPlaceholder : '',
30192     yearPlaceholder : '',
30193     dayFormat : 'd',
30194     monthFormat : 'm',
30195     yearFormat : 'Y',
30196     isFormField : true,
30197     labellg : 0,
30198     labelmd : 0,
30199     labelsm : 0,
30200     labelxs : 0,
30201     
30202     getAutoCreate : function()
30203     {
30204         var cfg = {
30205             tag : 'div',
30206             cls : 'row roo-date-split-field-group',
30207             cn : [
30208                 {
30209                     tag : 'input',
30210                     type : 'hidden',
30211                     cls : 'form-hidden-field roo-date-split-field-group-value',
30212                     name : this.name
30213                 }
30214             ]
30215         };
30216         
30217         var labelCls = 'col-md-12';
30218         var contentCls = 'col-md-4';
30219         
30220         if(this.fieldLabel){
30221             
30222             var label = {
30223                 tag : 'div',
30224                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30225                 cn : [
30226                     {
30227                         tag : 'label',
30228                         html : this.fieldLabel
30229                     }
30230                 ]
30231             };
30232             
30233             if(this.labelAlign == 'left'){
30234             
30235                 if(this.labelWidth > 12){
30236                     label.style = "width: " + this.labelWidth + 'px';
30237                 }
30238
30239                 if(this.labelWidth < 13 && this.labelmd == 0){
30240                     this.labelmd = this.labelWidth;
30241                 }
30242
30243                 if(this.labellg > 0){
30244                     labelCls = ' col-lg-' + this.labellg;
30245                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30246                 }
30247
30248                 if(this.labelmd > 0){
30249                     labelCls = ' col-md-' + this.labelmd;
30250                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30251                 }
30252
30253                 if(this.labelsm > 0){
30254                     labelCls = ' col-sm-' + this.labelsm;
30255                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30256                 }
30257
30258                 if(this.labelxs > 0){
30259                     labelCls = ' col-xs-' + this.labelxs;
30260                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30261                 }
30262             }
30263             
30264             label.cls += ' ' + labelCls;
30265             
30266             cfg.cn.push(label);
30267         }
30268         
30269         Roo.each(['day', 'month', 'year'], function(t){
30270             cfg.cn.push({
30271                 tag : 'div',
30272                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30273             });
30274         }, this);
30275         
30276         return cfg;
30277     },
30278     
30279     inputEl: function ()
30280     {
30281         return this.el.select('.roo-date-split-field-group-value', true).first();
30282     },
30283     
30284     onRender : function(ct, position) 
30285     {
30286         var _this = this;
30287         
30288         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30289         
30290         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30291         
30292         this.dayField = new Roo.bootstrap.ComboBox({
30293             allowBlank : this.dayAllowBlank,
30294             alwaysQuery : true,
30295             displayField : 'value',
30296             editable : false,
30297             fieldLabel : '',
30298             forceSelection : true,
30299             mode : 'local',
30300             placeholder : this.dayPlaceholder,
30301             selectOnFocus : true,
30302             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30303             triggerAction : 'all',
30304             typeAhead : true,
30305             valueField : 'value',
30306             store : new Roo.data.SimpleStore({
30307                 data : (function() {    
30308                     var days = [];
30309                     _this.fireEvent('days', _this, days);
30310                     return days;
30311                 })(),
30312                 fields : [ 'value' ]
30313             }),
30314             listeners : {
30315                 select : function (_self, record, index)
30316                 {
30317                     _this.setValue(_this.getValue());
30318                 }
30319             }
30320         });
30321
30322         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30323         
30324         this.monthField = new Roo.bootstrap.MonthField({
30325             after : '<i class=\"fa fa-calendar\"></i>',
30326             allowBlank : this.monthAllowBlank,
30327             placeholder : this.monthPlaceholder,
30328             readOnly : true,
30329             listeners : {
30330                 render : function (_self)
30331                 {
30332                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30333                         e.preventDefault();
30334                         _self.focus();
30335                     });
30336                 },
30337                 select : function (_self, oldvalue, newvalue)
30338                 {
30339                     _this.setValue(_this.getValue());
30340                 }
30341             }
30342         });
30343         
30344         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30345         
30346         this.yearField = new Roo.bootstrap.ComboBox({
30347             allowBlank : this.yearAllowBlank,
30348             alwaysQuery : true,
30349             displayField : 'value',
30350             editable : false,
30351             fieldLabel : '',
30352             forceSelection : true,
30353             mode : 'local',
30354             placeholder : this.yearPlaceholder,
30355             selectOnFocus : true,
30356             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30357             triggerAction : 'all',
30358             typeAhead : true,
30359             valueField : 'value',
30360             store : new Roo.data.SimpleStore({
30361                 data : (function() {
30362                     var years = [];
30363                     _this.fireEvent('years', _this, years);
30364                     return years;
30365                 })(),
30366                 fields : [ 'value' ]
30367             }),
30368             listeners : {
30369                 select : function (_self, record, index)
30370                 {
30371                     _this.setValue(_this.getValue());
30372                 }
30373             }
30374         });
30375
30376         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30377     },
30378     
30379     setValue : function(v, format)
30380     {
30381         this.inputEl.dom.value = v;
30382         
30383         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30384         
30385         var d = Date.parseDate(v, f);
30386         
30387         if(!d){
30388             this.validate();
30389             return;
30390         }
30391         
30392         this.setDay(d.format(this.dayFormat));
30393         this.setMonth(d.format(this.monthFormat));
30394         this.setYear(d.format(this.yearFormat));
30395         
30396         this.validate();
30397         
30398         return;
30399     },
30400     
30401     setDay : function(v)
30402     {
30403         this.dayField.setValue(v);
30404         this.inputEl.dom.value = this.getValue();
30405         this.validate();
30406         return;
30407     },
30408     
30409     setMonth : function(v)
30410     {
30411         this.monthField.setValue(v, true);
30412         this.inputEl.dom.value = this.getValue();
30413         this.validate();
30414         return;
30415     },
30416     
30417     setYear : function(v)
30418     {
30419         this.yearField.setValue(v);
30420         this.inputEl.dom.value = this.getValue();
30421         this.validate();
30422         return;
30423     },
30424     
30425     getDay : function()
30426     {
30427         return this.dayField.getValue();
30428     },
30429     
30430     getMonth : function()
30431     {
30432         return this.monthField.getValue();
30433     },
30434     
30435     getYear : function()
30436     {
30437         return this.yearField.getValue();
30438     },
30439     
30440     getValue : function()
30441     {
30442         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30443         
30444         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30445         
30446         return date;
30447     },
30448     
30449     reset : function()
30450     {
30451         this.setDay('');
30452         this.setMonth('');
30453         this.setYear('');
30454         this.inputEl.dom.value = '';
30455         this.validate();
30456         return;
30457     },
30458     
30459     validate : function()
30460     {
30461         var d = this.dayField.validate();
30462         var m = this.monthField.validate();
30463         var y = this.yearField.validate();
30464         
30465         var valid = true;
30466         
30467         if(
30468                 (!this.dayAllowBlank && !d) ||
30469                 (!this.monthAllowBlank && !m) ||
30470                 (!this.yearAllowBlank && !y)
30471         ){
30472             valid = false;
30473         }
30474         
30475         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30476             return valid;
30477         }
30478         
30479         if(valid){
30480             this.markValid();
30481             return valid;
30482         }
30483         
30484         this.markInvalid();
30485         
30486         return valid;
30487     },
30488     
30489     markValid : function()
30490     {
30491         
30492         var label = this.el.select('label', true).first();
30493         var icon = this.el.select('i.fa-star', true).first();
30494
30495         if(label && icon){
30496             icon.remove();
30497         }
30498         
30499         this.fireEvent('valid', this);
30500     },
30501     
30502      /**
30503      * Mark this field as invalid
30504      * @param {String} msg The validation message
30505      */
30506     markInvalid : function(msg)
30507     {
30508         
30509         var label = this.el.select('label', true).first();
30510         var icon = this.el.select('i.fa-star', true).first();
30511
30512         if(label && !icon){
30513             this.el.select('.roo-date-split-field-label', true).createChild({
30514                 tag : 'i',
30515                 cls : 'text-danger fa fa-lg fa-star',
30516                 tooltip : 'This field is required',
30517                 style : 'margin-right:5px;'
30518             }, label, true);
30519         }
30520         
30521         this.fireEvent('invalid', this, msg);
30522     },
30523     
30524     clearInvalid : function()
30525     {
30526         var label = this.el.select('label', true).first();
30527         var icon = this.el.select('i.fa-star', true).first();
30528
30529         if(label && icon){
30530             icon.remove();
30531         }
30532         
30533         this.fireEvent('valid', this);
30534     },
30535     
30536     getName: function()
30537     {
30538         return this.name;
30539     }
30540     
30541 });
30542
30543  /**
30544  *
30545  * This is based on 
30546  * http://masonry.desandro.com
30547  *
30548  * The idea is to render all the bricks based on vertical width...
30549  *
30550  * The original code extends 'outlayer' - we might need to use that....
30551  * 
30552  */
30553
30554
30555 /**
30556  * @class Roo.bootstrap.LayoutMasonry
30557  * @extends Roo.bootstrap.Component
30558  * Bootstrap Layout Masonry class
30559  * 
30560  * @constructor
30561  * Create a new Element
30562  * @param {Object} config The config object
30563  */
30564
30565 Roo.bootstrap.LayoutMasonry = function(config){
30566     
30567     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30568     
30569     this.bricks = [];
30570     
30571     Roo.bootstrap.LayoutMasonry.register(this);
30572     
30573     this.addEvents({
30574         // raw events
30575         /**
30576          * @event layout
30577          * Fire after layout the items
30578          * @param {Roo.bootstrap.LayoutMasonry} this
30579          * @param {Roo.EventObject} e
30580          */
30581         "layout" : true
30582     });
30583     
30584 };
30585
30586 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30587     
30588     /**
30589      * @cfg {Boolean} isLayoutInstant = no animation?
30590      */   
30591     isLayoutInstant : false, // needed?
30592    
30593     /**
30594      * @cfg {Number} boxWidth  width of the columns
30595      */   
30596     boxWidth : 450,
30597     
30598       /**
30599      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30600      */   
30601     boxHeight : 0,
30602     
30603     /**
30604      * @cfg {Number} padWidth padding below box..
30605      */   
30606     padWidth : 10, 
30607     
30608     /**
30609      * @cfg {Number} gutter gutter width..
30610      */   
30611     gutter : 10,
30612     
30613      /**
30614      * @cfg {Number} maxCols maximum number of columns
30615      */   
30616     
30617     maxCols: 0,
30618     
30619     /**
30620      * @cfg {Boolean} isAutoInitial defalut true
30621      */   
30622     isAutoInitial : true, 
30623     
30624     containerWidth: 0,
30625     
30626     /**
30627      * @cfg {Boolean} isHorizontal defalut false
30628      */   
30629     isHorizontal : false, 
30630
30631     currentSize : null,
30632     
30633     tag: 'div',
30634     
30635     cls: '',
30636     
30637     bricks: null, //CompositeElement
30638     
30639     cols : 1,
30640     
30641     _isLayoutInited : false,
30642     
30643 //    isAlternative : false, // only use for vertical layout...
30644     
30645     /**
30646      * @cfg {Number} alternativePadWidth padding below box..
30647      */   
30648     alternativePadWidth : 50,
30649     
30650     selectedBrick : [],
30651     
30652     getAutoCreate : function(){
30653         
30654         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30655         
30656         var cfg = {
30657             tag: this.tag,
30658             cls: 'blog-masonary-wrapper ' + this.cls,
30659             cn : {
30660                 cls : 'mas-boxes masonary'
30661             }
30662         };
30663         
30664         return cfg;
30665     },
30666     
30667     getChildContainer: function( )
30668     {
30669         if (this.boxesEl) {
30670             return this.boxesEl;
30671         }
30672         
30673         this.boxesEl = this.el.select('.mas-boxes').first();
30674         
30675         return this.boxesEl;
30676     },
30677     
30678     
30679     initEvents : function()
30680     {
30681         var _this = this;
30682         
30683         if(this.isAutoInitial){
30684             Roo.log('hook children rendered');
30685             this.on('childrenrendered', function() {
30686                 Roo.log('children rendered');
30687                 _this.initial();
30688             } ,this);
30689         }
30690     },
30691     
30692     initial : function()
30693     {
30694         this.selectedBrick = [];
30695         
30696         this.currentSize = this.el.getBox(true);
30697         
30698         Roo.EventManager.onWindowResize(this.resize, this); 
30699
30700         if(!this.isAutoInitial){
30701             this.layout();
30702             return;
30703         }
30704         
30705         this.layout();
30706         
30707         return;
30708         //this.layout.defer(500,this);
30709         
30710     },
30711     
30712     resize : function()
30713     {
30714         var cs = this.el.getBox(true);
30715         
30716         if (
30717                 this.currentSize.width == cs.width && 
30718                 this.currentSize.x == cs.x && 
30719                 this.currentSize.height == cs.height && 
30720                 this.currentSize.y == cs.y 
30721         ) {
30722             Roo.log("no change in with or X or Y");
30723             return;
30724         }
30725         
30726         this.currentSize = cs;
30727         
30728         this.layout();
30729         
30730     },
30731     
30732     layout : function()
30733     {   
30734         this._resetLayout();
30735         
30736         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30737         
30738         this.layoutItems( isInstant );
30739       
30740         this._isLayoutInited = true;
30741         
30742         this.fireEvent('layout', this);
30743         
30744     },
30745     
30746     _resetLayout : function()
30747     {
30748         if(this.isHorizontal){
30749             this.horizontalMeasureColumns();
30750             return;
30751         }
30752         
30753         this.verticalMeasureColumns();
30754         
30755     },
30756     
30757     verticalMeasureColumns : function()
30758     {
30759         this.getContainerWidth();
30760         
30761 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30762 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30763 //            return;
30764 //        }
30765         
30766         var boxWidth = this.boxWidth + this.padWidth;
30767         
30768         if(this.containerWidth < this.boxWidth){
30769             boxWidth = this.containerWidth
30770         }
30771         
30772         var containerWidth = this.containerWidth;
30773         
30774         var cols = Math.floor(containerWidth / boxWidth);
30775         
30776         this.cols = Math.max( cols, 1 );
30777         
30778         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30779         
30780         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30781         
30782         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30783         
30784         this.colWidth = boxWidth + avail - this.padWidth;
30785         
30786         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30787         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30788     },
30789     
30790     horizontalMeasureColumns : function()
30791     {
30792         this.getContainerWidth();
30793         
30794         var boxWidth = this.boxWidth;
30795         
30796         if(this.containerWidth < boxWidth){
30797             boxWidth = this.containerWidth;
30798         }
30799         
30800         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30801         
30802         this.el.setHeight(boxWidth);
30803         
30804     },
30805     
30806     getContainerWidth : function()
30807     {
30808         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30809     },
30810     
30811     layoutItems : function( isInstant )
30812     {
30813         Roo.log(this.bricks);
30814         
30815         var items = Roo.apply([], this.bricks);
30816         
30817         if(this.isHorizontal){
30818             this._horizontalLayoutItems( items , isInstant );
30819             return;
30820         }
30821         
30822 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30823 //            this._verticalAlternativeLayoutItems( items , isInstant );
30824 //            return;
30825 //        }
30826         
30827         this._verticalLayoutItems( items , isInstant );
30828         
30829     },
30830     
30831     _verticalLayoutItems : function ( items , isInstant)
30832     {
30833         if ( !items || !items.length ) {
30834             return;
30835         }
30836         
30837         var standard = [
30838             ['xs', 'xs', 'xs', 'tall'],
30839             ['xs', 'xs', 'tall'],
30840             ['xs', 'xs', 'sm'],
30841             ['xs', 'xs', 'xs'],
30842             ['xs', 'tall'],
30843             ['xs', 'sm'],
30844             ['xs', 'xs'],
30845             ['xs'],
30846             
30847             ['sm', 'xs', 'xs'],
30848             ['sm', 'xs'],
30849             ['sm'],
30850             
30851             ['tall', 'xs', 'xs', 'xs'],
30852             ['tall', 'xs', 'xs'],
30853             ['tall', 'xs'],
30854             ['tall']
30855             
30856         ];
30857         
30858         var queue = [];
30859         
30860         var boxes = [];
30861         
30862         var box = [];
30863         
30864         Roo.each(items, function(item, k){
30865             
30866             switch (item.size) {
30867                 // these layouts take up a full box,
30868                 case 'md' :
30869                 case 'md-left' :
30870                 case 'md-right' :
30871                 case 'wide' :
30872                     
30873                     if(box.length){
30874                         boxes.push(box);
30875                         box = [];
30876                     }
30877                     
30878                     boxes.push([item]);
30879                     
30880                     break;
30881                     
30882                 case 'xs' :
30883                 case 'sm' :
30884                 case 'tall' :
30885                     
30886                     box.push(item);
30887                     
30888                     break;
30889                 default :
30890                     break;
30891                     
30892             }
30893             
30894         }, this);
30895         
30896         if(box.length){
30897             boxes.push(box);
30898             box = [];
30899         }
30900         
30901         var filterPattern = function(box, length)
30902         {
30903             if(!box.length){
30904                 return;
30905             }
30906             
30907             var match = false;
30908             
30909             var pattern = box.slice(0, length);
30910             
30911             var format = [];
30912             
30913             Roo.each(pattern, function(i){
30914                 format.push(i.size);
30915             }, this);
30916             
30917             Roo.each(standard, function(s){
30918                 
30919                 if(String(s) != String(format)){
30920                     return;
30921                 }
30922                 
30923                 match = true;
30924                 return false;
30925                 
30926             }, this);
30927             
30928             if(!match && length == 1){
30929                 return;
30930             }
30931             
30932             if(!match){
30933                 filterPattern(box, length - 1);
30934                 return;
30935             }
30936                 
30937             queue.push(pattern);
30938
30939             box = box.slice(length, box.length);
30940
30941             filterPattern(box, 4);
30942
30943             return;
30944             
30945         }
30946         
30947         Roo.each(boxes, function(box, k){
30948             
30949             if(!box.length){
30950                 return;
30951             }
30952             
30953             if(box.length == 1){
30954                 queue.push(box);
30955                 return;
30956             }
30957             
30958             filterPattern(box, 4);
30959             
30960         }, this);
30961         
30962         this._processVerticalLayoutQueue( queue, isInstant );
30963         
30964     },
30965     
30966 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30967 //    {
30968 //        if ( !items || !items.length ) {
30969 //            return;
30970 //        }
30971 //
30972 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30973 //        
30974 //    },
30975     
30976     _horizontalLayoutItems : function ( items , isInstant)
30977     {
30978         if ( !items || !items.length || items.length < 3) {
30979             return;
30980         }
30981         
30982         items.reverse();
30983         
30984         var eItems = items.slice(0, 3);
30985         
30986         items = items.slice(3, items.length);
30987         
30988         var standard = [
30989             ['xs', 'xs', 'xs', 'wide'],
30990             ['xs', 'xs', 'wide'],
30991             ['xs', 'xs', 'sm'],
30992             ['xs', 'xs', 'xs'],
30993             ['xs', 'wide'],
30994             ['xs', 'sm'],
30995             ['xs', 'xs'],
30996             ['xs'],
30997             
30998             ['sm', 'xs', 'xs'],
30999             ['sm', 'xs'],
31000             ['sm'],
31001             
31002             ['wide', 'xs', 'xs', 'xs'],
31003             ['wide', 'xs', 'xs'],
31004             ['wide', 'xs'],
31005             ['wide'],
31006             
31007             ['wide-thin']
31008         ];
31009         
31010         var queue = [];
31011         
31012         var boxes = [];
31013         
31014         var box = [];
31015         
31016         Roo.each(items, function(item, k){
31017             
31018             switch (item.size) {
31019                 case 'md' :
31020                 case 'md-left' :
31021                 case 'md-right' :
31022                 case 'tall' :
31023                     
31024                     if(box.length){
31025                         boxes.push(box);
31026                         box = [];
31027                     }
31028                     
31029                     boxes.push([item]);
31030                     
31031                     break;
31032                     
31033                 case 'xs' :
31034                 case 'sm' :
31035                 case 'wide' :
31036                 case 'wide-thin' :
31037                     
31038                     box.push(item);
31039                     
31040                     break;
31041                 default :
31042                     break;
31043                     
31044             }
31045             
31046         }, this);
31047         
31048         if(box.length){
31049             boxes.push(box);
31050             box = [];
31051         }
31052         
31053         var filterPattern = function(box, length)
31054         {
31055             if(!box.length){
31056                 return;
31057             }
31058             
31059             var match = false;
31060             
31061             var pattern = box.slice(0, length);
31062             
31063             var format = [];
31064             
31065             Roo.each(pattern, function(i){
31066                 format.push(i.size);
31067             }, this);
31068             
31069             Roo.each(standard, function(s){
31070                 
31071                 if(String(s) != String(format)){
31072                     return;
31073                 }
31074                 
31075                 match = true;
31076                 return false;
31077                 
31078             }, this);
31079             
31080             if(!match && length == 1){
31081                 return;
31082             }
31083             
31084             if(!match){
31085                 filterPattern(box, length - 1);
31086                 return;
31087             }
31088                 
31089             queue.push(pattern);
31090
31091             box = box.slice(length, box.length);
31092
31093             filterPattern(box, 4);
31094
31095             return;
31096             
31097         }
31098         
31099         Roo.each(boxes, function(box, k){
31100             
31101             if(!box.length){
31102                 return;
31103             }
31104             
31105             if(box.length == 1){
31106                 queue.push(box);
31107                 return;
31108             }
31109             
31110             filterPattern(box, 4);
31111             
31112         }, this);
31113         
31114         
31115         var prune = [];
31116         
31117         var pos = this.el.getBox(true);
31118         
31119         var minX = pos.x;
31120         
31121         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31122         
31123         var hit_end = false;
31124         
31125         Roo.each(queue, function(box){
31126             
31127             if(hit_end){
31128                 
31129                 Roo.each(box, function(b){
31130                 
31131                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31132                     b.el.hide();
31133
31134                 }, this);
31135
31136                 return;
31137             }
31138             
31139             var mx = 0;
31140             
31141             Roo.each(box, function(b){
31142                 
31143                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31144                 b.el.show();
31145
31146                 mx = Math.max(mx, b.x);
31147                 
31148             }, this);
31149             
31150             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31151             
31152             if(maxX < minX){
31153                 
31154                 Roo.each(box, function(b){
31155                 
31156                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31157                     b.el.hide();
31158                     
31159                 }, this);
31160                 
31161                 hit_end = true;
31162                 
31163                 return;
31164             }
31165             
31166             prune.push(box);
31167             
31168         }, this);
31169         
31170         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31171     },
31172     
31173     /** Sets position of item in DOM
31174     * @param {Element} item
31175     * @param {Number} x - horizontal position
31176     * @param {Number} y - vertical position
31177     * @param {Boolean} isInstant - disables transitions
31178     */
31179     _processVerticalLayoutQueue : function( queue, isInstant )
31180     {
31181         var pos = this.el.getBox(true);
31182         var x = pos.x;
31183         var y = pos.y;
31184         var maxY = [];
31185         
31186         for (var i = 0; i < this.cols; i++){
31187             maxY[i] = pos.y;
31188         }
31189         
31190         Roo.each(queue, function(box, k){
31191             
31192             var col = k % this.cols;
31193             
31194             Roo.each(box, function(b,kk){
31195                 
31196                 b.el.position('absolute');
31197                 
31198                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31199                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31200                 
31201                 if(b.size == 'md-left' || b.size == 'md-right'){
31202                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31203                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31204                 }
31205                 
31206                 b.el.setWidth(width);
31207                 b.el.setHeight(height);
31208                 // iframe?
31209                 b.el.select('iframe',true).setSize(width,height);
31210                 
31211             }, this);
31212             
31213             for (var i = 0; i < this.cols; i++){
31214                 
31215                 if(maxY[i] < maxY[col]){
31216                     col = i;
31217                     continue;
31218                 }
31219                 
31220                 col = Math.min(col, i);
31221                 
31222             }
31223             
31224             x = pos.x + col * (this.colWidth + this.padWidth);
31225             
31226             y = maxY[col];
31227             
31228             var positions = [];
31229             
31230             switch (box.length){
31231                 case 1 :
31232                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31233                     break;
31234                 case 2 :
31235                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31236                     break;
31237                 case 3 :
31238                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31239                     break;
31240                 case 4 :
31241                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31242                     break;
31243                 default :
31244                     break;
31245             }
31246             
31247             Roo.each(box, function(b,kk){
31248                 
31249                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31250                 
31251                 var sz = b.el.getSize();
31252                 
31253                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31254                 
31255             }, this);
31256             
31257         }, this);
31258         
31259         var mY = 0;
31260         
31261         for (var i = 0; i < this.cols; i++){
31262             mY = Math.max(mY, maxY[i]);
31263         }
31264         
31265         this.el.setHeight(mY - pos.y);
31266         
31267     },
31268     
31269 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31270 //    {
31271 //        var pos = this.el.getBox(true);
31272 //        var x = pos.x;
31273 //        var y = pos.y;
31274 //        var maxX = pos.right;
31275 //        
31276 //        var maxHeight = 0;
31277 //        
31278 //        Roo.each(items, function(item, k){
31279 //            
31280 //            var c = k % 2;
31281 //            
31282 //            item.el.position('absolute');
31283 //                
31284 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31285 //
31286 //            item.el.setWidth(width);
31287 //
31288 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31289 //
31290 //            item.el.setHeight(height);
31291 //            
31292 //            if(c == 0){
31293 //                item.el.setXY([x, y], isInstant ? false : true);
31294 //            } else {
31295 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31296 //            }
31297 //            
31298 //            y = y + height + this.alternativePadWidth;
31299 //            
31300 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31301 //            
31302 //        }, this);
31303 //        
31304 //        this.el.setHeight(maxHeight);
31305 //        
31306 //    },
31307     
31308     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31309     {
31310         var pos = this.el.getBox(true);
31311         
31312         var minX = pos.x;
31313         var minY = pos.y;
31314         
31315         var maxX = pos.right;
31316         
31317         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31318         
31319         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31320         
31321         Roo.each(queue, function(box, k){
31322             
31323             Roo.each(box, function(b, kk){
31324                 
31325                 b.el.position('absolute');
31326                 
31327                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31328                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31329                 
31330                 if(b.size == 'md-left' || b.size == 'md-right'){
31331                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31332                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31333                 }
31334                 
31335                 b.el.setWidth(width);
31336                 b.el.setHeight(height);
31337                 
31338             }, this);
31339             
31340             if(!box.length){
31341                 return;
31342             }
31343             
31344             var positions = [];
31345             
31346             switch (box.length){
31347                 case 1 :
31348                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31349                     break;
31350                 case 2 :
31351                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31352                     break;
31353                 case 3 :
31354                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31355                     break;
31356                 case 4 :
31357                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31358                     break;
31359                 default :
31360                     break;
31361             }
31362             
31363             Roo.each(box, function(b,kk){
31364                 
31365                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31366                 
31367                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31368                 
31369             }, this);
31370             
31371         }, this);
31372         
31373     },
31374     
31375     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31376     {
31377         Roo.each(eItems, function(b,k){
31378             
31379             b.size = (k == 0) ? 'sm' : 'xs';
31380             b.x = (k == 0) ? 2 : 1;
31381             b.y = (k == 0) ? 2 : 1;
31382             
31383             b.el.position('absolute');
31384             
31385             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31386                 
31387             b.el.setWidth(width);
31388             
31389             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31390             
31391             b.el.setHeight(height);
31392             
31393         }, this);
31394
31395         var positions = [];
31396         
31397         positions.push({
31398             x : maxX - this.unitWidth * 2 - this.gutter,
31399             y : minY
31400         });
31401         
31402         positions.push({
31403             x : maxX - this.unitWidth,
31404             y : minY + (this.unitWidth + this.gutter) * 2
31405         });
31406         
31407         positions.push({
31408             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31409             y : minY
31410         });
31411         
31412         Roo.each(eItems, function(b,k){
31413             
31414             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31415
31416         }, this);
31417         
31418     },
31419     
31420     getVerticalOneBoxColPositions : function(x, y, box)
31421     {
31422         var pos = [];
31423         
31424         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31425         
31426         if(box[0].size == 'md-left'){
31427             rand = 0;
31428         }
31429         
31430         if(box[0].size == 'md-right'){
31431             rand = 1;
31432         }
31433         
31434         pos.push({
31435             x : x + (this.unitWidth + this.gutter) * rand,
31436             y : y
31437         });
31438         
31439         return pos;
31440     },
31441     
31442     getVerticalTwoBoxColPositions : function(x, y, box)
31443     {
31444         var pos = [];
31445         
31446         if(box[0].size == 'xs'){
31447             
31448             pos.push({
31449                 x : x,
31450                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31451             });
31452
31453             pos.push({
31454                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31455                 y : y
31456             });
31457             
31458             return pos;
31459             
31460         }
31461         
31462         pos.push({
31463             x : x,
31464             y : y
31465         });
31466
31467         pos.push({
31468             x : x + (this.unitWidth + this.gutter) * 2,
31469             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31470         });
31471         
31472         return pos;
31473         
31474     },
31475     
31476     getVerticalThreeBoxColPositions : function(x, y, box)
31477     {
31478         var pos = [];
31479         
31480         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31481             
31482             pos.push({
31483                 x : x,
31484                 y : y
31485             });
31486
31487             pos.push({
31488                 x : x + (this.unitWidth + this.gutter) * 1,
31489                 y : y
31490             });
31491             
31492             pos.push({
31493                 x : x + (this.unitWidth + this.gutter) * 2,
31494                 y : y
31495             });
31496             
31497             return pos;
31498             
31499         }
31500         
31501         if(box[0].size == 'xs' && box[1].size == 'xs'){
31502             
31503             pos.push({
31504                 x : x,
31505                 y : y
31506             });
31507
31508             pos.push({
31509                 x : x,
31510                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31511             });
31512             
31513             pos.push({
31514                 x : x + (this.unitWidth + this.gutter) * 1,
31515                 y : y
31516             });
31517             
31518             return pos;
31519             
31520         }
31521         
31522         pos.push({
31523             x : x,
31524             y : y
31525         });
31526
31527         pos.push({
31528             x : x + (this.unitWidth + this.gutter) * 2,
31529             y : y
31530         });
31531
31532         pos.push({
31533             x : x + (this.unitWidth + this.gutter) * 2,
31534             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31535         });
31536             
31537         return pos;
31538         
31539     },
31540     
31541     getVerticalFourBoxColPositions : function(x, y, box)
31542     {
31543         var pos = [];
31544         
31545         if(box[0].size == 'xs'){
31546             
31547             pos.push({
31548                 x : x,
31549                 y : y
31550             });
31551
31552             pos.push({
31553                 x : x,
31554                 y : y + (this.unitHeight + this.gutter) * 1
31555             });
31556             
31557             pos.push({
31558                 x : x,
31559                 y : y + (this.unitHeight + this.gutter) * 2
31560             });
31561             
31562             pos.push({
31563                 x : x + (this.unitWidth + this.gutter) * 1,
31564                 y : y
31565             });
31566             
31567             return pos;
31568             
31569         }
31570         
31571         pos.push({
31572             x : x,
31573             y : y
31574         });
31575
31576         pos.push({
31577             x : x + (this.unitWidth + this.gutter) * 2,
31578             y : y
31579         });
31580
31581         pos.push({
31582             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31583             y : y + (this.unitHeight + this.gutter) * 1
31584         });
31585
31586         pos.push({
31587             x : x + (this.unitWidth + this.gutter) * 2,
31588             y : y + (this.unitWidth + this.gutter) * 2
31589         });
31590
31591         return pos;
31592         
31593     },
31594     
31595     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31596     {
31597         var pos = [];
31598         
31599         if(box[0].size == 'md-left'){
31600             pos.push({
31601                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31602                 y : minY
31603             });
31604             
31605             return pos;
31606         }
31607         
31608         if(box[0].size == 'md-right'){
31609             pos.push({
31610                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31611                 y : minY + (this.unitWidth + this.gutter) * 1
31612             });
31613             
31614             return pos;
31615         }
31616         
31617         var rand = Math.floor(Math.random() * (4 - box[0].y));
31618         
31619         pos.push({
31620             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31621             y : minY + (this.unitWidth + this.gutter) * rand
31622         });
31623         
31624         return pos;
31625         
31626     },
31627     
31628     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31629     {
31630         var pos = [];
31631         
31632         if(box[0].size == 'xs'){
31633             
31634             pos.push({
31635                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31636                 y : minY
31637             });
31638
31639             pos.push({
31640                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31641                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31642             });
31643             
31644             return pos;
31645             
31646         }
31647         
31648         pos.push({
31649             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31650             y : minY
31651         });
31652
31653         pos.push({
31654             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31655             y : minY + (this.unitWidth + this.gutter) * 2
31656         });
31657         
31658         return pos;
31659         
31660     },
31661     
31662     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31663     {
31664         var pos = [];
31665         
31666         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31667             
31668             pos.push({
31669                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31670                 y : minY
31671             });
31672
31673             pos.push({
31674                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31675                 y : minY + (this.unitWidth + this.gutter) * 1
31676             });
31677             
31678             pos.push({
31679                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31680                 y : minY + (this.unitWidth + this.gutter) * 2
31681             });
31682             
31683             return pos;
31684             
31685         }
31686         
31687         if(box[0].size == 'xs' && box[1].size == 'xs'){
31688             
31689             pos.push({
31690                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31691                 y : minY
31692             });
31693
31694             pos.push({
31695                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31696                 y : minY
31697             });
31698             
31699             pos.push({
31700                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31701                 y : minY + (this.unitWidth + this.gutter) * 1
31702             });
31703             
31704             return pos;
31705             
31706         }
31707         
31708         pos.push({
31709             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31710             y : minY
31711         });
31712
31713         pos.push({
31714             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31715             y : minY + (this.unitWidth + this.gutter) * 2
31716         });
31717
31718         pos.push({
31719             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31720             y : minY + (this.unitWidth + this.gutter) * 2
31721         });
31722             
31723         return pos;
31724         
31725     },
31726     
31727     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31728     {
31729         var pos = [];
31730         
31731         if(box[0].size == 'xs'){
31732             
31733             pos.push({
31734                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31735                 y : minY
31736             });
31737
31738             pos.push({
31739                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31740                 y : minY
31741             });
31742             
31743             pos.push({
31744                 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),
31745                 y : minY
31746             });
31747             
31748             pos.push({
31749                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31750                 y : minY + (this.unitWidth + this.gutter) * 1
31751             });
31752             
31753             return pos;
31754             
31755         }
31756         
31757         pos.push({
31758             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31759             y : minY
31760         });
31761         
31762         pos.push({
31763             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31764             y : minY + (this.unitWidth + this.gutter) * 2
31765         });
31766         
31767         pos.push({
31768             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31769             y : minY + (this.unitWidth + this.gutter) * 2
31770         });
31771         
31772         pos.push({
31773             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),
31774             y : minY + (this.unitWidth + this.gutter) * 2
31775         });
31776
31777         return pos;
31778         
31779     },
31780     
31781     /**
31782     * remove a Masonry Brick
31783     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31784     */
31785     removeBrick : function(brick_id)
31786     {
31787         if (!brick_id) {
31788             return;
31789         }
31790         
31791         for (var i = 0; i<this.bricks.length; i++) {
31792             if (this.bricks[i].id == brick_id) {
31793                 this.bricks.splice(i,1);
31794                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31795                 this.initial();
31796             }
31797         }
31798     },
31799     
31800     /**
31801     * adds a Masonry Brick
31802     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31803     */
31804     addBrick : function(cfg)
31805     {
31806         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31807         //this.register(cn);
31808         cn.parentId = this.id;
31809         cn.onRender(this.el, null);
31810         return cn;
31811     },
31812     
31813     /**
31814     * register a Masonry Brick
31815     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31816     */
31817     
31818     register : function(brick)
31819     {
31820         this.bricks.push(brick);
31821         brick.masonryId = this.id;
31822     },
31823     
31824     /**
31825     * clear all the Masonry Brick
31826     */
31827     clearAll : function()
31828     {
31829         this.bricks = [];
31830         //this.getChildContainer().dom.innerHTML = "";
31831         this.el.dom.innerHTML = '';
31832     },
31833     
31834     getSelected : function()
31835     {
31836         if (!this.selectedBrick) {
31837             return false;
31838         }
31839         
31840         return this.selectedBrick;
31841     }
31842 });
31843
31844 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31845     
31846     groups: {},
31847      /**
31848     * register a Masonry Layout
31849     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31850     */
31851     
31852     register : function(layout)
31853     {
31854         this.groups[layout.id] = layout;
31855     },
31856     /**
31857     * fetch a  Masonry Layout based on the masonry layout ID
31858     * @param {string} the masonry layout to add
31859     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31860     */
31861     
31862     get: function(layout_id) {
31863         if (typeof(this.groups[layout_id]) == 'undefined') {
31864             return false;
31865         }
31866         return this.groups[layout_id] ;
31867     }
31868     
31869     
31870     
31871 });
31872
31873  
31874
31875  /**
31876  *
31877  * This is based on 
31878  * http://masonry.desandro.com
31879  *
31880  * The idea is to render all the bricks based on vertical width...
31881  *
31882  * The original code extends 'outlayer' - we might need to use that....
31883  * 
31884  */
31885
31886
31887 /**
31888  * @class Roo.bootstrap.LayoutMasonryAuto
31889  * @extends Roo.bootstrap.Component
31890  * Bootstrap Layout Masonry class
31891  * 
31892  * @constructor
31893  * Create a new Element
31894  * @param {Object} config The config object
31895  */
31896
31897 Roo.bootstrap.LayoutMasonryAuto = function(config){
31898     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31899 };
31900
31901 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31902     
31903       /**
31904      * @cfg {Boolean} isFitWidth  - resize the width..
31905      */   
31906     isFitWidth : false,  // options..
31907     /**
31908      * @cfg {Boolean} isOriginLeft = left align?
31909      */   
31910     isOriginLeft : true,
31911     /**
31912      * @cfg {Boolean} isOriginTop = top align?
31913      */   
31914     isOriginTop : false,
31915     /**
31916      * @cfg {Boolean} isLayoutInstant = no animation?
31917      */   
31918     isLayoutInstant : false, // needed?
31919     /**
31920      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31921      */   
31922     isResizingContainer : true,
31923     /**
31924      * @cfg {Number} columnWidth  width of the columns 
31925      */   
31926     
31927     columnWidth : 0,
31928     
31929     /**
31930      * @cfg {Number} maxCols maximum number of columns
31931      */   
31932     
31933     maxCols: 0,
31934     /**
31935      * @cfg {Number} padHeight padding below box..
31936      */   
31937     
31938     padHeight : 10, 
31939     
31940     /**
31941      * @cfg {Boolean} isAutoInitial defalut true
31942      */   
31943     
31944     isAutoInitial : true, 
31945     
31946     // private?
31947     gutter : 0,
31948     
31949     containerWidth: 0,
31950     initialColumnWidth : 0,
31951     currentSize : null,
31952     
31953     colYs : null, // array.
31954     maxY : 0,
31955     padWidth: 10,
31956     
31957     
31958     tag: 'div',
31959     cls: '',
31960     bricks: null, //CompositeElement
31961     cols : 0, // array?
31962     // element : null, // wrapped now this.el
31963     _isLayoutInited : null, 
31964     
31965     
31966     getAutoCreate : function(){
31967         
31968         var cfg = {
31969             tag: this.tag,
31970             cls: 'blog-masonary-wrapper ' + this.cls,
31971             cn : {
31972                 cls : 'mas-boxes masonary'
31973             }
31974         };
31975         
31976         return cfg;
31977     },
31978     
31979     getChildContainer: function( )
31980     {
31981         if (this.boxesEl) {
31982             return this.boxesEl;
31983         }
31984         
31985         this.boxesEl = this.el.select('.mas-boxes').first();
31986         
31987         return this.boxesEl;
31988     },
31989     
31990     
31991     initEvents : function()
31992     {
31993         var _this = this;
31994         
31995         if(this.isAutoInitial){
31996             Roo.log('hook children rendered');
31997             this.on('childrenrendered', function() {
31998                 Roo.log('children rendered');
31999                 _this.initial();
32000             } ,this);
32001         }
32002         
32003     },
32004     
32005     initial : function()
32006     {
32007         this.reloadItems();
32008
32009         this.currentSize = this.el.getBox(true);
32010
32011         /// was window resize... - let's see if this works..
32012         Roo.EventManager.onWindowResize(this.resize, this); 
32013
32014         if(!this.isAutoInitial){
32015             this.layout();
32016             return;
32017         }
32018         
32019         this.layout.defer(500,this);
32020     },
32021     
32022     reloadItems: function()
32023     {
32024         this.bricks = this.el.select('.masonry-brick', true);
32025         
32026         this.bricks.each(function(b) {
32027             //Roo.log(b.getSize());
32028             if (!b.attr('originalwidth')) {
32029                 b.attr('originalwidth',  b.getSize().width);
32030             }
32031             
32032         });
32033         
32034         Roo.log(this.bricks.elements.length);
32035     },
32036     
32037     resize : function()
32038     {
32039         Roo.log('resize');
32040         var cs = this.el.getBox(true);
32041         
32042         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32043             Roo.log("no change in with or X");
32044             return;
32045         }
32046         this.currentSize = cs;
32047         this.layout();
32048     },
32049     
32050     layout : function()
32051     {
32052          Roo.log('layout');
32053         this._resetLayout();
32054         //this._manageStamps();
32055       
32056         // don't animate first layout
32057         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32058         this.layoutItems( isInstant );
32059       
32060         // flag for initalized
32061         this._isLayoutInited = true;
32062     },
32063     
32064     layoutItems : function( isInstant )
32065     {
32066         //var items = this._getItemsForLayout( this.items );
32067         // original code supports filtering layout items.. we just ignore it..
32068         
32069         this._layoutItems( this.bricks , isInstant );
32070       
32071         this._postLayout();
32072     },
32073     _layoutItems : function ( items , isInstant)
32074     {
32075        //this.fireEvent( 'layout', this, items );
32076     
32077
32078         if ( !items || !items.elements.length ) {
32079           // no items, emit event with empty array
32080             return;
32081         }
32082
32083         var queue = [];
32084         items.each(function(item) {
32085             Roo.log("layout item");
32086             Roo.log(item);
32087             // get x/y object from method
32088             var position = this._getItemLayoutPosition( item );
32089             // enqueue
32090             position.item = item;
32091             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32092             queue.push( position );
32093         }, this);
32094       
32095         this._processLayoutQueue( queue );
32096     },
32097     /** Sets position of item in DOM
32098     * @param {Element} item
32099     * @param {Number} x - horizontal position
32100     * @param {Number} y - vertical position
32101     * @param {Boolean} isInstant - disables transitions
32102     */
32103     _processLayoutQueue : function( queue )
32104     {
32105         for ( var i=0, len = queue.length; i < len; i++ ) {
32106             var obj = queue[i];
32107             obj.item.position('absolute');
32108             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32109         }
32110     },
32111       
32112     
32113     /**
32114     * Any logic you want to do after each layout,
32115     * i.e. size the container
32116     */
32117     _postLayout : function()
32118     {
32119         this.resizeContainer();
32120     },
32121     
32122     resizeContainer : function()
32123     {
32124         if ( !this.isResizingContainer ) {
32125             return;
32126         }
32127         var size = this._getContainerSize();
32128         if ( size ) {
32129             this.el.setSize(size.width,size.height);
32130             this.boxesEl.setSize(size.width,size.height);
32131         }
32132     },
32133     
32134     
32135     
32136     _resetLayout : function()
32137     {
32138         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32139         this.colWidth = this.el.getWidth();
32140         //this.gutter = this.el.getWidth(); 
32141         
32142         this.measureColumns();
32143
32144         // reset column Y
32145         var i = this.cols;
32146         this.colYs = [];
32147         while (i--) {
32148             this.colYs.push( 0 );
32149         }
32150     
32151         this.maxY = 0;
32152     },
32153
32154     measureColumns : function()
32155     {
32156         this.getContainerWidth();
32157       // if columnWidth is 0, default to outerWidth of first item
32158         if ( !this.columnWidth ) {
32159             var firstItem = this.bricks.first();
32160             Roo.log(firstItem);
32161             this.columnWidth  = this.containerWidth;
32162             if (firstItem && firstItem.attr('originalwidth') ) {
32163                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32164             }
32165             // columnWidth fall back to item of first element
32166             Roo.log("set column width?");
32167                         this.initialColumnWidth = this.columnWidth  ;
32168
32169             // if first elem has no width, default to size of container
32170             
32171         }
32172         
32173         
32174         if (this.initialColumnWidth) {
32175             this.columnWidth = this.initialColumnWidth;
32176         }
32177         
32178         
32179             
32180         // column width is fixed at the top - however if container width get's smaller we should
32181         // reduce it...
32182         
32183         // this bit calcs how man columns..
32184             
32185         var columnWidth = this.columnWidth += this.gutter;
32186       
32187         // calculate columns
32188         var containerWidth = this.containerWidth + this.gutter;
32189         
32190         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32191         // fix rounding errors, typically with gutters
32192         var excess = columnWidth - containerWidth % columnWidth;
32193         
32194         
32195         // if overshoot is less than a pixel, round up, otherwise floor it
32196         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32197         cols = Math[ mathMethod ]( cols );
32198         this.cols = Math.max( cols, 1 );
32199         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32200         
32201          // padding positioning..
32202         var totalColWidth = this.cols * this.columnWidth;
32203         var padavail = this.containerWidth - totalColWidth;
32204         // so for 2 columns - we need 3 'pads'
32205         
32206         var padNeeded = (1+this.cols) * this.padWidth;
32207         
32208         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32209         
32210         this.columnWidth += padExtra
32211         //this.padWidth = Math.floor(padavail /  ( this.cols));
32212         
32213         // adjust colum width so that padding is fixed??
32214         
32215         // we have 3 columns ... total = width * 3
32216         // we have X left over... that should be used by 
32217         
32218         //if (this.expandC) {
32219             
32220         //}
32221         
32222         
32223         
32224     },
32225     
32226     getContainerWidth : function()
32227     {
32228        /* // container is parent if fit width
32229         var container = this.isFitWidth ? this.element.parentNode : this.element;
32230         // check that this.size and size are there
32231         // IE8 triggers resize on body size change, so they might not be
32232         
32233         var size = getSize( container );  //FIXME
32234         this.containerWidth = size && size.innerWidth; //FIXME
32235         */
32236          
32237         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32238         
32239     },
32240     
32241     _getItemLayoutPosition : function( item )  // what is item?
32242     {
32243         // we resize the item to our columnWidth..
32244       
32245         item.setWidth(this.columnWidth);
32246         item.autoBoxAdjust  = false;
32247         
32248         var sz = item.getSize();
32249  
32250         // how many columns does this brick span
32251         var remainder = this.containerWidth % this.columnWidth;
32252         
32253         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32254         // round if off by 1 pixel, otherwise use ceil
32255         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32256         colSpan = Math.min( colSpan, this.cols );
32257         
32258         // normally this should be '1' as we dont' currently allow multi width columns..
32259         
32260         var colGroup = this._getColGroup( colSpan );
32261         // get the minimum Y value from the columns
32262         var minimumY = Math.min.apply( Math, colGroup );
32263         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32264         
32265         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32266          
32267         // position the brick
32268         var position = {
32269             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32270             y: this.currentSize.y + minimumY + this.padHeight
32271         };
32272         
32273         Roo.log(position);
32274         // apply setHeight to necessary columns
32275         var setHeight = minimumY + sz.height + this.padHeight;
32276         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32277         
32278         var setSpan = this.cols + 1 - colGroup.length;
32279         for ( var i = 0; i < setSpan; i++ ) {
32280           this.colYs[ shortColIndex + i ] = setHeight ;
32281         }
32282       
32283         return position;
32284     },
32285     
32286     /**
32287      * @param {Number} colSpan - number of columns the element spans
32288      * @returns {Array} colGroup
32289      */
32290     _getColGroup : function( colSpan )
32291     {
32292         if ( colSpan < 2 ) {
32293           // if brick spans only one column, use all the column Ys
32294           return this.colYs;
32295         }
32296       
32297         var colGroup = [];
32298         // how many different places could this brick fit horizontally
32299         var groupCount = this.cols + 1 - colSpan;
32300         // for each group potential horizontal position
32301         for ( var i = 0; i < groupCount; i++ ) {
32302           // make an array of colY values for that one group
32303           var groupColYs = this.colYs.slice( i, i + colSpan );
32304           // and get the max value of the array
32305           colGroup[i] = Math.max.apply( Math, groupColYs );
32306         }
32307         return colGroup;
32308     },
32309     /*
32310     _manageStamp : function( stamp )
32311     {
32312         var stampSize =  stamp.getSize();
32313         var offset = stamp.getBox();
32314         // get the columns that this stamp affects
32315         var firstX = this.isOriginLeft ? offset.x : offset.right;
32316         var lastX = firstX + stampSize.width;
32317         var firstCol = Math.floor( firstX / this.columnWidth );
32318         firstCol = Math.max( 0, firstCol );
32319         
32320         var lastCol = Math.floor( lastX / this.columnWidth );
32321         // lastCol should not go over if multiple of columnWidth #425
32322         lastCol -= lastX % this.columnWidth ? 0 : 1;
32323         lastCol = Math.min( this.cols - 1, lastCol );
32324         
32325         // set colYs to bottom of the stamp
32326         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32327             stampSize.height;
32328             
32329         for ( var i = firstCol; i <= lastCol; i++ ) {
32330           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32331         }
32332     },
32333     */
32334     
32335     _getContainerSize : function()
32336     {
32337         this.maxY = Math.max.apply( Math, this.colYs );
32338         var size = {
32339             height: this.maxY
32340         };
32341       
32342         if ( this.isFitWidth ) {
32343             size.width = this._getContainerFitWidth();
32344         }
32345       
32346         return size;
32347     },
32348     
32349     _getContainerFitWidth : function()
32350     {
32351         var unusedCols = 0;
32352         // count unused columns
32353         var i = this.cols;
32354         while ( --i ) {
32355           if ( this.colYs[i] !== 0 ) {
32356             break;
32357           }
32358           unusedCols++;
32359         }
32360         // fit container to columns that have been used
32361         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32362     },
32363     
32364     needsResizeLayout : function()
32365     {
32366         var previousWidth = this.containerWidth;
32367         this.getContainerWidth();
32368         return previousWidth !== this.containerWidth;
32369     }
32370  
32371 });
32372
32373  
32374
32375  /*
32376  * - LGPL
32377  *
32378  * element
32379  * 
32380  */
32381
32382 /**
32383  * @class Roo.bootstrap.MasonryBrick
32384  * @extends Roo.bootstrap.Component
32385  * Bootstrap MasonryBrick class
32386  * 
32387  * @constructor
32388  * Create a new MasonryBrick
32389  * @param {Object} config The config object
32390  */
32391
32392 Roo.bootstrap.MasonryBrick = function(config){
32393     
32394     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32395     
32396     Roo.bootstrap.MasonryBrick.register(this);
32397     
32398     this.addEvents({
32399         // raw events
32400         /**
32401          * @event click
32402          * When a MasonryBrick is clcik
32403          * @param {Roo.bootstrap.MasonryBrick} this
32404          * @param {Roo.EventObject} e
32405          */
32406         "click" : true
32407     });
32408 };
32409
32410 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32411     
32412     /**
32413      * @cfg {String} title
32414      */   
32415     title : '',
32416     /**
32417      * @cfg {String} html
32418      */   
32419     html : '',
32420     /**
32421      * @cfg {String} bgimage
32422      */   
32423     bgimage : '',
32424     /**
32425      * @cfg {String} videourl
32426      */   
32427     videourl : '',
32428     /**
32429      * @cfg {String} cls
32430      */   
32431     cls : '',
32432     /**
32433      * @cfg {String} href
32434      */   
32435     href : '',
32436     /**
32437      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32438      */   
32439     size : 'xs',
32440     
32441     /**
32442      * @cfg {String} placetitle (center|bottom)
32443      */   
32444     placetitle : '',
32445     
32446     /**
32447      * @cfg {Boolean} isFitContainer defalut true
32448      */   
32449     isFitContainer : true, 
32450     
32451     /**
32452      * @cfg {Boolean} preventDefault defalut false
32453      */   
32454     preventDefault : false, 
32455     
32456     /**
32457      * @cfg {Boolean} inverse defalut false
32458      */   
32459     maskInverse : false, 
32460     
32461     getAutoCreate : function()
32462     {
32463         if(!this.isFitContainer){
32464             return this.getSplitAutoCreate();
32465         }
32466         
32467         var cls = 'masonry-brick masonry-brick-full';
32468         
32469         if(this.href.length){
32470             cls += ' masonry-brick-link';
32471         }
32472         
32473         if(this.bgimage.length){
32474             cls += ' masonry-brick-image';
32475         }
32476         
32477         if(this.maskInverse){
32478             cls += ' mask-inverse';
32479         }
32480         
32481         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32482             cls += ' enable-mask';
32483         }
32484         
32485         if(this.size){
32486             cls += ' masonry-' + this.size + '-brick';
32487         }
32488         
32489         if(this.placetitle.length){
32490             
32491             switch (this.placetitle) {
32492                 case 'center' :
32493                     cls += ' masonry-center-title';
32494                     break;
32495                 case 'bottom' :
32496                     cls += ' masonry-bottom-title';
32497                     break;
32498                 default:
32499                     break;
32500             }
32501             
32502         } else {
32503             if(!this.html.length && !this.bgimage.length){
32504                 cls += ' masonry-center-title';
32505             }
32506
32507             if(!this.html.length && this.bgimage.length){
32508                 cls += ' masonry-bottom-title';
32509             }
32510         }
32511         
32512         if(this.cls){
32513             cls += ' ' + this.cls;
32514         }
32515         
32516         var cfg = {
32517             tag: (this.href.length) ? 'a' : 'div',
32518             cls: cls,
32519             cn: [
32520                 {
32521                     tag: 'div',
32522                     cls: 'masonry-brick-mask'
32523                 },
32524                 {
32525                     tag: 'div',
32526                     cls: 'masonry-brick-paragraph',
32527                     cn: []
32528                 }
32529             ]
32530         };
32531         
32532         if(this.href.length){
32533             cfg.href = this.href;
32534         }
32535         
32536         var cn = cfg.cn[1].cn;
32537         
32538         if(this.title.length){
32539             cn.push({
32540                 tag: 'h4',
32541                 cls: 'masonry-brick-title',
32542                 html: this.title
32543             });
32544         }
32545         
32546         if(this.html.length){
32547             cn.push({
32548                 tag: 'p',
32549                 cls: 'masonry-brick-text',
32550                 html: this.html
32551             });
32552         }
32553         
32554         if (!this.title.length && !this.html.length) {
32555             cfg.cn[1].cls += ' hide';
32556         }
32557         
32558         if(this.bgimage.length){
32559             cfg.cn.push({
32560                 tag: 'img',
32561                 cls: 'masonry-brick-image-view',
32562                 src: this.bgimage
32563             });
32564         }
32565         
32566         if(this.videourl.length){
32567             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32568             // youtube support only?
32569             cfg.cn.push({
32570                 tag: 'iframe',
32571                 cls: 'masonry-brick-image-view',
32572                 src: vurl,
32573                 frameborder : 0,
32574                 allowfullscreen : true
32575             });
32576         }
32577         
32578         return cfg;
32579         
32580     },
32581     
32582     getSplitAutoCreate : function()
32583     {
32584         var cls = 'masonry-brick masonry-brick-split';
32585         
32586         if(this.href.length){
32587             cls += ' masonry-brick-link';
32588         }
32589         
32590         if(this.bgimage.length){
32591             cls += ' masonry-brick-image';
32592         }
32593         
32594         if(this.size){
32595             cls += ' masonry-' + this.size + '-brick';
32596         }
32597         
32598         switch (this.placetitle) {
32599             case 'center' :
32600                 cls += ' masonry-center-title';
32601                 break;
32602             case 'bottom' :
32603                 cls += ' masonry-bottom-title';
32604                 break;
32605             default:
32606                 if(!this.bgimage.length){
32607                     cls += ' masonry-center-title';
32608                 }
32609
32610                 if(this.bgimage.length){
32611                     cls += ' masonry-bottom-title';
32612                 }
32613                 break;
32614         }
32615         
32616         if(this.cls){
32617             cls += ' ' + this.cls;
32618         }
32619         
32620         var cfg = {
32621             tag: (this.href.length) ? 'a' : 'div',
32622             cls: cls,
32623             cn: [
32624                 {
32625                     tag: 'div',
32626                     cls: 'masonry-brick-split-head',
32627                     cn: [
32628                         {
32629                             tag: 'div',
32630                             cls: 'masonry-brick-paragraph',
32631                             cn: []
32632                         }
32633                     ]
32634                 },
32635                 {
32636                     tag: 'div',
32637                     cls: 'masonry-brick-split-body',
32638                     cn: []
32639                 }
32640             ]
32641         };
32642         
32643         if(this.href.length){
32644             cfg.href = this.href;
32645         }
32646         
32647         if(this.title.length){
32648             cfg.cn[0].cn[0].cn.push({
32649                 tag: 'h4',
32650                 cls: 'masonry-brick-title',
32651                 html: this.title
32652             });
32653         }
32654         
32655         if(this.html.length){
32656             cfg.cn[1].cn.push({
32657                 tag: 'p',
32658                 cls: 'masonry-brick-text',
32659                 html: this.html
32660             });
32661         }
32662
32663         if(this.bgimage.length){
32664             cfg.cn[0].cn.push({
32665                 tag: 'img',
32666                 cls: 'masonry-brick-image-view',
32667                 src: this.bgimage
32668             });
32669         }
32670         
32671         if(this.videourl.length){
32672             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32673             // youtube support only?
32674             cfg.cn[0].cn.cn.push({
32675                 tag: 'iframe',
32676                 cls: 'masonry-brick-image-view',
32677                 src: vurl,
32678                 frameborder : 0,
32679                 allowfullscreen : true
32680             });
32681         }
32682         
32683         return cfg;
32684     },
32685     
32686     initEvents: function() 
32687     {
32688         switch (this.size) {
32689             case 'xs' :
32690                 this.x = 1;
32691                 this.y = 1;
32692                 break;
32693             case 'sm' :
32694                 this.x = 2;
32695                 this.y = 2;
32696                 break;
32697             case 'md' :
32698             case 'md-left' :
32699             case 'md-right' :
32700                 this.x = 3;
32701                 this.y = 3;
32702                 break;
32703             case 'tall' :
32704                 this.x = 2;
32705                 this.y = 3;
32706                 break;
32707             case 'wide' :
32708                 this.x = 3;
32709                 this.y = 2;
32710                 break;
32711             case 'wide-thin' :
32712                 this.x = 3;
32713                 this.y = 1;
32714                 break;
32715                         
32716             default :
32717                 break;
32718         }
32719         
32720         if(Roo.isTouch){
32721             this.el.on('touchstart', this.onTouchStart, this);
32722             this.el.on('touchmove', this.onTouchMove, this);
32723             this.el.on('touchend', this.onTouchEnd, this);
32724             this.el.on('contextmenu', this.onContextMenu, this);
32725         } else {
32726             this.el.on('mouseenter'  ,this.enter, this);
32727             this.el.on('mouseleave', this.leave, this);
32728             this.el.on('click', this.onClick, this);
32729         }
32730         
32731         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32732             this.parent().bricks.push(this);   
32733         }
32734         
32735     },
32736     
32737     onClick: function(e, el)
32738     {
32739         var time = this.endTimer - this.startTimer;
32740         // Roo.log(e.preventDefault());
32741         if(Roo.isTouch){
32742             if(time > 1000){
32743                 e.preventDefault();
32744                 return;
32745             }
32746         }
32747         
32748         if(!this.preventDefault){
32749             return;
32750         }
32751         
32752         e.preventDefault();
32753         
32754         if (this.activeClass != '') {
32755             this.selectBrick();
32756         }
32757         
32758         this.fireEvent('click', this, e);
32759     },
32760     
32761     enter: function(e, el)
32762     {
32763         e.preventDefault();
32764         
32765         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32766             return;
32767         }
32768         
32769         if(this.bgimage.length && this.html.length){
32770             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32771         }
32772     },
32773     
32774     leave: function(e, el)
32775     {
32776         e.preventDefault();
32777         
32778         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32779             return;
32780         }
32781         
32782         if(this.bgimage.length && this.html.length){
32783             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32784         }
32785     },
32786     
32787     onTouchStart: function(e, el)
32788     {
32789 //        e.preventDefault();
32790         
32791         this.touchmoved = false;
32792         
32793         if(!this.isFitContainer){
32794             return;
32795         }
32796         
32797         if(!this.bgimage.length || !this.html.length){
32798             return;
32799         }
32800         
32801         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32802         
32803         this.timer = new Date().getTime();
32804         
32805     },
32806     
32807     onTouchMove: function(e, el)
32808     {
32809         this.touchmoved = true;
32810     },
32811     
32812     onContextMenu : function(e,el)
32813     {
32814         e.preventDefault();
32815         e.stopPropagation();
32816         return false;
32817     },
32818     
32819     onTouchEnd: function(e, el)
32820     {
32821 //        e.preventDefault();
32822         
32823         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32824         
32825             this.leave(e,el);
32826             
32827             return;
32828         }
32829         
32830         if(!this.bgimage.length || !this.html.length){
32831             
32832             if(this.href.length){
32833                 window.location.href = this.href;
32834             }
32835             
32836             return;
32837         }
32838         
32839         if(!this.isFitContainer){
32840             return;
32841         }
32842         
32843         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32844         
32845         window.location.href = this.href;
32846     },
32847     
32848     //selection on single brick only
32849     selectBrick : function() {
32850         
32851         if (!this.parentId) {
32852             return;
32853         }
32854         
32855         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32856         var index = m.selectedBrick.indexOf(this.id);
32857         
32858         if ( index > -1) {
32859             m.selectedBrick.splice(index,1);
32860             this.el.removeClass(this.activeClass);
32861             return;
32862         }
32863         
32864         for(var i = 0; i < m.selectedBrick.length; i++) {
32865             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32866             b.el.removeClass(b.activeClass);
32867         }
32868         
32869         m.selectedBrick = [];
32870         
32871         m.selectedBrick.push(this.id);
32872         this.el.addClass(this.activeClass);
32873         return;
32874     },
32875     
32876     isSelected : function(){
32877         return this.el.hasClass(this.activeClass);
32878         
32879     }
32880 });
32881
32882 Roo.apply(Roo.bootstrap.MasonryBrick, {
32883     
32884     //groups: {},
32885     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32886      /**
32887     * register a Masonry Brick
32888     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32889     */
32890     
32891     register : function(brick)
32892     {
32893         //this.groups[brick.id] = brick;
32894         this.groups.add(brick.id, brick);
32895     },
32896     /**
32897     * fetch a  masonry brick based on the masonry brick ID
32898     * @param {string} the masonry brick to add
32899     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32900     */
32901     
32902     get: function(brick_id) 
32903     {
32904         // if (typeof(this.groups[brick_id]) == 'undefined') {
32905         //     return false;
32906         // }
32907         // return this.groups[brick_id] ;
32908         
32909         if(this.groups.key(brick_id)) {
32910             return this.groups.key(brick_id);
32911         }
32912         
32913         return false;
32914     }
32915     
32916     
32917     
32918 });
32919
32920  /*
32921  * - LGPL
32922  *
32923  * element
32924  * 
32925  */
32926
32927 /**
32928  * @class Roo.bootstrap.Brick
32929  * @extends Roo.bootstrap.Component
32930  * Bootstrap Brick class
32931  * 
32932  * @constructor
32933  * Create a new Brick
32934  * @param {Object} config The config object
32935  */
32936
32937 Roo.bootstrap.Brick = function(config){
32938     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32939     
32940     this.addEvents({
32941         // raw events
32942         /**
32943          * @event click
32944          * When a Brick is click
32945          * @param {Roo.bootstrap.Brick} this
32946          * @param {Roo.EventObject} e
32947          */
32948         "click" : true
32949     });
32950 };
32951
32952 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32953     
32954     /**
32955      * @cfg {String} title
32956      */   
32957     title : '',
32958     /**
32959      * @cfg {String} html
32960      */   
32961     html : '',
32962     /**
32963      * @cfg {String} bgimage
32964      */   
32965     bgimage : '',
32966     /**
32967      * @cfg {String} cls
32968      */   
32969     cls : '',
32970     /**
32971      * @cfg {String} href
32972      */   
32973     href : '',
32974     /**
32975      * @cfg {String} video
32976      */   
32977     video : '',
32978     /**
32979      * @cfg {Boolean} square
32980      */   
32981     square : true,
32982     
32983     getAutoCreate : function()
32984     {
32985         var cls = 'roo-brick';
32986         
32987         if(this.href.length){
32988             cls += ' roo-brick-link';
32989         }
32990         
32991         if(this.bgimage.length){
32992             cls += ' roo-brick-image';
32993         }
32994         
32995         if(!this.html.length && !this.bgimage.length){
32996             cls += ' roo-brick-center-title';
32997         }
32998         
32999         if(!this.html.length && this.bgimage.length){
33000             cls += ' roo-brick-bottom-title';
33001         }
33002         
33003         if(this.cls){
33004             cls += ' ' + this.cls;
33005         }
33006         
33007         var cfg = {
33008             tag: (this.href.length) ? 'a' : 'div',
33009             cls: cls,
33010             cn: [
33011                 {
33012                     tag: 'div',
33013                     cls: 'roo-brick-paragraph',
33014                     cn: []
33015                 }
33016             ]
33017         };
33018         
33019         if(this.href.length){
33020             cfg.href = this.href;
33021         }
33022         
33023         var cn = cfg.cn[0].cn;
33024         
33025         if(this.title.length){
33026             cn.push({
33027                 tag: 'h4',
33028                 cls: 'roo-brick-title',
33029                 html: this.title
33030             });
33031         }
33032         
33033         if(this.html.length){
33034             cn.push({
33035                 tag: 'p',
33036                 cls: 'roo-brick-text',
33037                 html: this.html
33038             });
33039         } else {
33040             cn.cls += ' hide';
33041         }
33042         
33043         if(this.bgimage.length){
33044             cfg.cn.push({
33045                 tag: 'img',
33046                 cls: 'roo-brick-image-view',
33047                 src: this.bgimage
33048             });
33049         }
33050         
33051         return cfg;
33052     },
33053     
33054     initEvents: function() 
33055     {
33056         if(this.title.length || this.html.length){
33057             this.el.on('mouseenter'  ,this.enter, this);
33058             this.el.on('mouseleave', this.leave, this);
33059         }
33060         
33061         Roo.EventManager.onWindowResize(this.resize, this); 
33062         
33063         if(this.bgimage.length){
33064             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33065             this.imageEl.on('load', this.onImageLoad, this);
33066             return;
33067         }
33068         
33069         this.resize();
33070     },
33071     
33072     onImageLoad : function()
33073     {
33074         this.resize();
33075     },
33076     
33077     resize : function()
33078     {
33079         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33080         
33081         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33082         
33083         if(this.bgimage.length){
33084             var image = this.el.select('.roo-brick-image-view', true).first();
33085             
33086             image.setWidth(paragraph.getWidth());
33087             
33088             if(this.square){
33089                 image.setHeight(paragraph.getWidth());
33090             }
33091             
33092             this.el.setHeight(image.getHeight());
33093             paragraph.setHeight(image.getHeight());
33094             
33095         }
33096         
33097     },
33098     
33099     enter: function(e, el)
33100     {
33101         e.preventDefault();
33102         
33103         if(this.bgimage.length){
33104             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33105             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33106         }
33107     },
33108     
33109     leave: function(e, el)
33110     {
33111         e.preventDefault();
33112         
33113         if(this.bgimage.length){
33114             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33115             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33116         }
33117     }
33118     
33119 });
33120
33121  
33122
33123  /*
33124  * - LGPL
33125  *
33126  * Number field 
33127  */
33128
33129 /**
33130  * @class Roo.bootstrap.NumberField
33131  * @extends Roo.bootstrap.Input
33132  * Bootstrap NumberField class
33133  * 
33134  * 
33135  * 
33136  * 
33137  * @constructor
33138  * Create a new NumberField
33139  * @param {Object} config The config object
33140  */
33141
33142 Roo.bootstrap.NumberField = function(config){
33143     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33144 };
33145
33146 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33147     
33148     /**
33149      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33150      */
33151     allowDecimals : true,
33152     /**
33153      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33154      */
33155     decimalSeparator : ".",
33156     /**
33157      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33158      */
33159     decimalPrecision : 2,
33160     /**
33161      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33162      */
33163     allowNegative : true,
33164     
33165     /**
33166      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33167      */
33168     allowZero: true,
33169     /**
33170      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33171      */
33172     minValue : Number.NEGATIVE_INFINITY,
33173     /**
33174      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33175      */
33176     maxValue : Number.MAX_VALUE,
33177     /**
33178      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33179      */
33180     minText : "The minimum value for this field is {0}",
33181     /**
33182      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33183      */
33184     maxText : "The maximum value for this field is {0}",
33185     /**
33186      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33187      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33188      */
33189     nanText : "{0} is not a valid number",
33190     /**
33191      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33192      */
33193     castInt : true,
33194     /**
33195      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33196      */
33197     thousandsDelimiter : false,
33198     /**
33199      * @cfg {String} valueAlign alignment of value
33200      */
33201     valueAlign : "left",
33202
33203     getAutoCreate : function()
33204     {
33205         var hiddenInput = {
33206             tag: 'input',
33207             type: 'hidden',
33208             id: Roo.id(),
33209             cls: 'hidden-number-input'
33210         };
33211         
33212         if (this.name) {
33213             hiddenInput.name = this.name;
33214         }
33215         
33216         this.name = '';
33217         
33218         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33219         
33220         this.name = hiddenInput.name;
33221         
33222         if(cfg.cn.length > 0) {
33223             cfg.cn.push(hiddenInput);
33224         }
33225         
33226         return cfg;
33227     },
33228
33229     // private
33230     initEvents : function()
33231     {   
33232         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33233         
33234         var allowed = "0123456789";
33235         
33236         if(this.allowDecimals){
33237             allowed += this.decimalSeparator;
33238         }
33239         
33240         if(this.allowNegative){
33241             allowed += "-";
33242         }
33243         
33244         if(this.thousandsDelimiter) {
33245             allowed += ",";
33246         }
33247         
33248         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33249         
33250         var keyPress = function(e){
33251             
33252             var k = e.getKey();
33253             
33254             var c = e.getCharCode();
33255             
33256             if(
33257                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33258                     allowed.indexOf(String.fromCharCode(c)) === -1
33259             ){
33260                 e.stopEvent();
33261                 return;
33262             }
33263             
33264             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33265                 return;
33266             }
33267             
33268             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33269                 e.stopEvent();
33270             }
33271         };
33272         
33273         this.el.on("keypress", keyPress, this);
33274     },
33275     
33276     validateValue : function(value)
33277     {
33278         
33279         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33280             return false;
33281         }
33282         
33283         var num = this.parseValue(value);
33284         
33285         if(isNaN(num)){
33286             this.markInvalid(String.format(this.nanText, value));
33287             return false;
33288         }
33289         
33290         if(num < this.minValue){
33291             this.markInvalid(String.format(this.minText, this.minValue));
33292             return false;
33293         }
33294         
33295         if(num > this.maxValue){
33296             this.markInvalid(String.format(this.maxText, this.maxValue));
33297             return false;
33298         }
33299         
33300         return true;
33301     },
33302
33303     getValue : function()
33304     {
33305         var v = this.hiddenEl().getValue();
33306         
33307         return this.fixPrecision(this.parseValue(v));
33308     },
33309
33310     parseValue : function(value)
33311     {
33312         if(this.thousandsDelimiter) {
33313             value += "";
33314             r = new RegExp(",", "g");
33315             value = value.replace(r, "");
33316         }
33317         
33318         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33319         return isNaN(value) ? '' : value;
33320     },
33321
33322     fixPrecision : function(value)
33323     {
33324         if(this.thousandsDelimiter) {
33325             value += "";
33326             r = new RegExp(",", "g");
33327             value = value.replace(r, "");
33328         }
33329         
33330         var nan = isNaN(value);
33331         
33332         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33333             return nan ? '' : value;
33334         }
33335         return parseFloat(value).toFixed(this.decimalPrecision);
33336     },
33337
33338     setValue : function(v)
33339     {
33340         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33341         
33342         this.value = v;
33343         
33344         if(this.rendered){
33345             
33346             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33347             
33348             this.inputEl().dom.value = (v == '') ? '' :
33349                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33350             
33351             if(!this.allowZero && v === '0') {
33352                 this.hiddenEl().dom.value = '';
33353                 this.inputEl().dom.value = '';
33354             }
33355             
33356             this.validate();
33357         }
33358     },
33359
33360     decimalPrecisionFcn : function(v)
33361     {
33362         return Math.floor(v);
33363     },
33364
33365     beforeBlur : function()
33366     {
33367         if(!this.castInt){
33368             return;
33369         }
33370         
33371         var v = this.parseValue(this.getRawValue());
33372         
33373         if(v || v === 0){
33374             this.setValue(v);
33375         }
33376     },
33377     
33378     hiddenEl : function()
33379     {
33380         return this.el.select('input.hidden-number-input',true).first();
33381     }
33382     
33383 });
33384
33385  
33386
33387 /*
33388 * Licence: LGPL
33389 */
33390
33391 /**
33392  * @class Roo.bootstrap.DocumentSlider
33393  * @extends Roo.bootstrap.Component
33394  * Bootstrap DocumentSlider class
33395  * 
33396  * @constructor
33397  * Create a new DocumentViewer
33398  * @param {Object} config The config object
33399  */
33400
33401 Roo.bootstrap.DocumentSlider = function(config){
33402     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33403     
33404     this.files = [];
33405     
33406     this.addEvents({
33407         /**
33408          * @event initial
33409          * Fire after initEvent
33410          * @param {Roo.bootstrap.DocumentSlider} this
33411          */
33412         "initial" : true,
33413         /**
33414          * @event update
33415          * Fire after update
33416          * @param {Roo.bootstrap.DocumentSlider} this
33417          */
33418         "update" : true,
33419         /**
33420          * @event click
33421          * Fire after click
33422          * @param {Roo.bootstrap.DocumentSlider} this
33423          */
33424         "click" : true
33425     });
33426 };
33427
33428 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33429     
33430     files : false,
33431     
33432     indicator : 0,
33433     
33434     getAutoCreate : function()
33435     {
33436         var cfg = {
33437             tag : 'div',
33438             cls : 'roo-document-slider',
33439             cn : [
33440                 {
33441                     tag : 'div',
33442                     cls : 'roo-document-slider-header',
33443                     cn : [
33444                         {
33445                             tag : 'div',
33446                             cls : 'roo-document-slider-header-title'
33447                         }
33448                     ]
33449                 },
33450                 {
33451                     tag : 'div',
33452                     cls : 'roo-document-slider-body',
33453                     cn : [
33454                         {
33455                             tag : 'div',
33456                             cls : 'roo-document-slider-prev',
33457                             cn : [
33458                                 {
33459                                     tag : 'i',
33460                                     cls : 'fa fa-chevron-left'
33461                                 }
33462                             ]
33463                         },
33464                         {
33465                             tag : 'div',
33466                             cls : 'roo-document-slider-thumb',
33467                             cn : [
33468                                 {
33469                                     tag : 'img',
33470                                     cls : 'roo-document-slider-image'
33471                                 }
33472                             ]
33473                         },
33474                         {
33475                             tag : 'div',
33476                             cls : 'roo-document-slider-next',
33477                             cn : [
33478                                 {
33479                                     tag : 'i',
33480                                     cls : 'fa fa-chevron-right'
33481                                 }
33482                             ]
33483                         }
33484                     ]
33485                 }
33486             ]
33487         };
33488         
33489         return cfg;
33490     },
33491     
33492     initEvents : function()
33493     {
33494         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33495         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33496         
33497         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33498         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33499         
33500         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33501         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33502         
33503         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33504         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33505         
33506         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33507         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33508         
33509         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33510         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33511         
33512         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33513         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33514         
33515         this.thumbEl.on('click', this.onClick, this);
33516         
33517         this.prevIndicator.on('click', this.prev, this);
33518         
33519         this.nextIndicator.on('click', this.next, this);
33520         
33521     },
33522     
33523     initial : function()
33524     {
33525         if(this.files.length){
33526             this.indicator = 1;
33527             this.update()
33528         }
33529         
33530         this.fireEvent('initial', this);
33531     },
33532     
33533     update : function()
33534     {
33535         this.imageEl.attr('src', this.files[this.indicator - 1]);
33536         
33537         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33538         
33539         this.prevIndicator.show();
33540         
33541         if(this.indicator == 1){
33542             this.prevIndicator.hide();
33543         }
33544         
33545         this.nextIndicator.show();
33546         
33547         if(this.indicator == this.files.length){
33548             this.nextIndicator.hide();
33549         }
33550         
33551         this.thumbEl.scrollTo('top');
33552         
33553         this.fireEvent('update', this);
33554     },
33555     
33556     onClick : function(e)
33557     {
33558         e.preventDefault();
33559         
33560         this.fireEvent('click', this);
33561     },
33562     
33563     prev : function(e)
33564     {
33565         e.preventDefault();
33566         
33567         this.indicator = Math.max(1, this.indicator - 1);
33568         
33569         this.update();
33570     },
33571     
33572     next : function(e)
33573     {
33574         e.preventDefault();
33575         
33576         this.indicator = Math.min(this.files.length, this.indicator + 1);
33577         
33578         this.update();
33579     }
33580 });
33581 /*
33582  * - LGPL
33583  *
33584  * RadioSet
33585  *
33586  *
33587  */
33588
33589 /**
33590  * @class Roo.bootstrap.RadioSet
33591  * @extends Roo.bootstrap.Input
33592  * Bootstrap RadioSet class
33593  * @cfg {String} indicatorpos (left|right) default left
33594  * @cfg {Boolean} inline (true|false) inline the element (default true)
33595  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33596  * @constructor
33597  * Create a new RadioSet
33598  * @param {Object} config The config object
33599  */
33600
33601 Roo.bootstrap.RadioSet = function(config){
33602     
33603     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33604     
33605     this.radioes = [];
33606     
33607     Roo.bootstrap.RadioSet.register(this);
33608     
33609     this.addEvents({
33610         /**
33611         * @event check
33612         * Fires when the element is checked or unchecked.
33613         * @param {Roo.bootstrap.RadioSet} this This radio
33614         * @param {Roo.bootstrap.Radio} item The checked item
33615         */
33616        check : true,
33617        /**
33618         * @event click
33619         * Fires when the element is click.
33620         * @param {Roo.bootstrap.RadioSet} this This radio set
33621         * @param {Roo.bootstrap.Radio} item The checked item
33622         * @param {Roo.EventObject} e The event object
33623         */
33624        click : true
33625     });
33626     
33627 };
33628
33629 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33630
33631     radioes : false,
33632     
33633     inline : true,
33634     
33635     weight : '',
33636     
33637     indicatorpos : 'left',
33638     
33639     getAutoCreate : function()
33640     {
33641         var label = {
33642             tag : 'label',
33643             cls : 'roo-radio-set-label',
33644             cn : [
33645                 {
33646                     tag : 'span',
33647                     html : this.fieldLabel
33648                 }
33649             ]
33650         };
33651         
33652         if(this.indicatorpos == 'left'){
33653             label.cn.unshift({
33654                 tag : 'i',
33655                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33656                 tooltip : 'This field is required'
33657             });
33658         } else {
33659             label.cn.push({
33660                 tag : 'i',
33661                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33662                 tooltip : 'This field is required'
33663             });
33664         }
33665         
33666         var items = {
33667             tag : 'div',
33668             cls : 'roo-radio-set-items'
33669         };
33670         
33671         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33672         
33673         if (align === 'left' && this.fieldLabel.length) {
33674             
33675             items = {
33676                 cls : "roo-radio-set-right", 
33677                 cn: [
33678                     items
33679                 ]
33680             };
33681             
33682             if(this.labelWidth > 12){
33683                 label.style = "width: " + this.labelWidth + 'px';
33684             }
33685             
33686             if(this.labelWidth < 13 && this.labelmd == 0){
33687                 this.labelmd = this.labelWidth;
33688             }
33689             
33690             if(this.labellg > 0){
33691                 label.cls += ' col-lg-' + this.labellg;
33692                 items.cls += ' col-lg-' + (12 - this.labellg);
33693             }
33694             
33695             if(this.labelmd > 0){
33696                 label.cls += ' col-md-' + this.labelmd;
33697                 items.cls += ' col-md-' + (12 - this.labelmd);
33698             }
33699             
33700             if(this.labelsm > 0){
33701                 label.cls += ' col-sm-' + this.labelsm;
33702                 items.cls += ' col-sm-' + (12 - this.labelsm);
33703             }
33704             
33705             if(this.labelxs > 0){
33706                 label.cls += ' col-xs-' + this.labelxs;
33707                 items.cls += ' col-xs-' + (12 - this.labelxs);
33708             }
33709         }
33710         
33711         var cfg = {
33712             tag : 'div',
33713             cls : 'roo-radio-set',
33714             cn : [
33715                 {
33716                     tag : 'input',
33717                     cls : 'roo-radio-set-input',
33718                     type : 'hidden',
33719                     name : this.name,
33720                     value : this.value ? this.value :  ''
33721                 },
33722                 label,
33723                 items
33724             ]
33725         };
33726         
33727         if(this.weight.length){
33728             cfg.cls += ' roo-radio-' + this.weight;
33729         }
33730         
33731         if(this.inline) {
33732             cfg.cls += ' roo-radio-set-inline';
33733         }
33734         
33735         var settings=this;
33736         ['xs','sm','md','lg'].map(function(size){
33737             if (settings[size]) {
33738                 cfg.cls += ' col-' + size + '-' + settings[size];
33739             }
33740         });
33741         
33742         return cfg;
33743         
33744     },
33745
33746     initEvents : function()
33747     {
33748         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33749         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33750         
33751         if(!this.fieldLabel.length){
33752             this.labelEl.hide();
33753         }
33754         
33755         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33756         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33757         
33758         this.indicator = this.indicatorEl();
33759         
33760         if(this.indicator){
33761             this.indicator.addClass('invisible');
33762         }
33763         
33764         this.originalValue = this.getValue();
33765         
33766     },
33767     
33768     inputEl: function ()
33769     {
33770         return this.el.select('.roo-radio-set-input', true).first();
33771     },
33772     
33773     getChildContainer : function()
33774     {
33775         return this.itemsEl;
33776     },
33777     
33778     register : function(item)
33779     {
33780         this.radioes.push(item);
33781         
33782     },
33783     
33784     validate : function()
33785     {   
33786         if(this.getVisibilityEl().hasClass('hidden')){
33787             return true;
33788         }
33789         
33790         var valid = false;
33791         
33792         Roo.each(this.radioes, function(i){
33793             if(!i.checked){
33794                 return;
33795             }
33796             
33797             valid = true;
33798             return false;
33799         });
33800         
33801         if(this.allowBlank) {
33802             return true;
33803         }
33804         
33805         if(this.disabled || valid){
33806             this.markValid();
33807             return true;
33808         }
33809         
33810         this.markInvalid();
33811         return false;
33812         
33813     },
33814     
33815     markValid : function()
33816     {
33817         if(this.labelEl.isVisible(true)){
33818             this.indicatorEl().removeClass('visible');
33819             this.indicatorEl().addClass('invisible');
33820         }
33821         
33822         this.el.removeClass([this.invalidClass, this.validClass]);
33823         this.el.addClass(this.validClass);
33824         
33825         this.fireEvent('valid', this);
33826     },
33827     
33828     markInvalid : function(msg)
33829     {
33830         if(this.allowBlank || this.disabled){
33831             return;
33832         }
33833         
33834         if(this.labelEl.isVisible(true)){
33835             this.indicatorEl().removeClass('invisible');
33836             this.indicatorEl().addClass('visible');
33837         }
33838         
33839         this.el.removeClass([this.invalidClass, this.validClass]);
33840         this.el.addClass(this.invalidClass);
33841         
33842         this.fireEvent('invalid', this, msg);
33843         
33844     },
33845     
33846     setValue : function(v, suppressEvent)
33847     {   
33848         if(this.value === v){
33849             return;
33850         }
33851         
33852         this.value = v;
33853         
33854         if(this.rendered){
33855             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33856         }
33857         
33858         Roo.each(this.radioes, function(i){
33859             i.checked = false;
33860             i.el.removeClass('checked');
33861         });
33862         
33863         Roo.each(this.radioes, function(i){
33864             
33865             if(i.value === v || i.value.toString() === v.toString()){
33866                 i.checked = true;
33867                 i.el.addClass('checked');
33868                 
33869                 if(suppressEvent !== true){
33870                     this.fireEvent('check', this, i);
33871                 }
33872                 
33873                 return false;
33874             }
33875             
33876         }, this);
33877         
33878         this.validate();
33879     },
33880     
33881     clearInvalid : function(){
33882         
33883         if(!this.el || this.preventMark){
33884             return;
33885         }
33886         
33887         this.el.removeClass([this.invalidClass]);
33888         
33889         this.fireEvent('valid', this);
33890     }
33891     
33892 });
33893
33894 Roo.apply(Roo.bootstrap.RadioSet, {
33895     
33896     groups: {},
33897     
33898     register : function(set)
33899     {
33900         this.groups[set.name] = set;
33901     },
33902     
33903     get: function(name) 
33904     {
33905         if (typeof(this.groups[name]) == 'undefined') {
33906             return false;
33907         }
33908         
33909         return this.groups[name] ;
33910     }
33911     
33912 });
33913 /*
33914  * Based on:
33915  * Ext JS Library 1.1.1
33916  * Copyright(c) 2006-2007, Ext JS, LLC.
33917  *
33918  * Originally Released Under LGPL - original licence link has changed is not relivant.
33919  *
33920  * Fork - LGPL
33921  * <script type="text/javascript">
33922  */
33923
33924
33925 /**
33926  * @class Roo.bootstrap.SplitBar
33927  * @extends Roo.util.Observable
33928  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33929  * <br><br>
33930  * Usage:
33931  * <pre><code>
33932 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33933                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33934 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33935 split.minSize = 100;
33936 split.maxSize = 600;
33937 split.animate = true;
33938 split.on('moved', splitterMoved);
33939 </code></pre>
33940  * @constructor
33941  * Create a new SplitBar
33942  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33943  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33944  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33945  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33946                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33947                         position of the SplitBar).
33948  */
33949 Roo.bootstrap.SplitBar = function(cfg){
33950     
33951     /** @private */
33952     
33953     //{
33954     //  dragElement : elm
33955     //  resizingElement: el,
33956         // optional..
33957     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33958     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33959         // existingProxy ???
33960     //}
33961     
33962     this.el = Roo.get(cfg.dragElement, true);
33963     this.el.dom.unselectable = "on";
33964     /** @private */
33965     this.resizingEl = Roo.get(cfg.resizingElement, true);
33966
33967     /**
33968      * @private
33969      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33970      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33971      * @type Number
33972      */
33973     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33974     
33975     /**
33976      * The minimum size of the resizing element. (Defaults to 0)
33977      * @type Number
33978      */
33979     this.minSize = 0;
33980     
33981     /**
33982      * The maximum size of the resizing element. (Defaults to 2000)
33983      * @type Number
33984      */
33985     this.maxSize = 2000;
33986     
33987     /**
33988      * Whether to animate the transition to the new size
33989      * @type Boolean
33990      */
33991     this.animate = false;
33992     
33993     /**
33994      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33995      * @type Boolean
33996      */
33997     this.useShim = false;
33998     
33999     /** @private */
34000     this.shim = null;
34001     
34002     if(!cfg.existingProxy){
34003         /** @private */
34004         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34005     }else{
34006         this.proxy = Roo.get(cfg.existingProxy).dom;
34007     }
34008     /** @private */
34009     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34010     
34011     /** @private */
34012     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34013     
34014     /** @private */
34015     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34016     
34017     /** @private */
34018     this.dragSpecs = {};
34019     
34020     /**
34021      * @private The adapter to use to positon and resize elements
34022      */
34023     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34024     this.adapter.init(this);
34025     
34026     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34027         /** @private */
34028         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34029         this.el.addClass("roo-splitbar-h");
34030     }else{
34031         /** @private */
34032         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34033         this.el.addClass("roo-splitbar-v");
34034     }
34035     
34036     this.addEvents({
34037         /**
34038          * @event resize
34039          * Fires when the splitter is moved (alias for {@link #event-moved})
34040          * @param {Roo.bootstrap.SplitBar} this
34041          * @param {Number} newSize the new width or height
34042          */
34043         "resize" : true,
34044         /**
34045          * @event moved
34046          * Fires when the splitter is moved
34047          * @param {Roo.bootstrap.SplitBar} this
34048          * @param {Number} newSize the new width or height
34049          */
34050         "moved" : true,
34051         /**
34052          * @event beforeresize
34053          * Fires before the splitter is dragged
34054          * @param {Roo.bootstrap.SplitBar} this
34055          */
34056         "beforeresize" : true,
34057
34058         "beforeapply" : true
34059     });
34060
34061     Roo.util.Observable.call(this);
34062 };
34063
34064 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34065     onStartProxyDrag : function(x, y){
34066         this.fireEvent("beforeresize", this);
34067         if(!this.overlay){
34068             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34069             o.unselectable();
34070             o.enableDisplayMode("block");
34071             // all splitbars share the same overlay
34072             Roo.bootstrap.SplitBar.prototype.overlay = o;
34073         }
34074         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34075         this.overlay.show();
34076         Roo.get(this.proxy).setDisplayed("block");
34077         var size = this.adapter.getElementSize(this);
34078         this.activeMinSize = this.getMinimumSize();;
34079         this.activeMaxSize = this.getMaximumSize();;
34080         var c1 = size - this.activeMinSize;
34081         var c2 = Math.max(this.activeMaxSize - size, 0);
34082         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34083             this.dd.resetConstraints();
34084             this.dd.setXConstraint(
34085                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34086                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34087             );
34088             this.dd.setYConstraint(0, 0);
34089         }else{
34090             this.dd.resetConstraints();
34091             this.dd.setXConstraint(0, 0);
34092             this.dd.setYConstraint(
34093                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34094                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34095             );
34096          }
34097         this.dragSpecs.startSize = size;
34098         this.dragSpecs.startPoint = [x, y];
34099         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34100     },
34101     
34102     /** 
34103      * @private Called after the drag operation by the DDProxy
34104      */
34105     onEndProxyDrag : function(e){
34106         Roo.get(this.proxy).setDisplayed(false);
34107         var endPoint = Roo.lib.Event.getXY(e);
34108         if(this.overlay){
34109             this.overlay.hide();
34110         }
34111         var newSize;
34112         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34113             newSize = this.dragSpecs.startSize + 
34114                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34115                     endPoint[0] - this.dragSpecs.startPoint[0] :
34116                     this.dragSpecs.startPoint[0] - endPoint[0]
34117                 );
34118         }else{
34119             newSize = this.dragSpecs.startSize + 
34120                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34121                     endPoint[1] - this.dragSpecs.startPoint[1] :
34122                     this.dragSpecs.startPoint[1] - endPoint[1]
34123                 );
34124         }
34125         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34126         if(newSize != this.dragSpecs.startSize){
34127             if(this.fireEvent('beforeapply', this, newSize) !== false){
34128                 this.adapter.setElementSize(this, newSize);
34129                 this.fireEvent("moved", this, newSize);
34130                 this.fireEvent("resize", this, newSize);
34131             }
34132         }
34133     },
34134     
34135     /**
34136      * Get the adapter this SplitBar uses
34137      * @return The adapter object
34138      */
34139     getAdapter : function(){
34140         return this.adapter;
34141     },
34142     
34143     /**
34144      * Set the adapter this SplitBar uses
34145      * @param {Object} adapter A SplitBar adapter object
34146      */
34147     setAdapter : function(adapter){
34148         this.adapter = adapter;
34149         this.adapter.init(this);
34150     },
34151     
34152     /**
34153      * Gets the minimum size for the resizing element
34154      * @return {Number} The minimum size
34155      */
34156     getMinimumSize : function(){
34157         return this.minSize;
34158     },
34159     
34160     /**
34161      * Sets the minimum size for the resizing element
34162      * @param {Number} minSize The minimum size
34163      */
34164     setMinimumSize : function(minSize){
34165         this.minSize = minSize;
34166     },
34167     
34168     /**
34169      * Gets the maximum size for the resizing element
34170      * @return {Number} The maximum size
34171      */
34172     getMaximumSize : function(){
34173         return this.maxSize;
34174     },
34175     
34176     /**
34177      * Sets the maximum size for the resizing element
34178      * @param {Number} maxSize The maximum size
34179      */
34180     setMaximumSize : function(maxSize){
34181         this.maxSize = maxSize;
34182     },
34183     
34184     /**
34185      * Sets the initialize size for the resizing element
34186      * @param {Number} size The initial size
34187      */
34188     setCurrentSize : function(size){
34189         var oldAnimate = this.animate;
34190         this.animate = false;
34191         this.adapter.setElementSize(this, size);
34192         this.animate = oldAnimate;
34193     },
34194     
34195     /**
34196      * Destroy this splitbar. 
34197      * @param {Boolean} removeEl True to remove the element
34198      */
34199     destroy : function(removeEl){
34200         if(this.shim){
34201             this.shim.remove();
34202         }
34203         this.dd.unreg();
34204         this.proxy.parentNode.removeChild(this.proxy);
34205         if(removeEl){
34206             this.el.remove();
34207         }
34208     }
34209 });
34210
34211 /**
34212  * @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.
34213  */
34214 Roo.bootstrap.SplitBar.createProxy = function(dir){
34215     var proxy = new Roo.Element(document.createElement("div"));
34216     proxy.unselectable();
34217     var cls = 'roo-splitbar-proxy';
34218     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34219     document.body.appendChild(proxy.dom);
34220     return proxy.dom;
34221 };
34222
34223 /** 
34224  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34225  * Default Adapter. It assumes the splitter and resizing element are not positioned
34226  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34227  */
34228 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34229 };
34230
34231 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34232     // do nothing for now
34233     init : function(s){
34234     
34235     },
34236     /**
34237      * Called before drag operations to get the current size of the resizing element. 
34238      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34239      */
34240      getElementSize : function(s){
34241         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34242             return s.resizingEl.getWidth();
34243         }else{
34244             return s.resizingEl.getHeight();
34245         }
34246     },
34247     
34248     /**
34249      * Called after drag operations to set the size of the resizing element.
34250      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34251      * @param {Number} newSize The new size to set
34252      * @param {Function} onComplete A function to be invoked when resizing is complete
34253      */
34254     setElementSize : function(s, newSize, onComplete){
34255         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34256             if(!s.animate){
34257                 s.resizingEl.setWidth(newSize);
34258                 if(onComplete){
34259                     onComplete(s, newSize);
34260                 }
34261             }else{
34262                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34263             }
34264         }else{
34265             
34266             if(!s.animate){
34267                 s.resizingEl.setHeight(newSize);
34268                 if(onComplete){
34269                     onComplete(s, newSize);
34270                 }
34271             }else{
34272                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34273             }
34274         }
34275     }
34276 };
34277
34278 /** 
34279  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34280  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34281  * Adapter that  moves the splitter element to align with the resized sizing element. 
34282  * Used with an absolute positioned SplitBar.
34283  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34284  * document.body, make sure you assign an id to the body element.
34285  */
34286 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34287     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34288     this.container = Roo.get(container);
34289 };
34290
34291 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34292     init : function(s){
34293         this.basic.init(s);
34294     },
34295     
34296     getElementSize : function(s){
34297         return this.basic.getElementSize(s);
34298     },
34299     
34300     setElementSize : function(s, newSize, onComplete){
34301         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34302     },
34303     
34304     moveSplitter : function(s){
34305         var yes = Roo.bootstrap.SplitBar;
34306         switch(s.placement){
34307             case yes.LEFT:
34308                 s.el.setX(s.resizingEl.getRight());
34309                 break;
34310             case yes.RIGHT:
34311                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34312                 break;
34313             case yes.TOP:
34314                 s.el.setY(s.resizingEl.getBottom());
34315                 break;
34316             case yes.BOTTOM:
34317                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34318                 break;
34319         }
34320     }
34321 };
34322
34323 /**
34324  * Orientation constant - Create a vertical SplitBar
34325  * @static
34326  * @type Number
34327  */
34328 Roo.bootstrap.SplitBar.VERTICAL = 1;
34329
34330 /**
34331  * Orientation constant - Create a horizontal SplitBar
34332  * @static
34333  * @type Number
34334  */
34335 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34336
34337 /**
34338  * Placement constant - The resizing element is to the left of the splitter element
34339  * @static
34340  * @type Number
34341  */
34342 Roo.bootstrap.SplitBar.LEFT = 1;
34343
34344 /**
34345  * Placement constant - The resizing element is to the right of the splitter element
34346  * @static
34347  * @type Number
34348  */
34349 Roo.bootstrap.SplitBar.RIGHT = 2;
34350
34351 /**
34352  * Placement constant - The resizing element is positioned above the splitter element
34353  * @static
34354  * @type Number
34355  */
34356 Roo.bootstrap.SplitBar.TOP = 3;
34357
34358 /**
34359  * Placement constant - The resizing element is positioned under splitter element
34360  * @static
34361  * @type Number
34362  */
34363 Roo.bootstrap.SplitBar.BOTTOM = 4;
34364 Roo.namespace("Roo.bootstrap.layout");/*
34365  * Based on:
34366  * Ext JS Library 1.1.1
34367  * Copyright(c) 2006-2007, Ext JS, LLC.
34368  *
34369  * Originally Released Under LGPL - original licence link has changed is not relivant.
34370  *
34371  * Fork - LGPL
34372  * <script type="text/javascript">
34373  */
34374
34375 /**
34376  * @class Roo.bootstrap.layout.Manager
34377  * @extends Roo.bootstrap.Component
34378  * Base class for layout managers.
34379  */
34380 Roo.bootstrap.layout.Manager = function(config)
34381 {
34382     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34383
34384
34385
34386
34387
34388     /** false to disable window resize monitoring @type Boolean */
34389     this.monitorWindowResize = true;
34390     this.regions = {};
34391     this.addEvents({
34392         /**
34393          * @event layout
34394          * Fires when a layout is performed.
34395          * @param {Roo.LayoutManager} this
34396          */
34397         "layout" : true,
34398         /**
34399          * @event regionresized
34400          * Fires when the user resizes a region.
34401          * @param {Roo.LayoutRegion} region The resized region
34402          * @param {Number} newSize The new size (width for east/west, height for north/south)
34403          */
34404         "regionresized" : true,
34405         /**
34406          * @event regioncollapsed
34407          * Fires when a region is collapsed.
34408          * @param {Roo.LayoutRegion} region The collapsed region
34409          */
34410         "regioncollapsed" : true,
34411         /**
34412          * @event regionexpanded
34413          * Fires when a region is expanded.
34414          * @param {Roo.LayoutRegion} region The expanded region
34415          */
34416         "regionexpanded" : true
34417     });
34418     this.updating = false;
34419
34420     if (config.el) {
34421         this.el = Roo.get(config.el);
34422         this.initEvents();
34423     }
34424
34425 };
34426
34427 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34428
34429
34430     regions : null,
34431
34432     monitorWindowResize : true,
34433
34434
34435     updating : false,
34436
34437
34438     onRender : function(ct, position)
34439     {
34440         if(!this.el){
34441             this.el = Roo.get(ct);
34442             this.initEvents();
34443         }
34444         //this.fireEvent('render',this);
34445     },
34446
34447
34448     initEvents: function()
34449     {
34450
34451
34452         // ie scrollbar fix
34453         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34454             document.body.scroll = "no";
34455         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34456             this.el.position('relative');
34457         }
34458         this.id = this.el.id;
34459         this.el.addClass("roo-layout-container");
34460         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34461         if(this.el.dom != document.body ) {
34462             this.el.on('resize', this.layout,this);
34463             this.el.on('show', this.layout,this);
34464         }
34465
34466     },
34467
34468     /**
34469      * Returns true if this layout is currently being updated
34470      * @return {Boolean}
34471      */
34472     isUpdating : function(){
34473         return this.updating;
34474     },
34475
34476     /**
34477      * Suspend the LayoutManager from doing auto-layouts while
34478      * making multiple add or remove calls
34479      */
34480     beginUpdate : function(){
34481         this.updating = true;
34482     },
34483
34484     /**
34485      * Restore auto-layouts and optionally disable the manager from performing a layout
34486      * @param {Boolean} noLayout true to disable a layout update
34487      */
34488     endUpdate : function(noLayout){
34489         this.updating = false;
34490         if(!noLayout){
34491             this.layout();
34492         }
34493     },
34494
34495     layout: function(){
34496         // abstract...
34497     },
34498
34499     onRegionResized : function(region, newSize){
34500         this.fireEvent("regionresized", region, newSize);
34501         this.layout();
34502     },
34503
34504     onRegionCollapsed : function(region){
34505         this.fireEvent("regioncollapsed", region);
34506     },
34507
34508     onRegionExpanded : function(region){
34509         this.fireEvent("regionexpanded", region);
34510     },
34511
34512     /**
34513      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34514      * performs box-model adjustments.
34515      * @return {Object} The size as an object {width: (the width), height: (the height)}
34516      */
34517     getViewSize : function()
34518     {
34519         var size;
34520         if(this.el.dom != document.body){
34521             size = this.el.getSize();
34522         }else{
34523             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34524         }
34525         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34526         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34527         return size;
34528     },
34529
34530     /**
34531      * Returns the Element this layout is bound to.
34532      * @return {Roo.Element}
34533      */
34534     getEl : function(){
34535         return this.el;
34536     },
34537
34538     /**
34539      * Returns the specified region.
34540      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34541      * @return {Roo.LayoutRegion}
34542      */
34543     getRegion : function(target){
34544         return this.regions[target.toLowerCase()];
34545     },
34546
34547     onWindowResize : function(){
34548         if(this.monitorWindowResize){
34549             this.layout();
34550         }
34551     }
34552 });
34553 /*
34554  * Based on:
34555  * Ext JS Library 1.1.1
34556  * Copyright(c) 2006-2007, Ext JS, LLC.
34557  *
34558  * Originally Released Under LGPL - original licence link has changed is not relivant.
34559  *
34560  * Fork - LGPL
34561  * <script type="text/javascript">
34562  */
34563 /**
34564  * @class Roo.bootstrap.layout.Border
34565  * @extends Roo.bootstrap.layout.Manager
34566  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34567  * please see: examples/bootstrap/nested.html<br><br>
34568  
34569 <b>The container the layout is rendered into can be either the body element or any other element.
34570 If it is not the body element, the container needs to either be an absolute positioned element,
34571 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34572 the container size if it is not the body element.</b>
34573
34574 * @constructor
34575 * Create a new Border
34576 * @param {Object} config Configuration options
34577  */
34578 Roo.bootstrap.layout.Border = function(config){
34579     config = config || {};
34580     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34581     
34582     
34583     
34584     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34585         if(config[region]){
34586             config[region].region = region;
34587             this.addRegion(config[region]);
34588         }
34589     },this);
34590     
34591 };
34592
34593 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34594
34595 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34596     /**
34597      * Creates and adds a new region if it doesn't already exist.
34598      * @param {String} target The target region key (north, south, east, west or center).
34599      * @param {Object} config The regions config object
34600      * @return {BorderLayoutRegion} The new region
34601      */
34602     addRegion : function(config)
34603     {
34604         if(!this.regions[config.region]){
34605             var r = this.factory(config);
34606             this.bindRegion(r);
34607         }
34608         return this.regions[config.region];
34609     },
34610
34611     // private (kinda)
34612     bindRegion : function(r){
34613         this.regions[r.config.region] = r;
34614         
34615         r.on("visibilitychange",    this.layout, this);
34616         r.on("paneladded",          this.layout, this);
34617         r.on("panelremoved",        this.layout, this);
34618         r.on("invalidated",         this.layout, this);
34619         r.on("resized",             this.onRegionResized, this);
34620         r.on("collapsed",           this.onRegionCollapsed, this);
34621         r.on("expanded",            this.onRegionExpanded, this);
34622     },
34623
34624     /**
34625      * Performs a layout update.
34626      */
34627     layout : function()
34628     {
34629         if(this.updating) {
34630             return;
34631         }
34632         
34633         // render all the rebions if they have not been done alreayd?
34634         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34635             if(this.regions[region] && !this.regions[region].bodyEl){
34636                 this.regions[region].onRender(this.el)
34637             }
34638         },this);
34639         
34640         var size = this.getViewSize();
34641         var w = size.width;
34642         var h = size.height;
34643         var centerW = w;
34644         var centerH = h;
34645         var centerY = 0;
34646         var centerX = 0;
34647         //var x = 0, y = 0;
34648
34649         var rs = this.regions;
34650         var north = rs["north"];
34651         var south = rs["south"]; 
34652         var west = rs["west"];
34653         var east = rs["east"];
34654         var center = rs["center"];
34655         //if(this.hideOnLayout){ // not supported anymore
34656             //c.el.setStyle("display", "none");
34657         //}
34658         if(north && north.isVisible()){
34659             var b = north.getBox();
34660             var m = north.getMargins();
34661             b.width = w - (m.left+m.right);
34662             b.x = m.left;
34663             b.y = m.top;
34664             centerY = b.height + b.y + m.bottom;
34665             centerH -= centerY;
34666             north.updateBox(this.safeBox(b));
34667         }
34668         if(south && south.isVisible()){
34669             var b = south.getBox();
34670             var m = south.getMargins();
34671             b.width = w - (m.left+m.right);
34672             b.x = m.left;
34673             var totalHeight = (b.height + m.top + m.bottom);
34674             b.y = h - totalHeight + m.top;
34675             centerH -= totalHeight;
34676             south.updateBox(this.safeBox(b));
34677         }
34678         if(west && west.isVisible()){
34679             var b = west.getBox();
34680             var m = west.getMargins();
34681             b.height = centerH - (m.top+m.bottom);
34682             b.x = m.left;
34683             b.y = centerY + m.top;
34684             var totalWidth = (b.width + m.left + m.right);
34685             centerX += totalWidth;
34686             centerW -= totalWidth;
34687             west.updateBox(this.safeBox(b));
34688         }
34689         if(east && east.isVisible()){
34690             var b = east.getBox();
34691             var m = east.getMargins();
34692             b.height = centerH - (m.top+m.bottom);
34693             var totalWidth = (b.width + m.left + m.right);
34694             b.x = w - totalWidth + m.left;
34695             b.y = centerY + m.top;
34696             centerW -= totalWidth;
34697             east.updateBox(this.safeBox(b));
34698         }
34699         if(center){
34700             var m = center.getMargins();
34701             var centerBox = {
34702                 x: centerX + m.left,
34703                 y: centerY + m.top,
34704                 width: centerW - (m.left+m.right),
34705                 height: centerH - (m.top+m.bottom)
34706             };
34707             //if(this.hideOnLayout){
34708                 //center.el.setStyle("display", "block");
34709             //}
34710             center.updateBox(this.safeBox(centerBox));
34711         }
34712         this.el.repaint();
34713         this.fireEvent("layout", this);
34714     },
34715
34716     // private
34717     safeBox : function(box){
34718         box.width = Math.max(0, box.width);
34719         box.height = Math.max(0, box.height);
34720         return box;
34721     },
34722
34723     /**
34724      * Adds a ContentPanel (or subclass) to this layout.
34725      * @param {String} target The target region key (north, south, east, west or center).
34726      * @param {Roo.ContentPanel} panel The panel to add
34727      * @return {Roo.ContentPanel} The added panel
34728      */
34729     add : function(target, panel){
34730          
34731         target = target.toLowerCase();
34732         return this.regions[target].add(panel);
34733     },
34734
34735     /**
34736      * Remove a ContentPanel (or subclass) to this layout.
34737      * @param {String} target The target region key (north, south, east, west or center).
34738      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34739      * @return {Roo.ContentPanel} The removed panel
34740      */
34741     remove : function(target, panel){
34742         target = target.toLowerCase();
34743         return this.regions[target].remove(panel);
34744     },
34745
34746     /**
34747      * Searches all regions for a panel with the specified id
34748      * @param {String} panelId
34749      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34750      */
34751     findPanel : function(panelId){
34752         var rs = this.regions;
34753         for(var target in rs){
34754             if(typeof rs[target] != "function"){
34755                 var p = rs[target].getPanel(panelId);
34756                 if(p){
34757                     return p;
34758                 }
34759             }
34760         }
34761         return null;
34762     },
34763
34764     /**
34765      * Searches all regions for a panel with the specified id and activates (shows) it.
34766      * @param {String/ContentPanel} panelId The panels id or the panel itself
34767      * @return {Roo.ContentPanel} The shown panel or null
34768      */
34769     showPanel : function(panelId) {
34770       var rs = this.regions;
34771       for(var target in rs){
34772          var r = rs[target];
34773          if(typeof r != "function"){
34774             if(r.hasPanel(panelId)){
34775                return r.showPanel(panelId);
34776             }
34777          }
34778       }
34779       return null;
34780    },
34781
34782    /**
34783      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34784      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34785      */
34786    /*
34787     restoreState : function(provider){
34788         if(!provider){
34789             provider = Roo.state.Manager;
34790         }
34791         var sm = new Roo.LayoutStateManager();
34792         sm.init(this, provider);
34793     },
34794 */
34795  
34796  
34797     /**
34798      * Adds a xtype elements to the layout.
34799      * <pre><code>
34800
34801 layout.addxtype({
34802        xtype : 'ContentPanel',
34803        region: 'west',
34804        items: [ .... ]
34805    }
34806 );
34807
34808 layout.addxtype({
34809         xtype : 'NestedLayoutPanel',
34810         region: 'west',
34811         layout: {
34812            center: { },
34813            west: { }   
34814         },
34815         items : [ ... list of content panels or nested layout panels.. ]
34816    }
34817 );
34818 </code></pre>
34819      * @param {Object} cfg Xtype definition of item to add.
34820      */
34821     addxtype : function(cfg)
34822     {
34823         // basically accepts a pannel...
34824         // can accept a layout region..!?!?
34825         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34826         
34827         
34828         // theory?  children can only be panels??
34829         
34830         //if (!cfg.xtype.match(/Panel$/)) {
34831         //    return false;
34832         //}
34833         var ret = false;
34834         
34835         if (typeof(cfg.region) == 'undefined') {
34836             Roo.log("Failed to add Panel, region was not set");
34837             Roo.log(cfg);
34838             return false;
34839         }
34840         var region = cfg.region;
34841         delete cfg.region;
34842         
34843           
34844         var xitems = [];
34845         if (cfg.items) {
34846             xitems = cfg.items;
34847             delete cfg.items;
34848         }
34849         var nb = false;
34850         
34851         switch(cfg.xtype) 
34852         {
34853             case 'Content':  // ContentPanel (el, cfg)
34854             case 'Scroll':  // ContentPanel (el, cfg)
34855             case 'View': 
34856                 cfg.autoCreate = true;
34857                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34858                 //} else {
34859                 //    var el = this.el.createChild();
34860                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34861                 //}
34862                 
34863                 this.add(region, ret);
34864                 break;
34865             
34866             /*
34867             case 'TreePanel': // our new panel!
34868                 cfg.el = this.el.createChild();
34869                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34870                 this.add(region, ret);
34871                 break;
34872             */
34873             
34874             case 'Nest': 
34875                 // create a new Layout (which is  a Border Layout...
34876                 
34877                 var clayout = cfg.layout;
34878                 clayout.el  = this.el.createChild();
34879                 clayout.items   = clayout.items  || [];
34880                 
34881                 delete cfg.layout;
34882                 
34883                 // replace this exitems with the clayout ones..
34884                 xitems = clayout.items;
34885                  
34886                 // force background off if it's in center...
34887                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34888                     cfg.background = false;
34889                 }
34890                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34891                 
34892                 
34893                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34894                 //console.log('adding nested layout panel '  + cfg.toSource());
34895                 this.add(region, ret);
34896                 nb = {}; /// find first...
34897                 break;
34898             
34899             case 'Grid':
34900                 
34901                 // needs grid and region
34902                 
34903                 //var el = this.getRegion(region).el.createChild();
34904                 /*
34905                  *var el = this.el.createChild();
34906                 // create the grid first...
34907                 cfg.grid.container = el;
34908                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34909                 */
34910                 
34911                 if (region == 'center' && this.active ) {
34912                     cfg.background = false;
34913                 }
34914                 
34915                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34916                 
34917                 this.add(region, ret);
34918                 /*
34919                 if (cfg.background) {
34920                     // render grid on panel activation (if panel background)
34921                     ret.on('activate', function(gp) {
34922                         if (!gp.grid.rendered) {
34923                     //        gp.grid.render(el);
34924                         }
34925                     });
34926                 } else {
34927                   //  cfg.grid.render(el);
34928                 }
34929                 */
34930                 break;
34931            
34932            
34933             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34934                 // it was the old xcomponent building that caused this before.
34935                 // espeically if border is the top element in the tree.
34936                 ret = this;
34937                 break; 
34938                 
34939                     
34940                 
34941                 
34942                 
34943             default:
34944                 /*
34945                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34946                     
34947                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34948                     this.add(region, ret);
34949                 } else {
34950                 */
34951                     Roo.log(cfg);
34952                     throw "Can not add '" + cfg.xtype + "' to Border";
34953                     return null;
34954              
34955                                 
34956              
34957         }
34958         this.beginUpdate();
34959         // add children..
34960         var region = '';
34961         var abn = {};
34962         Roo.each(xitems, function(i)  {
34963             region = nb && i.region ? i.region : false;
34964             
34965             var add = ret.addxtype(i);
34966            
34967             if (region) {
34968                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34969                 if (!i.background) {
34970                     abn[region] = nb[region] ;
34971                 }
34972             }
34973             
34974         });
34975         this.endUpdate();
34976
34977         // make the last non-background panel active..
34978         //if (nb) { Roo.log(abn); }
34979         if (nb) {
34980             
34981             for(var r in abn) {
34982                 region = this.getRegion(r);
34983                 if (region) {
34984                     // tried using nb[r], but it does not work..
34985                      
34986                     region.showPanel(abn[r]);
34987                    
34988                 }
34989             }
34990         }
34991         return ret;
34992         
34993     },
34994     
34995     
34996 // private
34997     factory : function(cfg)
34998     {
34999         
35000         var validRegions = Roo.bootstrap.layout.Border.regions;
35001
35002         var target = cfg.region;
35003         cfg.mgr = this;
35004         
35005         var r = Roo.bootstrap.layout;
35006         Roo.log(target);
35007         switch(target){
35008             case "north":
35009                 return new r.North(cfg);
35010             case "south":
35011                 return new r.South(cfg);
35012             case "east":
35013                 return new r.East(cfg);
35014             case "west":
35015                 return new r.West(cfg);
35016             case "center":
35017                 return new r.Center(cfg);
35018         }
35019         throw 'Layout region "'+target+'" not supported.';
35020     }
35021     
35022     
35023 });
35024  /*
35025  * Based on:
35026  * Ext JS Library 1.1.1
35027  * Copyright(c) 2006-2007, Ext JS, LLC.
35028  *
35029  * Originally Released Under LGPL - original licence link has changed is not relivant.
35030  *
35031  * Fork - LGPL
35032  * <script type="text/javascript">
35033  */
35034  
35035 /**
35036  * @class Roo.bootstrap.layout.Basic
35037  * @extends Roo.util.Observable
35038  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35039  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35040  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35041  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35042  * @cfg {string}   region  the region that it inhabits..
35043  * @cfg {bool}   skipConfig skip config?
35044  * 
35045
35046  */
35047 Roo.bootstrap.layout.Basic = function(config){
35048     
35049     this.mgr = config.mgr;
35050     
35051     this.position = config.region;
35052     
35053     var skipConfig = config.skipConfig;
35054     
35055     this.events = {
35056         /**
35057          * @scope Roo.BasicLayoutRegion
35058          */
35059         
35060         /**
35061          * @event beforeremove
35062          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35063          * @param {Roo.LayoutRegion} this
35064          * @param {Roo.ContentPanel} panel The panel
35065          * @param {Object} e The cancel event object
35066          */
35067         "beforeremove" : true,
35068         /**
35069          * @event invalidated
35070          * Fires when the layout for this region is changed.
35071          * @param {Roo.LayoutRegion} this
35072          */
35073         "invalidated" : true,
35074         /**
35075          * @event visibilitychange
35076          * Fires when this region is shown or hidden 
35077          * @param {Roo.LayoutRegion} this
35078          * @param {Boolean} visibility true or false
35079          */
35080         "visibilitychange" : true,
35081         /**
35082          * @event paneladded
35083          * Fires when a panel is added. 
35084          * @param {Roo.LayoutRegion} this
35085          * @param {Roo.ContentPanel} panel The panel
35086          */
35087         "paneladded" : true,
35088         /**
35089          * @event panelremoved
35090          * Fires when a panel is removed. 
35091          * @param {Roo.LayoutRegion} this
35092          * @param {Roo.ContentPanel} panel The panel
35093          */
35094         "panelremoved" : true,
35095         /**
35096          * @event beforecollapse
35097          * Fires when this region before collapse.
35098          * @param {Roo.LayoutRegion} this
35099          */
35100         "beforecollapse" : true,
35101         /**
35102          * @event collapsed
35103          * Fires when this region is collapsed.
35104          * @param {Roo.LayoutRegion} this
35105          */
35106         "collapsed" : true,
35107         /**
35108          * @event expanded
35109          * Fires when this region is expanded.
35110          * @param {Roo.LayoutRegion} this
35111          */
35112         "expanded" : true,
35113         /**
35114          * @event slideshow
35115          * Fires when this region is slid into view.
35116          * @param {Roo.LayoutRegion} this
35117          */
35118         "slideshow" : true,
35119         /**
35120          * @event slidehide
35121          * Fires when this region slides out of view. 
35122          * @param {Roo.LayoutRegion} this
35123          */
35124         "slidehide" : true,
35125         /**
35126          * @event panelactivated
35127          * Fires when a panel is activated. 
35128          * @param {Roo.LayoutRegion} this
35129          * @param {Roo.ContentPanel} panel The activated panel
35130          */
35131         "panelactivated" : true,
35132         /**
35133          * @event resized
35134          * Fires when the user resizes this region. 
35135          * @param {Roo.LayoutRegion} this
35136          * @param {Number} newSize The new size (width for east/west, height for north/south)
35137          */
35138         "resized" : true
35139     };
35140     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35141     this.panels = new Roo.util.MixedCollection();
35142     this.panels.getKey = this.getPanelId.createDelegate(this);
35143     this.box = null;
35144     this.activePanel = null;
35145     // ensure listeners are added...
35146     
35147     if (config.listeners || config.events) {
35148         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35149             listeners : config.listeners || {},
35150             events : config.events || {}
35151         });
35152     }
35153     
35154     if(skipConfig !== true){
35155         this.applyConfig(config);
35156     }
35157 };
35158
35159 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35160 {
35161     getPanelId : function(p){
35162         return p.getId();
35163     },
35164     
35165     applyConfig : function(config){
35166         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35167         this.config = config;
35168         
35169     },
35170     
35171     /**
35172      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35173      * the width, for horizontal (north, south) the height.
35174      * @param {Number} newSize The new width or height
35175      */
35176     resizeTo : function(newSize){
35177         var el = this.el ? this.el :
35178                  (this.activePanel ? this.activePanel.getEl() : null);
35179         if(el){
35180             switch(this.position){
35181                 case "east":
35182                 case "west":
35183                     el.setWidth(newSize);
35184                     this.fireEvent("resized", this, newSize);
35185                 break;
35186                 case "north":
35187                 case "south":
35188                     el.setHeight(newSize);
35189                     this.fireEvent("resized", this, newSize);
35190                 break;                
35191             }
35192         }
35193     },
35194     
35195     getBox : function(){
35196         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35197     },
35198     
35199     getMargins : function(){
35200         return this.margins;
35201     },
35202     
35203     updateBox : function(box){
35204         this.box = box;
35205         var el = this.activePanel.getEl();
35206         el.dom.style.left = box.x + "px";
35207         el.dom.style.top = box.y + "px";
35208         this.activePanel.setSize(box.width, box.height);
35209     },
35210     
35211     /**
35212      * Returns the container element for this region.
35213      * @return {Roo.Element}
35214      */
35215     getEl : function(){
35216         return this.activePanel;
35217     },
35218     
35219     /**
35220      * Returns true if this region is currently visible.
35221      * @return {Boolean}
35222      */
35223     isVisible : function(){
35224         return this.activePanel ? true : false;
35225     },
35226     
35227     setActivePanel : function(panel){
35228         panel = this.getPanel(panel);
35229         if(this.activePanel && this.activePanel != panel){
35230             this.activePanel.setActiveState(false);
35231             this.activePanel.getEl().setLeftTop(-10000,-10000);
35232         }
35233         this.activePanel = panel;
35234         panel.setActiveState(true);
35235         if(this.box){
35236             panel.setSize(this.box.width, this.box.height);
35237         }
35238         this.fireEvent("panelactivated", this, panel);
35239         this.fireEvent("invalidated");
35240     },
35241     
35242     /**
35243      * Show the specified panel.
35244      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35245      * @return {Roo.ContentPanel} The shown panel or null
35246      */
35247     showPanel : function(panel){
35248         panel = this.getPanel(panel);
35249         if(panel){
35250             this.setActivePanel(panel);
35251         }
35252         return panel;
35253     },
35254     
35255     /**
35256      * Get the active panel for this region.
35257      * @return {Roo.ContentPanel} The active panel or null
35258      */
35259     getActivePanel : function(){
35260         return this.activePanel;
35261     },
35262     
35263     /**
35264      * Add the passed ContentPanel(s)
35265      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35266      * @return {Roo.ContentPanel} The panel added (if only one was added)
35267      */
35268     add : function(panel){
35269         if(arguments.length > 1){
35270             for(var i = 0, len = arguments.length; i < len; i++) {
35271                 this.add(arguments[i]);
35272             }
35273             return null;
35274         }
35275         if(this.hasPanel(panel)){
35276             this.showPanel(panel);
35277             return panel;
35278         }
35279         var el = panel.getEl();
35280         if(el.dom.parentNode != this.mgr.el.dom){
35281             this.mgr.el.dom.appendChild(el.dom);
35282         }
35283         if(panel.setRegion){
35284             panel.setRegion(this);
35285         }
35286         this.panels.add(panel);
35287         el.setStyle("position", "absolute");
35288         if(!panel.background){
35289             this.setActivePanel(panel);
35290             if(this.config.initialSize && this.panels.getCount()==1){
35291                 this.resizeTo(this.config.initialSize);
35292             }
35293         }
35294         this.fireEvent("paneladded", this, panel);
35295         return panel;
35296     },
35297     
35298     /**
35299      * Returns true if the panel is in this region.
35300      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35301      * @return {Boolean}
35302      */
35303     hasPanel : function(panel){
35304         if(typeof panel == "object"){ // must be panel obj
35305             panel = panel.getId();
35306         }
35307         return this.getPanel(panel) ? true : false;
35308     },
35309     
35310     /**
35311      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35312      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35313      * @param {Boolean} preservePanel Overrides the config preservePanel option
35314      * @return {Roo.ContentPanel} The panel that was removed
35315      */
35316     remove : function(panel, preservePanel){
35317         panel = this.getPanel(panel);
35318         if(!panel){
35319             return null;
35320         }
35321         var e = {};
35322         this.fireEvent("beforeremove", this, panel, e);
35323         if(e.cancel === true){
35324             return null;
35325         }
35326         var panelId = panel.getId();
35327         this.panels.removeKey(panelId);
35328         return panel;
35329     },
35330     
35331     /**
35332      * Returns the panel specified or null if it's not in this region.
35333      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35334      * @return {Roo.ContentPanel}
35335      */
35336     getPanel : function(id){
35337         if(typeof id == "object"){ // must be panel obj
35338             return id;
35339         }
35340         return this.panels.get(id);
35341     },
35342     
35343     /**
35344      * Returns this regions position (north/south/east/west/center).
35345      * @return {String} 
35346      */
35347     getPosition: function(){
35348         return this.position;    
35349     }
35350 });/*
35351  * Based on:
35352  * Ext JS Library 1.1.1
35353  * Copyright(c) 2006-2007, Ext JS, LLC.
35354  *
35355  * Originally Released Under LGPL - original licence link has changed is not relivant.
35356  *
35357  * Fork - LGPL
35358  * <script type="text/javascript">
35359  */
35360  
35361 /**
35362  * @class Roo.bootstrap.layout.Region
35363  * @extends Roo.bootstrap.layout.Basic
35364  * This class represents a region in a layout manager.
35365  
35366  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35367  * @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})
35368  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35369  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35370  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35371  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35372  * @cfg {String}    title           The title for the region (overrides panel titles)
35373  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35374  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35375  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35376  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35377  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35378  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35379  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35380  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35381  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35382  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35383
35384  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35385  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35386  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35387  * @cfg {Number}    width           For East/West panels
35388  * @cfg {Number}    height          For North/South panels
35389  * @cfg {Boolean}   split           To show the splitter
35390  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35391  * 
35392  * @cfg {string}   cls             Extra CSS classes to add to region
35393  * 
35394  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35395  * @cfg {string}   region  the region that it inhabits..
35396  *
35397
35398  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35399  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35400
35401  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35402  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35403  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35404  */
35405 Roo.bootstrap.layout.Region = function(config)
35406 {
35407     this.applyConfig(config);
35408
35409     var mgr = config.mgr;
35410     var pos = config.region;
35411     config.skipConfig = true;
35412     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35413     
35414     if (mgr.el) {
35415         this.onRender(mgr.el);   
35416     }
35417      
35418     this.visible = true;
35419     this.collapsed = false;
35420     this.unrendered_panels = [];
35421 };
35422
35423 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35424
35425     position: '', // set by wrapper (eg. north/south etc..)
35426     unrendered_panels : null,  // unrendered panels.
35427     createBody : function(){
35428         /** This region's body element 
35429         * @type Roo.Element */
35430         this.bodyEl = this.el.createChild({
35431                 tag: "div",
35432                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35433         });
35434     },
35435
35436     onRender: function(ctr, pos)
35437     {
35438         var dh = Roo.DomHelper;
35439         /** This region's container element 
35440         * @type Roo.Element */
35441         this.el = dh.append(ctr.dom, {
35442                 tag: "div",
35443                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35444             }, true);
35445         /** This region's title element 
35446         * @type Roo.Element */
35447     
35448         this.titleEl = dh.append(this.el.dom,
35449             {
35450                     tag: "div",
35451                     unselectable: "on",
35452                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35453                     children:[
35454                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35455                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35456                     ]}, true);
35457         
35458         this.titleEl.enableDisplayMode();
35459         /** This region's title text element 
35460         * @type HTMLElement */
35461         this.titleTextEl = this.titleEl.dom.firstChild;
35462         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35463         /*
35464         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35465         this.closeBtn.enableDisplayMode();
35466         this.closeBtn.on("click", this.closeClicked, this);
35467         this.closeBtn.hide();
35468     */
35469         this.createBody(this.config);
35470         if(this.config.hideWhenEmpty){
35471             this.hide();
35472             this.on("paneladded", this.validateVisibility, this);
35473             this.on("panelremoved", this.validateVisibility, this);
35474         }
35475         if(this.autoScroll){
35476             this.bodyEl.setStyle("overflow", "auto");
35477         }else{
35478             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35479         }
35480         //if(c.titlebar !== false){
35481             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35482                 this.titleEl.hide();
35483             }else{
35484                 this.titleEl.show();
35485                 if(this.config.title){
35486                     this.titleTextEl.innerHTML = this.config.title;
35487                 }
35488             }
35489         //}
35490         if(this.config.collapsed){
35491             this.collapse(true);
35492         }
35493         if(this.config.hidden){
35494             this.hide();
35495         }
35496         
35497         if (this.unrendered_panels && this.unrendered_panels.length) {
35498             for (var i =0;i< this.unrendered_panels.length; i++) {
35499                 this.add(this.unrendered_panels[i]);
35500             }
35501             this.unrendered_panels = null;
35502             
35503         }
35504         
35505     },
35506     
35507     applyConfig : function(c)
35508     {
35509         /*
35510          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35511             var dh = Roo.DomHelper;
35512             if(c.titlebar !== false){
35513                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35514                 this.collapseBtn.on("click", this.collapse, this);
35515                 this.collapseBtn.enableDisplayMode();
35516                 /*
35517                 if(c.showPin === true || this.showPin){
35518                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35519                     this.stickBtn.enableDisplayMode();
35520                     this.stickBtn.on("click", this.expand, this);
35521                     this.stickBtn.hide();
35522                 }
35523                 
35524             }
35525             */
35526             /** This region's collapsed element
35527             * @type Roo.Element */
35528             /*
35529              *
35530             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35531                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35532             ]}, true);
35533             
35534             if(c.floatable !== false){
35535                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35536                this.collapsedEl.on("click", this.collapseClick, this);
35537             }
35538
35539             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35540                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35541                    id: "message", unselectable: "on", style:{"float":"left"}});
35542                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35543              }
35544             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35545             this.expandBtn.on("click", this.expand, this);
35546             
35547         }
35548         
35549         if(this.collapseBtn){
35550             this.collapseBtn.setVisible(c.collapsible == true);
35551         }
35552         
35553         this.cmargins = c.cmargins || this.cmargins ||
35554                          (this.position == "west" || this.position == "east" ?
35555                              {top: 0, left: 2, right:2, bottom: 0} :
35556                              {top: 2, left: 0, right:0, bottom: 2});
35557         */
35558         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35559         
35560         
35561         this.bottomTabs = c.tabPosition != "top";
35562         
35563         this.autoScroll = c.autoScroll || false;
35564         
35565         
35566        
35567         
35568         this.duration = c.duration || .30;
35569         this.slideDuration = c.slideDuration || .45;
35570         this.config = c;
35571        
35572     },
35573     /**
35574      * Returns true if this region is currently visible.
35575      * @return {Boolean}
35576      */
35577     isVisible : function(){
35578         return this.visible;
35579     },
35580
35581     /**
35582      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35583      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35584      */
35585     //setCollapsedTitle : function(title){
35586     //    title = title || "&#160;";
35587      //   if(this.collapsedTitleTextEl){
35588       //      this.collapsedTitleTextEl.innerHTML = title;
35589        // }
35590     //},
35591
35592     getBox : function(){
35593         var b;
35594       //  if(!this.collapsed){
35595             b = this.el.getBox(false, true);
35596        // }else{
35597           //  b = this.collapsedEl.getBox(false, true);
35598         //}
35599         return b;
35600     },
35601
35602     getMargins : function(){
35603         return this.margins;
35604         //return this.collapsed ? this.cmargins : this.margins;
35605     },
35606 /*
35607     highlight : function(){
35608         this.el.addClass("x-layout-panel-dragover");
35609     },
35610
35611     unhighlight : function(){
35612         this.el.removeClass("x-layout-panel-dragover");
35613     },
35614 */
35615     updateBox : function(box)
35616     {
35617         if (!this.bodyEl) {
35618             return; // not rendered yet..
35619         }
35620         
35621         this.box = box;
35622         if(!this.collapsed){
35623             this.el.dom.style.left = box.x + "px";
35624             this.el.dom.style.top = box.y + "px";
35625             this.updateBody(box.width, box.height);
35626         }else{
35627             this.collapsedEl.dom.style.left = box.x + "px";
35628             this.collapsedEl.dom.style.top = box.y + "px";
35629             this.collapsedEl.setSize(box.width, box.height);
35630         }
35631         if(this.tabs){
35632             this.tabs.autoSizeTabs();
35633         }
35634     },
35635
35636     updateBody : function(w, h)
35637     {
35638         if(w !== null){
35639             this.el.setWidth(w);
35640             w -= this.el.getBorderWidth("rl");
35641             if(this.config.adjustments){
35642                 w += this.config.adjustments[0];
35643             }
35644         }
35645         if(h !== null && h > 0){
35646             this.el.setHeight(h);
35647             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35648             h -= this.el.getBorderWidth("tb");
35649             if(this.config.adjustments){
35650                 h += this.config.adjustments[1];
35651             }
35652             this.bodyEl.setHeight(h);
35653             if(this.tabs){
35654                 h = this.tabs.syncHeight(h);
35655             }
35656         }
35657         if(this.panelSize){
35658             w = w !== null ? w : this.panelSize.width;
35659             h = h !== null ? h : this.panelSize.height;
35660         }
35661         if(this.activePanel){
35662             var el = this.activePanel.getEl();
35663             w = w !== null ? w : el.getWidth();
35664             h = h !== null ? h : el.getHeight();
35665             this.panelSize = {width: w, height: h};
35666             this.activePanel.setSize(w, h);
35667         }
35668         if(Roo.isIE && this.tabs){
35669             this.tabs.el.repaint();
35670         }
35671     },
35672
35673     /**
35674      * Returns the container element for this region.
35675      * @return {Roo.Element}
35676      */
35677     getEl : function(){
35678         return this.el;
35679     },
35680
35681     /**
35682      * Hides this region.
35683      */
35684     hide : function(){
35685         //if(!this.collapsed){
35686             this.el.dom.style.left = "-2000px";
35687             this.el.hide();
35688         //}else{
35689          //   this.collapsedEl.dom.style.left = "-2000px";
35690          //   this.collapsedEl.hide();
35691        // }
35692         this.visible = false;
35693         this.fireEvent("visibilitychange", this, false);
35694     },
35695
35696     /**
35697      * Shows this region if it was previously hidden.
35698      */
35699     show : function(){
35700         //if(!this.collapsed){
35701             this.el.show();
35702         //}else{
35703         //    this.collapsedEl.show();
35704        // }
35705         this.visible = true;
35706         this.fireEvent("visibilitychange", this, true);
35707     },
35708 /*
35709     closeClicked : function(){
35710         if(this.activePanel){
35711             this.remove(this.activePanel);
35712         }
35713     },
35714
35715     collapseClick : function(e){
35716         if(this.isSlid){
35717            e.stopPropagation();
35718            this.slideIn();
35719         }else{
35720            e.stopPropagation();
35721            this.slideOut();
35722         }
35723     },
35724 */
35725     /**
35726      * Collapses this region.
35727      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35728      */
35729     /*
35730     collapse : function(skipAnim, skipCheck = false){
35731         if(this.collapsed) {
35732             return;
35733         }
35734         
35735         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35736             
35737             this.collapsed = true;
35738             if(this.split){
35739                 this.split.el.hide();
35740             }
35741             if(this.config.animate && skipAnim !== true){
35742                 this.fireEvent("invalidated", this);
35743                 this.animateCollapse();
35744             }else{
35745                 this.el.setLocation(-20000,-20000);
35746                 this.el.hide();
35747                 this.collapsedEl.show();
35748                 this.fireEvent("collapsed", this);
35749                 this.fireEvent("invalidated", this);
35750             }
35751         }
35752         
35753     },
35754 */
35755     animateCollapse : function(){
35756         // overridden
35757     },
35758
35759     /**
35760      * Expands this region if it was previously collapsed.
35761      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35762      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35763      */
35764     /*
35765     expand : function(e, skipAnim){
35766         if(e) {
35767             e.stopPropagation();
35768         }
35769         if(!this.collapsed || this.el.hasActiveFx()) {
35770             return;
35771         }
35772         if(this.isSlid){
35773             this.afterSlideIn();
35774             skipAnim = true;
35775         }
35776         this.collapsed = false;
35777         if(this.config.animate && skipAnim !== true){
35778             this.animateExpand();
35779         }else{
35780             this.el.show();
35781             if(this.split){
35782                 this.split.el.show();
35783             }
35784             this.collapsedEl.setLocation(-2000,-2000);
35785             this.collapsedEl.hide();
35786             this.fireEvent("invalidated", this);
35787             this.fireEvent("expanded", this);
35788         }
35789     },
35790 */
35791     animateExpand : function(){
35792         // overridden
35793     },
35794
35795     initTabs : function()
35796     {
35797         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35798         
35799         var ts = new Roo.bootstrap.panel.Tabs({
35800                 el: this.bodyEl.dom,
35801                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35802                 disableTooltips: this.config.disableTabTips,
35803                 toolbar : this.config.toolbar
35804             });
35805         
35806         if(this.config.hideTabs){
35807             ts.stripWrap.setDisplayed(false);
35808         }
35809         this.tabs = ts;
35810         ts.resizeTabs = this.config.resizeTabs === true;
35811         ts.minTabWidth = this.config.minTabWidth || 40;
35812         ts.maxTabWidth = this.config.maxTabWidth || 250;
35813         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35814         ts.monitorResize = false;
35815         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35816         ts.bodyEl.addClass('roo-layout-tabs-body');
35817         this.panels.each(this.initPanelAsTab, this);
35818     },
35819
35820     initPanelAsTab : function(panel){
35821         var ti = this.tabs.addTab(
35822             panel.getEl().id,
35823             panel.getTitle(),
35824             null,
35825             this.config.closeOnTab && panel.isClosable(),
35826             panel.tpl
35827         );
35828         if(panel.tabTip !== undefined){
35829             ti.setTooltip(panel.tabTip);
35830         }
35831         ti.on("activate", function(){
35832               this.setActivePanel(panel);
35833         }, this);
35834         
35835         if(this.config.closeOnTab){
35836             ti.on("beforeclose", function(t, e){
35837                 e.cancel = true;
35838                 this.remove(panel);
35839             }, this);
35840         }
35841         
35842         panel.tabItem = ti;
35843         
35844         return ti;
35845     },
35846
35847     updatePanelTitle : function(panel, title)
35848     {
35849         if(this.activePanel == panel){
35850             this.updateTitle(title);
35851         }
35852         if(this.tabs){
35853             var ti = this.tabs.getTab(panel.getEl().id);
35854             ti.setText(title);
35855             if(panel.tabTip !== undefined){
35856                 ti.setTooltip(panel.tabTip);
35857             }
35858         }
35859     },
35860
35861     updateTitle : function(title){
35862         if(this.titleTextEl && !this.config.title){
35863             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35864         }
35865     },
35866
35867     setActivePanel : function(panel)
35868     {
35869         panel = this.getPanel(panel);
35870         if(this.activePanel && this.activePanel != panel){
35871             if(this.activePanel.setActiveState(false) === false){
35872                 return;
35873             }
35874         }
35875         this.activePanel = panel;
35876         panel.setActiveState(true);
35877         if(this.panelSize){
35878             panel.setSize(this.panelSize.width, this.panelSize.height);
35879         }
35880         if(this.closeBtn){
35881             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35882         }
35883         this.updateTitle(panel.getTitle());
35884         if(this.tabs){
35885             this.fireEvent("invalidated", this);
35886         }
35887         this.fireEvent("panelactivated", this, panel);
35888     },
35889
35890     /**
35891      * Shows the specified panel.
35892      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35893      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35894      */
35895     showPanel : function(panel)
35896     {
35897         panel = this.getPanel(panel);
35898         if(panel){
35899             if(this.tabs){
35900                 var tab = this.tabs.getTab(panel.getEl().id);
35901                 if(tab.isHidden()){
35902                     this.tabs.unhideTab(tab.id);
35903                 }
35904                 tab.activate();
35905             }else{
35906                 this.setActivePanel(panel);
35907             }
35908         }
35909         return panel;
35910     },
35911
35912     /**
35913      * Get the active panel for this region.
35914      * @return {Roo.ContentPanel} The active panel or null
35915      */
35916     getActivePanel : function(){
35917         return this.activePanel;
35918     },
35919
35920     validateVisibility : function(){
35921         if(this.panels.getCount() < 1){
35922             this.updateTitle("&#160;");
35923             this.closeBtn.hide();
35924             this.hide();
35925         }else{
35926             if(!this.isVisible()){
35927                 this.show();
35928             }
35929         }
35930     },
35931
35932     /**
35933      * Adds the passed ContentPanel(s) to this region.
35934      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35935      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35936      */
35937     add : function(panel)
35938     {
35939         if(arguments.length > 1){
35940             for(var i = 0, len = arguments.length; i < len; i++) {
35941                 this.add(arguments[i]);
35942             }
35943             return null;
35944         }
35945         
35946         // if we have not been rendered yet, then we can not really do much of this..
35947         if (!this.bodyEl) {
35948             this.unrendered_panels.push(panel);
35949             return panel;
35950         }
35951         
35952         
35953         
35954         
35955         if(this.hasPanel(panel)){
35956             this.showPanel(panel);
35957             return panel;
35958         }
35959         panel.setRegion(this);
35960         this.panels.add(panel);
35961        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35962             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35963             // and hide them... ???
35964             this.bodyEl.dom.appendChild(panel.getEl().dom);
35965             if(panel.background !== true){
35966                 this.setActivePanel(panel);
35967             }
35968             this.fireEvent("paneladded", this, panel);
35969             return panel;
35970         }
35971         */
35972         if(!this.tabs){
35973             this.initTabs();
35974         }else{
35975             this.initPanelAsTab(panel);
35976         }
35977         
35978         
35979         if(panel.background !== true){
35980             this.tabs.activate(panel.getEl().id);
35981         }
35982         this.fireEvent("paneladded", this, panel);
35983         return panel;
35984     },
35985
35986     /**
35987      * Hides the tab for the specified panel.
35988      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35989      */
35990     hidePanel : function(panel){
35991         if(this.tabs && (panel = this.getPanel(panel))){
35992             this.tabs.hideTab(panel.getEl().id);
35993         }
35994     },
35995
35996     /**
35997      * Unhides the tab for a previously hidden panel.
35998      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35999      */
36000     unhidePanel : function(panel){
36001         if(this.tabs && (panel = this.getPanel(panel))){
36002             this.tabs.unhideTab(panel.getEl().id);
36003         }
36004     },
36005
36006     clearPanels : function(){
36007         while(this.panels.getCount() > 0){
36008              this.remove(this.panels.first());
36009         }
36010     },
36011
36012     /**
36013      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36014      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36015      * @param {Boolean} preservePanel Overrides the config preservePanel option
36016      * @return {Roo.ContentPanel} The panel that was removed
36017      */
36018     remove : function(panel, preservePanel)
36019     {
36020         panel = this.getPanel(panel);
36021         if(!panel){
36022             return null;
36023         }
36024         var e = {};
36025         this.fireEvent("beforeremove", this, panel, e);
36026         if(e.cancel === true){
36027             return null;
36028         }
36029         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36030         var panelId = panel.getId();
36031         this.panels.removeKey(panelId);
36032         if(preservePanel){
36033             document.body.appendChild(panel.getEl().dom);
36034         }
36035         if(this.tabs){
36036             this.tabs.removeTab(panel.getEl().id);
36037         }else if (!preservePanel){
36038             this.bodyEl.dom.removeChild(panel.getEl().dom);
36039         }
36040         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36041             var p = this.panels.first();
36042             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36043             tempEl.appendChild(p.getEl().dom);
36044             this.bodyEl.update("");
36045             this.bodyEl.dom.appendChild(p.getEl().dom);
36046             tempEl = null;
36047             this.updateTitle(p.getTitle());
36048             this.tabs = null;
36049             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36050             this.setActivePanel(p);
36051         }
36052         panel.setRegion(null);
36053         if(this.activePanel == panel){
36054             this.activePanel = null;
36055         }
36056         if(this.config.autoDestroy !== false && preservePanel !== true){
36057             try{panel.destroy();}catch(e){}
36058         }
36059         this.fireEvent("panelremoved", this, panel);
36060         return panel;
36061     },
36062
36063     /**
36064      * Returns the TabPanel component used by this region
36065      * @return {Roo.TabPanel}
36066      */
36067     getTabs : function(){
36068         return this.tabs;
36069     },
36070
36071     createTool : function(parentEl, className){
36072         var btn = Roo.DomHelper.append(parentEl, {
36073             tag: "div",
36074             cls: "x-layout-tools-button",
36075             children: [ {
36076                 tag: "div",
36077                 cls: "roo-layout-tools-button-inner " + className,
36078                 html: "&#160;"
36079             }]
36080         }, true);
36081         btn.addClassOnOver("roo-layout-tools-button-over");
36082         return btn;
36083     }
36084 });/*
36085  * Based on:
36086  * Ext JS Library 1.1.1
36087  * Copyright(c) 2006-2007, Ext JS, LLC.
36088  *
36089  * Originally Released Under LGPL - original licence link has changed is not relivant.
36090  *
36091  * Fork - LGPL
36092  * <script type="text/javascript">
36093  */
36094  
36095
36096
36097 /**
36098  * @class Roo.SplitLayoutRegion
36099  * @extends Roo.LayoutRegion
36100  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36101  */
36102 Roo.bootstrap.layout.Split = function(config){
36103     this.cursor = config.cursor;
36104     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36105 };
36106
36107 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36108 {
36109     splitTip : "Drag to resize.",
36110     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36111     useSplitTips : false,
36112
36113     applyConfig : function(config){
36114         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36115     },
36116     
36117     onRender : function(ctr,pos) {
36118         
36119         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36120         if(!this.config.split){
36121             return;
36122         }
36123         if(!this.split){
36124             
36125             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36126                             tag: "div",
36127                             id: this.el.id + "-split",
36128                             cls: "roo-layout-split roo-layout-split-"+this.position,
36129                             html: "&#160;"
36130             });
36131             /** The SplitBar for this region 
36132             * @type Roo.SplitBar */
36133             // does not exist yet...
36134             Roo.log([this.position, this.orientation]);
36135             
36136             this.split = new Roo.bootstrap.SplitBar({
36137                 dragElement : splitEl,
36138                 resizingElement: this.el,
36139                 orientation : this.orientation
36140             });
36141             
36142             this.split.on("moved", this.onSplitMove, this);
36143             this.split.useShim = this.config.useShim === true;
36144             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36145             if(this.useSplitTips){
36146                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36147             }
36148             //if(config.collapsible){
36149             //    this.split.el.on("dblclick", this.collapse,  this);
36150             //}
36151         }
36152         if(typeof this.config.minSize != "undefined"){
36153             this.split.minSize = this.config.minSize;
36154         }
36155         if(typeof this.config.maxSize != "undefined"){
36156             this.split.maxSize = this.config.maxSize;
36157         }
36158         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36159             this.hideSplitter();
36160         }
36161         
36162     },
36163
36164     getHMaxSize : function(){
36165          var cmax = this.config.maxSize || 10000;
36166          var center = this.mgr.getRegion("center");
36167          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36168     },
36169
36170     getVMaxSize : function(){
36171          var cmax = this.config.maxSize || 10000;
36172          var center = this.mgr.getRegion("center");
36173          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36174     },
36175
36176     onSplitMove : function(split, newSize){
36177         this.fireEvent("resized", this, newSize);
36178     },
36179     
36180     /** 
36181      * Returns the {@link Roo.SplitBar} for this region.
36182      * @return {Roo.SplitBar}
36183      */
36184     getSplitBar : function(){
36185         return this.split;
36186     },
36187     
36188     hide : function(){
36189         this.hideSplitter();
36190         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36191     },
36192
36193     hideSplitter : function(){
36194         if(this.split){
36195             this.split.el.setLocation(-2000,-2000);
36196             this.split.el.hide();
36197         }
36198     },
36199
36200     show : function(){
36201         if(this.split){
36202             this.split.el.show();
36203         }
36204         Roo.bootstrap.layout.Split.superclass.show.call(this);
36205     },
36206     
36207     beforeSlide: function(){
36208         if(Roo.isGecko){// firefox overflow auto bug workaround
36209             this.bodyEl.clip();
36210             if(this.tabs) {
36211                 this.tabs.bodyEl.clip();
36212             }
36213             if(this.activePanel){
36214                 this.activePanel.getEl().clip();
36215                 
36216                 if(this.activePanel.beforeSlide){
36217                     this.activePanel.beforeSlide();
36218                 }
36219             }
36220         }
36221     },
36222     
36223     afterSlide : function(){
36224         if(Roo.isGecko){// firefox overflow auto bug workaround
36225             this.bodyEl.unclip();
36226             if(this.tabs) {
36227                 this.tabs.bodyEl.unclip();
36228             }
36229             if(this.activePanel){
36230                 this.activePanel.getEl().unclip();
36231                 if(this.activePanel.afterSlide){
36232                     this.activePanel.afterSlide();
36233                 }
36234             }
36235         }
36236     },
36237
36238     initAutoHide : function(){
36239         if(this.autoHide !== false){
36240             if(!this.autoHideHd){
36241                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36242                 this.autoHideHd = {
36243                     "mouseout": function(e){
36244                         if(!e.within(this.el, true)){
36245                             st.delay(500);
36246                         }
36247                     },
36248                     "mouseover" : function(e){
36249                         st.cancel();
36250                     },
36251                     scope : this
36252                 };
36253             }
36254             this.el.on(this.autoHideHd);
36255         }
36256     },
36257
36258     clearAutoHide : function(){
36259         if(this.autoHide !== false){
36260             this.el.un("mouseout", this.autoHideHd.mouseout);
36261             this.el.un("mouseover", this.autoHideHd.mouseover);
36262         }
36263     },
36264
36265     clearMonitor : function(){
36266         Roo.get(document).un("click", this.slideInIf, this);
36267     },
36268
36269     // these names are backwards but not changed for compat
36270     slideOut : function(){
36271         if(this.isSlid || this.el.hasActiveFx()){
36272             return;
36273         }
36274         this.isSlid = true;
36275         if(this.collapseBtn){
36276             this.collapseBtn.hide();
36277         }
36278         this.closeBtnState = this.closeBtn.getStyle('display');
36279         this.closeBtn.hide();
36280         if(this.stickBtn){
36281             this.stickBtn.show();
36282         }
36283         this.el.show();
36284         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36285         this.beforeSlide();
36286         this.el.setStyle("z-index", 10001);
36287         this.el.slideIn(this.getSlideAnchor(), {
36288             callback: function(){
36289                 this.afterSlide();
36290                 this.initAutoHide();
36291                 Roo.get(document).on("click", this.slideInIf, this);
36292                 this.fireEvent("slideshow", this);
36293             },
36294             scope: this,
36295             block: true
36296         });
36297     },
36298
36299     afterSlideIn : function(){
36300         this.clearAutoHide();
36301         this.isSlid = false;
36302         this.clearMonitor();
36303         this.el.setStyle("z-index", "");
36304         if(this.collapseBtn){
36305             this.collapseBtn.show();
36306         }
36307         this.closeBtn.setStyle('display', this.closeBtnState);
36308         if(this.stickBtn){
36309             this.stickBtn.hide();
36310         }
36311         this.fireEvent("slidehide", this);
36312     },
36313
36314     slideIn : function(cb){
36315         if(!this.isSlid || this.el.hasActiveFx()){
36316             Roo.callback(cb);
36317             return;
36318         }
36319         this.isSlid = false;
36320         this.beforeSlide();
36321         this.el.slideOut(this.getSlideAnchor(), {
36322             callback: function(){
36323                 this.el.setLeftTop(-10000, -10000);
36324                 this.afterSlide();
36325                 this.afterSlideIn();
36326                 Roo.callback(cb);
36327             },
36328             scope: this,
36329             block: true
36330         });
36331     },
36332     
36333     slideInIf : function(e){
36334         if(!e.within(this.el)){
36335             this.slideIn();
36336         }
36337     },
36338
36339     animateCollapse : function(){
36340         this.beforeSlide();
36341         this.el.setStyle("z-index", 20000);
36342         var anchor = this.getSlideAnchor();
36343         this.el.slideOut(anchor, {
36344             callback : function(){
36345                 this.el.setStyle("z-index", "");
36346                 this.collapsedEl.slideIn(anchor, {duration:.3});
36347                 this.afterSlide();
36348                 this.el.setLocation(-10000,-10000);
36349                 this.el.hide();
36350                 this.fireEvent("collapsed", this);
36351             },
36352             scope: this,
36353             block: true
36354         });
36355     },
36356
36357     animateExpand : function(){
36358         this.beforeSlide();
36359         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36360         this.el.setStyle("z-index", 20000);
36361         this.collapsedEl.hide({
36362             duration:.1
36363         });
36364         this.el.slideIn(this.getSlideAnchor(), {
36365             callback : function(){
36366                 this.el.setStyle("z-index", "");
36367                 this.afterSlide();
36368                 if(this.split){
36369                     this.split.el.show();
36370                 }
36371                 this.fireEvent("invalidated", this);
36372                 this.fireEvent("expanded", this);
36373             },
36374             scope: this,
36375             block: true
36376         });
36377     },
36378
36379     anchors : {
36380         "west" : "left",
36381         "east" : "right",
36382         "north" : "top",
36383         "south" : "bottom"
36384     },
36385
36386     sanchors : {
36387         "west" : "l",
36388         "east" : "r",
36389         "north" : "t",
36390         "south" : "b"
36391     },
36392
36393     canchors : {
36394         "west" : "tl-tr",
36395         "east" : "tr-tl",
36396         "north" : "tl-bl",
36397         "south" : "bl-tl"
36398     },
36399
36400     getAnchor : function(){
36401         return this.anchors[this.position];
36402     },
36403
36404     getCollapseAnchor : function(){
36405         return this.canchors[this.position];
36406     },
36407
36408     getSlideAnchor : function(){
36409         return this.sanchors[this.position];
36410     },
36411
36412     getAlignAdj : function(){
36413         var cm = this.cmargins;
36414         switch(this.position){
36415             case "west":
36416                 return [0, 0];
36417             break;
36418             case "east":
36419                 return [0, 0];
36420             break;
36421             case "north":
36422                 return [0, 0];
36423             break;
36424             case "south":
36425                 return [0, 0];
36426             break;
36427         }
36428     },
36429
36430     getExpandAdj : function(){
36431         var c = this.collapsedEl, cm = this.cmargins;
36432         switch(this.position){
36433             case "west":
36434                 return [-(cm.right+c.getWidth()+cm.left), 0];
36435             break;
36436             case "east":
36437                 return [cm.right+c.getWidth()+cm.left, 0];
36438             break;
36439             case "north":
36440                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36441             break;
36442             case "south":
36443                 return [0, cm.top+cm.bottom+c.getHeight()];
36444             break;
36445         }
36446     }
36447 });/*
36448  * Based on:
36449  * Ext JS Library 1.1.1
36450  * Copyright(c) 2006-2007, Ext JS, LLC.
36451  *
36452  * Originally Released Under LGPL - original licence link has changed is not relivant.
36453  *
36454  * Fork - LGPL
36455  * <script type="text/javascript">
36456  */
36457 /*
36458  * These classes are private internal classes
36459  */
36460 Roo.bootstrap.layout.Center = function(config){
36461     config.region = "center";
36462     Roo.bootstrap.layout.Region.call(this, config);
36463     this.visible = true;
36464     this.minWidth = config.minWidth || 20;
36465     this.minHeight = config.minHeight || 20;
36466 };
36467
36468 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36469     hide : function(){
36470         // center panel can't be hidden
36471     },
36472     
36473     show : function(){
36474         // center panel can't be hidden
36475     },
36476     
36477     getMinWidth: function(){
36478         return this.minWidth;
36479     },
36480     
36481     getMinHeight: function(){
36482         return this.minHeight;
36483     }
36484 });
36485
36486
36487
36488
36489  
36490
36491
36492
36493
36494
36495 Roo.bootstrap.layout.North = function(config)
36496 {
36497     config.region = 'north';
36498     config.cursor = 'n-resize';
36499     
36500     Roo.bootstrap.layout.Split.call(this, config);
36501     
36502     
36503     if(this.split){
36504         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36505         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36506         this.split.el.addClass("roo-layout-split-v");
36507     }
36508     var size = config.initialSize || config.height;
36509     if(typeof size != "undefined"){
36510         this.el.setHeight(size);
36511     }
36512 };
36513 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36514 {
36515     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36516     
36517     
36518     
36519     getBox : function(){
36520         if(this.collapsed){
36521             return this.collapsedEl.getBox();
36522         }
36523         var box = this.el.getBox();
36524         if(this.split){
36525             box.height += this.split.el.getHeight();
36526         }
36527         return box;
36528     },
36529     
36530     updateBox : function(box){
36531         if(this.split && !this.collapsed){
36532             box.height -= this.split.el.getHeight();
36533             this.split.el.setLeft(box.x);
36534             this.split.el.setTop(box.y+box.height);
36535             this.split.el.setWidth(box.width);
36536         }
36537         if(this.collapsed){
36538             this.updateBody(box.width, null);
36539         }
36540         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36541     }
36542 });
36543
36544
36545
36546
36547
36548 Roo.bootstrap.layout.South = function(config){
36549     config.region = 'south';
36550     config.cursor = 's-resize';
36551     Roo.bootstrap.layout.Split.call(this, config);
36552     if(this.split){
36553         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36554         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36555         this.split.el.addClass("roo-layout-split-v");
36556     }
36557     var size = config.initialSize || config.height;
36558     if(typeof size != "undefined"){
36559         this.el.setHeight(size);
36560     }
36561 };
36562
36563 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36564     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36565     getBox : function(){
36566         if(this.collapsed){
36567             return this.collapsedEl.getBox();
36568         }
36569         var box = this.el.getBox();
36570         if(this.split){
36571             var sh = this.split.el.getHeight();
36572             box.height += sh;
36573             box.y -= sh;
36574         }
36575         return box;
36576     },
36577     
36578     updateBox : function(box){
36579         if(this.split && !this.collapsed){
36580             var sh = this.split.el.getHeight();
36581             box.height -= sh;
36582             box.y += sh;
36583             this.split.el.setLeft(box.x);
36584             this.split.el.setTop(box.y-sh);
36585             this.split.el.setWidth(box.width);
36586         }
36587         if(this.collapsed){
36588             this.updateBody(box.width, null);
36589         }
36590         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36591     }
36592 });
36593
36594 Roo.bootstrap.layout.East = function(config){
36595     config.region = "east";
36596     config.cursor = "e-resize";
36597     Roo.bootstrap.layout.Split.call(this, config);
36598     if(this.split){
36599         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36600         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36601         this.split.el.addClass("roo-layout-split-h");
36602     }
36603     var size = config.initialSize || config.width;
36604     if(typeof size != "undefined"){
36605         this.el.setWidth(size);
36606     }
36607 };
36608 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36609     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36610     getBox : function(){
36611         if(this.collapsed){
36612             return this.collapsedEl.getBox();
36613         }
36614         var box = this.el.getBox();
36615         if(this.split){
36616             var sw = this.split.el.getWidth();
36617             box.width += sw;
36618             box.x -= sw;
36619         }
36620         return box;
36621     },
36622
36623     updateBox : function(box){
36624         if(this.split && !this.collapsed){
36625             var sw = this.split.el.getWidth();
36626             box.width -= sw;
36627             this.split.el.setLeft(box.x);
36628             this.split.el.setTop(box.y);
36629             this.split.el.setHeight(box.height);
36630             box.x += sw;
36631         }
36632         if(this.collapsed){
36633             this.updateBody(null, box.height);
36634         }
36635         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36636     }
36637 });
36638
36639 Roo.bootstrap.layout.West = function(config){
36640     config.region = "west";
36641     config.cursor = "w-resize";
36642     
36643     Roo.bootstrap.layout.Split.call(this, config);
36644     if(this.split){
36645         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36646         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36647         this.split.el.addClass("roo-layout-split-h");
36648     }
36649     
36650 };
36651 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36652     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36653     
36654     onRender: function(ctr, pos)
36655     {
36656         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36657         var size = this.config.initialSize || this.config.width;
36658         if(typeof size != "undefined"){
36659             this.el.setWidth(size);
36660         }
36661     },
36662     
36663     getBox : function(){
36664         if(this.collapsed){
36665             return this.collapsedEl.getBox();
36666         }
36667         var box = this.el.getBox();
36668         if(this.split){
36669             box.width += this.split.el.getWidth();
36670         }
36671         return box;
36672     },
36673     
36674     updateBox : function(box){
36675         if(this.split && !this.collapsed){
36676             var sw = this.split.el.getWidth();
36677             box.width -= sw;
36678             this.split.el.setLeft(box.x+box.width);
36679             this.split.el.setTop(box.y);
36680             this.split.el.setHeight(box.height);
36681         }
36682         if(this.collapsed){
36683             this.updateBody(null, box.height);
36684         }
36685         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36686     }
36687 });
36688 Roo.namespace("Roo.bootstrap.panel");/*
36689  * Based on:
36690  * Ext JS Library 1.1.1
36691  * Copyright(c) 2006-2007, Ext JS, LLC.
36692  *
36693  * Originally Released Under LGPL - original licence link has changed is not relivant.
36694  *
36695  * Fork - LGPL
36696  * <script type="text/javascript">
36697  */
36698 /**
36699  * @class Roo.ContentPanel
36700  * @extends Roo.util.Observable
36701  * A basic ContentPanel element.
36702  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36703  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36704  * @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
36705  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36706  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36707  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36708  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36709  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36710  * @cfg {String} title          The title for this panel
36711  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36712  * @cfg {String} url            Calls {@link #setUrl} with this value
36713  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36714  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36715  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36716  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36717  * @cfg {Boolean} badges render the badges
36718
36719  * @constructor
36720  * Create a new ContentPanel.
36721  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36722  * @param {String/Object} config A string to set only the title or a config object
36723  * @param {String} content (optional) Set the HTML content for this panel
36724  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36725  */
36726 Roo.bootstrap.panel.Content = function( config){
36727     
36728     this.tpl = config.tpl || false;
36729     
36730     var el = config.el;
36731     var content = config.content;
36732
36733     if(config.autoCreate){ // xtype is available if this is called from factory
36734         el = Roo.id();
36735     }
36736     this.el = Roo.get(el);
36737     if(!this.el && config && config.autoCreate){
36738         if(typeof config.autoCreate == "object"){
36739             if(!config.autoCreate.id){
36740                 config.autoCreate.id = config.id||el;
36741             }
36742             this.el = Roo.DomHelper.append(document.body,
36743                         config.autoCreate, true);
36744         }else{
36745             var elcfg =  {   tag: "div",
36746                             cls: "roo-layout-inactive-content",
36747                             id: config.id||el
36748                             };
36749             if (config.html) {
36750                 elcfg.html = config.html;
36751                 
36752             }
36753                         
36754             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36755         }
36756     } 
36757     this.closable = false;
36758     this.loaded = false;
36759     this.active = false;
36760    
36761       
36762     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36763         
36764         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36765         
36766         this.wrapEl = this.el; //this.el.wrap();
36767         var ti = [];
36768         if (config.toolbar.items) {
36769             ti = config.toolbar.items ;
36770             delete config.toolbar.items ;
36771         }
36772         
36773         var nitems = [];
36774         this.toolbar.render(this.wrapEl, 'before');
36775         for(var i =0;i < ti.length;i++) {
36776           //  Roo.log(['add child', items[i]]);
36777             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36778         }
36779         this.toolbar.items = nitems;
36780         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36781         delete config.toolbar;
36782         
36783     }
36784     /*
36785     // xtype created footer. - not sure if will work as we normally have to render first..
36786     if (this.footer && !this.footer.el && this.footer.xtype) {
36787         if (!this.wrapEl) {
36788             this.wrapEl = this.el.wrap();
36789         }
36790     
36791         this.footer.container = this.wrapEl.createChild();
36792          
36793         this.footer = Roo.factory(this.footer, Roo);
36794         
36795     }
36796     */
36797     
36798      if(typeof config == "string"){
36799         this.title = config;
36800     }else{
36801         Roo.apply(this, config);
36802     }
36803     
36804     if(this.resizeEl){
36805         this.resizeEl = Roo.get(this.resizeEl, true);
36806     }else{
36807         this.resizeEl = this.el;
36808     }
36809     // handle view.xtype
36810     
36811  
36812     
36813     
36814     this.addEvents({
36815         /**
36816          * @event activate
36817          * Fires when this panel is activated. 
36818          * @param {Roo.ContentPanel} this
36819          */
36820         "activate" : true,
36821         /**
36822          * @event deactivate
36823          * Fires when this panel is activated. 
36824          * @param {Roo.ContentPanel} this
36825          */
36826         "deactivate" : true,
36827
36828         /**
36829          * @event resize
36830          * Fires when this panel is resized if fitToFrame is true.
36831          * @param {Roo.ContentPanel} this
36832          * @param {Number} width The width after any component adjustments
36833          * @param {Number} height The height after any component adjustments
36834          */
36835         "resize" : true,
36836         
36837          /**
36838          * @event render
36839          * Fires when this tab is created
36840          * @param {Roo.ContentPanel} this
36841          */
36842         "render" : true
36843         
36844         
36845         
36846     });
36847     
36848
36849     
36850     
36851     if(this.autoScroll){
36852         this.resizeEl.setStyle("overflow", "auto");
36853     } else {
36854         // fix randome scrolling
36855         //this.el.on('scroll', function() {
36856         //    Roo.log('fix random scolling');
36857         //    this.scrollTo('top',0); 
36858         //});
36859     }
36860     content = content || this.content;
36861     if(content){
36862         this.setContent(content);
36863     }
36864     if(config && config.url){
36865         this.setUrl(this.url, this.params, this.loadOnce);
36866     }
36867     
36868     
36869     
36870     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36871     
36872     if (this.view && typeof(this.view.xtype) != 'undefined') {
36873         this.view.el = this.el.appendChild(document.createElement("div"));
36874         this.view = Roo.factory(this.view); 
36875         this.view.render  &&  this.view.render(false, '');  
36876     }
36877     
36878     
36879     this.fireEvent('render', this);
36880 };
36881
36882 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36883     
36884     tabTip : '',
36885     
36886     setRegion : function(region){
36887         this.region = region;
36888         this.setActiveClass(region && !this.background);
36889     },
36890     
36891     
36892     setActiveClass: function(state)
36893     {
36894         if(state){
36895            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36896            this.el.setStyle('position','relative');
36897         }else{
36898            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36899            this.el.setStyle('position', 'absolute');
36900         } 
36901     },
36902     
36903     /**
36904      * Returns the toolbar for this Panel if one was configured. 
36905      * @return {Roo.Toolbar} 
36906      */
36907     getToolbar : function(){
36908         return this.toolbar;
36909     },
36910     
36911     setActiveState : function(active)
36912     {
36913         this.active = active;
36914         this.setActiveClass(active);
36915         if(!active){
36916             if(this.fireEvent("deactivate", this) === false){
36917                 return false;
36918             }
36919             return true;
36920         }
36921         this.fireEvent("activate", this);
36922         return true;
36923     },
36924     /**
36925      * Updates this panel's element
36926      * @param {String} content The new content
36927      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36928     */
36929     setContent : function(content, loadScripts){
36930         this.el.update(content, loadScripts);
36931     },
36932
36933     ignoreResize : function(w, h){
36934         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36935             return true;
36936         }else{
36937             this.lastSize = {width: w, height: h};
36938             return false;
36939         }
36940     },
36941     /**
36942      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36943      * @return {Roo.UpdateManager} The UpdateManager
36944      */
36945     getUpdateManager : function(){
36946         return this.el.getUpdateManager();
36947     },
36948      /**
36949      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36950      * @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:
36951 <pre><code>
36952 panel.load({
36953     url: "your-url.php",
36954     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36955     callback: yourFunction,
36956     scope: yourObject, //(optional scope)
36957     discardUrl: false,
36958     nocache: false,
36959     text: "Loading...",
36960     timeout: 30,
36961     scripts: false
36962 });
36963 </code></pre>
36964      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36965      * 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.
36966      * @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}
36967      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36968      * @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.
36969      * @return {Roo.ContentPanel} this
36970      */
36971     load : function(){
36972         var um = this.el.getUpdateManager();
36973         um.update.apply(um, arguments);
36974         return this;
36975     },
36976
36977
36978     /**
36979      * 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.
36980      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36981      * @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)
36982      * @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)
36983      * @return {Roo.UpdateManager} The UpdateManager
36984      */
36985     setUrl : function(url, params, loadOnce){
36986         if(this.refreshDelegate){
36987             this.removeListener("activate", this.refreshDelegate);
36988         }
36989         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36990         this.on("activate", this.refreshDelegate);
36991         return this.el.getUpdateManager();
36992     },
36993     
36994     _handleRefresh : function(url, params, loadOnce){
36995         if(!loadOnce || !this.loaded){
36996             var updater = this.el.getUpdateManager();
36997             updater.update(url, params, this._setLoaded.createDelegate(this));
36998         }
36999     },
37000     
37001     _setLoaded : function(){
37002         this.loaded = true;
37003     }, 
37004     
37005     /**
37006      * Returns this panel's id
37007      * @return {String} 
37008      */
37009     getId : function(){
37010         return this.el.id;
37011     },
37012     
37013     /** 
37014      * Returns this panel's element - used by regiosn to add.
37015      * @return {Roo.Element} 
37016      */
37017     getEl : function(){
37018         return this.wrapEl || this.el;
37019     },
37020     
37021    
37022     
37023     adjustForComponents : function(width, height)
37024     {
37025         //Roo.log('adjustForComponents ');
37026         if(this.resizeEl != this.el){
37027             width -= this.el.getFrameWidth('lr');
37028             height -= this.el.getFrameWidth('tb');
37029         }
37030         if(this.toolbar){
37031             var te = this.toolbar.getEl();
37032             te.setWidth(width);
37033             height -= te.getHeight();
37034         }
37035         if(this.footer){
37036             var te = this.footer.getEl();
37037             te.setWidth(width);
37038             height -= te.getHeight();
37039         }
37040         
37041         
37042         if(this.adjustments){
37043             width += this.adjustments[0];
37044             height += this.adjustments[1];
37045         }
37046         return {"width": width, "height": height};
37047     },
37048     
37049     setSize : function(width, height){
37050         if(this.fitToFrame && !this.ignoreResize(width, height)){
37051             if(this.fitContainer && this.resizeEl != this.el){
37052                 this.el.setSize(width, height);
37053             }
37054             var size = this.adjustForComponents(width, height);
37055             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37056             this.fireEvent('resize', this, size.width, size.height);
37057         }
37058     },
37059     
37060     /**
37061      * Returns this panel's title
37062      * @return {String} 
37063      */
37064     getTitle : function(){
37065         
37066         if (typeof(this.title) != 'object') {
37067             return this.title;
37068         }
37069         
37070         var t = '';
37071         for (var k in this.title) {
37072             if (!this.title.hasOwnProperty(k)) {
37073                 continue;
37074             }
37075             
37076             if (k.indexOf('-') >= 0) {
37077                 var s = k.split('-');
37078                 for (var i = 0; i<s.length; i++) {
37079                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37080                 }
37081             } else {
37082                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37083             }
37084         }
37085         return t;
37086     },
37087     
37088     /**
37089      * Set this panel's title
37090      * @param {String} title
37091      */
37092     setTitle : function(title){
37093         this.title = title;
37094         if(this.region){
37095             this.region.updatePanelTitle(this, title);
37096         }
37097     },
37098     
37099     /**
37100      * Returns true is this panel was configured to be closable
37101      * @return {Boolean} 
37102      */
37103     isClosable : function(){
37104         return this.closable;
37105     },
37106     
37107     beforeSlide : function(){
37108         this.el.clip();
37109         this.resizeEl.clip();
37110     },
37111     
37112     afterSlide : function(){
37113         this.el.unclip();
37114         this.resizeEl.unclip();
37115     },
37116     
37117     /**
37118      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37119      *   Will fail silently if the {@link #setUrl} method has not been called.
37120      *   This does not activate the panel, just updates its content.
37121      */
37122     refresh : function(){
37123         if(this.refreshDelegate){
37124            this.loaded = false;
37125            this.refreshDelegate();
37126         }
37127     },
37128     
37129     /**
37130      * Destroys this panel
37131      */
37132     destroy : function(){
37133         this.el.removeAllListeners();
37134         var tempEl = document.createElement("span");
37135         tempEl.appendChild(this.el.dom);
37136         tempEl.innerHTML = "";
37137         this.el.remove();
37138         this.el = null;
37139     },
37140     
37141     /**
37142      * form - if the content panel contains a form - this is a reference to it.
37143      * @type {Roo.form.Form}
37144      */
37145     form : false,
37146     /**
37147      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37148      *    This contains a reference to it.
37149      * @type {Roo.View}
37150      */
37151     view : false,
37152     
37153       /**
37154      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37155      * <pre><code>
37156
37157 layout.addxtype({
37158        xtype : 'Form',
37159        items: [ .... ]
37160    }
37161 );
37162
37163 </code></pre>
37164      * @param {Object} cfg Xtype definition of item to add.
37165      */
37166     
37167     
37168     getChildContainer: function () {
37169         return this.getEl();
37170     }
37171     
37172     
37173     /*
37174         var  ret = new Roo.factory(cfg);
37175         return ret;
37176         
37177         
37178         // add form..
37179         if (cfg.xtype.match(/^Form$/)) {
37180             
37181             var el;
37182             //if (this.footer) {
37183             //    el = this.footer.container.insertSibling(false, 'before');
37184             //} else {
37185                 el = this.el.createChild();
37186             //}
37187
37188             this.form = new  Roo.form.Form(cfg);
37189             
37190             
37191             if ( this.form.allItems.length) {
37192                 this.form.render(el.dom);
37193             }
37194             return this.form;
37195         }
37196         // should only have one of theses..
37197         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37198             // views.. should not be just added - used named prop 'view''
37199             
37200             cfg.el = this.el.appendChild(document.createElement("div"));
37201             // factory?
37202             
37203             var ret = new Roo.factory(cfg);
37204              
37205              ret.render && ret.render(false, ''); // render blank..
37206             this.view = ret;
37207             return ret;
37208         }
37209         return false;
37210     }
37211     \*/
37212 });
37213  
37214 /**
37215  * @class Roo.bootstrap.panel.Grid
37216  * @extends Roo.bootstrap.panel.Content
37217  * @constructor
37218  * Create a new GridPanel.
37219  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37220  * @param {Object} config A the config object
37221   
37222  */
37223
37224
37225
37226 Roo.bootstrap.panel.Grid = function(config)
37227 {
37228     
37229       
37230     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37231         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37232
37233     config.el = this.wrapper;
37234     //this.el = this.wrapper;
37235     
37236       if (config.container) {
37237         // ctor'ed from a Border/panel.grid
37238         
37239         
37240         this.wrapper.setStyle("overflow", "hidden");
37241         this.wrapper.addClass('roo-grid-container');
37242
37243     }
37244     
37245     
37246     if(config.toolbar){
37247         var tool_el = this.wrapper.createChild();    
37248         this.toolbar = Roo.factory(config.toolbar);
37249         var ti = [];
37250         if (config.toolbar.items) {
37251             ti = config.toolbar.items ;
37252             delete config.toolbar.items ;
37253         }
37254         
37255         var nitems = [];
37256         this.toolbar.render(tool_el);
37257         for(var i =0;i < ti.length;i++) {
37258           //  Roo.log(['add child', items[i]]);
37259             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37260         }
37261         this.toolbar.items = nitems;
37262         
37263         delete config.toolbar;
37264     }
37265     
37266     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37267     config.grid.scrollBody = true;;
37268     config.grid.monitorWindowResize = false; // turn off autosizing
37269     config.grid.autoHeight = false;
37270     config.grid.autoWidth = false;
37271     
37272     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37273     
37274     if (config.background) {
37275         // render grid on panel activation (if panel background)
37276         this.on('activate', function(gp) {
37277             if (!gp.grid.rendered) {
37278                 gp.grid.render(this.wrapper);
37279                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37280             }
37281         });
37282             
37283     } else {
37284         this.grid.render(this.wrapper);
37285         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37286
37287     }
37288     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37289     // ??? needed ??? config.el = this.wrapper;
37290     
37291     
37292     
37293   
37294     // xtype created footer. - not sure if will work as we normally have to render first..
37295     if (this.footer && !this.footer.el && this.footer.xtype) {
37296         
37297         var ctr = this.grid.getView().getFooterPanel(true);
37298         this.footer.dataSource = this.grid.dataSource;
37299         this.footer = Roo.factory(this.footer, Roo);
37300         this.footer.render(ctr);
37301         
37302     }
37303     
37304     
37305     
37306     
37307      
37308 };
37309
37310 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37311     getId : function(){
37312         return this.grid.id;
37313     },
37314     
37315     /**
37316      * Returns the grid for this panel
37317      * @return {Roo.bootstrap.Table} 
37318      */
37319     getGrid : function(){
37320         return this.grid;    
37321     },
37322     
37323     setSize : function(width, height){
37324         if(!this.ignoreResize(width, height)){
37325             var grid = this.grid;
37326             var size = this.adjustForComponents(width, height);
37327             var gridel = grid.getGridEl();
37328             gridel.setSize(size.width, size.height);
37329             /*
37330             var thd = grid.getGridEl().select('thead',true).first();
37331             var tbd = grid.getGridEl().select('tbody', true).first();
37332             if (tbd) {
37333                 tbd.setSize(width, height - thd.getHeight());
37334             }
37335             */
37336             grid.autoSize();
37337         }
37338     },
37339      
37340     
37341     
37342     beforeSlide : function(){
37343         this.grid.getView().scroller.clip();
37344     },
37345     
37346     afterSlide : function(){
37347         this.grid.getView().scroller.unclip();
37348     },
37349     
37350     destroy : function(){
37351         this.grid.destroy();
37352         delete this.grid;
37353         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37354     }
37355 });
37356
37357 /**
37358  * @class Roo.bootstrap.panel.Nest
37359  * @extends Roo.bootstrap.panel.Content
37360  * @constructor
37361  * Create a new Panel, that can contain a layout.Border.
37362  * 
37363  * 
37364  * @param {Roo.BorderLayout} layout The layout for this panel
37365  * @param {String/Object} config A string to set only the title or a config object
37366  */
37367 Roo.bootstrap.panel.Nest = function(config)
37368 {
37369     // construct with only one argument..
37370     /* FIXME - implement nicer consturctors
37371     if (layout.layout) {
37372         config = layout;
37373         layout = config.layout;
37374         delete config.layout;
37375     }
37376     if (layout.xtype && !layout.getEl) {
37377         // then layout needs constructing..
37378         layout = Roo.factory(layout, Roo);
37379     }
37380     */
37381     
37382     config.el =  config.layout.getEl();
37383     
37384     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37385     
37386     config.layout.monitorWindowResize = false; // turn off autosizing
37387     this.layout = config.layout;
37388     this.layout.getEl().addClass("roo-layout-nested-layout");
37389     
37390     
37391     
37392     
37393 };
37394
37395 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37396
37397     setSize : function(width, height){
37398         if(!this.ignoreResize(width, height)){
37399             var size = this.adjustForComponents(width, height);
37400             var el = this.layout.getEl();
37401             if (size.height < 1) {
37402                 el.setWidth(size.width);   
37403             } else {
37404                 el.setSize(size.width, size.height);
37405             }
37406             var touch = el.dom.offsetWidth;
37407             this.layout.layout();
37408             // ie requires a double layout on the first pass
37409             if(Roo.isIE && !this.initialized){
37410                 this.initialized = true;
37411                 this.layout.layout();
37412             }
37413         }
37414     },
37415     
37416     // activate all subpanels if not currently active..
37417     
37418     setActiveState : function(active){
37419         this.active = active;
37420         this.setActiveClass(active);
37421         
37422         if(!active){
37423             this.fireEvent("deactivate", this);
37424             return;
37425         }
37426         
37427         this.fireEvent("activate", this);
37428         // not sure if this should happen before or after..
37429         if (!this.layout) {
37430             return; // should not happen..
37431         }
37432         var reg = false;
37433         for (var r in this.layout.regions) {
37434             reg = this.layout.getRegion(r);
37435             if (reg.getActivePanel()) {
37436                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37437                 reg.setActivePanel(reg.getActivePanel());
37438                 continue;
37439             }
37440             if (!reg.panels.length) {
37441                 continue;
37442             }
37443             reg.showPanel(reg.getPanel(0));
37444         }
37445         
37446         
37447         
37448         
37449     },
37450     
37451     /**
37452      * Returns the nested BorderLayout for this panel
37453      * @return {Roo.BorderLayout} 
37454      */
37455     getLayout : function(){
37456         return this.layout;
37457     },
37458     
37459      /**
37460      * Adds a xtype elements to the layout of the nested panel
37461      * <pre><code>
37462
37463 panel.addxtype({
37464        xtype : 'ContentPanel',
37465        region: 'west',
37466        items: [ .... ]
37467    }
37468 );
37469
37470 panel.addxtype({
37471         xtype : 'NestedLayoutPanel',
37472         region: 'west',
37473         layout: {
37474            center: { },
37475            west: { }   
37476         },
37477         items : [ ... list of content panels or nested layout panels.. ]
37478    }
37479 );
37480 </code></pre>
37481      * @param {Object} cfg Xtype definition of item to add.
37482      */
37483     addxtype : function(cfg) {
37484         return this.layout.addxtype(cfg);
37485     
37486     }
37487 });        /*
37488  * Based on:
37489  * Ext JS Library 1.1.1
37490  * Copyright(c) 2006-2007, Ext JS, LLC.
37491  *
37492  * Originally Released Under LGPL - original licence link has changed is not relivant.
37493  *
37494  * Fork - LGPL
37495  * <script type="text/javascript">
37496  */
37497 /**
37498  * @class Roo.TabPanel
37499  * @extends Roo.util.Observable
37500  * A lightweight tab container.
37501  * <br><br>
37502  * Usage:
37503  * <pre><code>
37504 // basic tabs 1, built from existing content
37505 var tabs = new Roo.TabPanel("tabs1");
37506 tabs.addTab("script", "View Script");
37507 tabs.addTab("markup", "View Markup");
37508 tabs.activate("script");
37509
37510 // more advanced tabs, built from javascript
37511 var jtabs = new Roo.TabPanel("jtabs");
37512 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37513
37514 // set up the UpdateManager
37515 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37516 var updater = tab2.getUpdateManager();
37517 updater.setDefaultUrl("ajax1.htm");
37518 tab2.on('activate', updater.refresh, updater, true);
37519
37520 // Use setUrl for Ajax loading
37521 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37522 tab3.setUrl("ajax2.htm", null, true);
37523
37524 // Disabled tab
37525 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37526 tab4.disable();
37527
37528 jtabs.activate("jtabs-1");
37529  * </code></pre>
37530  * @constructor
37531  * Create a new TabPanel.
37532  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37533  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37534  */
37535 Roo.bootstrap.panel.Tabs = function(config){
37536     /**
37537     * The container element for this TabPanel.
37538     * @type Roo.Element
37539     */
37540     this.el = Roo.get(config.el);
37541     delete config.el;
37542     if(config){
37543         if(typeof config == "boolean"){
37544             this.tabPosition = config ? "bottom" : "top";
37545         }else{
37546             Roo.apply(this, config);
37547         }
37548     }
37549     
37550     if(this.tabPosition == "bottom"){
37551         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37552         this.el.addClass("roo-tabs-bottom");
37553     }
37554     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37555     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37556     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37557     if(Roo.isIE){
37558         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37559     }
37560     if(this.tabPosition != "bottom"){
37561         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37562          * @type Roo.Element
37563          */
37564         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37565         this.el.addClass("roo-tabs-top");
37566     }
37567     this.items = [];
37568
37569     this.bodyEl.setStyle("position", "relative");
37570
37571     this.active = null;
37572     this.activateDelegate = this.activate.createDelegate(this);
37573
37574     this.addEvents({
37575         /**
37576          * @event tabchange
37577          * Fires when the active tab changes
37578          * @param {Roo.TabPanel} this
37579          * @param {Roo.TabPanelItem} activePanel The new active tab
37580          */
37581         "tabchange": true,
37582         /**
37583          * @event beforetabchange
37584          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37585          * @param {Roo.TabPanel} this
37586          * @param {Object} e Set cancel to true on this object to cancel the tab change
37587          * @param {Roo.TabPanelItem} tab The tab being changed to
37588          */
37589         "beforetabchange" : true
37590     });
37591
37592     Roo.EventManager.onWindowResize(this.onResize, this);
37593     this.cpad = this.el.getPadding("lr");
37594     this.hiddenCount = 0;
37595
37596
37597     // toolbar on the tabbar support...
37598     if (this.toolbar) {
37599         alert("no toolbar support yet");
37600         this.toolbar  = false;
37601         /*
37602         var tcfg = this.toolbar;
37603         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37604         this.toolbar = new Roo.Toolbar(tcfg);
37605         if (Roo.isSafari) {
37606             var tbl = tcfg.container.child('table', true);
37607             tbl.setAttribute('width', '100%');
37608         }
37609         */
37610         
37611     }
37612    
37613
37614
37615     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37616 };
37617
37618 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37619     /*
37620      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37621      */
37622     tabPosition : "top",
37623     /*
37624      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37625      */
37626     currentTabWidth : 0,
37627     /*
37628      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37629      */
37630     minTabWidth : 40,
37631     /*
37632      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37633      */
37634     maxTabWidth : 250,
37635     /*
37636      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37637      */
37638     preferredTabWidth : 175,
37639     /*
37640      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37641      */
37642     resizeTabs : false,
37643     /*
37644      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37645      */
37646     monitorResize : true,
37647     /*
37648      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37649      */
37650     toolbar : false,
37651
37652     /**
37653      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37654      * @param {String} id The id of the div to use <b>or create</b>
37655      * @param {String} text The text for the tab
37656      * @param {String} content (optional) Content to put in the TabPanelItem body
37657      * @param {Boolean} closable (optional) True to create a close icon on the tab
37658      * @return {Roo.TabPanelItem} The created TabPanelItem
37659      */
37660     addTab : function(id, text, content, closable, tpl)
37661     {
37662         var item = new Roo.bootstrap.panel.TabItem({
37663             panel: this,
37664             id : id,
37665             text : text,
37666             closable : closable,
37667             tpl : tpl
37668         });
37669         this.addTabItem(item);
37670         if(content){
37671             item.setContent(content);
37672         }
37673         return item;
37674     },
37675
37676     /**
37677      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37678      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37679      * @return {Roo.TabPanelItem}
37680      */
37681     getTab : function(id){
37682         return this.items[id];
37683     },
37684
37685     /**
37686      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37687      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37688      */
37689     hideTab : function(id){
37690         var t = this.items[id];
37691         if(!t.isHidden()){
37692            t.setHidden(true);
37693            this.hiddenCount++;
37694            this.autoSizeTabs();
37695         }
37696     },
37697
37698     /**
37699      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37700      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37701      */
37702     unhideTab : function(id){
37703         var t = this.items[id];
37704         if(t.isHidden()){
37705            t.setHidden(false);
37706            this.hiddenCount--;
37707            this.autoSizeTabs();
37708         }
37709     },
37710
37711     /**
37712      * Adds an existing {@link Roo.TabPanelItem}.
37713      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37714      */
37715     addTabItem : function(item){
37716         this.items[item.id] = item;
37717         this.items.push(item);
37718       //  if(this.resizeTabs){
37719     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37720   //         this.autoSizeTabs();
37721 //        }else{
37722 //            item.autoSize();
37723        // }
37724     },
37725
37726     /**
37727      * Removes a {@link Roo.TabPanelItem}.
37728      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37729      */
37730     removeTab : function(id){
37731         var items = this.items;
37732         var tab = items[id];
37733         if(!tab) { return; }
37734         var index = items.indexOf(tab);
37735         if(this.active == tab && items.length > 1){
37736             var newTab = this.getNextAvailable(index);
37737             if(newTab) {
37738                 newTab.activate();
37739             }
37740         }
37741         this.stripEl.dom.removeChild(tab.pnode.dom);
37742         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37743             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37744         }
37745         items.splice(index, 1);
37746         delete this.items[tab.id];
37747         tab.fireEvent("close", tab);
37748         tab.purgeListeners();
37749         this.autoSizeTabs();
37750     },
37751
37752     getNextAvailable : function(start){
37753         var items = this.items;
37754         var index = start;
37755         // look for a next tab that will slide over to
37756         // replace the one being removed
37757         while(index < items.length){
37758             var item = items[++index];
37759             if(item && !item.isHidden()){
37760                 return item;
37761             }
37762         }
37763         // if one isn't found select the previous tab (on the left)
37764         index = start;
37765         while(index >= 0){
37766             var item = items[--index];
37767             if(item && !item.isHidden()){
37768                 return item;
37769             }
37770         }
37771         return null;
37772     },
37773
37774     /**
37775      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37776      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37777      */
37778     disableTab : function(id){
37779         var tab = this.items[id];
37780         if(tab && this.active != tab){
37781             tab.disable();
37782         }
37783     },
37784
37785     /**
37786      * Enables a {@link Roo.TabPanelItem} that is disabled.
37787      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37788      */
37789     enableTab : function(id){
37790         var tab = this.items[id];
37791         tab.enable();
37792     },
37793
37794     /**
37795      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37796      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37797      * @return {Roo.TabPanelItem} The TabPanelItem.
37798      */
37799     activate : function(id){
37800         var tab = this.items[id];
37801         if(!tab){
37802             return null;
37803         }
37804         if(tab == this.active || tab.disabled){
37805             return tab;
37806         }
37807         var e = {};
37808         this.fireEvent("beforetabchange", this, e, tab);
37809         if(e.cancel !== true && !tab.disabled){
37810             if(this.active){
37811                 this.active.hide();
37812             }
37813             this.active = this.items[id];
37814             this.active.show();
37815             this.fireEvent("tabchange", this, this.active);
37816         }
37817         return tab;
37818     },
37819
37820     /**
37821      * Gets the active {@link Roo.TabPanelItem}.
37822      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37823      */
37824     getActiveTab : function(){
37825         return this.active;
37826     },
37827
37828     /**
37829      * Updates the tab body element to fit the height of the container element
37830      * for overflow scrolling
37831      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37832      */
37833     syncHeight : function(targetHeight){
37834         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37835         var bm = this.bodyEl.getMargins();
37836         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37837         this.bodyEl.setHeight(newHeight);
37838         return newHeight;
37839     },
37840
37841     onResize : function(){
37842         if(this.monitorResize){
37843             this.autoSizeTabs();
37844         }
37845     },
37846
37847     /**
37848      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37849      */
37850     beginUpdate : function(){
37851         this.updating = true;
37852     },
37853
37854     /**
37855      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37856      */
37857     endUpdate : function(){
37858         this.updating = false;
37859         this.autoSizeTabs();
37860     },
37861
37862     /**
37863      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37864      */
37865     autoSizeTabs : function(){
37866         var count = this.items.length;
37867         var vcount = count - this.hiddenCount;
37868         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37869             return;
37870         }
37871         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37872         var availWidth = Math.floor(w / vcount);
37873         var b = this.stripBody;
37874         if(b.getWidth() > w){
37875             var tabs = this.items;
37876             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37877             if(availWidth < this.minTabWidth){
37878                 /*if(!this.sleft){    // incomplete scrolling code
37879                     this.createScrollButtons();
37880                 }
37881                 this.showScroll();
37882                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37883             }
37884         }else{
37885             if(this.currentTabWidth < this.preferredTabWidth){
37886                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37887             }
37888         }
37889     },
37890
37891     /**
37892      * Returns the number of tabs in this TabPanel.
37893      * @return {Number}
37894      */
37895      getCount : function(){
37896          return this.items.length;
37897      },
37898
37899     /**
37900      * Resizes all the tabs to the passed width
37901      * @param {Number} The new width
37902      */
37903     setTabWidth : function(width){
37904         this.currentTabWidth = width;
37905         for(var i = 0, len = this.items.length; i < len; i++) {
37906                 if(!this.items[i].isHidden()) {
37907                 this.items[i].setWidth(width);
37908             }
37909         }
37910     },
37911
37912     /**
37913      * Destroys this TabPanel
37914      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37915      */
37916     destroy : function(removeEl){
37917         Roo.EventManager.removeResizeListener(this.onResize, this);
37918         for(var i = 0, len = this.items.length; i < len; i++){
37919             this.items[i].purgeListeners();
37920         }
37921         if(removeEl === true){
37922             this.el.update("");
37923             this.el.remove();
37924         }
37925     },
37926     
37927     createStrip : function(container)
37928     {
37929         var strip = document.createElement("nav");
37930         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37931         container.appendChild(strip);
37932         return strip;
37933     },
37934     
37935     createStripList : function(strip)
37936     {
37937         // div wrapper for retard IE
37938         // returns the "tr" element.
37939         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37940         //'<div class="x-tabs-strip-wrap">'+
37941           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37942           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37943         return strip.firstChild; //.firstChild.firstChild.firstChild;
37944     },
37945     createBody : function(container)
37946     {
37947         var body = document.createElement("div");
37948         Roo.id(body, "tab-body");
37949         //Roo.fly(body).addClass("x-tabs-body");
37950         Roo.fly(body).addClass("tab-content");
37951         container.appendChild(body);
37952         return body;
37953     },
37954     createItemBody :function(bodyEl, id){
37955         var body = Roo.getDom(id);
37956         if(!body){
37957             body = document.createElement("div");
37958             body.id = id;
37959         }
37960         //Roo.fly(body).addClass("x-tabs-item-body");
37961         Roo.fly(body).addClass("tab-pane");
37962          bodyEl.insertBefore(body, bodyEl.firstChild);
37963         return body;
37964     },
37965     /** @private */
37966     createStripElements :  function(stripEl, text, closable, tpl)
37967     {
37968         var td = document.createElement("li"); // was td..
37969         
37970         
37971         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37972         
37973         
37974         stripEl.appendChild(td);
37975         /*if(closable){
37976             td.className = "x-tabs-closable";
37977             if(!this.closeTpl){
37978                 this.closeTpl = new Roo.Template(
37979                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37980                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37981                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37982                 );
37983             }
37984             var el = this.closeTpl.overwrite(td, {"text": text});
37985             var close = el.getElementsByTagName("div")[0];
37986             var inner = el.getElementsByTagName("em")[0];
37987             return {"el": el, "close": close, "inner": inner};
37988         } else {
37989         */
37990         // not sure what this is..
37991 //            if(!this.tabTpl){
37992                 //this.tabTpl = new Roo.Template(
37993                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37994                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37995                 //);
37996 //                this.tabTpl = new Roo.Template(
37997 //                   '<a href="#">' +
37998 //                   '<span unselectable="on"' +
37999 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38000 //                            ' >{text}</span></a>'
38001 //                );
38002 //                
38003 //            }
38004
38005
38006             var template = tpl || this.tabTpl || false;
38007             
38008             if(!template){
38009                 
38010                 template = new Roo.Template(
38011                    '<a href="#">' +
38012                    '<span unselectable="on"' +
38013                             (this.disableTooltips ? '' : ' title="{text}"') +
38014                             ' >{text}</span></a>'
38015                 );
38016             }
38017             
38018             switch (typeof(template)) {
38019                 case 'object' :
38020                     break;
38021                 case 'string' :
38022                     template = new Roo.Template(template);
38023                     break;
38024                 default :
38025                     break;
38026             }
38027             
38028             var el = template.overwrite(td, {"text": text});
38029             
38030             var inner = el.getElementsByTagName("span")[0];
38031             
38032             return {"el": el, "inner": inner};
38033             
38034     }
38035         
38036     
38037 });
38038
38039 /**
38040  * @class Roo.TabPanelItem
38041  * @extends Roo.util.Observable
38042  * Represents an individual item (tab plus body) in a TabPanel.
38043  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38044  * @param {String} id The id of this TabPanelItem
38045  * @param {String} text The text for the tab of this TabPanelItem
38046  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38047  */
38048 Roo.bootstrap.panel.TabItem = function(config){
38049     /**
38050      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38051      * @type Roo.TabPanel
38052      */
38053     this.tabPanel = config.panel;
38054     /**
38055      * The id for this TabPanelItem
38056      * @type String
38057      */
38058     this.id = config.id;
38059     /** @private */
38060     this.disabled = false;
38061     /** @private */
38062     this.text = config.text;
38063     /** @private */
38064     this.loaded = false;
38065     this.closable = config.closable;
38066
38067     /**
38068      * The body element for this TabPanelItem.
38069      * @type Roo.Element
38070      */
38071     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38072     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38073     this.bodyEl.setStyle("display", "block");
38074     this.bodyEl.setStyle("zoom", "1");
38075     //this.hideAction();
38076
38077     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38078     /** @private */
38079     this.el = Roo.get(els.el);
38080     this.inner = Roo.get(els.inner, true);
38081     this.textEl = Roo.get(this.el.dom.firstChild, true);
38082     this.pnode = Roo.get(els.el.parentNode, true);
38083 //    this.el.on("mousedown", this.onTabMouseDown, this);
38084     this.el.on("click", this.onTabClick, this);
38085     /** @private */
38086     if(config.closable){
38087         var c = Roo.get(els.close, true);
38088         c.dom.title = this.closeText;
38089         c.addClassOnOver("close-over");
38090         c.on("click", this.closeClick, this);
38091      }
38092
38093     this.addEvents({
38094          /**
38095          * @event activate
38096          * Fires when this tab becomes the active tab.
38097          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38098          * @param {Roo.TabPanelItem} this
38099          */
38100         "activate": true,
38101         /**
38102          * @event beforeclose
38103          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38104          * @param {Roo.TabPanelItem} this
38105          * @param {Object} e Set cancel to true on this object to cancel the close.
38106          */
38107         "beforeclose": true,
38108         /**
38109          * @event close
38110          * Fires when this tab is closed.
38111          * @param {Roo.TabPanelItem} this
38112          */
38113          "close": true,
38114         /**
38115          * @event deactivate
38116          * Fires when this tab is no longer the active tab.
38117          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38118          * @param {Roo.TabPanelItem} this
38119          */
38120          "deactivate" : true
38121     });
38122     this.hidden = false;
38123
38124     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38125 };
38126
38127 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38128            {
38129     purgeListeners : function(){
38130        Roo.util.Observable.prototype.purgeListeners.call(this);
38131        this.el.removeAllListeners();
38132     },
38133     /**
38134      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38135      */
38136     show : function(){
38137         this.pnode.addClass("active");
38138         this.showAction();
38139         if(Roo.isOpera){
38140             this.tabPanel.stripWrap.repaint();
38141         }
38142         this.fireEvent("activate", this.tabPanel, this);
38143     },
38144
38145     /**
38146      * Returns true if this tab is the active tab.
38147      * @return {Boolean}
38148      */
38149     isActive : function(){
38150         return this.tabPanel.getActiveTab() == this;
38151     },
38152
38153     /**
38154      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38155      */
38156     hide : function(){
38157         this.pnode.removeClass("active");
38158         this.hideAction();
38159         this.fireEvent("deactivate", this.tabPanel, this);
38160     },
38161
38162     hideAction : function(){
38163         this.bodyEl.hide();
38164         this.bodyEl.setStyle("position", "absolute");
38165         this.bodyEl.setLeft("-20000px");
38166         this.bodyEl.setTop("-20000px");
38167     },
38168
38169     showAction : function(){
38170         this.bodyEl.setStyle("position", "relative");
38171         this.bodyEl.setTop("");
38172         this.bodyEl.setLeft("");
38173         this.bodyEl.show();
38174     },
38175
38176     /**
38177      * Set the tooltip for the tab.
38178      * @param {String} tooltip The tab's tooltip
38179      */
38180     setTooltip : function(text){
38181         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38182             this.textEl.dom.qtip = text;
38183             this.textEl.dom.removeAttribute('title');
38184         }else{
38185             this.textEl.dom.title = text;
38186         }
38187     },
38188
38189     onTabClick : function(e){
38190         e.preventDefault();
38191         this.tabPanel.activate(this.id);
38192     },
38193
38194     onTabMouseDown : function(e){
38195         e.preventDefault();
38196         this.tabPanel.activate(this.id);
38197     },
38198 /*
38199     getWidth : function(){
38200         return this.inner.getWidth();
38201     },
38202
38203     setWidth : function(width){
38204         var iwidth = width - this.pnode.getPadding("lr");
38205         this.inner.setWidth(iwidth);
38206         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38207         this.pnode.setWidth(width);
38208     },
38209 */
38210     /**
38211      * Show or hide the tab
38212      * @param {Boolean} hidden True to hide or false to show.
38213      */
38214     setHidden : function(hidden){
38215         this.hidden = hidden;
38216         this.pnode.setStyle("display", hidden ? "none" : "");
38217     },
38218
38219     /**
38220      * Returns true if this tab is "hidden"
38221      * @return {Boolean}
38222      */
38223     isHidden : function(){
38224         return this.hidden;
38225     },
38226
38227     /**
38228      * Returns the text for this tab
38229      * @return {String}
38230      */
38231     getText : function(){
38232         return this.text;
38233     },
38234     /*
38235     autoSize : function(){
38236         //this.el.beginMeasure();
38237         this.textEl.setWidth(1);
38238         /*
38239          *  #2804 [new] Tabs in Roojs
38240          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38241          */
38242         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38243         //this.el.endMeasure();
38244     //},
38245
38246     /**
38247      * Sets the text for the tab (Note: this also sets the tooltip text)
38248      * @param {String} text The tab's text and tooltip
38249      */
38250     setText : function(text){
38251         this.text = text;
38252         this.textEl.update(text);
38253         this.setTooltip(text);
38254         //if(!this.tabPanel.resizeTabs){
38255         //    this.autoSize();
38256         //}
38257     },
38258     /**
38259      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38260      */
38261     activate : function(){
38262         this.tabPanel.activate(this.id);
38263     },
38264
38265     /**
38266      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38267      */
38268     disable : function(){
38269         if(this.tabPanel.active != this){
38270             this.disabled = true;
38271             this.pnode.addClass("disabled");
38272         }
38273     },
38274
38275     /**
38276      * Enables this TabPanelItem if it was previously disabled.
38277      */
38278     enable : function(){
38279         this.disabled = false;
38280         this.pnode.removeClass("disabled");
38281     },
38282
38283     /**
38284      * Sets the content for this TabPanelItem.
38285      * @param {String} content The content
38286      * @param {Boolean} loadScripts true to look for and load scripts
38287      */
38288     setContent : function(content, loadScripts){
38289         this.bodyEl.update(content, loadScripts);
38290     },
38291
38292     /**
38293      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38294      * @return {Roo.UpdateManager} The UpdateManager
38295      */
38296     getUpdateManager : function(){
38297         return this.bodyEl.getUpdateManager();
38298     },
38299
38300     /**
38301      * Set a URL to be used to load the content for this TabPanelItem.
38302      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38303      * @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)
38304      * @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)
38305      * @return {Roo.UpdateManager} The UpdateManager
38306      */
38307     setUrl : function(url, params, loadOnce){
38308         if(this.refreshDelegate){
38309             this.un('activate', this.refreshDelegate);
38310         }
38311         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38312         this.on("activate", this.refreshDelegate);
38313         return this.bodyEl.getUpdateManager();
38314     },
38315
38316     /** @private */
38317     _handleRefresh : function(url, params, loadOnce){
38318         if(!loadOnce || !this.loaded){
38319             var updater = this.bodyEl.getUpdateManager();
38320             updater.update(url, params, this._setLoaded.createDelegate(this));
38321         }
38322     },
38323
38324     /**
38325      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38326      *   Will fail silently if the setUrl method has not been called.
38327      *   This does not activate the panel, just updates its content.
38328      */
38329     refresh : function(){
38330         if(this.refreshDelegate){
38331            this.loaded = false;
38332            this.refreshDelegate();
38333         }
38334     },
38335
38336     /** @private */
38337     _setLoaded : function(){
38338         this.loaded = true;
38339     },
38340
38341     /** @private */
38342     closeClick : function(e){
38343         var o = {};
38344         e.stopEvent();
38345         this.fireEvent("beforeclose", this, o);
38346         if(o.cancel !== true){
38347             this.tabPanel.removeTab(this.id);
38348         }
38349     },
38350     /**
38351      * The text displayed in the tooltip for the close icon.
38352      * @type String
38353      */
38354     closeText : "Close this tab"
38355 });
38356 /**
38357 *    This script refer to:
38358 *    Title: International Telephone Input
38359 *    Author: Jack O'Connor
38360 *    Code version:  v12.1.12
38361 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38362 **/
38363
38364 Roo.bootstrap.PhoneInputData = function() {
38365     var d = [
38366       [
38367         "Afghanistan (‫افغانستان‬‎)",
38368         "af",
38369         "93"
38370       ],
38371       [
38372         "Albania (Shqipëri)",
38373         "al",
38374         "355"
38375       ],
38376       [
38377         "Algeria (‫الجزائر‬‎)",
38378         "dz",
38379         "213"
38380       ],
38381       [
38382         "American Samoa",
38383         "as",
38384         "1684"
38385       ],
38386       [
38387         "Andorra",
38388         "ad",
38389         "376"
38390       ],
38391       [
38392         "Angola",
38393         "ao",
38394         "244"
38395       ],
38396       [
38397         "Anguilla",
38398         "ai",
38399         "1264"
38400       ],
38401       [
38402         "Antigua and Barbuda",
38403         "ag",
38404         "1268"
38405       ],
38406       [
38407         "Argentina",
38408         "ar",
38409         "54"
38410       ],
38411       [
38412         "Armenia (Հայաստան)",
38413         "am",
38414         "374"
38415       ],
38416       [
38417         "Aruba",
38418         "aw",
38419         "297"
38420       ],
38421       [
38422         "Australia",
38423         "au",
38424         "61",
38425         0
38426       ],
38427       [
38428         "Austria (Österreich)",
38429         "at",
38430         "43"
38431       ],
38432       [
38433         "Azerbaijan (Azərbaycan)",
38434         "az",
38435         "994"
38436       ],
38437       [
38438         "Bahamas",
38439         "bs",
38440         "1242"
38441       ],
38442       [
38443         "Bahrain (‫البحرين‬‎)",
38444         "bh",
38445         "973"
38446       ],
38447       [
38448         "Bangladesh (বাংলাদেশ)",
38449         "bd",
38450         "880"
38451       ],
38452       [
38453         "Barbados",
38454         "bb",
38455         "1246"
38456       ],
38457       [
38458         "Belarus (Беларусь)",
38459         "by",
38460         "375"
38461       ],
38462       [
38463         "Belgium (België)",
38464         "be",
38465         "32"
38466       ],
38467       [
38468         "Belize",
38469         "bz",
38470         "501"
38471       ],
38472       [
38473         "Benin (Bénin)",
38474         "bj",
38475         "229"
38476       ],
38477       [
38478         "Bermuda",
38479         "bm",
38480         "1441"
38481       ],
38482       [
38483         "Bhutan (འབྲུག)",
38484         "bt",
38485         "975"
38486       ],
38487       [
38488         "Bolivia",
38489         "bo",
38490         "591"
38491       ],
38492       [
38493         "Bosnia and Herzegovina (Босна и Херцеговина)",
38494         "ba",
38495         "387"
38496       ],
38497       [
38498         "Botswana",
38499         "bw",
38500         "267"
38501       ],
38502       [
38503         "Brazil (Brasil)",
38504         "br",
38505         "55"
38506       ],
38507       [
38508         "British Indian Ocean Territory",
38509         "io",
38510         "246"
38511       ],
38512       [
38513         "British Virgin Islands",
38514         "vg",
38515         "1284"
38516       ],
38517       [
38518         "Brunei",
38519         "bn",
38520         "673"
38521       ],
38522       [
38523         "Bulgaria (България)",
38524         "bg",
38525         "359"
38526       ],
38527       [
38528         "Burkina Faso",
38529         "bf",
38530         "226"
38531       ],
38532       [
38533         "Burundi (Uburundi)",
38534         "bi",
38535         "257"
38536       ],
38537       [
38538         "Cambodia (កម្ពុជា)",
38539         "kh",
38540         "855"
38541       ],
38542       [
38543         "Cameroon (Cameroun)",
38544         "cm",
38545         "237"
38546       ],
38547       [
38548         "Canada",
38549         "ca",
38550         "1",
38551         1,
38552         ["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"]
38553       ],
38554       [
38555         "Cape Verde (Kabu Verdi)",
38556         "cv",
38557         "238"
38558       ],
38559       [
38560         "Caribbean Netherlands",
38561         "bq",
38562         "599",
38563         1
38564       ],
38565       [
38566         "Cayman Islands",
38567         "ky",
38568         "1345"
38569       ],
38570       [
38571         "Central African Republic (République centrafricaine)",
38572         "cf",
38573         "236"
38574       ],
38575       [
38576         "Chad (Tchad)",
38577         "td",
38578         "235"
38579       ],
38580       [
38581         "Chile",
38582         "cl",
38583         "56"
38584       ],
38585       [
38586         "China (中国)",
38587         "cn",
38588         "86"
38589       ],
38590       [
38591         "Christmas Island",
38592         "cx",
38593         "61",
38594         2
38595       ],
38596       [
38597         "Cocos (Keeling) Islands",
38598         "cc",
38599         "61",
38600         1
38601       ],
38602       [
38603         "Colombia",
38604         "co",
38605         "57"
38606       ],
38607       [
38608         "Comoros (‫جزر القمر‬‎)",
38609         "km",
38610         "269"
38611       ],
38612       [
38613         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38614         "cd",
38615         "243"
38616       ],
38617       [
38618         "Congo (Republic) (Congo-Brazzaville)",
38619         "cg",
38620         "242"
38621       ],
38622       [
38623         "Cook Islands",
38624         "ck",
38625         "682"
38626       ],
38627       [
38628         "Costa Rica",
38629         "cr",
38630         "506"
38631       ],
38632       [
38633         "Côte d’Ivoire",
38634         "ci",
38635         "225"
38636       ],
38637       [
38638         "Croatia (Hrvatska)",
38639         "hr",
38640         "385"
38641       ],
38642       [
38643         "Cuba",
38644         "cu",
38645         "53"
38646       ],
38647       [
38648         "Curaçao",
38649         "cw",
38650         "599",
38651         0
38652       ],
38653       [
38654         "Cyprus (Κύπρος)",
38655         "cy",
38656         "357"
38657       ],
38658       [
38659         "Czech Republic (Česká republika)",
38660         "cz",
38661         "420"
38662       ],
38663       [
38664         "Denmark (Danmark)",
38665         "dk",
38666         "45"
38667       ],
38668       [
38669         "Djibouti",
38670         "dj",
38671         "253"
38672       ],
38673       [
38674         "Dominica",
38675         "dm",
38676         "1767"
38677       ],
38678       [
38679         "Dominican Republic (República Dominicana)",
38680         "do",
38681         "1",
38682         2,
38683         ["809", "829", "849"]
38684       ],
38685       [
38686         "Ecuador",
38687         "ec",
38688         "593"
38689       ],
38690       [
38691         "Egypt (‫مصر‬‎)",
38692         "eg",
38693         "20"
38694       ],
38695       [
38696         "El Salvador",
38697         "sv",
38698         "503"
38699       ],
38700       [
38701         "Equatorial Guinea (Guinea Ecuatorial)",
38702         "gq",
38703         "240"
38704       ],
38705       [
38706         "Eritrea",
38707         "er",
38708         "291"
38709       ],
38710       [
38711         "Estonia (Eesti)",
38712         "ee",
38713         "372"
38714       ],
38715       [
38716         "Ethiopia",
38717         "et",
38718         "251"
38719       ],
38720       [
38721         "Falkland Islands (Islas Malvinas)",
38722         "fk",
38723         "500"
38724       ],
38725       [
38726         "Faroe Islands (Føroyar)",
38727         "fo",
38728         "298"
38729       ],
38730       [
38731         "Fiji",
38732         "fj",
38733         "679"
38734       ],
38735       [
38736         "Finland (Suomi)",
38737         "fi",
38738         "358",
38739         0
38740       ],
38741       [
38742         "France",
38743         "fr",
38744         "33"
38745       ],
38746       [
38747         "French Guiana (Guyane française)",
38748         "gf",
38749         "594"
38750       ],
38751       [
38752         "French Polynesia (Polynésie française)",
38753         "pf",
38754         "689"
38755       ],
38756       [
38757         "Gabon",
38758         "ga",
38759         "241"
38760       ],
38761       [
38762         "Gambia",
38763         "gm",
38764         "220"
38765       ],
38766       [
38767         "Georgia (საქართველო)",
38768         "ge",
38769         "995"
38770       ],
38771       [
38772         "Germany (Deutschland)",
38773         "de",
38774         "49"
38775       ],
38776       [
38777         "Ghana (Gaana)",
38778         "gh",
38779         "233"
38780       ],
38781       [
38782         "Gibraltar",
38783         "gi",
38784         "350"
38785       ],
38786       [
38787         "Greece (Ελλάδα)",
38788         "gr",
38789         "30"
38790       ],
38791       [
38792         "Greenland (Kalaallit Nunaat)",
38793         "gl",
38794         "299"
38795       ],
38796       [
38797         "Grenada",
38798         "gd",
38799         "1473"
38800       ],
38801       [
38802         "Guadeloupe",
38803         "gp",
38804         "590",
38805         0
38806       ],
38807       [
38808         "Guam",
38809         "gu",
38810         "1671"
38811       ],
38812       [
38813         "Guatemala",
38814         "gt",
38815         "502"
38816       ],
38817       [
38818         "Guernsey",
38819         "gg",
38820         "44",
38821         1
38822       ],
38823       [
38824         "Guinea (Guinée)",
38825         "gn",
38826         "224"
38827       ],
38828       [
38829         "Guinea-Bissau (Guiné Bissau)",
38830         "gw",
38831         "245"
38832       ],
38833       [
38834         "Guyana",
38835         "gy",
38836         "592"
38837       ],
38838       [
38839         "Haiti",
38840         "ht",
38841         "509"
38842       ],
38843       [
38844         "Honduras",
38845         "hn",
38846         "504"
38847       ],
38848       [
38849         "Hong Kong (香港)",
38850         "hk",
38851         "852"
38852       ],
38853       [
38854         "Hungary (Magyarország)",
38855         "hu",
38856         "36"
38857       ],
38858       [
38859         "Iceland (Ísland)",
38860         "is",
38861         "354"
38862       ],
38863       [
38864         "India (भारत)",
38865         "in",
38866         "91"
38867       ],
38868       [
38869         "Indonesia",
38870         "id",
38871         "62"
38872       ],
38873       [
38874         "Iran (‫ایران‬‎)",
38875         "ir",
38876         "98"
38877       ],
38878       [
38879         "Iraq (‫العراق‬‎)",
38880         "iq",
38881         "964"
38882       ],
38883       [
38884         "Ireland",
38885         "ie",
38886         "353"
38887       ],
38888       [
38889         "Isle of Man",
38890         "im",
38891         "44",
38892         2
38893       ],
38894       [
38895         "Israel (‫ישראל‬‎)",
38896         "il",
38897         "972"
38898       ],
38899       [
38900         "Italy (Italia)",
38901         "it",
38902         "39",
38903         0
38904       ],
38905       [
38906         "Jamaica",
38907         "jm",
38908         "1876"
38909       ],
38910       [
38911         "Japan (日本)",
38912         "jp",
38913         "81"
38914       ],
38915       [
38916         "Jersey",
38917         "je",
38918         "44",
38919         3
38920       ],
38921       [
38922         "Jordan (‫الأردن‬‎)",
38923         "jo",
38924         "962"
38925       ],
38926       [
38927         "Kazakhstan (Казахстан)",
38928         "kz",
38929         "7",
38930         1
38931       ],
38932       [
38933         "Kenya",
38934         "ke",
38935         "254"
38936       ],
38937       [
38938         "Kiribati",
38939         "ki",
38940         "686"
38941       ],
38942       [
38943         "Kosovo",
38944         "xk",
38945         "383"
38946       ],
38947       [
38948         "Kuwait (‫الكويت‬‎)",
38949         "kw",
38950         "965"
38951       ],
38952       [
38953         "Kyrgyzstan (Кыргызстан)",
38954         "kg",
38955         "996"
38956       ],
38957       [
38958         "Laos (ລາວ)",
38959         "la",
38960         "856"
38961       ],
38962       [
38963         "Latvia (Latvija)",
38964         "lv",
38965         "371"
38966       ],
38967       [
38968         "Lebanon (‫لبنان‬‎)",
38969         "lb",
38970         "961"
38971       ],
38972       [
38973         "Lesotho",
38974         "ls",
38975         "266"
38976       ],
38977       [
38978         "Liberia",
38979         "lr",
38980         "231"
38981       ],
38982       [
38983         "Libya (‫ليبيا‬‎)",
38984         "ly",
38985         "218"
38986       ],
38987       [
38988         "Liechtenstein",
38989         "li",
38990         "423"
38991       ],
38992       [
38993         "Lithuania (Lietuva)",
38994         "lt",
38995         "370"
38996       ],
38997       [
38998         "Luxembourg",
38999         "lu",
39000         "352"
39001       ],
39002       [
39003         "Macau (澳門)",
39004         "mo",
39005         "853"
39006       ],
39007       [
39008         "Macedonia (FYROM) (Македонија)",
39009         "mk",
39010         "389"
39011       ],
39012       [
39013         "Madagascar (Madagasikara)",
39014         "mg",
39015         "261"
39016       ],
39017       [
39018         "Malawi",
39019         "mw",
39020         "265"
39021       ],
39022       [
39023         "Malaysia",
39024         "my",
39025         "60"
39026       ],
39027       [
39028         "Maldives",
39029         "mv",
39030         "960"
39031       ],
39032       [
39033         "Mali",
39034         "ml",
39035         "223"
39036       ],
39037       [
39038         "Malta",
39039         "mt",
39040         "356"
39041       ],
39042       [
39043         "Marshall Islands",
39044         "mh",
39045         "692"
39046       ],
39047       [
39048         "Martinique",
39049         "mq",
39050         "596"
39051       ],
39052       [
39053         "Mauritania (‫موريتانيا‬‎)",
39054         "mr",
39055         "222"
39056       ],
39057       [
39058         "Mauritius (Moris)",
39059         "mu",
39060         "230"
39061       ],
39062       [
39063         "Mayotte",
39064         "yt",
39065         "262",
39066         1
39067       ],
39068       [
39069         "Mexico (México)",
39070         "mx",
39071         "52"
39072       ],
39073       [
39074         "Micronesia",
39075         "fm",
39076         "691"
39077       ],
39078       [
39079         "Moldova (Republica Moldova)",
39080         "md",
39081         "373"
39082       ],
39083       [
39084         "Monaco",
39085         "mc",
39086         "377"
39087       ],
39088       [
39089         "Mongolia (Монгол)",
39090         "mn",
39091         "976"
39092       ],
39093       [
39094         "Montenegro (Crna Gora)",
39095         "me",
39096         "382"
39097       ],
39098       [
39099         "Montserrat",
39100         "ms",
39101         "1664"
39102       ],
39103       [
39104         "Morocco (‫المغرب‬‎)",
39105         "ma",
39106         "212",
39107         0
39108       ],
39109       [
39110         "Mozambique (Moçambique)",
39111         "mz",
39112         "258"
39113       ],
39114       [
39115         "Myanmar (Burma) (မြန်မာ)",
39116         "mm",
39117         "95"
39118       ],
39119       [
39120         "Namibia (Namibië)",
39121         "na",
39122         "264"
39123       ],
39124       [
39125         "Nauru",
39126         "nr",
39127         "674"
39128       ],
39129       [
39130         "Nepal (नेपाल)",
39131         "np",
39132         "977"
39133       ],
39134       [
39135         "Netherlands (Nederland)",
39136         "nl",
39137         "31"
39138       ],
39139       [
39140         "New Caledonia (Nouvelle-Calédonie)",
39141         "nc",
39142         "687"
39143       ],
39144       [
39145         "New Zealand",
39146         "nz",
39147         "64"
39148       ],
39149       [
39150         "Nicaragua",
39151         "ni",
39152         "505"
39153       ],
39154       [
39155         "Niger (Nijar)",
39156         "ne",
39157         "227"
39158       ],
39159       [
39160         "Nigeria",
39161         "ng",
39162         "234"
39163       ],
39164       [
39165         "Niue",
39166         "nu",
39167         "683"
39168       ],
39169       [
39170         "Norfolk Island",
39171         "nf",
39172         "672"
39173       ],
39174       [
39175         "North Korea (조선 민주주의 인민 공화국)",
39176         "kp",
39177         "850"
39178       ],
39179       [
39180         "Northern Mariana Islands",
39181         "mp",
39182         "1670"
39183       ],
39184       [
39185         "Norway (Norge)",
39186         "no",
39187         "47",
39188         0
39189       ],
39190       [
39191         "Oman (‫عُمان‬‎)",
39192         "om",
39193         "968"
39194       ],
39195       [
39196         "Pakistan (‫پاکستان‬‎)",
39197         "pk",
39198         "92"
39199       ],
39200       [
39201         "Palau",
39202         "pw",
39203         "680"
39204       ],
39205       [
39206         "Palestine (‫فلسطين‬‎)",
39207         "ps",
39208         "970"
39209       ],
39210       [
39211         "Panama (Panamá)",
39212         "pa",
39213         "507"
39214       ],
39215       [
39216         "Papua New Guinea",
39217         "pg",
39218         "675"
39219       ],
39220       [
39221         "Paraguay",
39222         "py",
39223         "595"
39224       ],
39225       [
39226         "Peru (Perú)",
39227         "pe",
39228         "51"
39229       ],
39230       [
39231         "Philippines",
39232         "ph",
39233         "63"
39234       ],
39235       [
39236         "Poland (Polska)",
39237         "pl",
39238         "48"
39239       ],
39240       [
39241         "Portugal",
39242         "pt",
39243         "351"
39244       ],
39245       [
39246         "Puerto Rico",
39247         "pr",
39248         "1",
39249         3,
39250         ["787", "939"]
39251       ],
39252       [
39253         "Qatar (‫قطر‬‎)",
39254         "qa",
39255         "974"
39256       ],
39257       [
39258         "Réunion (La Réunion)",
39259         "re",
39260         "262",
39261         0
39262       ],
39263       [
39264         "Romania (România)",
39265         "ro",
39266         "40"
39267       ],
39268       [
39269         "Russia (Россия)",
39270         "ru",
39271         "7",
39272         0
39273       ],
39274       [
39275         "Rwanda",
39276         "rw",
39277         "250"
39278       ],
39279       [
39280         "Saint Barthélemy",
39281         "bl",
39282         "590",
39283         1
39284       ],
39285       [
39286         "Saint Helena",
39287         "sh",
39288         "290"
39289       ],
39290       [
39291         "Saint Kitts and Nevis",
39292         "kn",
39293         "1869"
39294       ],
39295       [
39296         "Saint Lucia",
39297         "lc",
39298         "1758"
39299       ],
39300       [
39301         "Saint Martin (Saint-Martin (partie française))",
39302         "mf",
39303         "590",
39304         2
39305       ],
39306       [
39307         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39308         "pm",
39309         "508"
39310       ],
39311       [
39312         "Saint Vincent and the Grenadines",
39313         "vc",
39314         "1784"
39315       ],
39316       [
39317         "Samoa",
39318         "ws",
39319         "685"
39320       ],
39321       [
39322         "San Marino",
39323         "sm",
39324         "378"
39325       ],
39326       [
39327         "São Tomé and Príncipe (São Tomé e Príncipe)",
39328         "st",
39329         "239"
39330       ],
39331       [
39332         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39333         "sa",
39334         "966"
39335       ],
39336       [
39337         "Senegal (Sénégal)",
39338         "sn",
39339         "221"
39340       ],
39341       [
39342         "Serbia (Србија)",
39343         "rs",
39344         "381"
39345       ],
39346       [
39347         "Seychelles",
39348         "sc",
39349         "248"
39350       ],
39351       [
39352         "Sierra Leone",
39353         "sl",
39354         "232"
39355       ],
39356       [
39357         "Singapore",
39358         "sg",
39359         "65"
39360       ],
39361       [
39362         "Sint Maarten",
39363         "sx",
39364         "1721"
39365       ],
39366       [
39367         "Slovakia (Slovensko)",
39368         "sk",
39369         "421"
39370       ],
39371       [
39372         "Slovenia (Slovenija)",
39373         "si",
39374         "386"
39375       ],
39376       [
39377         "Solomon Islands",
39378         "sb",
39379         "677"
39380       ],
39381       [
39382         "Somalia (Soomaaliya)",
39383         "so",
39384         "252"
39385       ],
39386       [
39387         "South Africa",
39388         "za",
39389         "27"
39390       ],
39391       [
39392         "South Korea (대한민국)",
39393         "kr",
39394         "82"
39395       ],
39396       [
39397         "South Sudan (‫جنوب السودان‬‎)",
39398         "ss",
39399         "211"
39400       ],
39401       [
39402         "Spain (España)",
39403         "es",
39404         "34"
39405       ],
39406       [
39407         "Sri Lanka (ශ්‍රී ලංකාව)",
39408         "lk",
39409         "94"
39410       ],
39411       [
39412         "Sudan (‫السودان‬‎)",
39413         "sd",
39414         "249"
39415       ],
39416       [
39417         "Suriname",
39418         "sr",
39419         "597"
39420       ],
39421       [
39422         "Svalbard and Jan Mayen",
39423         "sj",
39424         "47",
39425         1
39426       ],
39427       [
39428         "Swaziland",
39429         "sz",
39430         "268"
39431       ],
39432       [
39433         "Sweden (Sverige)",
39434         "se",
39435         "46"
39436       ],
39437       [
39438         "Switzerland (Schweiz)",
39439         "ch",
39440         "41"
39441       ],
39442       [
39443         "Syria (‫سوريا‬‎)",
39444         "sy",
39445         "963"
39446       ],
39447       [
39448         "Taiwan (台灣)",
39449         "tw",
39450         "886"
39451       ],
39452       [
39453         "Tajikistan",
39454         "tj",
39455         "992"
39456       ],
39457       [
39458         "Tanzania",
39459         "tz",
39460         "255"
39461       ],
39462       [
39463         "Thailand (ไทย)",
39464         "th",
39465         "66"
39466       ],
39467       [
39468         "Timor-Leste",
39469         "tl",
39470         "670"
39471       ],
39472       [
39473         "Togo",
39474         "tg",
39475         "228"
39476       ],
39477       [
39478         "Tokelau",
39479         "tk",
39480         "690"
39481       ],
39482       [
39483         "Tonga",
39484         "to",
39485         "676"
39486       ],
39487       [
39488         "Trinidad and Tobago",
39489         "tt",
39490         "1868"
39491       ],
39492       [
39493         "Tunisia (‫تونس‬‎)",
39494         "tn",
39495         "216"
39496       ],
39497       [
39498         "Turkey (Türkiye)",
39499         "tr",
39500         "90"
39501       ],
39502       [
39503         "Turkmenistan",
39504         "tm",
39505         "993"
39506       ],
39507       [
39508         "Turks and Caicos Islands",
39509         "tc",
39510         "1649"
39511       ],
39512       [
39513         "Tuvalu",
39514         "tv",
39515         "688"
39516       ],
39517       [
39518         "U.S. Virgin Islands",
39519         "vi",
39520         "1340"
39521       ],
39522       [
39523         "Uganda",
39524         "ug",
39525         "256"
39526       ],
39527       [
39528         "Ukraine (Україна)",
39529         "ua",
39530         "380"
39531       ],
39532       [
39533         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39534         "ae",
39535         "971"
39536       ],
39537       [
39538         "United Kingdom",
39539         "gb",
39540         "44",
39541         0
39542       ],
39543       [
39544         "United States",
39545         "us",
39546         "1",
39547         0
39548       ],
39549       [
39550         "Uruguay",
39551         "uy",
39552         "598"
39553       ],
39554       [
39555         "Uzbekistan (Oʻzbekiston)",
39556         "uz",
39557         "998"
39558       ],
39559       [
39560         "Vanuatu",
39561         "vu",
39562         "678"
39563       ],
39564       [
39565         "Vatican City (Città del Vaticano)",
39566         "va",
39567         "39",
39568         1
39569       ],
39570       [
39571         "Venezuela",
39572         "ve",
39573         "58"
39574       ],
39575       [
39576         "Vietnam (Việt Nam)",
39577         "vn",
39578         "84"
39579       ],
39580       [
39581         "Wallis and Futuna (Wallis-et-Futuna)",
39582         "wf",
39583         "681"
39584       ],
39585       [
39586         "Western Sahara (‫الصحراء الغربية‬‎)",
39587         "eh",
39588         "212",
39589         1
39590       ],
39591       [
39592         "Yemen (‫اليمن‬‎)",
39593         "ye",
39594         "967"
39595       ],
39596       [
39597         "Zambia",
39598         "zm",
39599         "260"
39600       ],
39601       [
39602         "Zimbabwe",
39603         "zw",
39604         "263"
39605       ],
39606       [
39607         "Åland Islands",
39608         "ax",
39609         "358",
39610         1
39611       ]
39612   ];
39613   
39614   return d;
39615 }/**
39616 *    This script refer to:
39617 *    Title: International Telephone Input
39618 *    Author: Jack O'Connor
39619 *    Code version:  v12.1.12
39620 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39621 **/
39622
39623 /**
39624  * @class Roo.bootstrap.PhoneInput
39625  * @extends Roo.bootstrap.TriggerField
39626  * An input with International dial-code selection
39627  
39628  * @cfg {String} defaultDialCode default '+852'
39629  * @cfg {Array} preferedCountries default []
39630   
39631  * @constructor
39632  * Create a new PhoneInput.
39633  * @param {Object} config Configuration options
39634  */
39635
39636 Roo.bootstrap.PhoneInput = function(config) {
39637     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39638 };
39639
39640 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39641         
39642         listWidth: undefined,
39643         
39644         selectedClass: 'active',
39645         
39646         invalidClass : "has-warning",
39647         
39648         validClass: 'has-success',
39649         
39650         allowed: '0123456789',
39651         
39652         /**
39653          * @cfg {String} defaultDialCode The default dial code when initializing the input
39654          */
39655         defaultDialCode: '+852',
39656         
39657         /**
39658          * @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
39659          */
39660         preferedCountries: false,
39661         
39662         getAutoCreate : function()
39663         {
39664             var data = Roo.bootstrap.PhoneInputData();
39665             var align = this.labelAlign || this.parentLabelAlign();
39666             var id = Roo.id();
39667             
39668             this.allCountries = [];
39669             this.dialCodeMapping = [];
39670             
39671             for (var i = 0; i < data.length; i++) {
39672               var c = data[i];
39673               this.allCountries[i] = {
39674                 name: c[0],
39675                 iso2: c[1],
39676                 dialCode: c[2],
39677                 priority: c[3] || 0,
39678                 areaCodes: c[4] || null
39679               };
39680               this.dialCodeMapping[c[2]] = {
39681                   name: c[0],
39682                   iso2: c[1],
39683                   priority: c[3] || 0,
39684                   areaCodes: c[4] || null
39685               };
39686             }
39687             
39688             var cfg = {
39689                 cls: 'form-group',
39690                 cn: []
39691             };
39692             
39693             var input =  {
39694                 tag: 'input',
39695                 id : id,
39696                 cls : 'form-control tel-input',
39697                 autocomplete: 'new-password'
39698             };
39699             
39700             var hiddenInput = {
39701                 tag: 'input',
39702                 type: 'hidden',
39703                 cls: 'hidden-tel-input'
39704             };
39705             
39706             if (this.name) {
39707                 hiddenInput.name = this.name;
39708             }
39709             
39710             if (this.disabled) {
39711                 input.disabled = true;
39712             }
39713             
39714             var flag_container = {
39715                 tag: 'div',
39716                 cls: 'flag-box',
39717                 cn: [
39718                     {
39719                         tag: 'div',
39720                         cls: 'flag'
39721                     },
39722                     {
39723                         tag: 'div',
39724                         cls: 'caret'
39725                     }
39726                 ]
39727             };
39728             
39729             var box = {
39730                 tag: 'div',
39731                 cls: this.hasFeedback ? 'has-feedback' : '',
39732                 cn: [
39733                     hiddenInput,
39734                     input,
39735                     {
39736                         tag: 'input',
39737                         cls: 'dial-code-holder',
39738                         disabled: true
39739                     }
39740                 ]
39741             };
39742             
39743             var container = {
39744                 cls: 'roo-select2-container input-group',
39745                 cn: [
39746                     flag_container,
39747                     box
39748                 ]
39749             };
39750             
39751             if (this.fieldLabel.length) {
39752                 var indicator = {
39753                     tag: 'i',
39754                     tooltip: 'This field is required'
39755                 };
39756                 
39757                 var label = {
39758                     tag: 'label',
39759                     'for':  id,
39760                     cls: 'control-label',
39761                     cn: []
39762                 };
39763                 
39764                 var label_text = {
39765                     tag: 'span',
39766                     html: this.fieldLabel
39767                 };
39768                 
39769                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39770                 label.cn = [
39771                     indicator,
39772                     label_text
39773                 ];
39774                 
39775                 if(this.indicatorpos == 'right') {
39776                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39777                     label.cn = [
39778                         label_text,
39779                         indicator
39780                     ];
39781                 }
39782                 
39783                 if(align == 'left') {
39784                     container = {
39785                         tag: 'div',
39786                         cn: [
39787                             container
39788                         ]
39789                     };
39790                     
39791                     if(this.labelWidth > 12){
39792                         label.style = "width: " + this.labelWidth + 'px';
39793                     }
39794                     if(this.labelWidth < 13 && this.labelmd == 0){
39795                         this.labelmd = this.labelWidth;
39796                     }
39797                     if(this.labellg > 0){
39798                         label.cls += ' col-lg-' + this.labellg;
39799                         input.cls += ' col-lg-' + (12 - this.labellg);
39800                     }
39801                     if(this.labelmd > 0){
39802                         label.cls += ' col-md-' + this.labelmd;
39803                         container.cls += ' col-md-' + (12 - this.labelmd);
39804                     }
39805                     if(this.labelsm > 0){
39806                         label.cls += ' col-sm-' + this.labelsm;
39807                         container.cls += ' col-sm-' + (12 - this.labelsm);
39808                     }
39809                     if(this.labelxs > 0){
39810                         label.cls += ' col-xs-' + this.labelxs;
39811                         container.cls += ' col-xs-' + (12 - this.labelxs);
39812                     }
39813                 }
39814             }
39815             
39816             cfg.cn = [
39817                 label,
39818                 container
39819             ];
39820             
39821             var settings = this;
39822             
39823             ['xs','sm','md','lg'].map(function(size){
39824                 if (settings[size]) {
39825                     cfg.cls += ' col-' + size + '-' + settings[size];
39826                 }
39827             });
39828             
39829             this.store = new Roo.data.Store({
39830                 proxy : new Roo.data.MemoryProxy({}),
39831                 reader : new Roo.data.JsonReader({
39832                     fields : [
39833                         {
39834                             'name' : 'name',
39835                             'type' : 'string'
39836                         },
39837                         {
39838                             'name' : 'iso2',
39839                             'type' : 'string'
39840                         },
39841                         {
39842                             'name' : 'dialCode',
39843                             'type' : 'string'
39844                         },
39845                         {
39846                             'name' : 'priority',
39847                             'type' : 'string'
39848                         },
39849                         {
39850                             'name' : 'areaCodes',
39851                             'type' : 'string'
39852                         }
39853                     ]
39854                 })
39855             });
39856             
39857             if(!this.preferedCountries) {
39858                 this.preferedCountries = [
39859                     'hk',
39860                     'gb',
39861                     'us'
39862                 ];
39863             }
39864             
39865             var p = this.preferedCountries.reverse();
39866             
39867             if(p) {
39868                 for (var i = 0; i < p.length; i++) {
39869                     for (var j = 0; j < this.allCountries.length; j++) {
39870                         if(this.allCountries[j].iso2 == p[i]) {
39871                             var t = this.allCountries[j];
39872                             this.allCountries.splice(j,1);
39873                             this.allCountries.unshift(t);
39874                         }
39875                     } 
39876                 }
39877             }
39878             
39879             this.store.proxy.data = {
39880                 success: true,
39881                 data: this.allCountries
39882             };
39883             
39884             return cfg;
39885         },
39886         
39887         initEvents : function()
39888         {
39889             this.createList();
39890             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39891             
39892             this.indicator = this.indicatorEl();
39893             this.flag = this.flagEl();
39894             this.dialCodeHolder = this.dialCodeHolderEl();
39895             
39896             this.trigger = this.el.select('div.flag-box',true).first();
39897             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39898             
39899             var _this = this;
39900             
39901             (function(){
39902                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39903                 _this.list.setWidth(lw);
39904             }).defer(100);
39905             
39906             this.list.on('mouseover', this.onViewOver, this);
39907             this.list.on('mousemove', this.onViewMove, this);
39908             this.inputEl().on("keyup", this.onKeyUp, this);
39909             
39910             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39911
39912             this.view = new Roo.View(this.list, this.tpl, {
39913                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39914             });
39915             
39916             this.view.on('click', this.onViewClick, this);
39917             this.setValue(this.defaultDialCode);
39918         },
39919         
39920         onTriggerClick : function(e)
39921         {
39922             Roo.log('trigger click');
39923             if(this.disabled){
39924                 return;
39925             }
39926             
39927             if(this.isExpanded()){
39928                 this.collapse();
39929                 this.hasFocus = false;
39930             }else {
39931                 this.store.load({});
39932                 this.hasFocus = true;
39933                 this.expand();
39934             }
39935         },
39936         
39937         isExpanded : function()
39938         {
39939             return this.list.isVisible();
39940         },
39941         
39942         collapse : function()
39943         {
39944             if(!this.isExpanded()){
39945                 return;
39946             }
39947             this.list.hide();
39948             Roo.get(document).un('mousedown', this.collapseIf, this);
39949             Roo.get(document).un('mousewheel', this.collapseIf, this);
39950             this.fireEvent('collapse', this);
39951             this.validate();
39952         },
39953         
39954         expand : function()
39955         {
39956             Roo.log('expand');
39957
39958             if(this.isExpanded() || !this.hasFocus){
39959                 return;
39960             }
39961             
39962             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39963             this.list.setWidth(lw);
39964             
39965             this.list.show();
39966             this.restrictHeight();
39967             
39968             Roo.get(document).on('mousedown', this.collapseIf, this);
39969             Roo.get(document).on('mousewheel', this.collapseIf, this);
39970             
39971             this.fireEvent('expand', this);
39972         },
39973         
39974         restrictHeight : function()
39975         {
39976             this.list.alignTo(this.inputEl(), this.listAlign);
39977             this.list.alignTo(this.inputEl(), this.listAlign);
39978         },
39979         
39980         onViewOver : function(e, t)
39981         {
39982             if(this.inKeyMode){
39983                 return;
39984             }
39985             var item = this.view.findItemFromChild(t);
39986             
39987             if(item){
39988                 var index = this.view.indexOf(item);
39989                 this.select(index, false);
39990             }
39991         },
39992
39993         // private
39994         onViewClick : function(view, doFocus, el, e)
39995         {
39996             var index = this.view.getSelectedIndexes()[0];
39997             
39998             var r = this.store.getAt(index);
39999             
40000             if(r){
40001                 this.onSelect(r, index);
40002             }
40003             if(doFocus !== false && !this.blockFocus){
40004                 this.inputEl().focus();
40005             }
40006         },
40007         
40008         onViewMove : function(e, t)
40009         {
40010             this.inKeyMode = false;
40011         },
40012         
40013         select : function(index, scrollIntoView)
40014         {
40015             this.selectedIndex = index;
40016             this.view.select(index);
40017             if(scrollIntoView !== false){
40018                 var el = this.view.getNode(index);
40019                 if(el){
40020                     this.list.scrollChildIntoView(el, false);
40021                 }
40022             }
40023         },
40024         
40025         createList : function()
40026         {
40027             this.list = Roo.get(document.body).createChild({
40028                 tag: 'ul',
40029                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40030                 style: 'display:none'
40031             });
40032             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
40033         },
40034         
40035         collapseIf : function(e)
40036         {
40037             var in_combo  = e.within(this.el);
40038             var in_list =  e.within(this.list);
40039             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40040             
40041             if (in_combo || in_list || is_list) {
40042                 return;
40043             }
40044             this.collapse();
40045         },
40046         
40047         onSelect : function(record, index)
40048         {
40049             if(this.fireEvent('beforeselect', this, record, index) !== false){
40050                 
40051                 this.setFlagClass(record.data.iso2);
40052                 this.setDialCode(record.data.dialCode);
40053                 this.hasFocus = false;
40054                 this.collapse();
40055                 this.fireEvent('select', this, record, index);
40056             }
40057         },
40058         
40059         flagEl : function()
40060         {
40061             var flag = this.el.select('div.flag',true).first();
40062             if(!flag){
40063                 return false;
40064             }
40065             return flag;
40066         },
40067         
40068         dialCodeHolderEl : function()
40069         {
40070             var d = this.el.select('input.dial-code-holder',true).first();
40071             if(!d){
40072                 return false;
40073             }
40074             return d;
40075         },
40076         
40077         setDialCode : function(v)
40078         {
40079             this.dialCodeHolder.dom.value = '+'+v;
40080         },
40081         
40082         setFlagClass : function(n)
40083         {
40084             this.flag.dom.className = 'flag '+n;
40085         },
40086         
40087         getValue : function()
40088         {
40089             var v = this.inputEl().getValue();
40090             if(this.dialCodeHolder) {
40091                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40092             }
40093             return v;
40094         },
40095         
40096         setValue : function(v)
40097         {
40098             var d = this.getDialCode(v);
40099             
40100             //invalid dial code
40101             if(v.length == 0 || !d || d.length == 0) {
40102                 if(this.rendered){
40103                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40104                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40105                 }
40106                 return;
40107             }
40108             
40109             //valid dial code
40110             this.setFlagClass(this.dialCodeMapping[d].iso2);
40111             this.setDialCode(d);
40112             this.inputEl().dom.value = v.replace('+'+d,'');
40113             this.hiddenEl().dom.value = this.getValue();
40114             
40115             this.validate();
40116         },
40117         
40118         getDialCode : function(v = '')
40119         {
40120             if (v.length == 0) {
40121                 return this.dialCodeHolder.dom.value;
40122             }
40123             
40124             var dialCode = "";
40125             if (v.charAt(0) != "+") {
40126                 return false;
40127             }
40128             var numericChars = "";
40129             for (var i = 1; i < v.length; i++) {
40130               var c = v.charAt(i);
40131               if (!isNaN(c)) {
40132                 numericChars += c;
40133                 if (this.dialCodeMapping[numericChars]) {
40134                   dialCode = v.substr(1, i);
40135                 }
40136                 if (numericChars.length == 4) {
40137                   break;
40138                 }
40139               }
40140             }
40141             return dialCode;
40142         },
40143         
40144         reset : function()
40145         {
40146             this.setValue(this.defaultDialCode);
40147             this.validate();
40148         },
40149         
40150         hiddenEl : function()
40151         {
40152             return this.el.select('input.hidden-tel-input',true).first();
40153         },
40154         
40155         onKeyUp : function(e){
40156             
40157             var k = e.getKey();
40158             var c = e.getCharCode();
40159             
40160             if(
40161                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40162                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40163             ){
40164                 e.stopEvent();
40165             }
40166             
40167             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40168             //     return;
40169             // }
40170             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40171                 e.stopEvent();
40172             }
40173             
40174             this.setValue(this.getValue());
40175         }
40176         
40177 });
40178 /**
40179  * @class Roo.bootstrap.MoneyField
40180  * @extends Roo.bootstrap.ComboBox
40181  * Bootstrap MoneyField class
40182  * 
40183  * @constructor
40184  * Create a new MoneyField.
40185  * @param {Object} config Configuration options
40186  */
40187
40188 Roo.bootstrap.MoneyField = function(config) {
40189     
40190     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40191     
40192 };
40193
40194 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40195     
40196     /**
40197      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40198      */
40199     allowDecimals : true,
40200     /**
40201      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40202      */
40203     decimalSeparator : ".",
40204     /**
40205      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40206      */
40207     decimalPrecision : 0,
40208     /**
40209      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40210      */
40211     allowNegative : true,
40212     /**
40213      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40214      */
40215     minValue : Number.NEGATIVE_INFINITY,
40216     /**
40217      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40218      */
40219     maxValue : Number.MAX_VALUE,
40220     /**
40221      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40222      */
40223     minText : "The minimum value for this field is {0}",
40224     /**
40225      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40226      */
40227     maxText : "The maximum value for this field is {0}",
40228     /**
40229      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40230      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40231      */
40232     nanText : "{0} is not a valid number",
40233     /**
40234      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40235      */
40236     castInt : true,
40237     /**
40238      * @cfg {String} defaults currency of the MoneyField
40239      * value should be in lkey
40240      */
40241     defaultCurrency : false,
40242     /**
40243      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40244      */
40245     thousandsDelimiter : false,
40246     
40247     
40248     inputlg : 9,
40249     inputmd : 9,
40250     inputsm : 9,
40251     inputxs : 6,
40252     
40253     store : false,
40254     
40255     getAutoCreate : function()
40256     {
40257         var align = this.labelAlign || this.parentLabelAlign();
40258         
40259         var id = Roo.id();
40260
40261         var cfg = {
40262             cls: 'form-group',
40263             cn: []
40264         };
40265
40266         var input =  {
40267             tag: 'input',
40268             id : id,
40269             cls : 'form-control roo-money-amount-input',
40270             autocomplete: 'new-password'
40271         };
40272         
40273         var hiddenInput = {
40274             tag: 'input',
40275             type: 'hidden',
40276             id: Roo.id(),
40277             cls: 'hidden-number-input'
40278         };
40279         
40280         if (this.name) {
40281             hiddenInput.name = this.name;
40282         }
40283
40284         if (this.disabled) {
40285             input.disabled = true;
40286         }
40287
40288         var clg = 12 - this.inputlg;
40289         var cmd = 12 - this.inputmd;
40290         var csm = 12 - this.inputsm;
40291         var cxs = 12 - this.inputxs;
40292         
40293         var container = {
40294             tag : 'div',
40295             cls : 'row roo-money-field',
40296             cn : [
40297                 {
40298                     tag : 'div',
40299                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40300                     cn : [
40301                         {
40302                             tag : 'div',
40303                             cls: 'roo-select2-container input-group',
40304                             cn: [
40305                                 {
40306                                     tag : 'input',
40307                                     cls : 'form-control roo-money-currency-input',
40308                                     autocomplete: 'new-password',
40309                                     readOnly : 1,
40310                                     name : this.currencyName
40311                                 },
40312                                 {
40313                                     tag :'span',
40314                                     cls : 'input-group-addon',
40315                                     cn : [
40316                                         {
40317                                             tag: 'span',
40318                                             cls: 'caret'
40319                                         }
40320                                     ]
40321                                 }
40322                             ]
40323                         }
40324                     ]
40325                 },
40326                 {
40327                     tag : 'div',
40328                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40329                     cn : [
40330                         {
40331                             tag: 'div',
40332                             cls: this.hasFeedback ? 'has-feedback' : '',
40333                             cn: [
40334                                 input
40335                             ]
40336                         }
40337                     ]
40338                 }
40339             ]
40340             
40341         };
40342         
40343         if (this.fieldLabel.length) {
40344             var indicator = {
40345                 tag: 'i',
40346                 tooltip: 'This field is required'
40347             };
40348
40349             var label = {
40350                 tag: 'label',
40351                 'for':  id,
40352                 cls: 'control-label',
40353                 cn: []
40354             };
40355
40356             var label_text = {
40357                 tag: 'span',
40358                 html: this.fieldLabel
40359             };
40360
40361             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40362             label.cn = [
40363                 indicator,
40364                 label_text
40365             ];
40366
40367             if(this.indicatorpos == 'right') {
40368                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40369                 label.cn = [
40370                     label_text,
40371                     indicator
40372                 ];
40373             }
40374
40375             if(align == 'left') {
40376                 container = {
40377                     tag: 'div',
40378                     cn: [
40379                         container
40380                     ]
40381                 };
40382
40383                 if(this.labelWidth > 12){
40384                     label.style = "width: " + this.labelWidth + 'px';
40385                 }
40386                 if(this.labelWidth < 13 && this.labelmd == 0){
40387                     this.labelmd = this.labelWidth;
40388                 }
40389                 if(this.labellg > 0){
40390                     label.cls += ' col-lg-' + this.labellg;
40391                     input.cls += ' col-lg-' + (12 - this.labellg);
40392                 }
40393                 if(this.labelmd > 0){
40394                     label.cls += ' col-md-' + this.labelmd;
40395                     container.cls += ' col-md-' + (12 - this.labelmd);
40396                 }
40397                 if(this.labelsm > 0){
40398                     label.cls += ' col-sm-' + this.labelsm;
40399                     container.cls += ' col-sm-' + (12 - this.labelsm);
40400                 }
40401                 if(this.labelxs > 0){
40402                     label.cls += ' col-xs-' + this.labelxs;
40403                     container.cls += ' col-xs-' + (12 - this.labelxs);
40404                 }
40405             }
40406         }
40407
40408         cfg.cn = [
40409             label,
40410             container,
40411             hiddenInput
40412         ];
40413         
40414         var settings = this;
40415
40416         ['xs','sm','md','lg'].map(function(size){
40417             if (settings[size]) {
40418                 cfg.cls += ' col-' + size + '-' + settings[size];
40419             }
40420         });
40421         
40422         return cfg;
40423     },
40424     
40425     initEvents : function()
40426     {
40427         this.indicator = this.indicatorEl();
40428         
40429         this.initCurrencyEvent();
40430         
40431         this.initNumberEvent();
40432     },
40433     
40434     initCurrencyEvent : function()
40435     {
40436         if (!this.store) {
40437             throw "can not find store for combo";
40438         }
40439         
40440         this.store = Roo.factory(this.store, Roo.data);
40441         this.store.parent = this;
40442         
40443         this.createList();
40444         
40445         this.triggerEl = this.el.select('.input-group-addon', true).first();
40446         
40447         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40448         
40449         var _this = this;
40450         
40451         (function(){
40452             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40453             _this.list.setWidth(lw);
40454         }).defer(100);
40455         
40456         this.list.on('mouseover', this.onViewOver, this);
40457         this.list.on('mousemove', this.onViewMove, this);
40458         this.list.on('scroll', this.onViewScroll, this);
40459         
40460         if(!this.tpl){
40461             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40462         }
40463         
40464         this.view = new Roo.View(this.list, this.tpl, {
40465             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40466         });
40467         
40468         this.view.on('click', this.onViewClick, this);
40469         
40470         this.store.on('beforeload', this.onBeforeLoad, this);
40471         this.store.on('load', this.onLoad, this);
40472         this.store.on('loadexception', this.onLoadException, this);
40473         
40474         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40475             "up" : function(e){
40476                 this.inKeyMode = true;
40477                 this.selectPrev();
40478             },
40479
40480             "down" : function(e){
40481                 if(!this.isExpanded()){
40482                     this.onTriggerClick();
40483                 }else{
40484                     this.inKeyMode = true;
40485                     this.selectNext();
40486                 }
40487             },
40488
40489             "enter" : function(e){
40490                 this.collapse();
40491                 
40492                 if(this.fireEvent("specialkey", this, e)){
40493                     this.onViewClick(false);
40494                 }
40495                 
40496                 return true;
40497             },
40498
40499             "esc" : function(e){
40500                 this.collapse();
40501             },
40502
40503             "tab" : function(e){
40504                 this.collapse();
40505                 
40506                 if(this.fireEvent("specialkey", this, e)){
40507                     this.onViewClick(false);
40508                 }
40509                 
40510                 return true;
40511             },
40512
40513             scope : this,
40514
40515             doRelay : function(foo, bar, hname){
40516                 if(hname == 'down' || this.scope.isExpanded()){
40517                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40518                 }
40519                 return true;
40520             },
40521
40522             forceKeyDown: true
40523         });
40524         
40525         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40526         
40527     },
40528     
40529     initNumberEvent : function(e)
40530     {
40531         this.inputEl().on("keydown" , this.fireKey,  this);
40532         this.inputEl().on("focus", this.onFocus,  this);
40533         this.inputEl().on("blur", this.onBlur,  this);
40534         
40535         this.inputEl().relayEvent('keyup', this);
40536         
40537         if(this.indicator){
40538             this.indicator.addClass('invisible');
40539         }
40540  
40541         this.originalValue = this.getValue();
40542         
40543         if(this.validationEvent == 'keyup'){
40544             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40545             this.inputEl().on('keyup', this.filterValidation, this);
40546         }
40547         else if(this.validationEvent !== false){
40548             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40549         }
40550         
40551         if(this.selectOnFocus){
40552             this.on("focus", this.preFocus, this);
40553             
40554         }
40555         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40556             this.inputEl().on("keypress", this.filterKeys, this);
40557         } else {
40558             this.inputEl().relayEvent('keypress', this);
40559         }
40560         
40561         var allowed = "0123456789";
40562         
40563         if(this.allowDecimals){
40564             allowed += this.decimalSeparator;
40565         }
40566         
40567         if(this.allowNegative){
40568             allowed += "-";
40569         }
40570         
40571         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40572         
40573         var keyPress = function(e){
40574             
40575             var k = e.getKey();
40576             
40577             var c = e.getCharCode();
40578             
40579             if(
40580                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40581                     allowed.indexOf(String.fromCharCode(c)) === -1
40582             ){
40583                 e.stopEvent();
40584                 return;
40585             }
40586             
40587             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40588                 return;
40589             }
40590             
40591             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40592                 e.stopEvent();
40593             }
40594         };
40595         
40596         this.inputEl().on("keypress", keyPress, this);
40597         
40598     },
40599     
40600     onTriggerClick : function(e)
40601     {   
40602         if(this.disabled){
40603             return;
40604         }
40605         
40606         this.page = 0;
40607         this.loadNext = false;
40608         
40609         if(this.isExpanded()){
40610             this.collapse();
40611             return;
40612         }
40613         
40614         this.hasFocus = true;
40615         
40616         if(this.triggerAction == 'all') {
40617             this.doQuery(this.allQuery, true);
40618             return;
40619         }
40620         
40621         this.doQuery(this.getRawValue());
40622     },
40623     
40624     getCurrency : function()
40625     {   
40626         var v = this.currencyEl().getValue();
40627         
40628         return v;
40629     },
40630     
40631     restrictHeight : function()
40632     {
40633         this.list.alignTo(this.currencyEl(), this.listAlign);
40634         this.list.alignTo(this.currencyEl(), this.listAlign);
40635     },
40636     
40637     onViewClick : function(view, doFocus, el, e)
40638     {
40639         var index = this.view.getSelectedIndexes()[0];
40640         
40641         var r = this.store.getAt(index);
40642         
40643         if(r){
40644             this.onSelect(r, index);
40645         }
40646     },
40647     
40648     onSelect : function(record, index){
40649         
40650         if(this.fireEvent('beforeselect', this, record, index) !== false){
40651         
40652             this.setFromCurrencyData(index > -1 ? record.data : false);
40653             
40654             this.collapse();
40655             
40656             this.fireEvent('select', this, record, index);
40657         }
40658     },
40659     
40660     setFromCurrencyData : function(o)
40661     {
40662         var currency = '';
40663         
40664         this.lastCurrency = o;
40665         
40666         if (this.currencyField) {
40667             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40668         } else {
40669             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40670         }
40671         
40672         this.lastSelectionText = currency;
40673         
40674         //setting default currency
40675         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40676             this.setCurrency(this.defaultCurrency);
40677             return;
40678         }
40679         
40680         this.setCurrency(currency);
40681     },
40682     
40683     setFromData : function(o)
40684     {
40685         var c = {};
40686         
40687         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40688         
40689         this.setFromCurrencyData(c);
40690         
40691         var value = '';
40692         
40693         if (this.name) {
40694             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40695         } else {
40696             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40697         }
40698         
40699         this.setValue(value);
40700         
40701     },
40702     
40703     setCurrency : function(v)
40704     {   
40705         this.currencyValue = v;
40706         
40707         if(this.rendered){
40708             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40709             this.validate();
40710         }
40711     },
40712     
40713     setValue : function(v)
40714     {
40715         v = this.fixPrecision(v);
40716         
40717         v = String(v).replace(".", this.decimalSeparator);
40718         
40719         this.value = v;
40720         
40721         if(this.rendered){
40722             
40723             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40724             
40725             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40726                 this.thousandsDelimiter || ','
40727             );
40728             
40729             if(this.allowBlank && !v) {
40730                 this.inputEl().dom.value = '';
40731             }
40732             
40733             this.validate();
40734         }
40735     },
40736     
40737     getRawValue : function()
40738     {
40739         var v = this.inputEl().getValue();
40740         
40741         return v;
40742     },
40743     
40744     getValue : function()
40745     {
40746         return this.fixPrecision(this.parseValue(this.getRawValue()));
40747     },
40748     
40749     parseValue : function(value)
40750     {
40751         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40752         return isNaN(value) ? '' : value;
40753     },
40754     
40755     fixPrecision : function(value)
40756     {
40757         var nan = isNaN(value);
40758         
40759         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40760             return nan ? '' : value;
40761         }
40762         
40763         return parseFloat(value).toFixed(this.decimalPrecision);
40764     },
40765     
40766     decimalPrecisionFcn : function(v)
40767     {
40768         return Math.floor(v);
40769     },
40770     
40771     validateValue : function(value)
40772     {
40773         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40774             return false;
40775         }
40776         
40777         var num = this.parseValue(value);
40778         
40779         if(isNaN(num)){
40780             this.markInvalid(String.format(this.nanText, value));
40781             return false;
40782         }
40783         
40784         if(num < this.minValue){
40785             this.markInvalid(String.format(this.minText, this.minValue));
40786             return false;
40787         }
40788         
40789         if(num > this.maxValue){
40790             this.markInvalid(String.format(this.maxText, this.maxValue));
40791             return false;
40792         }
40793         
40794         return true;
40795     },
40796     
40797     validate : function()
40798     {
40799         if(this.disabled || this.allowBlank){
40800             this.markValid();
40801             return true;
40802         }
40803         
40804         var currency = this.getCurrency();
40805         
40806         if(this.validateValue(this.getRawValue()) && currency.length){
40807             this.markValid();
40808             return true;
40809         }
40810         
40811         this.markInvalid();
40812         return false;
40813     },
40814     
40815     getName: function()
40816     {
40817         return this.name;
40818     },
40819     
40820     beforeBlur : function()
40821     {
40822         if(!this.castInt){
40823             return;
40824         }
40825         
40826         var v = this.parseValue(this.getRawValue());
40827         
40828         if(v || v == 0){
40829             this.setValue(v);
40830         }
40831     },
40832     
40833     onBlur : function()
40834     {
40835         this.beforeBlur();
40836         
40837         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40838             //this.el.removeClass(this.focusClass);
40839         }
40840         
40841         this.hasFocus = false;
40842         
40843         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40844             this.validate();
40845         }
40846         
40847         var v = this.getValue();
40848         
40849         if(String(v) !== String(this.startValue)){
40850             this.fireEvent('change', this, v, this.startValue);
40851         }
40852         
40853         this.fireEvent("blur", this);
40854     },
40855     
40856     inputEl : function()
40857     {
40858         return this.el.select('.roo-money-amount-input', true).first();
40859     },
40860     
40861     currencyEl : function()
40862     {
40863         return this.el.select('.roo-money-currency-input', true).first();
40864     },
40865     
40866     hiddenEl : function()
40867     {
40868         return this.el.select('input.hidden-number-input',true).first();
40869     }
40870     
40871 });