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, arguments);
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             header.cn.push(c)
6499         }
6500         
6501         return header;
6502     },
6503     
6504     renderBody : function()
6505     {
6506         var body = {
6507             tag: 'tbody',
6508             cn : [
6509                 {
6510                     tag: 'tr',
6511                     cn : [
6512                         {
6513                             tag : 'td',
6514                             colspan :  this.cm.getColumnCount()
6515                         }
6516                     ]
6517                 }
6518             ]
6519         };
6520         
6521         return body;
6522     },
6523     
6524     renderFooter : function()
6525     {
6526         var footer = {
6527             tag: 'tfoot',
6528             cn : [
6529                 {
6530                     tag: 'tr',
6531                     cn : [
6532                         {
6533                             tag : 'td',
6534                             colspan :  this.cm.getColumnCount()
6535                         }
6536                     ]
6537                 }
6538             ]
6539         };
6540         
6541         return footer;
6542     },
6543     
6544     
6545     
6546     onLoad : function()
6547     {
6548 //        Roo.log('ds onload');
6549         this.clear();
6550         
6551         var _this = this;
6552         var cm = this.cm;
6553         var ds = this.store;
6554         
6555         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6556             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6557             if (_this.store.sortInfo) {
6558                     
6559                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6560                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6561                 }
6562                 
6563                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6564                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6565                 }
6566             }
6567         });
6568         
6569         var tbody =  this.mainBody;
6570               
6571         if(ds.getCount() > 0){
6572             ds.data.each(function(d,rowIndex){
6573                 var row =  this.renderRow(cm, ds, rowIndex);
6574                 
6575                 tbody.createChild(row);
6576                 
6577                 var _this = this;
6578                 
6579                 if(row.cellObjects.length){
6580                     Roo.each(row.cellObjects, function(r){
6581                         _this.renderCellObject(r);
6582                     })
6583                 }
6584                 
6585             }, this);
6586         }
6587         
6588         Roo.each(this.el.select('tbody td', true).elements, function(e){
6589             e.on('mouseover', _this.onMouseover, _this);
6590         });
6591         
6592         Roo.each(this.el.select('tbody td', true).elements, function(e){
6593             e.on('mouseout', _this.onMouseout, _this);
6594         });
6595         this.fireEvent('rowsrendered', this);
6596         //if(this.loadMask){
6597         //    this.maskEl.hide();
6598         //}
6599         
6600         this.autoSize();
6601     },
6602     
6603     
6604     onUpdate : function(ds,record)
6605     {
6606         this.refreshRow(record);
6607         this.autoSize();
6608     },
6609     
6610     onRemove : function(ds, record, index, isUpdate){
6611         if(isUpdate !== true){
6612             this.fireEvent("beforerowremoved", this, index, record);
6613         }
6614         var bt = this.mainBody.dom;
6615         
6616         var rows = this.el.select('tbody > tr', true).elements;
6617         
6618         if(typeof(rows[index]) != 'undefined'){
6619             bt.removeChild(rows[index].dom);
6620         }
6621         
6622 //        if(bt.rows[index]){
6623 //            bt.removeChild(bt.rows[index]);
6624 //        }
6625         
6626         if(isUpdate !== true){
6627             //this.stripeRows(index);
6628             //this.syncRowHeights(index, index);
6629             //this.layout();
6630             this.fireEvent("rowremoved", this, index, record);
6631         }
6632     },
6633     
6634     onAdd : function(ds, records, rowIndex)
6635     {
6636         //Roo.log('on Add called');
6637         // - note this does not handle multiple adding very well..
6638         var bt = this.mainBody.dom;
6639         for (var i =0 ; i < records.length;i++) {
6640             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6641             //Roo.log(records[i]);
6642             //Roo.log(this.store.getAt(rowIndex+i));
6643             this.insertRow(this.store, rowIndex + i, false);
6644             return;
6645         }
6646         
6647     },
6648     
6649     
6650     refreshRow : function(record){
6651         var ds = this.store, index;
6652         if(typeof record == 'number'){
6653             index = record;
6654             record = ds.getAt(index);
6655         }else{
6656             index = ds.indexOf(record);
6657         }
6658         this.insertRow(ds, index, true);
6659         this.autoSize();
6660         this.onRemove(ds, record, index+1, true);
6661         this.autoSize();
6662         //this.syncRowHeights(index, index);
6663         //this.layout();
6664         this.fireEvent("rowupdated", this, index, record);
6665     },
6666     
6667     insertRow : function(dm, rowIndex, isUpdate){
6668         
6669         if(!isUpdate){
6670             this.fireEvent("beforerowsinserted", this, rowIndex);
6671         }
6672             //var s = this.getScrollState();
6673         var row = this.renderRow(this.cm, this.store, rowIndex);
6674         // insert before rowIndex..
6675         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6676         
6677         var _this = this;
6678                 
6679         if(row.cellObjects.length){
6680             Roo.each(row.cellObjects, function(r){
6681                 _this.renderCellObject(r);
6682             })
6683         }
6684             
6685         if(!isUpdate){
6686             this.fireEvent("rowsinserted", this, rowIndex);
6687             //this.syncRowHeights(firstRow, lastRow);
6688             //this.stripeRows(firstRow);
6689             //this.layout();
6690         }
6691         
6692     },
6693     
6694     
6695     getRowDom : function(rowIndex)
6696     {
6697         var rows = this.el.select('tbody > tr', true).elements;
6698         
6699         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6700         
6701     },
6702     // returns the object tree for a tr..
6703   
6704     
6705     renderRow : function(cm, ds, rowIndex) 
6706     {
6707         var d = ds.getAt(rowIndex);
6708         
6709         var row = {
6710             tag : 'tr',
6711             cls : 'roo-bootstrap-tbody-row-' + rowIndex,
6712             cn : []
6713         };
6714             
6715         var cellObjects = [];
6716         
6717         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6718             var config = cm.config[i];
6719             
6720             var renderer = cm.getRenderer(i);
6721             var value = '';
6722             var id = false;
6723             
6724             if(typeof(renderer) !== 'undefined'){
6725                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6726             }
6727             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6728             // and are rendered into the cells after the row is rendered - using the id for the element.
6729             
6730             if(typeof(value) === 'object'){
6731                 id = Roo.id();
6732                 cellObjects.push({
6733                     container : id,
6734                     cfg : value 
6735                 })
6736             }
6737             
6738             var rowcfg = {
6739                 record: d,
6740                 rowIndex : rowIndex,
6741                 colIndex : i,
6742                 rowClass : ''
6743             };
6744
6745             this.fireEvent('rowclass', this, rowcfg);
6746             
6747             var td = {
6748                 tag: 'td',
6749                 cls : rowcfg.rowClass + ' roo-bootstrap-tbody-col-' + i,
6750                 style: '',
6751                 html: (typeof(value) === 'object') ? '' : value
6752             };
6753             
6754             if (id) {
6755                 td.id = id;
6756             }
6757             
6758             if(typeof(config.colspan) != 'undefined'){
6759                 td.colspan = config.colspan;
6760             }
6761             
6762             if(typeof(config.hidden) != 'undefined' && config.hidden){
6763                 td.style += ' display:none;';
6764             }
6765             
6766             if(typeof(config.align) != 'undefined' && config.align.length){
6767                 td.style += ' text-align:' + config.align + ';';
6768             }
6769             
6770             if(typeof(config.width) != 'undefined'){
6771                 td.style += ' width:' +  config.width + 'px;';
6772             }
6773             
6774             if(typeof(config.cursor) != 'undefined'){
6775                 td.style += ' cursor:' +  config.cursor + ';';
6776             }
6777             
6778             if(typeof(config.cls) != 'undefined'){
6779                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6780             }
6781             
6782             ['xs','sm','md','lg'].map(function(size){
6783                 
6784                 if(typeof(config[size]) == 'undefined'){
6785                     return;
6786                 }
6787                 
6788                 if (!config[size]) { // 0 = hidden
6789                     td.cls += ' hidden-' + size;
6790                     return;
6791                 }
6792                 
6793                 td.cls += ' col-' + size + '-' + config[size];
6794
6795             });
6796             
6797             row.cn.push(td);
6798            
6799         }
6800         
6801         row.cellObjects = cellObjects;
6802         
6803         return row;
6804           
6805     },
6806     
6807     
6808     
6809     onBeforeLoad : function()
6810     {
6811         //Roo.log('ds onBeforeLoad');
6812         
6813         //this.clear();
6814         
6815         //if(this.loadMask){
6816         //    this.maskEl.show();
6817         //}
6818     },
6819      /**
6820      * Remove all rows
6821      */
6822     clear : function()
6823     {
6824         this.el.select('tbody', true).first().dom.innerHTML = '';
6825     },
6826     /**
6827      * Show or hide a row.
6828      * @param {Number} rowIndex to show or hide
6829      * @param {Boolean} state hide
6830      */
6831     setRowVisibility : function(rowIndex, state)
6832     {
6833         var bt = this.mainBody.dom;
6834         
6835         var rows = this.el.select('tbody > tr', true).elements;
6836         
6837         if(typeof(rows[rowIndex]) == 'undefined'){
6838             return;
6839         }
6840         rows[rowIndex].dom.style.display = state ? '' : 'none';
6841     },
6842     
6843     
6844     getSelectionModel : function(){
6845         if(!this.selModel){
6846             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6847         }
6848         return this.selModel;
6849     },
6850     /*
6851      * Render the Roo.bootstrap object from renderder
6852      */
6853     renderCellObject : function(r)
6854     {
6855         var _this = this;
6856         
6857         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6858         
6859         var t = r.cfg.render(r.container);
6860         
6861         if(r.cfg.cn){
6862             Roo.each(r.cfg.cn, function(c){
6863                 var child = {
6864                     container: t.getChildContainer(),
6865                     cfg: c
6866                 };
6867                 _this.renderCellObject(child);
6868             })
6869         }
6870     },
6871     
6872     getRowIndex : function(row)
6873     {
6874         var rowIndex = -1;
6875         
6876         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6877             if(el != row){
6878                 return;
6879             }
6880             
6881             rowIndex = index;
6882         });
6883         
6884         return rowIndex;
6885     },
6886      /**
6887      * Returns the grid's underlying element = used by panel.Grid
6888      * @return {Element} The element
6889      */
6890     getGridEl : function(){
6891         return this.el;
6892     },
6893      /**
6894      * Forces a resize - used by panel.Grid
6895      * @return {Element} The element
6896      */
6897     autoSize : function()
6898     {
6899         //var ctr = Roo.get(this.container.dom.parentElement);
6900         var ctr = Roo.get(this.el.dom);
6901         
6902         var thd = this.getGridEl().select('thead',true).first();
6903         var tbd = this.getGridEl().select('tbody', true).first();
6904         var tfd = this.getGridEl().select('tfoot', true).first();
6905         
6906         var cw = ctr.getWidth();
6907         
6908         if (tbd) {
6909             
6910             tbd.setSize(ctr.getWidth(),
6911                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6912             );
6913             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6914             cw -= barsize;
6915         }
6916         cw = Math.max(cw, this.totalWidth);
6917         this.getGridEl().select('tr',true).setWidth(cw);
6918         // resize 'expandable coloumn?
6919         
6920         return; // we doe not have a view in this design..
6921         
6922     },
6923     onBodyScroll: function()
6924     {
6925         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6926         if(this.mainHead){
6927             this.mainHead.setStyle({
6928                 'position' : 'relative',
6929                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6930             });
6931         }
6932         
6933         if(this.lazyLoad){
6934             
6935             var scrollHeight = this.mainBody.dom.scrollHeight;
6936             
6937             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6938             
6939             var height = this.mainBody.getHeight();
6940             
6941             if(scrollHeight - height == scrollTop) {
6942                 
6943                 var total = this.ds.getTotalCount();
6944                 
6945                 if(this.footer.cursor + this.footer.pageSize < total){
6946                     
6947                     this.footer.ds.load({
6948                         params : {
6949                             start : this.footer.cursor + this.footer.pageSize,
6950                             limit : this.footer.pageSize
6951                         },
6952                         add : true
6953                     });
6954                 }
6955             }
6956             
6957         }
6958     },
6959     
6960     onHeaderChange : function()
6961     {
6962         
6963         var header = this.renderHeader();
6964         var table = this.el.select('table', true).first();
6965         
6966         this.mainHead.remove();
6967         this.mainHead = table.createChild(header, this.mainBody, false);
6968     },
6969     
6970     onHiddenChange : function(colModel, colIndex, hidden)
6971     {
6972         var thSelector = 'roo-bootstrap-thead-col-' + colIndex;
6973         var tdSelector = 'roo-bootstrap-tbody-col-' + colIndex;
6974         
6975         this.CSS.updateRule(thSelector, "display", "");
6976         this.CSS.updateRule(tdSelector, "display", "");
6977         
6978         if(hidden){
6979             this.CSS.updateRule(thSelector, "display", "none");
6980             this.CSS.updateRule(tdSelector, "display", "none");
6981         }
6982         
6983     }
6984     
6985 });
6986
6987  
6988
6989  /*
6990  * - LGPL
6991  *
6992  * table cell
6993  * 
6994  */
6995
6996 /**
6997  * @class Roo.bootstrap.TableCell
6998  * @extends Roo.bootstrap.Component
6999  * Bootstrap TableCell class
7000  * @cfg {String} html cell contain text
7001  * @cfg {String} cls cell class
7002  * @cfg {String} tag cell tag (td|th) default td
7003  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7004  * @cfg {String} align Aligns the content in a cell
7005  * @cfg {String} axis Categorizes cells
7006  * @cfg {String} bgcolor Specifies the background color of a cell
7007  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7008  * @cfg {Number} colspan Specifies the number of columns a cell should span
7009  * @cfg {String} headers Specifies one or more header cells a cell is related to
7010  * @cfg {Number} height Sets the height of a cell
7011  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7012  * @cfg {Number} rowspan Sets the number of rows a cell should span
7013  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7014  * @cfg {String} valign Vertical aligns the content in a cell
7015  * @cfg {Number} width Specifies the width of a cell
7016  * 
7017  * @constructor
7018  * Create a new TableCell
7019  * @param {Object} config The config object
7020  */
7021
7022 Roo.bootstrap.TableCell = function(config){
7023     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7024 };
7025
7026 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7027     
7028     html: false,
7029     cls: false,
7030     tag: false,
7031     abbr: false,
7032     align: false,
7033     axis: false,
7034     bgcolor: false,
7035     charoff: false,
7036     colspan: false,
7037     headers: false,
7038     height: false,
7039     nowrap: false,
7040     rowspan: false,
7041     scope: false,
7042     valign: false,
7043     width: false,
7044     
7045     
7046     getAutoCreate : function(){
7047         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7048         
7049         cfg = {
7050             tag: 'td'
7051         };
7052         
7053         if(this.tag){
7054             cfg.tag = this.tag;
7055         }
7056         
7057         if (this.html) {
7058             cfg.html=this.html
7059         }
7060         if (this.cls) {
7061             cfg.cls=this.cls
7062         }
7063         if (this.abbr) {
7064             cfg.abbr=this.abbr
7065         }
7066         if (this.align) {
7067             cfg.align=this.align
7068         }
7069         if (this.axis) {
7070             cfg.axis=this.axis
7071         }
7072         if (this.bgcolor) {
7073             cfg.bgcolor=this.bgcolor
7074         }
7075         if (this.charoff) {
7076             cfg.charoff=this.charoff
7077         }
7078         if (this.colspan) {
7079             cfg.colspan=this.colspan
7080         }
7081         if (this.headers) {
7082             cfg.headers=this.headers
7083         }
7084         if (this.height) {
7085             cfg.height=this.height
7086         }
7087         if (this.nowrap) {
7088             cfg.nowrap=this.nowrap
7089         }
7090         if (this.rowspan) {
7091             cfg.rowspan=this.rowspan
7092         }
7093         if (this.scope) {
7094             cfg.scope=this.scope
7095         }
7096         if (this.valign) {
7097             cfg.valign=this.valign
7098         }
7099         if (this.width) {
7100             cfg.width=this.width
7101         }
7102         
7103         
7104         return cfg;
7105     }
7106    
7107 });
7108
7109  
7110
7111  /*
7112  * - LGPL
7113  *
7114  * table row
7115  * 
7116  */
7117
7118 /**
7119  * @class Roo.bootstrap.TableRow
7120  * @extends Roo.bootstrap.Component
7121  * Bootstrap TableRow class
7122  * @cfg {String} cls row class
7123  * @cfg {String} align Aligns the content in a table row
7124  * @cfg {String} bgcolor Specifies a background color for a table row
7125  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7126  * @cfg {String} valign Vertical aligns the content in a table row
7127  * 
7128  * @constructor
7129  * Create a new TableRow
7130  * @param {Object} config The config object
7131  */
7132
7133 Roo.bootstrap.TableRow = function(config){
7134     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7135 };
7136
7137 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7138     
7139     cls: false,
7140     align: false,
7141     bgcolor: false,
7142     charoff: false,
7143     valign: false,
7144     
7145     getAutoCreate : function(){
7146         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7147         
7148         cfg = {
7149             tag: 'tr'
7150         };
7151             
7152         if(this.cls){
7153             cfg.cls = this.cls;
7154         }
7155         if(this.align){
7156             cfg.align = this.align;
7157         }
7158         if(this.bgcolor){
7159             cfg.bgcolor = this.bgcolor;
7160         }
7161         if(this.charoff){
7162             cfg.charoff = this.charoff;
7163         }
7164         if(this.valign){
7165             cfg.valign = this.valign;
7166         }
7167         
7168         return cfg;
7169     }
7170    
7171 });
7172
7173  
7174
7175  /*
7176  * - LGPL
7177  *
7178  * table body
7179  * 
7180  */
7181
7182 /**
7183  * @class Roo.bootstrap.TableBody
7184  * @extends Roo.bootstrap.Component
7185  * Bootstrap TableBody class
7186  * @cfg {String} cls element class
7187  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7188  * @cfg {String} align Aligns the content inside the element
7189  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7190  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7191  * 
7192  * @constructor
7193  * Create a new TableBody
7194  * @param {Object} config The config object
7195  */
7196
7197 Roo.bootstrap.TableBody = function(config){
7198     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7199 };
7200
7201 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7202     
7203     cls: false,
7204     tag: false,
7205     align: false,
7206     charoff: false,
7207     valign: false,
7208     
7209     getAutoCreate : function(){
7210         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7211         
7212         cfg = {
7213             tag: 'tbody'
7214         };
7215             
7216         if (this.cls) {
7217             cfg.cls=this.cls
7218         }
7219         if(this.tag){
7220             cfg.tag = this.tag;
7221         }
7222         
7223         if(this.align){
7224             cfg.align = this.align;
7225         }
7226         if(this.charoff){
7227             cfg.charoff = this.charoff;
7228         }
7229         if(this.valign){
7230             cfg.valign = this.valign;
7231         }
7232         
7233         return cfg;
7234     }
7235     
7236     
7237 //    initEvents : function()
7238 //    {
7239 //        
7240 //        if(!this.store){
7241 //            return;
7242 //        }
7243 //        
7244 //        this.store = Roo.factory(this.store, Roo.data);
7245 //        this.store.on('load', this.onLoad, this);
7246 //        
7247 //        this.store.load();
7248 //        
7249 //    },
7250 //    
7251 //    onLoad: function () 
7252 //    {   
7253 //        this.fireEvent('load', this);
7254 //    }
7255 //    
7256 //   
7257 });
7258
7259  
7260
7261  /*
7262  * Based on:
7263  * Ext JS Library 1.1.1
7264  * Copyright(c) 2006-2007, Ext JS, LLC.
7265  *
7266  * Originally Released Under LGPL - original licence link has changed is not relivant.
7267  *
7268  * Fork - LGPL
7269  * <script type="text/javascript">
7270  */
7271
7272 // as we use this in bootstrap.
7273 Roo.namespace('Roo.form');
7274  /**
7275  * @class Roo.form.Action
7276  * Internal Class used to handle form actions
7277  * @constructor
7278  * @param {Roo.form.BasicForm} el The form element or its id
7279  * @param {Object} config Configuration options
7280  */
7281
7282  
7283  
7284 // define the action interface
7285 Roo.form.Action = function(form, options){
7286     this.form = form;
7287     this.options = options || {};
7288 };
7289 /**
7290  * Client Validation Failed
7291  * @const 
7292  */
7293 Roo.form.Action.CLIENT_INVALID = 'client';
7294 /**
7295  * Server Validation Failed
7296  * @const 
7297  */
7298 Roo.form.Action.SERVER_INVALID = 'server';
7299  /**
7300  * Connect to Server Failed
7301  * @const 
7302  */
7303 Roo.form.Action.CONNECT_FAILURE = 'connect';
7304 /**
7305  * Reading Data from Server Failed
7306  * @const 
7307  */
7308 Roo.form.Action.LOAD_FAILURE = 'load';
7309
7310 Roo.form.Action.prototype = {
7311     type : 'default',
7312     failureType : undefined,
7313     response : undefined,
7314     result : undefined,
7315
7316     // interface method
7317     run : function(options){
7318
7319     },
7320
7321     // interface method
7322     success : function(response){
7323
7324     },
7325
7326     // interface method
7327     handleResponse : function(response){
7328
7329     },
7330
7331     // default connection failure
7332     failure : function(response){
7333         
7334         this.response = response;
7335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336         this.form.afterAction(this, false);
7337     },
7338
7339     processResponse : function(response){
7340         this.response = response;
7341         if(!response.responseText){
7342             return true;
7343         }
7344         this.result = this.handleResponse(response);
7345         return this.result;
7346     },
7347
7348     // utility functions used internally
7349     getUrl : function(appendParams){
7350         var url = this.options.url || this.form.url || this.form.el.dom.action;
7351         if(appendParams){
7352             var p = this.getParams();
7353             if(p){
7354                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7355             }
7356         }
7357         return url;
7358     },
7359
7360     getMethod : function(){
7361         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7362     },
7363
7364     getParams : function(){
7365         var bp = this.form.baseParams;
7366         var p = this.options.params;
7367         if(p){
7368             if(typeof p == "object"){
7369                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7370             }else if(typeof p == 'string' && bp){
7371                 p += '&' + Roo.urlEncode(bp);
7372             }
7373         }else if(bp){
7374             p = Roo.urlEncode(bp);
7375         }
7376         return p;
7377     },
7378
7379     createCallback : function(){
7380         return {
7381             success: this.success,
7382             failure: this.failure,
7383             scope: this,
7384             timeout: (this.form.timeout*1000),
7385             upload: this.form.fileUpload ? this.success : undefined
7386         };
7387     }
7388 };
7389
7390 Roo.form.Action.Submit = function(form, options){
7391     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7392 };
7393
7394 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7395     type : 'submit',
7396
7397     haveProgress : false,
7398     uploadComplete : false,
7399     
7400     // uploadProgress indicator.
7401     uploadProgress : function()
7402     {
7403         if (!this.form.progressUrl) {
7404             return;
7405         }
7406         
7407         if (!this.haveProgress) {
7408             Roo.MessageBox.progress("Uploading", "Uploading");
7409         }
7410         if (this.uploadComplete) {
7411            Roo.MessageBox.hide();
7412            return;
7413         }
7414         
7415         this.haveProgress = true;
7416    
7417         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7418         
7419         var c = new Roo.data.Connection();
7420         c.request({
7421             url : this.form.progressUrl,
7422             params: {
7423                 id : uid
7424             },
7425             method: 'GET',
7426             success : function(req){
7427                //console.log(data);
7428                 var rdata = false;
7429                 var edata;
7430                 try  {
7431                    rdata = Roo.decode(req.responseText)
7432                 } catch (e) {
7433                     Roo.log("Invalid data from server..");
7434                     Roo.log(edata);
7435                     return;
7436                 }
7437                 if (!rdata || !rdata.success) {
7438                     Roo.log(rdata);
7439                     Roo.MessageBox.alert(Roo.encode(rdata));
7440                     return;
7441                 }
7442                 var data = rdata.data;
7443                 
7444                 if (this.uploadComplete) {
7445                    Roo.MessageBox.hide();
7446                    return;
7447                 }
7448                    
7449                 if (data){
7450                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7451                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7452                     );
7453                 }
7454                 this.uploadProgress.defer(2000,this);
7455             },
7456        
7457             failure: function(data) {
7458                 Roo.log('progress url failed ');
7459                 Roo.log(data);
7460             },
7461             scope : this
7462         });
7463            
7464     },
7465     
7466     
7467     run : function()
7468     {
7469         // run get Values on the form, so it syncs any secondary forms.
7470         this.form.getValues();
7471         
7472         var o = this.options;
7473         var method = this.getMethod();
7474         var isPost = method == 'POST';
7475         if(o.clientValidation === false || this.form.isValid()){
7476             
7477             if (this.form.progressUrl) {
7478                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7479                     (new Date() * 1) + '' + Math.random());
7480                     
7481             } 
7482             
7483             
7484             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7485                 form:this.form.el.dom,
7486                 url:this.getUrl(!isPost),
7487                 method: method,
7488                 params:isPost ? this.getParams() : null,
7489                 isUpload: this.form.fileUpload
7490             }));
7491             
7492             this.uploadProgress();
7493
7494         }else if (o.clientValidation !== false){ // client validation failed
7495             this.failureType = Roo.form.Action.CLIENT_INVALID;
7496             this.form.afterAction(this, false);
7497         }
7498     },
7499
7500     success : function(response)
7501     {
7502         this.uploadComplete= true;
7503         if (this.haveProgress) {
7504             Roo.MessageBox.hide();
7505         }
7506         
7507         
7508         var result = this.processResponse(response);
7509         if(result === true || result.success){
7510             this.form.afterAction(this, true);
7511             return;
7512         }
7513         if(result.errors){
7514             this.form.markInvalid(result.errors);
7515             this.failureType = Roo.form.Action.SERVER_INVALID;
7516         }
7517         this.form.afterAction(this, false);
7518     },
7519     failure : function(response)
7520     {
7521         this.uploadComplete= true;
7522         if (this.haveProgress) {
7523             Roo.MessageBox.hide();
7524         }
7525         
7526         this.response = response;
7527         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7528         this.form.afterAction(this, false);
7529     },
7530     
7531     handleResponse : function(response){
7532         if(this.form.errorReader){
7533             var rs = this.form.errorReader.read(response);
7534             var errors = [];
7535             if(rs.records){
7536                 for(var i = 0, len = rs.records.length; i < len; i++) {
7537                     var r = rs.records[i];
7538                     errors[i] = r.data;
7539                 }
7540             }
7541             if(errors.length < 1){
7542                 errors = null;
7543             }
7544             return {
7545                 success : rs.success,
7546                 errors : errors
7547             };
7548         }
7549         var ret = false;
7550         try {
7551             ret = Roo.decode(response.responseText);
7552         } catch (e) {
7553             ret = {
7554                 success: false,
7555                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7556                 errors : []
7557             };
7558         }
7559         return ret;
7560         
7561     }
7562 });
7563
7564
7565 Roo.form.Action.Load = function(form, options){
7566     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7567     this.reader = this.form.reader;
7568 };
7569
7570 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7571     type : 'load',
7572
7573     run : function(){
7574         
7575         Roo.Ajax.request(Roo.apply(
7576                 this.createCallback(), {
7577                     method:this.getMethod(),
7578                     url:this.getUrl(false),
7579                     params:this.getParams()
7580         }));
7581     },
7582
7583     success : function(response){
7584         
7585         var result = this.processResponse(response);
7586         if(result === true || !result.success || !result.data){
7587             this.failureType = Roo.form.Action.LOAD_FAILURE;
7588             this.form.afterAction(this, false);
7589             return;
7590         }
7591         this.form.clearInvalid();
7592         this.form.setValues(result.data);
7593         this.form.afterAction(this, true);
7594     },
7595
7596     handleResponse : function(response){
7597         if(this.form.reader){
7598             var rs = this.form.reader.read(response);
7599             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7600             return {
7601                 success : rs.success,
7602                 data : data
7603             };
7604         }
7605         return Roo.decode(response.responseText);
7606     }
7607 });
7608
7609 Roo.form.Action.ACTION_TYPES = {
7610     'load' : Roo.form.Action.Load,
7611     'submit' : Roo.form.Action.Submit
7612 };/*
7613  * - LGPL
7614  *
7615  * form
7616  *
7617  */
7618
7619 /**
7620  * @class Roo.bootstrap.Form
7621  * @extends Roo.bootstrap.Component
7622  * Bootstrap Form class
7623  * @cfg {String} method  GET | POST (default POST)
7624  * @cfg {String} labelAlign top | left (default top)
7625  * @cfg {String} align left  | right - for navbars
7626  * @cfg {Boolean} loadMask load mask when submit (default true)
7627
7628  *
7629  * @constructor
7630  * Create a new Form
7631  * @param {Object} config The config object
7632  */
7633
7634
7635 Roo.bootstrap.Form = function(config){
7636     
7637     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7638     
7639     Roo.bootstrap.Form.popover.apply();
7640     
7641     this.addEvents({
7642         /**
7643          * @event clientvalidation
7644          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7645          * @param {Form} this
7646          * @param {Boolean} valid true if the form has passed client-side validation
7647          */
7648         clientvalidation: true,
7649         /**
7650          * @event beforeaction
7651          * Fires before any action is performed. Return false to cancel the action.
7652          * @param {Form} this
7653          * @param {Action} action The action to be performed
7654          */
7655         beforeaction: true,
7656         /**
7657          * @event actionfailed
7658          * Fires when an action fails.
7659          * @param {Form} this
7660          * @param {Action} action The action that failed
7661          */
7662         actionfailed : true,
7663         /**
7664          * @event actioncomplete
7665          * Fires when an action is completed.
7666          * @param {Form} this
7667          * @param {Action} action The action that completed
7668          */
7669         actioncomplete : true
7670     });
7671 };
7672
7673 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7674
7675      /**
7676      * @cfg {String} method
7677      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7678      */
7679     method : 'POST',
7680     /**
7681      * @cfg {String} url
7682      * The URL to use for form actions if one isn't supplied in the action options.
7683      */
7684     /**
7685      * @cfg {Boolean} fileUpload
7686      * Set to true if this form is a file upload.
7687      */
7688
7689     /**
7690      * @cfg {Object} baseParams
7691      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7692      */
7693
7694     /**
7695      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7696      */
7697     timeout: 30,
7698     /**
7699      * @cfg {Sting} align (left|right) for navbar forms
7700      */
7701     align : 'left',
7702
7703     // private
7704     activeAction : null,
7705
7706     /**
7707      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7708      * element by passing it or its id or mask the form itself by passing in true.
7709      * @type Mixed
7710      */
7711     waitMsgTarget : false,
7712
7713     loadMask : true,
7714     
7715     /**
7716      * @cfg {Boolean} errorMask (true|false) default false
7717      */
7718     errorMask : false,
7719     
7720     /**
7721      * @cfg {Number} maskOffset Default 100
7722      */
7723     maskOffset : 100,
7724     
7725     /**
7726      * @cfg {Boolean} maskBody
7727      */
7728     maskBody : false,
7729
7730     getAutoCreate : function(){
7731
7732         var cfg = {
7733             tag: 'form',
7734             method : this.method || 'POST',
7735             id : this.id || Roo.id(),
7736             cls : ''
7737         };
7738         if (this.parent().xtype.match(/^Nav/)) {
7739             cfg.cls = 'navbar-form navbar-' + this.align;
7740
7741         }
7742
7743         if (this.labelAlign == 'left' ) {
7744             cfg.cls += ' form-horizontal';
7745         }
7746
7747
7748         return cfg;
7749     },
7750     initEvents : function()
7751     {
7752         this.el.on('submit', this.onSubmit, this);
7753         // this was added as random key presses on the form where triggering form submit.
7754         this.el.on('keypress', function(e) {
7755             if (e.getCharCode() != 13) {
7756                 return true;
7757             }
7758             // we might need to allow it for textareas.. and some other items.
7759             // check e.getTarget().
7760
7761             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7762                 return true;
7763             }
7764
7765             Roo.log("keypress blocked");
7766
7767             e.preventDefault();
7768             return false;
7769         });
7770         
7771     },
7772     // private
7773     onSubmit : function(e){
7774         e.stopEvent();
7775     },
7776
7777      /**
7778      * Returns true if client-side validation on the form is successful.
7779      * @return Boolean
7780      */
7781     isValid : function(){
7782         var items = this.getItems();
7783         var valid = true;
7784         var target = false;
7785         
7786         items.each(function(f){
7787             if(f.validate()){
7788                 return;
7789             }
7790             valid = false;
7791
7792             if(!target && f.el.isVisible(true)){
7793                 target = f;
7794             }
7795            
7796         });
7797         
7798         if(this.errorMask && !valid){
7799             Roo.bootstrap.Form.popover.mask(this, target);
7800         }
7801         
7802         return valid;
7803     },
7804     
7805     /**
7806      * Returns true if any fields in this form have changed since their original load.
7807      * @return Boolean
7808      */
7809     isDirty : function(){
7810         var dirty = false;
7811         var items = this.getItems();
7812         items.each(function(f){
7813            if(f.isDirty()){
7814                dirty = true;
7815                return false;
7816            }
7817            return true;
7818         });
7819         return dirty;
7820     },
7821      /**
7822      * Performs a predefined action (submit or load) or custom actions you define on this form.
7823      * @param {String} actionName The name of the action type
7824      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7825      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7826      * accept other config options):
7827      * <pre>
7828 Property          Type             Description
7829 ----------------  ---------------  ----------------------------------------------------------------------------------
7830 url               String           The url for the action (defaults to the form's url)
7831 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7832 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7833 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7834                                    validate the form on the client (defaults to false)
7835      * </pre>
7836      * @return {BasicForm} this
7837      */
7838     doAction : function(action, options){
7839         if(typeof action == 'string'){
7840             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7841         }
7842         if(this.fireEvent('beforeaction', this, action) !== false){
7843             this.beforeAction(action);
7844             action.run.defer(100, action);
7845         }
7846         return this;
7847     },
7848
7849     // private
7850     beforeAction : function(action){
7851         var o = action.options;
7852         
7853         if(this.loadMask){
7854             
7855             if(this.maskBody){
7856                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7857             } else {
7858                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7859             }
7860         }
7861         // not really supported yet.. ??
7862
7863         //if(this.waitMsgTarget === true){
7864         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7865         //}else if(this.waitMsgTarget){
7866         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7867         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7868         //}else {
7869         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7870        // }
7871
7872     },
7873
7874     // private
7875     afterAction : function(action, success){
7876         this.activeAction = null;
7877         var o = action.options;
7878
7879         if(this.loadMask){
7880             
7881             if(this.maskBody){
7882                 Roo.get(document.body).unmask();
7883             } else {
7884                 this.el.unmask();
7885             }
7886         }
7887         
7888         //if(this.waitMsgTarget === true){
7889 //            this.el.unmask();
7890         //}else if(this.waitMsgTarget){
7891         //    this.waitMsgTarget.unmask();
7892         //}else{
7893         //    Roo.MessageBox.updateProgress(1);
7894         //    Roo.MessageBox.hide();
7895        // }
7896         //
7897         if(success){
7898             if(o.reset){
7899                 this.reset();
7900             }
7901             Roo.callback(o.success, o.scope, [this, action]);
7902             this.fireEvent('actioncomplete', this, action);
7903
7904         }else{
7905
7906             // failure condition..
7907             // we have a scenario where updates need confirming.
7908             // eg. if a locking scenario exists..
7909             // we look for { errors : { needs_confirm : true }} in the response.
7910             if (
7911                 (typeof(action.result) != 'undefined')  &&
7912                 (typeof(action.result.errors) != 'undefined')  &&
7913                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7914            ){
7915                 var _t = this;
7916                 Roo.log("not supported yet");
7917                  /*
7918
7919                 Roo.MessageBox.confirm(
7920                     "Change requires confirmation",
7921                     action.result.errorMsg,
7922                     function(r) {
7923                         if (r != 'yes') {
7924                             return;
7925                         }
7926                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7927                     }
7928
7929                 );
7930                 */
7931
7932
7933                 return;
7934             }
7935
7936             Roo.callback(o.failure, o.scope, [this, action]);
7937             // show an error message if no failed handler is set..
7938             if (!this.hasListener('actionfailed')) {
7939                 Roo.log("need to add dialog support");
7940                 /*
7941                 Roo.MessageBox.alert("Error",
7942                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7943                         action.result.errorMsg :
7944                         "Saving Failed, please check your entries or try again"
7945                 );
7946                 */
7947             }
7948
7949             this.fireEvent('actionfailed', this, action);
7950         }
7951
7952     },
7953     /**
7954      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7955      * @param {String} id The value to search for
7956      * @return Field
7957      */
7958     findField : function(id){
7959         var items = this.getItems();
7960         var field = items.get(id);
7961         if(!field){
7962              items.each(function(f){
7963                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7964                     field = f;
7965                     return false;
7966                 }
7967                 return true;
7968             });
7969         }
7970         return field || null;
7971     },
7972      /**
7973      * Mark fields in this form invalid in bulk.
7974      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7975      * @return {BasicForm} this
7976      */
7977     markInvalid : function(errors){
7978         if(errors instanceof Array){
7979             for(var i = 0, len = errors.length; i < len; i++){
7980                 var fieldError = errors[i];
7981                 var f = this.findField(fieldError.id);
7982                 if(f){
7983                     f.markInvalid(fieldError.msg);
7984                 }
7985             }
7986         }else{
7987             var field, id;
7988             for(id in errors){
7989                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7990                     field.markInvalid(errors[id]);
7991                 }
7992             }
7993         }
7994         //Roo.each(this.childForms || [], function (f) {
7995         //    f.markInvalid(errors);
7996         //});
7997
7998         return this;
7999     },
8000
8001     /**
8002      * Set values for fields in this form in bulk.
8003      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8004      * @return {BasicForm} this
8005      */
8006     setValues : function(values){
8007         if(values instanceof Array){ // array of objects
8008             for(var i = 0, len = values.length; i < len; i++){
8009                 var v = values[i];
8010                 var f = this.findField(v.id);
8011                 if(f){
8012                     f.setValue(v.value);
8013                     if(this.trackResetOnLoad){
8014                         f.originalValue = f.getValue();
8015                     }
8016                 }
8017             }
8018         }else{ // object hash
8019             var field, id;
8020             for(id in values){
8021                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8022
8023                     if (field.setFromData &&
8024                         field.valueField &&
8025                         field.displayField &&
8026                         // combos' with local stores can
8027                         // be queried via setValue()
8028                         // to set their value..
8029                         (field.store && !field.store.isLocal)
8030                         ) {
8031                         // it's a combo
8032                         var sd = { };
8033                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8034                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8035                         field.setFromData(sd);
8036
8037                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8038                         
8039                         field.setFromData(values);
8040                         
8041                     } else {
8042                         field.setValue(values[id]);
8043                     }
8044
8045
8046                     if(this.trackResetOnLoad){
8047                         field.originalValue = field.getValue();
8048                     }
8049                 }
8050             }
8051         }
8052
8053         //Roo.each(this.childForms || [], function (f) {
8054         //    f.setValues(values);
8055         //});
8056
8057         return this;
8058     },
8059
8060     /**
8061      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8062      * they are returned as an array.
8063      * @param {Boolean} asString
8064      * @return {Object}
8065      */
8066     getValues : function(asString){
8067         //if (this.childForms) {
8068             // copy values from the child forms
8069         //    Roo.each(this.childForms, function (f) {
8070         //        this.setValues(f.getValues());
8071         //    }, this);
8072         //}
8073
8074
8075
8076         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8077         if(asString === true){
8078             return fs;
8079         }
8080         return Roo.urlDecode(fs);
8081     },
8082
8083     /**
8084      * Returns the fields in this form as an object with key/value pairs.
8085      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8086      * @return {Object}
8087      */
8088     getFieldValues : function(with_hidden)
8089     {
8090         var items = this.getItems();
8091         var ret = {};
8092         items.each(function(f){
8093             
8094             if (!f.getName()) {
8095                 return;
8096             }
8097             
8098             var v = f.getValue();
8099             
8100             if (f.inputType =='radio') {
8101                 if (typeof(ret[f.getName()]) == 'undefined') {
8102                     ret[f.getName()] = ''; // empty..
8103                 }
8104
8105                 if (!f.el.dom.checked) {
8106                     return;
8107
8108                 }
8109                 v = f.el.dom.value;
8110
8111             }
8112             
8113             if(f.xtype == 'MoneyField'){
8114                 ret[f.currencyName] = f.getCurrency();
8115             }
8116
8117             // not sure if this supported any more..
8118             if ((typeof(v) == 'object') && f.getRawValue) {
8119                 v = f.getRawValue() ; // dates..
8120             }
8121             // combo boxes where name != hiddenName...
8122             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8123                 ret[f.name] = f.getRawValue();
8124             }
8125             ret[f.getName()] = v;
8126         });
8127
8128         return ret;
8129     },
8130
8131     /**
8132      * Clears all invalid messages in this form.
8133      * @return {BasicForm} this
8134      */
8135     clearInvalid : function(){
8136         var items = this.getItems();
8137
8138         items.each(function(f){
8139            f.clearInvalid();
8140         });
8141
8142         return this;
8143     },
8144
8145     /**
8146      * Resets this form.
8147      * @return {BasicForm} this
8148      */
8149     reset : function(){
8150         var items = this.getItems();
8151         items.each(function(f){
8152             f.reset();
8153         });
8154
8155         Roo.each(this.childForms || [], function (f) {
8156             f.reset();
8157         });
8158
8159
8160         return this;
8161     },
8162     
8163     getItems : function()
8164     {
8165         var r=new Roo.util.MixedCollection(false, function(o){
8166             return o.id || (o.id = Roo.id());
8167         });
8168         var iter = function(el) {
8169             if (el.inputEl) {
8170                 r.add(el);
8171             }
8172             if (!el.items) {
8173                 return;
8174             }
8175             Roo.each(el.items,function(e) {
8176                 iter(e);
8177             });
8178         };
8179
8180         iter(this);
8181         return r;
8182     },
8183     
8184     hideFields : function(items)
8185     {
8186         Roo.each(items, function(i){
8187             
8188             var f = this.findField(i);
8189             
8190             if(!f){
8191                 return;
8192             }
8193             
8194             if(f.xtype == 'DateField'){
8195                 f.setVisible(false);
8196                 return;
8197             }
8198             
8199             f.hide();
8200             
8201         }, this);
8202     },
8203     
8204     showFields : function(items)
8205     {
8206         Roo.each(items, function(i){
8207             
8208             var f = this.findField(i);
8209             
8210             if(!f){
8211                 return;
8212             }
8213             
8214             if(f.xtype == 'DateField'){
8215                 f.setVisible(true);
8216                 return;
8217             }
8218             
8219             f.show();
8220             
8221         }, this);
8222     }
8223
8224 });
8225
8226 Roo.apply(Roo.bootstrap.Form, {
8227     
8228     popover : {
8229         
8230         padding : 5,
8231         
8232         isApplied : false,
8233         
8234         isMasked : false,
8235         
8236         form : false,
8237         
8238         target : false,
8239         
8240         toolTip : false,
8241         
8242         intervalID : false,
8243         
8244         maskEl : false,
8245         
8246         apply : function()
8247         {
8248             if(this.isApplied){
8249                 return;
8250             }
8251             
8252             this.maskEl = {
8253                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8254                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8255                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8256                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8257             };
8258             
8259             this.maskEl.top.enableDisplayMode("block");
8260             this.maskEl.left.enableDisplayMode("block");
8261             this.maskEl.bottom.enableDisplayMode("block");
8262             this.maskEl.right.enableDisplayMode("block");
8263             
8264             this.toolTip = new Roo.bootstrap.Tooltip({
8265                 cls : 'roo-form-error-popover',
8266                 alignment : {
8267                     'left' : ['r-l', [-2,0], 'right'],
8268                     'right' : ['l-r', [2,0], 'left'],
8269                     'bottom' : ['tl-bl', [0,2], 'top'],
8270                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8271                 }
8272             });
8273             
8274             this.toolTip.render(Roo.get(document.body));
8275
8276             this.toolTip.el.enableDisplayMode("block");
8277             
8278             Roo.get(document.body).on('click', function(){
8279                 this.unmask();
8280             }, this);
8281             
8282             Roo.get(document.body).on('touchstart', function(){
8283                 this.unmask();
8284             }, this);
8285             
8286             this.isApplied = true
8287         },
8288         
8289         mask : function(form, target)
8290         {
8291             this.form = form;
8292             
8293             this.target = target;
8294             
8295             if(!this.form.errorMask || !target.el){
8296                 return;
8297             }
8298             
8299             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8300             
8301             Roo.log(scrollable);
8302             
8303             var ot = this.target.el.calcOffsetsTo(scrollable);
8304             
8305             var scrollTo = ot[1] - this.form.maskOffset;
8306             
8307             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8308             
8309             scrollable.scrollTo('top', scrollTo);
8310             
8311             var box = this.target.el.getBox();
8312             Roo.log(box);
8313             var zIndex = Roo.bootstrap.Modal.zIndex++;
8314
8315             
8316             this.maskEl.top.setStyle('position', 'absolute');
8317             this.maskEl.top.setStyle('z-index', zIndex);
8318             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8319             this.maskEl.top.setLeft(0);
8320             this.maskEl.top.setTop(0);
8321             this.maskEl.top.show();
8322             
8323             this.maskEl.left.setStyle('position', 'absolute');
8324             this.maskEl.left.setStyle('z-index', zIndex);
8325             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8326             this.maskEl.left.setLeft(0);
8327             this.maskEl.left.setTop(box.y - this.padding);
8328             this.maskEl.left.show();
8329
8330             this.maskEl.bottom.setStyle('position', 'absolute');
8331             this.maskEl.bottom.setStyle('z-index', zIndex);
8332             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8333             this.maskEl.bottom.setLeft(0);
8334             this.maskEl.bottom.setTop(box.bottom + this.padding);
8335             this.maskEl.bottom.show();
8336
8337             this.maskEl.right.setStyle('position', 'absolute');
8338             this.maskEl.right.setStyle('z-index', zIndex);
8339             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8340             this.maskEl.right.setLeft(box.right + this.padding);
8341             this.maskEl.right.setTop(box.y - this.padding);
8342             this.maskEl.right.show();
8343
8344             this.toolTip.bindEl = this.target.el;
8345
8346             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8347
8348             var tip = this.target.blankText;
8349
8350             if(this.target.getValue() !== '' ) {
8351                 
8352                 if (this.target.invalidText.length) {
8353                     tip = this.target.invalidText;
8354                 } else if (this.target.regexText.length){
8355                     tip = this.target.regexText;
8356                 }
8357             }
8358
8359             this.toolTip.show(tip);
8360
8361             this.intervalID = window.setInterval(function() {
8362                 Roo.bootstrap.Form.popover.unmask();
8363             }, 10000);
8364
8365             window.onwheel = function(){ return false;};
8366             
8367             (function(){ this.isMasked = true; }).defer(500, this);
8368             
8369         },
8370         
8371         unmask : function()
8372         {
8373             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8374                 return;
8375             }
8376             
8377             this.maskEl.top.setStyle('position', 'absolute');
8378             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8379             this.maskEl.top.hide();
8380
8381             this.maskEl.left.setStyle('position', 'absolute');
8382             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8383             this.maskEl.left.hide();
8384
8385             this.maskEl.bottom.setStyle('position', 'absolute');
8386             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8387             this.maskEl.bottom.hide();
8388
8389             this.maskEl.right.setStyle('position', 'absolute');
8390             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8391             this.maskEl.right.hide();
8392             
8393             this.toolTip.hide();
8394             
8395             this.toolTip.el.hide();
8396             
8397             window.onwheel = function(){ return true;};
8398             
8399             if(this.intervalID){
8400                 window.clearInterval(this.intervalID);
8401                 this.intervalID = false;
8402             }
8403             
8404             this.isMasked = false;
8405             
8406         }
8407         
8408     }
8409     
8410 });
8411
8412 /*
8413  * Based on:
8414  * Ext JS Library 1.1.1
8415  * Copyright(c) 2006-2007, Ext JS, LLC.
8416  *
8417  * Originally Released Under LGPL - original licence link has changed is not relivant.
8418  *
8419  * Fork - LGPL
8420  * <script type="text/javascript">
8421  */
8422 /**
8423  * @class Roo.form.VTypes
8424  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8425  * @singleton
8426  */
8427 Roo.form.VTypes = function(){
8428     // closure these in so they are only created once.
8429     var alpha = /^[a-zA-Z_]+$/;
8430     var alphanum = /^[a-zA-Z0-9_]+$/;
8431     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8432     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8433
8434     // All these messages and functions are configurable
8435     return {
8436         /**
8437          * The function used to validate email addresses
8438          * @param {String} value The email address
8439          */
8440         'email' : function(v){
8441             return email.test(v);
8442         },
8443         /**
8444          * The error text to display when the email validation function returns false
8445          * @type String
8446          */
8447         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8448         /**
8449          * The keystroke filter mask to be applied on email input
8450          * @type RegExp
8451          */
8452         'emailMask' : /[a-z0-9_\.\-@]/i,
8453
8454         /**
8455          * The function used to validate URLs
8456          * @param {String} value The URL
8457          */
8458         'url' : function(v){
8459             return url.test(v);
8460         },
8461         /**
8462          * The error text to display when the url validation function returns false
8463          * @type String
8464          */
8465         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8466         
8467         /**
8468          * The function used to validate alpha values
8469          * @param {String} value The value
8470          */
8471         'alpha' : function(v){
8472             return alpha.test(v);
8473         },
8474         /**
8475          * The error text to display when the alpha validation function returns false
8476          * @type String
8477          */
8478         'alphaText' : 'This field should only contain letters and _',
8479         /**
8480          * The keystroke filter mask to be applied on alpha input
8481          * @type RegExp
8482          */
8483         'alphaMask' : /[a-z_]/i,
8484
8485         /**
8486          * The function used to validate alphanumeric values
8487          * @param {String} value The value
8488          */
8489         'alphanum' : function(v){
8490             return alphanum.test(v);
8491         },
8492         /**
8493          * The error text to display when the alphanumeric validation function returns false
8494          * @type String
8495          */
8496         'alphanumText' : 'This field should only contain letters, numbers and _',
8497         /**
8498          * The keystroke filter mask to be applied on alphanumeric input
8499          * @type RegExp
8500          */
8501         'alphanumMask' : /[a-z0-9_]/i
8502     };
8503 }();/*
8504  * - LGPL
8505  *
8506  * Input
8507  * 
8508  */
8509
8510 /**
8511  * @class Roo.bootstrap.Input
8512  * @extends Roo.bootstrap.Component
8513  * Bootstrap Input class
8514  * @cfg {Boolean} disabled is it disabled
8515  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8516  * @cfg {String} name name of the input
8517  * @cfg {string} fieldLabel - the label associated
8518  * @cfg {string} placeholder - placeholder to put in text.
8519  * @cfg {string}  before - input group add on before
8520  * @cfg {string} after - input group add on after
8521  * @cfg {string} size - (lg|sm) or leave empty..
8522  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8523  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8524  * @cfg {Number} md colspan out of 12 for computer-sized screens
8525  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8526  * @cfg {string} value default value of the input
8527  * @cfg {Number} labelWidth set the width of label 
8528  * @cfg {Number} labellg set the width of label (1-12)
8529  * @cfg {Number} labelmd set the width of label (1-12)
8530  * @cfg {Number} labelsm set the width of label (1-12)
8531  * @cfg {Number} labelxs set the width of label (1-12)
8532  * @cfg {String} labelAlign (top|left)
8533  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8534  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8535  * @cfg {String} indicatorpos (left|right) default left
8536
8537  * @cfg {String} align (left|center|right) Default left
8538  * @cfg {Boolean} forceFeedback (true|false) Default false
8539  * 
8540  * @constructor
8541  * Create a new Input
8542  * @param {Object} config The config object
8543  */
8544
8545 Roo.bootstrap.Input = function(config){
8546     
8547     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8548     
8549     this.addEvents({
8550         /**
8551          * @event focus
8552          * Fires when this field receives input focus.
8553          * @param {Roo.form.Field} this
8554          */
8555         focus : true,
8556         /**
8557          * @event blur
8558          * Fires when this field loses input focus.
8559          * @param {Roo.form.Field} this
8560          */
8561         blur : true,
8562         /**
8563          * @event specialkey
8564          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8565          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8566          * @param {Roo.form.Field} this
8567          * @param {Roo.EventObject} e The event object
8568          */
8569         specialkey : true,
8570         /**
8571          * @event change
8572          * Fires just before the field blurs if the field value has changed.
8573          * @param {Roo.form.Field} this
8574          * @param {Mixed} newValue The new value
8575          * @param {Mixed} oldValue The original value
8576          */
8577         change : true,
8578         /**
8579          * @event invalid
8580          * Fires after the field has been marked as invalid.
8581          * @param {Roo.form.Field} this
8582          * @param {String} msg The validation message
8583          */
8584         invalid : true,
8585         /**
8586          * @event valid
8587          * Fires after the field has been validated with no errors.
8588          * @param {Roo.form.Field} this
8589          */
8590         valid : true,
8591          /**
8592          * @event keyup
8593          * Fires after the key up
8594          * @param {Roo.form.Field} this
8595          * @param {Roo.EventObject}  e The event Object
8596          */
8597         keyup : true
8598     });
8599 };
8600
8601 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8602      /**
8603      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8604       automatic validation (defaults to "keyup").
8605      */
8606     validationEvent : "keyup",
8607      /**
8608      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8609      */
8610     validateOnBlur : true,
8611     /**
8612      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8613      */
8614     validationDelay : 250,
8615      /**
8616      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8617      */
8618     focusClass : "x-form-focus",  // not needed???
8619     
8620        
8621     /**
8622      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8623      */
8624     invalidClass : "has-warning",
8625     
8626     /**
8627      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8628      */
8629     validClass : "has-success",
8630     
8631     /**
8632      * @cfg {Boolean} hasFeedback (true|false) default true
8633      */
8634     hasFeedback : true,
8635     
8636     /**
8637      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8638      */
8639     invalidFeedbackClass : "glyphicon-warning-sign",
8640     
8641     /**
8642      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8643      */
8644     validFeedbackClass : "glyphicon-ok",
8645     
8646     /**
8647      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8648      */
8649     selectOnFocus : false,
8650     
8651      /**
8652      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8653      */
8654     maskRe : null,
8655        /**
8656      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8657      */
8658     vtype : null,
8659     
8660       /**
8661      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8662      */
8663     disableKeyFilter : false,
8664     
8665        /**
8666      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8667      */
8668     disabled : false,
8669      /**
8670      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8671      */
8672     allowBlank : true,
8673     /**
8674      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8675      */
8676     blankText : "Please complete this mandatory field",
8677     
8678      /**
8679      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8680      */
8681     minLength : 0,
8682     /**
8683      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8684      */
8685     maxLength : Number.MAX_VALUE,
8686     /**
8687      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8688      */
8689     minLengthText : "The minimum length for this field is {0}",
8690     /**
8691      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8692      */
8693     maxLengthText : "The maximum length for this field is {0}",
8694   
8695     
8696     /**
8697      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8698      * If available, this function will be called only after the basic validators all return true, and will be passed the
8699      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8700      */
8701     validator : null,
8702     /**
8703      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8704      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8705      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8706      */
8707     regex : null,
8708     /**
8709      * @cfg {String} regexText -- Depricated - use Invalid Text
8710      */
8711     regexText : "",
8712     
8713     /**
8714      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8715      */
8716     invalidText : "",
8717     
8718     
8719     
8720     autocomplete: false,
8721     
8722     
8723     fieldLabel : '',
8724     inputType : 'text',
8725     
8726     name : false,
8727     placeholder: false,
8728     before : false,
8729     after : false,
8730     size : false,
8731     hasFocus : false,
8732     preventMark: false,
8733     isFormField : true,
8734     value : '',
8735     labelWidth : 2,
8736     labelAlign : false,
8737     readOnly : false,
8738     align : false,
8739     formatedValue : false,
8740     forceFeedback : false,
8741     
8742     indicatorpos : 'left',
8743     
8744     labellg : 0,
8745     labelmd : 0,
8746     labelsm : 0,
8747     labelxs : 0,
8748     
8749     parentLabelAlign : function()
8750     {
8751         var parent = this;
8752         while (parent.parent()) {
8753             parent = parent.parent();
8754             if (typeof(parent.labelAlign) !='undefined') {
8755                 return parent.labelAlign;
8756             }
8757         }
8758         return 'left';
8759         
8760     },
8761     
8762     getAutoCreate : function()
8763     {
8764         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8765         
8766         var id = Roo.id();
8767         
8768         var cfg = {};
8769         
8770         if(this.inputType != 'hidden'){
8771             cfg.cls = 'form-group' //input-group
8772         }
8773         
8774         var input =  {
8775             tag: 'input',
8776             id : id,
8777             type : this.inputType,
8778             value : this.value,
8779             cls : 'form-control',
8780             placeholder : this.placeholder || '',
8781             autocomplete : this.autocomplete || 'new-password'
8782         };
8783         
8784         if(this.align){
8785             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8786         }
8787         
8788         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8789             input.maxLength = this.maxLength;
8790         }
8791         
8792         if (this.disabled) {
8793             input.disabled=true;
8794         }
8795         
8796         if (this.readOnly) {
8797             input.readonly=true;
8798         }
8799         
8800         if (this.name) {
8801             input.name = this.name;
8802         }
8803         
8804         if (this.size) {
8805             input.cls += ' input-' + this.size;
8806         }
8807         
8808         var settings=this;
8809         ['xs','sm','md','lg'].map(function(size){
8810             if (settings[size]) {
8811                 cfg.cls += ' col-' + size + '-' + settings[size];
8812             }
8813         });
8814         
8815         var inputblock = input;
8816         
8817         var feedback = {
8818             tag: 'span',
8819             cls: 'glyphicon form-control-feedback'
8820         };
8821             
8822         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8823             
8824             inputblock = {
8825                 cls : 'has-feedback',
8826                 cn :  [
8827                     input,
8828                     feedback
8829                 ] 
8830             };  
8831         }
8832         
8833         if (this.before || this.after) {
8834             
8835             inputblock = {
8836                 cls : 'input-group',
8837                 cn :  [] 
8838             };
8839             
8840             if (this.before && typeof(this.before) == 'string') {
8841                 
8842                 inputblock.cn.push({
8843                     tag :'span',
8844                     cls : 'roo-input-before input-group-addon',
8845                     html : this.before
8846                 });
8847             }
8848             if (this.before && typeof(this.before) == 'object') {
8849                 this.before = Roo.factory(this.before);
8850                 
8851                 inputblock.cn.push({
8852                     tag :'span',
8853                     cls : 'roo-input-before input-group-' +
8854                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8855                 });
8856             }
8857             
8858             inputblock.cn.push(input);
8859             
8860             if (this.after && typeof(this.after) == 'string') {
8861                 inputblock.cn.push({
8862                     tag :'span',
8863                     cls : 'roo-input-after input-group-addon',
8864                     html : this.after
8865                 });
8866             }
8867             if (this.after && typeof(this.after) == 'object') {
8868                 this.after = Roo.factory(this.after);
8869                 
8870                 inputblock.cn.push({
8871                     tag :'span',
8872                     cls : 'roo-input-after input-group-' +
8873                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8874                 });
8875             }
8876             
8877             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8878                 inputblock.cls += ' has-feedback';
8879                 inputblock.cn.push(feedback);
8880             }
8881         };
8882         
8883         if (align ==='left' && this.fieldLabel.length) {
8884             
8885             cfg.cls += ' roo-form-group-label-left';
8886             
8887             cfg.cn = [
8888                 {
8889                     tag : 'i',
8890                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8891                     tooltip : 'This field is required'
8892                 },
8893                 {
8894                     tag: 'label',
8895                     'for' :  id,
8896                     cls : 'control-label',
8897                     html : this.fieldLabel
8898
8899                 },
8900                 {
8901                     cls : "", 
8902                     cn: [
8903                         inputblock
8904                     ]
8905                 }
8906             ];
8907             
8908             var labelCfg = cfg.cn[1];
8909             var contentCfg = cfg.cn[2];
8910             
8911             if(this.indicatorpos == 'right'){
8912                 cfg.cn = [
8913                     {
8914                         tag: 'label',
8915                         'for' :  id,
8916                         cls : 'control-label',
8917                         cn : [
8918                             {
8919                                 tag : 'span',
8920                                 html : this.fieldLabel
8921                             },
8922                             {
8923                                 tag : 'i',
8924                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8925                                 tooltip : 'This field is required'
8926                             }
8927                         ]
8928                     },
8929                     {
8930                         cls : "",
8931                         cn: [
8932                             inputblock
8933                         ]
8934                     }
8935
8936                 ];
8937                 
8938                 labelCfg = cfg.cn[0];
8939                 contentCfg = cfg.cn[1];
8940             
8941             }
8942             
8943             if(this.labelWidth > 12){
8944                 labelCfg.style = "width: " + this.labelWidth + 'px';
8945             }
8946             
8947             if(this.labelWidth < 13 && this.labelmd == 0){
8948                 this.labelmd = this.labelWidth;
8949             }
8950             
8951             if(this.labellg > 0){
8952                 labelCfg.cls += ' col-lg-' + this.labellg;
8953                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8954             }
8955             
8956             if(this.labelmd > 0){
8957                 labelCfg.cls += ' col-md-' + this.labelmd;
8958                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8959             }
8960             
8961             if(this.labelsm > 0){
8962                 labelCfg.cls += ' col-sm-' + this.labelsm;
8963                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8964             }
8965             
8966             if(this.labelxs > 0){
8967                 labelCfg.cls += ' col-xs-' + this.labelxs;
8968                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8969             }
8970             
8971             
8972         } else if ( this.fieldLabel.length) {
8973                 
8974             cfg.cn = [
8975                 {
8976                     tag : 'i',
8977                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8978                     tooltip : 'This field is required'
8979                 },
8980                 {
8981                     tag: 'label',
8982                    //cls : 'input-group-addon',
8983                     html : this.fieldLabel
8984
8985                 },
8986
8987                inputblock
8988
8989            ];
8990            
8991            if(this.indicatorpos == 'right'){
8992                 
8993                 cfg.cn = [
8994                     {
8995                         tag: 'label',
8996                        //cls : 'input-group-addon',
8997                         html : this.fieldLabel
8998
8999                     },
9000                     {
9001                         tag : 'i',
9002                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9003                         tooltip : 'This field is required'
9004                     },
9005
9006                    inputblock
9007
9008                ];
9009
9010             }
9011
9012         } else {
9013             
9014             cfg.cn = [
9015
9016                     inputblock
9017
9018             ];
9019                 
9020                 
9021         };
9022         
9023         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9024            cfg.cls += ' navbar-form';
9025         }
9026         
9027         if (this.parentType === 'NavGroup') {
9028            cfg.cls += ' navbar-form';
9029            cfg.tag = 'li';
9030         }
9031         
9032         return cfg;
9033         
9034     },
9035     /**
9036      * return the real input element.
9037      */
9038     inputEl: function ()
9039     {
9040         return this.el.select('input.form-control',true).first();
9041     },
9042     
9043     tooltipEl : function()
9044     {
9045         return this.inputEl();
9046     },
9047     
9048     indicatorEl : function()
9049     {
9050         var indicator = this.el.select('i.roo-required-indicator',true).first();
9051         
9052         if(!indicator){
9053             return false;
9054         }
9055         
9056         return indicator;
9057         
9058     },
9059     
9060     setDisabled : function(v)
9061     {
9062         var i  = this.inputEl().dom;
9063         if (!v) {
9064             i.removeAttribute('disabled');
9065             return;
9066             
9067         }
9068         i.setAttribute('disabled','true');
9069     },
9070     initEvents : function()
9071     {
9072           
9073         this.inputEl().on("keydown" , this.fireKey,  this);
9074         this.inputEl().on("focus", this.onFocus,  this);
9075         this.inputEl().on("blur", this.onBlur,  this);
9076         
9077         this.inputEl().relayEvent('keyup', this);
9078         
9079         this.indicator = this.indicatorEl();
9080         
9081         if(this.indicator){
9082             this.indicator.addClass('invisible');
9083         }
9084  
9085         // reference to original value for reset
9086         this.originalValue = this.getValue();
9087         //Roo.form.TextField.superclass.initEvents.call(this);
9088         if(this.validationEvent == 'keyup'){
9089             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9090             this.inputEl().on('keyup', this.filterValidation, this);
9091         }
9092         else if(this.validationEvent !== false){
9093             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9094         }
9095         
9096         if(this.selectOnFocus){
9097             this.on("focus", this.preFocus, this);
9098             
9099         }
9100         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9101             this.inputEl().on("keypress", this.filterKeys, this);
9102         } else {
9103             this.inputEl().relayEvent('keypress', this);
9104         }
9105        /* if(this.grow){
9106             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9107             this.el.on("click", this.autoSize,  this);
9108         }
9109         */
9110         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9111             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9112         }
9113         
9114         if (typeof(this.before) == 'object') {
9115             this.before.render(this.el.select('.roo-input-before',true).first());
9116         }
9117         if (typeof(this.after) == 'object') {
9118             this.after.render(this.el.select('.roo-input-after',true).first());
9119         }
9120         
9121         
9122     },
9123     filterValidation : function(e){
9124         if(!e.isNavKeyPress()){
9125             this.validationTask.delay(this.validationDelay);
9126         }
9127     },
9128      /**
9129      * Validates the field value
9130      * @return {Boolean} True if the value is valid, else false
9131      */
9132     validate : function(){
9133         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9134         if(this.disabled || this.validateValue(this.getRawValue())){
9135             this.markValid();
9136             return true;
9137         }
9138         
9139         this.markInvalid();
9140         return false;
9141     },
9142     
9143     
9144     /**
9145      * Validates a value according to the field's validation rules and marks the field as invalid
9146      * if the validation fails
9147      * @param {Mixed} value The value to validate
9148      * @return {Boolean} True if the value is valid, else false
9149      */
9150     validateValue : function(value)
9151     {
9152         if(this.getVisibilityEl().hasClass('hidden')){
9153             return true;
9154         }
9155         
9156         if(value.length < 1)  { // if it's blank
9157             if(this.allowBlank){
9158                 return true;
9159             }
9160             return false;
9161         }
9162         
9163         if(value.length < this.minLength){
9164             return false;
9165         }
9166         if(value.length > this.maxLength){
9167             return false;
9168         }
9169         if(this.vtype){
9170             var vt = Roo.form.VTypes;
9171             if(!vt[this.vtype](value, this)){
9172                 return false;
9173             }
9174         }
9175         if(typeof this.validator == "function"){
9176             var msg = this.validator(value);
9177             if(msg !== true){
9178                 return false;
9179             }
9180             if (typeof(msg) == 'string') {
9181                 this.invalidText = msg;
9182             }
9183         }
9184         
9185         if(this.regex && !this.regex.test(value)){
9186             return false;
9187         }
9188         
9189         return true;
9190     },
9191     
9192      // private
9193     fireKey : function(e){
9194         //Roo.log('field ' + e.getKey());
9195         if(e.isNavKeyPress()){
9196             this.fireEvent("specialkey", this, e);
9197         }
9198     },
9199     focus : function (selectText){
9200         if(this.rendered){
9201             this.inputEl().focus();
9202             if(selectText === true){
9203                 this.inputEl().dom.select();
9204             }
9205         }
9206         return this;
9207     } ,
9208     
9209     onFocus : function(){
9210         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9211            // this.el.addClass(this.focusClass);
9212         }
9213         if(!this.hasFocus){
9214             this.hasFocus = true;
9215             this.startValue = this.getValue();
9216             this.fireEvent("focus", this);
9217         }
9218     },
9219     
9220     beforeBlur : Roo.emptyFn,
9221
9222     
9223     // private
9224     onBlur : function(){
9225         this.beforeBlur();
9226         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9227             //this.el.removeClass(this.focusClass);
9228         }
9229         this.hasFocus = false;
9230         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9231             this.validate();
9232         }
9233         var v = this.getValue();
9234         if(String(v) !== String(this.startValue)){
9235             this.fireEvent('change', this, v, this.startValue);
9236         }
9237         this.fireEvent("blur", this);
9238     },
9239     
9240     /**
9241      * Resets the current field value to the originally loaded value and clears any validation messages
9242      */
9243     reset : function(){
9244         this.setValue(this.originalValue);
9245         this.validate();
9246     },
9247      /**
9248      * Returns the name of the field
9249      * @return {Mixed} name The name field
9250      */
9251     getName: function(){
9252         return this.name;
9253     },
9254      /**
9255      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9256      * @return {Mixed} value The field value
9257      */
9258     getValue : function(){
9259         
9260         var v = this.inputEl().getValue();
9261         
9262         return v;
9263     },
9264     /**
9265      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9266      * @return {Mixed} value The field value
9267      */
9268     getRawValue : function(){
9269         var v = this.inputEl().getValue();
9270         
9271         return v;
9272     },
9273     
9274     /**
9275      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9276      * @param {Mixed} value The value to set
9277      */
9278     setRawValue : function(v){
9279         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9280     },
9281     
9282     selectText : function(start, end){
9283         var v = this.getRawValue();
9284         if(v.length > 0){
9285             start = start === undefined ? 0 : start;
9286             end = end === undefined ? v.length : end;
9287             var d = this.inputEl().dom;
9288             if(d.setSelectionRange){
9289                 d.setSelectionRange(start, end);
9290             }else if(d.createTextRange){
9291                 var range = d.createTextRange();
9292                 range.moveStart("character", start);
9293                 range.moveEnd("character", v.length-end);
9294                 range.select();
9295             }
9296         }
9297     },
9298     
9299     /**
9300      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9301      * @param {Mixed} value The value to set
9302      */
9303     setValue : function(v){
9304         this.value = v;
9305         if(this.rendered){
9306             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9307             this.validate();
9308         }
9309     },
9310     
9311     /*
9312     processValue : function(value){
9313         if(this.stripCharsRe){
9314             var newValue = value.replace(this.stripCharsRe, '');
9315             if(newValue !== value){
9316                 this.setRawValue(newValue);
9317                 return newValue;
9318             }
9319         }
9320         return value;
9321     },
9322   */
9323     preFocus : function(){
9324         
9325         if(this.selectOnFocus){
9326             this.inputEl().dom.select();
9327         }
9328     },
9329     filterKeys : function(e){
9330         var k = e.getKey();
9331         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9332             return;
9333         }
9334         var c = e.getCharCode(), cc = String.fromCharCode(c);
9335         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9336             return;
9337         }
9338         if(!this.maskRe.test(cc)){
9339             e.stopEvent();
9340         }
9341     },
9342      /**
9343      * Clear any invalid styles/messages for this field
9344      */
9345     clearInvalid : function(){
9346         
9347         if(!this.el || this.preventMark){ // not rendered
9348             return;
9349         }
9350         
9351      
9352         this.el.removeClass(this.invalidClass);
9353         
9354         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9355             
9356             var feedback = this.el.select('.form-control-feedback', true).first();
9357             
9358             if(feedback){
9359                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9360             }
9361             
9362         }
9363         
9364         this.fireEvent('valid', this);
9365     },
9366     
9367      /**
9368      * Mark this field as valid
9369      */
9370     markValid : function()
9371     {
9372         if(!this.el  || this.preventMark){ // not rendered...
9373             return;
9374         }
9375         
9376         this.el.removeClass([this.invalidClass, this.validClass]);
9377         
9378         var feedback = this.el.select('.form-control-feedback', true).first();
9379             
9380         if(feedback){
9381             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9382         }
9383         
9384         if(this.indicator){
9385             this.indicator.removeClass('visible');
9386             this.indicator.addClass('invisible');
9387         }
9388         
9389         if(this.disabled){
9390             return;
9391         }
9392         
9393         if(this.allowBlank && !this.getRawValue().length){
9394             return;
9395         }
9396         
9397         this.el.addClass(this.validClass);
9398         
9399         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9400             
9401             var feedback = this.el.select('.form-control-feedback', true).first();
9402             
9403             if(feedback){
9404                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9405                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9406             }
9407             
9408         }
9409         
9410         this.fireEvent('valid', this);
9411     },
9412     
9413      /**
9414      * Mark this field as invalid
9415      * @param {String} msg The validation message
9416      */
9417     markInvalid : function(msg)
9418     {
9419         if(!this.el  || this.preventMark){ // not rendered
9420             return;
9421         }
9422         
9423         this.el.removeClass([this.invalidClass, this.validClass]);
9424         
9425         var feedback = this.el.select('.form-control-feedback', true).first();
9426             
9427         if(feedback){
9428             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9429         }
9430
9431         if(this.disabled){
9432             return;
9433         }
9434         
9435         if(this.allowBlank && !this.getRawValue().length){
9436             return;
9437         }
9438         
9439         if(this.indicator){
9440             this.indicator.removeClass('invisible');
9441             this.indicator.addClass('visible');
9442         }
9443         
9444         this.el.addClass(this.invalidClass);
9445         
9446         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9447             
9448             var feedback = this.el.select('.form-control-feedback', true).first();
9449             
9450             if(feedback){
9451                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9452                 
9453                 if(this.getValue().length || this.forceFeedback){
9454                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9455                 }
9456                 
9457             }
9458             
9459         }
9460         
9461         this.fireEvent('invalid', this, msg);
9462     },
9463     // private
9464     SafariOnKeyDown : function(event)
9465     {
9466         // this is a workaround for a password hang bug on chrome/ webkit.
9467         if (this.inputEl().dom.type != 'password') {
9468             return;
9469         }
9470         
9471         var isSelectAll = false;
9472         
9473         if(this.inputEl().dom.selectionEnd > 0){
9474             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9475         }
9476         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9477             event.preventDefault();
9478             this.setValue('');
9479             return;
9480         }
9481         
9482         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9483             
9484             event.preventDefault();
9485             // this is very hacky as keydown always get's upper case.
9486             //
9487             var cc = String.fromCharCode(event.getCharCode());
9488             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9489             
9490         }
9491     },
9492     adjustWidth : function(tag, w){
9493         tag = tag.toLowerCase();
9494         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9495             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9496                 if(tag == 'input'){
9497                     return w + 2;
9498                 }
9499                 if(tag == 'textarea'){
9500                     return w-2;
9501                 }
9502             }else if(Roo.isOpera){
9503                 if(tag == 'input'){
9504                     return w + 2;
9505                 }
9506                 if(tag == 'textarea'){
9507                     return w-2;
9508                 }
9509             }
9510         }
9511         return w;
9512     },
9513     
9514     setFieldLabel : function(v)
9515     {
9516         if(!this.rendered){
9517             return;
9518         }
9519         
9520         if(this.indicator){
9521             var ar = this.el.select('label > span',true);
9522             
9523             if (ar.elements.length) {
9524                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9525                 this.fieldLabel = v;
9526                 return;
9527             }
9528             
9529             var br = this.el.select('label',true);
9530             
9531             if(br.elements.length) {
9532                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9533                 this.fieldLabel = v;
9534                 return;
9535             }
9536             
9537             Roo.log('Cannot Found any of label > span || label in input');
9538             return;
9539         }
9540         
9541         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9542         this.fieldLabel = v;
9543         
9544         
9545     }
9546 });
9547
9548  
9549 /*
9550  * - LGPL
9551  *
9552  * Input
9553  * 
9554  */
9555
9556 /**
9557  * @class Roo.bootstrap.TextArea
9558  * @extends Roo.bootstrap.Input
9559  * Bootstrap TextArea class
9560  * @cfg {Number} cols Specifies the visible width of a text area
9561  * @cfg {Number} rows Specifies the visible number of lines in a text area
9562  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9563  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9564  * @cfg {string} html text
9565  * 
9566  * @constructor
9567  * Create a new TextArea
9568  * @param {Object} config The config object
9569  */
9570
9571 Roo.bootstrap.TextArea = function(config){
9572     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9573    
9574 };
9575
9576 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9577      
9578     cols : false,
9579     rows : 5,
9580     readOnly : false,
9581     warp : 'soft',
9582     resize : false,
9583     value: false,
9584     html: false,
9585     
9586     getAutoCreate : function(){
9587         
9588         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9589         
9590         var id = Roo.id();
9591         
9592         var cfg = {};
9593         
9594         if(this.inputType != 'hidden'){
9595             cfg.cls = 'form-group' //input-group
9596         }
9597         
9598         var input =  {
9599             tag: 'textarea',
9600             id : id,
9601             warp : this.warp,
9602             rows : this.rows,
9603             value : this.value || '',
9604             html: this.html || '',
9605             cls : 'form-control',
9606             placeholder : this.placeholder || '' 
9607             
9608         };
9609         
9610         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9611             input.maxLength = this.maxLength;
9612         }
9613         
9614         if(this.resize){
9615             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9616         }
9617         
9618         if(this.cols){
9619             input.cols = this.cols;
9620         }
9621         
9622         if (this.readOnly) {
9623             input.readonly = true;
9624         }
9625         
9626         if (this.name) {
9627             input.name = this.name;
9628         }
9629         
9630         if (this.size) {
9631             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9632         }
9633         
9634         var settings=this;
9635         ['xs','sm','md','lg'].map(function(size){
9636             if (settings[size]) {
9637                 cfg.cls += ' col-' + size + '-' + settings[size];
9638             }
9639         });
9640         
9641         var inputblock = input;
9642         
9643         if(this.hasFeedback && !this.allowBlank){
9644             
9645             var feedback = {
9646                 tag: 'span',
9647                 cls: 'glyphicon form-control-feedback'
9648             };
9649
9650             inputblock = {
9651                 cls : 'has-feedback',
9652                 cn :  [
9653                     input,
9654                     feedback
9655                 ] 
9656             };  
9657         }
9658         
9659         
9660         if (this.before || this.after) {
9661             
9662             inputblock = {
9663                 cls : 'input-group',
9664                 cn :  [] 
9665             };
9666             if (this.before) {
9667                 inputblock.cn.push({
9668                     tag :'span',
9669                     cls : 'input-group-addon',
9670                     html : this.before
9671                 });
9672             }
9673             
9674             inputblock.cn.push(input);
9675             
9676             if(this.hasFeedback && !this.allowBlank){
9677                 inputblock.cls += ' has-feedback';
9678                 inputblock.cn.push(feedback);
9679             }
9680             
9681             if (this.after) {
9682                 inputblock.cn.push({
9683                     tag :'span',
9684                     cls : 'input-group-addon',
9685                     html : this.after
9686                 });
9687             }
9688             
9689         }
9690         
9691         if (align ==='left' && this.fieldLabel.length) {
9692             cfg.cn = [
9693                 {
9694                     tag: 'label',
9695                     'for' :  id,
9696                     cls : 'control-label',
9697                     html : this.fieldLabel
9698                 },
9699                 {
9700                     cls : "",
9701                     cn: [
9702                         inputblock
9703                     ]
9704                 }
9705
9706             ];
9707             
9708             if(this.labelWidth > 12){
9709                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9710             }
9711
9712             if(this.labelWidth < 13 && this.labelmd == 0){
9713                 this.labelmd = this.labelWidth;
9714             }
9715
9716             if(this.labellg > 0){
9717                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9718                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9719             }
9720
9721             if(this.labelmd > 0){
9722                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9723                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9724             }
9725
9726             if(this.labelsm > 0){
9727                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9728                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9729             }
9730
9731             if(this.labelxs > 0){
9732                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9733                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9734             }
9735             
9736         } else if ( this.fieldLabel.length) {
9737             cfg.cn = [
9738
9739                {
9740                    tag: 'label',
9741                    //cls : 'input-group-addon',
9742                    html : this.fieldLabel
9743
9744                },
9745
9746                inputblock
9747
9748            ];
9749
9750         } else {
9751
9752             cfg.cn = [
9753
9754                 inputblock
9755
9756             ];
9757                 
9758         }
9759         
9760         if (this.disabled) {
9761             input.disabled=true;
9762         }
9763         
9764         return cfg;
9765         
9766     },
9767     /**
9768      * return the real textarea element.
9769      */
9770     inputEl: function ()
9771     {
9772         return this.el.select('textarea.form-control',true).first();
9773     },
9774     
9775     /**
9776      * Clear any invalid styles/messages for this field
9777      */
9778     clearInvalid : function()
9779     {
9780         
9781         if(!this.el || this.preventMark){ // not rendered
9782             return;
9783         }
9784         
9785         var label = this.el.select('label', true).first();
9786         var icon = this.el.select('i.fa-star', true).first();
9787         
9788         if(label && icon){
9789             icon.remove();
9790         }
9791         
9792         this.el.removeClass(this.invalidClass);
9793         
9794         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9795             
9796             var feedback = this.el.select('.form-control-feedback', true).first();
9797             
9798             if(feedback){
9799                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9800             }
9801             
9802         }
9803         
9804         this.fireEvent('valid', this);
9805     },
9806     
9807      /**
9808      * Mark this field as valid
9809      */
9810     markValid : function()
9811     {
9812         if(!this.el  || this.preventMark){ // not rendered
9813             return;
9814         }
9815         
9816         this.el.removeClass([this.invalidClass, this.validClass]);
9817         
9818         var feedback = this.el.select('.form-control-feedback', true).first();
9819             
9820         if(feedback){
9821             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9822         }
9823
9824         if(this.disabled || this.allowBlank){
9825             return;
9826         }
9827         
9828         var label = this.el.select('label', true).first();
9829         var icon = this.el.select('i.fa-star', true).first();
9830         
9831         if(label && icon){
9832             icon.remove();
9833         }
9834         
9835         this.el.addClass(this.validClass);
9836         
9837         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9838             
9839             var feedback = this.el.select('.form-control-feedback', true).first();
9840             
9841             if(feedback){
9842                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9843                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9844             }
9845             
9846         }
9847         
9848         this.fireEvent('valid', this);
9849     },
9850     
9851      /**
9852      * Mark this field as invalid
9853      * @param {String} msg The validation message
9854      */
9855     markInvalid : function(msg)
9856     {
9857         if(!this.el  || this.preventMark){ // not rendered
9858             return;
9859         }
9860         
9861         this.el.removeClass([this.invalidClass, this.validClass]);
9862         
9863         var feedback = this.el.select('.form-control-feedback', true).first();
9864             
9865         if(feedback){
9866             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9867         }
9868
9869         if(this.disabled || this.allowBlank){
9870             return;
9871         }
9872         
9873         var label = this.el.select('label', true).first();
9874         var icon = this.el.select('i.fa-star', true).first();
9875         
9876         if(!this.getValue().length && label && !icon){
9877             this.el.createChild({
9878                 tag : 'i',
9879                 cls : 'text-danger fa fa-lg fa-star',
9880                 tooltip : 'This field is required',
9881                 style : 'margin-right:5px;'
9882             }, label, true);
9883         }
9884
9885         this.el.addClass(this.invalidClass);
9886         
9887         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9888             
9889             var feedback = this.el.select('.form-control-feedback', true).first();
9890             
9891             if(feedback){
9892                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9893                 
9894                 if(this.getValue().length || this.forceFeedback){
9895                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9896                 }
9897                 
9898             }
9899             
9900         }
9901         
9902         this.fireEvent('invalid', this, msg);
9903     }
9904 });
9905
9906  
9907 /*
9908  * - LGPL
9909  *
9910  * trigger field - base class for combo..
9911  * 
9912  */
9913  
9914 /**
9915  * @class Roo.bootstrap.TriggerField
9916  * @extends Roo.bootstrap.Input
9917  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9918  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9919  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9920  * for which you can provide a custom implementation.  For example:
9921  * <pre><code>
9922 var trigger = new Roo.bootstrap.TriggerField();
9923 trigger.onTriggerClick = myTriggerFn;
9924 trigger.applyTo('my-field');
9925 </code></pre>
9926  *
9927  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9928  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9929  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9930  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9931  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9932
9933  * @constructor
9934  * Create a new TriggerField.
9935  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9936  * to the base TextField)
9937  */
9938 Roo.bootstrap.TriggerField = function(config){
9939     this.mimicing = false;
9940     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9941 };
9942
9943 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9944     /**
9945      * @cfg {String} triggerClass A CSS class to apply to the trigger
9946      */
9947      /**
9948      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9949      */
9950     hideTrigger:false,
9951
9952     /**
9953      * @cfg {Boolean} removable (true|false) special filter default false
9954      */
9955     removable : false,
9956     
9957     /** @cfg {Boolean} grow @hide */
9958     /** @cfg {Number} growMin @hide */
9959     /** @cfg {Number} growMax @hide */
9960
9961     /**
9962      * @hide 
9963      * @method
9964      */
9965     autoSize: Roo.emptyFn,
9966     // private
9967     monitorTab : true,
9968     // private
9969     deferHeight : true,
9970
9971     
9972     actionMode : 'wrap',
9973     
9974     caret : false,
9975     
9976     
9977     getAutoCreate : function(){
9978        
9979         var align = this.labelAlign || this.parentLabelAlign();
9980         
9981         var id = Roo.id();
9982         
9983         var cfg = {
9984             cls: 'form-group' //input-group
9985         };
9986         
9987         
9988         var input =  {
9989             tag: 'input',
9990             id : id,
9991             type : this.inputType,
9992             cls : 'form-control',
9993             autocomplete: 'new-password',
9994             placeholder : this.placeholder || '' 
9995             
9996         };
9997         if (this.name) {
9998             input.name = this.name;
9999         }
10000         if (this.size) {
10001             input.cls += ' input-' + this.size;
10002         }
10003         
10004         if (this.disabled) {
10005             input.disabled=true;
10006         }
10007         
10008         var inputblock = input;
10009         
10010         if(this.hasFeedback && !this.allowBlank){
10011             
10012             var feedback = {
10013                 tag: 'span',
10014                 cls: 'glyphicon form-control-feedback'
10015             };
10016             
10017             if(this.removable && !this.editable && !this.tickable){
10018                 inputblock = {
10019                     cls : 'has-feedback',
10020                     cn :  [
10021                         inputblock,
10022                         {
10023                             tag: 'button',
10024                             html : 'x',
10025                             cls : 'roo-combo-removable-btn close'
10026                         },
10027                         feedback
10028                     ] 
10029                 };
10030             } else {
10031                 inputblock = {
10032                     cls : 'has-feedback',
10033                     cn :  [
10034                         inputblock,
10035                         feedback
10036                     ] 
10037                 };
10038             }
10039
10040         } else {
10041             if(this.removable && !this.editable && !this.tickable){
10042                 inputblock = {
10043                     cls : 'roo-removable',
10044                     cn :  [
10045                         inputblock,
10046                         {
10047                             tag: 'button',
10048                             html : 'x',
10049                             cls : 'roo-combo-removable-btn close'
10050                         }
10051                     ] 
10052                 };
10053             }
10054         }
10055         
10056         if (this.before || this.after) {
10057             
10058             inputblock = {
10059                 cls : 'input-group',
10060                 cn :  [] 
10061             };
10062             if (this.before) {
10063                 inputblock.cn.push({
10064                     tag :'span',
10065                     cls : 'input-group-addon',
10066                     html : this.before
10067                 });
10068             }
10069             
10070             inputblock.cn.push(input);
10071             
10072             if(this.hasFeedback && !this.allowBlank){
10073                 inputblock.cls += ' has-feedback';
10074                 inputblock.cn.push(feedback);
10075             }
10076             
10077             if (this.after) {
10078                 inputblock.cn.push({
10079                     tag :'span',
10080                     cls : 'input-group-addon',
10081                     html : this.after
10082                 });
10083             }
10084             
10085         };
10086         
10087         var box = {
10088             tag: 'div',
10089             cn: [
10090                 {
10091                     tag: 'input',
10092                     type : 'hidden',
10093                     cls: 'form-hidden-field'
10094                 },
10095                 inputblock
10096             ]
10097             
10098         };
10099         
10100         if(this.multiple){
10101             box = {
10102                 tag: 'div',
10103                 cn: [
10104                     {
10105                         tag: 'input',
10106                         type : 'hidden',
10107                         cls: 'form-hidden-field'
10108                     },
10109                     {
10110                         tag: 'ul',
10111                         cls: 'roo-select2-choices',
10112                         cn:[
10113                             {
10114                                 tag: 'li',
10115                                 cls: 'roo-select2-search-field',
10116                                 cn: [
10117
10118                                     inputblock
10119                                 ]
10120                             }
10121                         ]
10122                     }
10123                 ]
10124             }
10125         };
10126         
10127         var combobox = {
10128             cls: 'roo-select2-container input-group',
10129             cn: [
10130                 box
10131 //                {
10132 //                    tag: 'ul',
10133 //                    cls: 'typeahead typeahead-long dropdown-menu',
10134 //                    style: 'display:none'
10135 //                }
10136             ]
10137         };
10138         
10139         if(!this.multiple && this.showToggleBtn){
10140             
10141             var caret = {
10142                         tag: 'span',
10143                         cls: 'caret'
10144              };
10145             if (this.caret != false) {
10146                 caret = {
10147                      tag: 'i',
10148                      cls: 'fa fa-' + this.caret
10149                 };
10150                 
10151             }
10152             
10153             combobox.cn.push({
10154                 tag :'span',
10155                 cls : 'input-group-addon btn dropdown-toggle',
10156                 cn : [
10157                     caret,
10158                     {
10159                         tag: 'span',
10160                         cls: 'combobox-clear',
10161                         cn  : [
10162                             {
10163                                 tag : 'i',
10164                                 cls: 'icon-remove'
10165                             }
10166                         ]
10167                     }
10168                 ]
10169
10170             })
10171         }
10172         
10173         if(this.multiple){
10174             combobox.cls += ' roo-select2-container-multi';
10175         }
10176         
10177         if (align ==='left' && this.fieldLabel.length) {
10178             
10179             cfg.cls += ' roo-form-group-label-left';
10180
10181             cfg.cn = [
10182                 {
10183                     tag : 'i',
10184                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10185                     tooltip : 'This field is required'
10186                 },
10187                 {
10188                     tag: 'label',
10189                     'for' :  id,
10190                     cls : 'control-label',
10191                     html : this.fieldLabel
10192
10193                 },
10194                 {
10195                     cls : "", 
10196                     cn: [
10197                         combobox
10198                     ]
10199                 }
10200
10201             ];
10202             
10203             var labelCfg = cfg.cn[1];
10204             var contentCfg = cfg.cn[2];
10205             
10206             if(this.indicatorpos == 'right'){
10207                 cfg.cn = [
10208                     {
10209                         tag: 'label',
10210                         'for' :  id,
10211                         cls : 'control-label',
10212                         cn : [
10213                             {
10214                                 tag : 'span',
10215                                 html : this.fieldLabel
10216                             },
10217                             {
10218                                 tag : 'i',
10219                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10220                                 tooltip : 'This field is required'
10221                             }
10222                         ]
10223                     },
10224                     {
10225                         cls : "", 
10226                         cn: [
10227                             combobox
10228                         ]
10229                     }
10230
10231                 ];
10232                 
10233                 labelCfg = cfg.cn[0];
10234                 contentCfg = cfg.cn[1];
10235             }
10236             
10237             if(this.labelWidth > 12){
10238                 labelCfg.style = "width: " + this.labelWidth + 'px';
10239             }
10240             
10241             if(this.labelWidth < 13 && this.labelmd == 0){
10242                 this.labelmd = this.labelWidth;
10243             }
10244             
10245             if(this.labellg > 0){
10246                 labelCfg.cls += ' col-lg-' + this.labellg;
10247                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10248             }
10249             
10250             if(this.labelmd > 0){
10251                 labelCfg.cls += ' col-md-' + this.labelmd;
10252                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10253             }
10254             
10255             if(this.labelsm > 0){
10256                 labelCfg.cls += ' col-sm-' + this.labelsm;
10257                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10258             }
10259             
10260             if(this.labelxs > 0){
10261                 labelCfg.cls += ' col-xs-' + this.labelxs;
10262                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10263             }
10264             
10265         } else if ( this.fieldLabel.length) {
10266 //                Roo.log(" label");
10267             cfg.cn = [
10268                 {
10269                    tag : 'i',
10270                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10271                    tooltip : 'This field is required'
10272                },
10273                {
10274                    tag: 'label',
10275                    //cls : 'input-group-addon',
10276                    html : this.fieldLabel
10277
10278                },
10279
10280                combobox
10281
10282             ];
10283             
10284             if(this.indicatorpos == 'right'){
10285                 
10286                 cfg.cn = [
10287                     {
10288                        tag: 'label',
10289                        cn : [
10290                            {
10291                                tag : 'span',
10292                                html : this.fieldLabel
10293                            },
10294                            {
10295                               tag : 'i',
10296                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10297                               tooltip : 'This field is required'
10298                            }
10299                        ]
10300
10301                     },
10302                     combobox
10303
10304                 ];
10305
10306             }
10307
10308         } else {
10309             
10310 //                Roo.log(" no label && no align");
10311                 cfg = combobox
10312                      
10313                 
10314         }
10315         
10316         var settings=this;
10317         ['xs','sm','md','lg'].map(function(size){
10318             if (settings[size]) {
10319                 cfg.cls += ' col-' + size + '-' + settings[size];
10320             }
10321         });
10322         
10323         return cfg;
10324         
10325     },
10326     
10327     
10328     
10329     // private
10330     onResize : function(w, h){
10331 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10332 //        if(typeof w == 'number'){
10333 //            var x = w - this.trigger.getWidth();
10334 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10335 //            this.trigger.setStyle('left', x+'px');
10336 //        }
10337     },
10338
10339     // private
10340     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10341
10342     // private
10343     getResizeEl : function(){
10344         return this.inputEl();
10345     },
10346
10347     // private
10348     getPositionEl : function(){
10349         return this.inputEl();
10350     },
10351
10352     // private
10353     alignErrorIcon : function(){
10354         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10355     },
10356
10357     // private
10358     initEvents : function(){
10359         
10360         this.createList();
10361         
10362         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10363         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10364         if(!this.multiple && this.showToggleBtn){
10365             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10366             if(this.hideTrigger){
10367                 this.trigger.setDisplayed(false);
10368             }
10369             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10370         }
10371         
10372         if(this.multiple){
10373             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10374         }
10375         
10376         if(this.removable && !this.editable && !this.tickable){
10377             var close = this.closeTriggerEl();
10378             
10379             if(close){
10380                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10381                 close.on('click', this.removeBtnClick, this, close);
10382             }
10383         }
10384         
10385         //this.trigger.addClassOnOver('x-form-trigger-over');
10386         //this.trigger.addClassOnClick('x-form-trigger-click');
10387         
10388         //if(!this.width){
10389         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10390         //}
10391     },
10392     
10393     closeTriggerEl : function()
10394     {
10395         var close = this.el.select('.roo-combo-removable-btn', true).first();
10396         return close ? close : false;
10397     },
10398     
10399     removeBtnClick : function(e, h, el)
10400     {
10401         e.preventDefault();
10402         
10403         if(this.fireEvent("remove", this) !== false){
10404             this.reset();
10405             this.fireEvent("afterremove", this)
10406         }
10407     },
10408     
10409     createList : function()
10410     {
10411         this.list = Roo.get(document.body).createChild({
10412             tag: 'ul',
10413             cls: 'typeahead typeahead-long dropdown-menu',
10414             style: 'display:none'
10415         });
10416         
10417         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10418         
10419     },
10420
10421     // private
10422     initTrigger : function(){
10423        
10424     },
10425
10426     // private
10427     onDestroy : function(){
10428         if(this.trigger){
10429             this.trigger.removeAllListeners();
10430           //  this.trigger.remove();
10431         }
10432         //if(this.wrap){
10433         //    this.wrap.remove();
10434         //}
10435         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10436     },
10437
10438     // private
10439     onFocus : function(){
10440         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10441         /*
10442         if(!this.mimicing){
10443             this.wrap.addClass('x-trigger-wrap-focus');
10444             this.mimicing = true;
10445             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10446             if(this.monitorTab){
10447                 this.el.on("keydown", this.checkTab, this);
10448             }
10449         }
10450         */
10451     },
10452
10453     // private
10454     checkTab : function(e){
10455         if(e.getKey() == e.TAB){
10456             this.triggerBlur();
10457         }
10458     },
10459
10460     // private
10461     onBlur : function(){
10462         // do nothing
10463     },
10464
10465     // private
10466     mimicBlur : function(e, t){
10467         /*
10468         if(!this.wrap.contains(t) && this.validateBlur()){
10469             this.triggerBlur();
10470         }
10471         */
10472     },
10473
10474     // private
10475     triggerBlur : function(){
10476         this.mimicing = false;
10477         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10478         if(this.monitorTab){
10479             this.el.un("keydown", this.checkTab, this);
10480         }
10481         //this.wrap.removeClass('x-trigger-wrap-focus');
10482         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10483     },
10484
10485     // private
10486     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10487     validateBlur : function(e, t){
10488         return true;
10489     },
10490
10491     // private
10492     onDisable : function(){
10493         this.inputEl().dom.disabled = true;
10494         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10495         //if(this.wrap){
10496         //    this.wrap.addClass('x-item-disabled');
10497         //}
10498     },
10499
10500     // private
10501     onEnable : function(){
10502         this.inputEl().dom.disabled = false;
10503         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10504         //if(this.wrap){
10505         //    this.el.removeClass('x-item-disabled');
10506         //}
10507     },
10508
10509     // private
10510     onShow : function(){
10511         var ae = this.getActionEl();
10512         
10513         if(ae){
10514             ae.dom.style.display = '';
10515             ae.dom.style.visibility = 'visible';
10516         }
10517     },
10518
10519     // private
10520     
10521     onHide : function(){
10522         var ae = this.getActionEl();
10523         ae.dom.style.display = 'none';
10524     },
10525
10526     /**
10527      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10528      * by an implementing function.
10529      * @method
10530      * @param {EventObject} e
10531      */
10532     onTriggerClick : Roo.emptyFn
10533 });
10534  /*
10535  * Based on:
10536  * Ext JS Library 1.1.1
10537  * Copyright(c) 2006-2007, Ext JS, LLC.
10538  *
10539  * Originally Released Under LGPL - original licence link has changed is not relivant.
10540  *
10541  * Fork - LGPL
10542  * <script type="text/javascript">
10543  */
10544
10545
10546 /**
10547  * @class Roo.data.SortTypes
10548  * @singleton
10549  * Defines the default sorting (casting?) comparison functions used when sorting data.
10550  */
10551 Roo.data.SortTypes = {
10552     /**
10553      * Default sort that does nothing
10554      * @param {Mixed} s The value being converted
10555      * @return {Mixed} The comparison value
10556      */
10557     none : function(s){
10558         return s;
10559     },
10560     
10561     /**
10562      * The regular expression used to strip tags
10563      * @type {RegExp}
10564      * @property
10565      */
10566     stripTagsRE : /<\/?[^>]+>/gi,
10567     
10568     /**
10569      * Strips all HTML tags to sort on text only
10570      * @param {Mixed} s The value being converted
10571      * @return {String} The comparison value
10572      */
10573     asText : function(s){
10574         return String(s).replace(this.stripTagsRE, "");
10575     },
10576     
10577     /**
10578      * Strips all HTML tags to sort on text only - Case insensitive
10579      * @param {Mixed} s The value being converted
10580      * @return {String} The comparison value
10581      */
10582     asUCText : function(s){
10583         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10584     },
10585     
10586     /**
10587      * Case insensitive string
10588      * @param {Mixed} s The value being converted
10589      * @return {String} The comparison value
10590      */
10591     asUCString : function(s) {
10592         return String(s).toUpperCase();
10593     },
10594     
10595     /**
10596      * Date sorting
10597      * @param {Mixed} s The value being converted
10598      * @return {Number} The comparison value
10599      */
10600     asDate : function(s) {
10601         if(!s){
10602             return 0;
10603         }
10604         if(s instanceof Date){
10605             return s.getTime();
10606         }
10607         return Date.parse(String(s));
10608     },
10609     
10610     /**
10611      * Float sorting
10612      * @param {Mixed} s The value being converted
10613      * @return {Float} The comparison value
10614      */
10615     asFloat : function(s) {
10616         var val = parseFloat(String(s).replace(/,/g, ""));
10617         if(isNaN(val)) {
10618             val = 0;
10619         }
10620         return val;
10621     },
10622     
10623     /**
10624      * Integer sorting
10625      * @param {Mixed} s The value being converted
10626      * @return {Number} The comparison value
10627      */
10628     asInt : function(s) {
10629         var val = parseInt(String(s).replace(/,/g, ""));
10630         if(isNaN(val)) {
10631             val = 0;
10632         }
10633         return val;
10634     }
10635 };/*
10636  * Based on:
10637  * Ext JS Library 1.1.1
10638  * Copyright(c) 2006-2007, Ext JS, LLC.
10639  *
10640  * Originally Released Under LGPL - original licence link has changed is not relivant.
10641  *
10642  * Fork - LGPL
10643  * <script type="text/javascript">
10644  */
10645
10646 /**
10647 * @class Roo.data.Record
10648  * Instances of this class encapsulate both record <em>definition</em> information, and record
10649  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10650  * to access Records cached in an {@link Roo.data.Store} object.<br>
10651  * <p>
10652  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10653  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10654  * objects.<br>
10655  * <p>
10656  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10657  * @constructor
10658  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10659  * {@link #create}. The parameters are the same.
10660  * @param {Array} data An associative Array of data values keyed by the field name.
10661  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10662  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10663  * not specified an integer id is generated.
10664  */
10665 Roo.data.Record = function(data, id){
10666     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10667     this.data = data;
10668 };
10669
10670 /**
10671  * Generate a constructor for a specific record layout.
10672  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10673  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10674  * Each field definition object may contain the following properties: <ul>
10675  * <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,
10676  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10677  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10678  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10679  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10680  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10681  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10682  * this may be omitted.</p></li>
10683  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10684  * <ul><li>auto (Default, implies no conversion)</li>
10685  * <li>string</li>
10686  * <li>int</li>
10687  * <li>float</li>
10688  * <li>boolean</li>
10689  * <li>date</li></ul></p></li>
10690  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10691  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10692  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10693  * by the Reader into an object that will be stored in the Record. It is passed the
10694  * following parameters:<ul>
10695  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10696  * </ul></p></li>
10697  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10698  * </ul>
10699  * <br>usage:<br><pre><code>
10700 var TopicRecord = Roo.data.Record.create(
10701     {name: 'title', mapping: 'topic_title'},
10702     {name: 'author', mapping: 'username'},
10703     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10704     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10705     {name: 'lastPoster', mapping: 'user2'},
10706     {name: 'excerpt', mapping: 'post_text'}
10707 );
10708
10709 var myNewRecord = new TopicRecord({
10710     title: 'Do my job please',
10711     author: 'noobie',
10712     totalPosts: 1,
10713     lastPost: new Date(),
10714     lastPoster: 'Animal',
10715     excerpt: 'No way dude!'
10716 });
10717 myStore.add(myNewRecord);
10718 </code></pre>
10719  * @method create
10720  * @static
10721  */
10722 Roo.data.Record.create = function(o){
10723     var f = function(){
10724         f.superclass.constructor.apply(this, arguments);
10725     };
10726     Roo.extend(f, Roo.data.Record);
10727     var p = f.prototype;
10728     p.fields = new Roo.util.MixedCollection(false, function(field){
10729         return field.name;
10730     });
10731     for(var i = 0, len = o.length; i < len; i++){
10732         p.fields.add(new Roo.data.Field(o[i]));
10733     }
10734     f.getField = function(name){
10735         return p.fields.get(name);  
10736     };
10737     return f;
10738 };
10739
10740 Roo.data.Record.AUTO_ID = 1000;
10741 Roo.data.Record.EDIT = 'edit';
10742 Roo.data.Record.REJECT = 'reject';
10743 Roo.data.Record.COMMIT = 'commit';
10744
10745 Roo.data.Record.prototype = {
10746     /**
10747      * Readonly flag - true if this record has been modified.
10748      * @type Boolean
10749      */
10750     dirty : false,
10751     editing : false,
10752     error: null,
10753     modified: null,
10754
10755     // private
10756     join : function(store){
10757         this.store = store;
10758     },
10759
10760     /**
10761      * Set the named field to the specified value.
10762      * @param {String} name The name of the field to set.
10763      * @param {Object} value The value to set the field to.
10764      */
10765     set : function(name, value){
10766         if(this.data[name] == value){
10767             return;
10768         }
10769         this.dirty = true;
10770         if(!this.modified){
10771             this.modified = {};
10772         }
10773         if(typeof this.modified[name] == 'undefined'){
10774             this.modified[name] = this.data[name];
10775         }
10776         this.data[name] = value;
10777         if(!this.editing && this.store){
10778             this.store.afterEdit(this);
10779         }       
10780     },
10781
10782     /**
10783      * Get the value of the named field.
10784      * @param {String} name The name of the field to get the value of.
10785      * @return {Object} The value of the field.
10786      */
10787     get : function(name){
10788         return this.data[name]; 
10789     },
10790
10791     // private
10792     beginEdit : function(){
10793         this.editing = true;
10794         this.modified = {}; 
10795     },
10796
10797     // private
10798     cancelEdit : function(){
10799         this.editing = false;
10800         delete this.modified;
10801     },
10802
10803     // private
10804     endEdit : function(){
10805         this.editing = false;
10806         if(this.dirty && this.store){
10807             this.store.afterEdit(this);
10808         }
10809     },
10810
10811     /**
10812      * Usually called by the {@link Roo.data.Store} which owns the Record.
10813      * Rejects all changes made to the Record since either creation, or the last commit operation.
10814      * Modified fields are reverted to their original values.
10815      * <p>
10816      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10817      * of reject operations.
10818      */
10819     reject : function(){
10820         var m = this.modified;
10821         for(var n in m){
10822             if(typeof m[n] != "function"){
10823                 this.data[n] = m[n];
10824             }
10825         }
10826         this.dirty = false;
10827         delete this.modified;
10828         this.editing = false;
10829         if(this.store){
10830             this.store.afterReject(this);
10831         }
10832     },
10833
10834     /**
10835      * Usually called by the {@link Roo.data.Store} which owns the Record.
10836      * Commits all changes made to the Record since either creation, or the last commit operation.
10837      * <p>
10838      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10839      * of commit operations.
10840      */
10841     commit : function(){
10842         this.dirty = false;
10843         delete this.modified;
10844         this.editing = false;
10845         if(this.store){
10846             this.store.afterCommit(this);
10847         }
10848     },
10849
10850     // private
10851     hasError : function(){
10852         return this.error != null;
10853     },
10854
10855     // private
10856     clearError : function(){
10857         this.error = null;
10858     },
10859
10860     /**
10861      * Creates a copy of this record.
10862      * @param {String} id (optional) A new record id if you don't want to use this record's id
10863      * @return {Record}
10864      */
10865     copy : function(newId) {
10866         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10867     }
10868 };/*
10869  * Based on:
10870  * Ext JS Library 1.1.1
10871  * Copyright(c) 2006-2007, Ext JS, LLC.
10872  *
10873  * Originally Released Under LGPL - original licence link has changed is not relivant.
10874  *
10875  * Fork - LGPL
10876  * <script type="text/javascript">
10877  */
10878
10879
10880
10881 /**
10882  * @class Roo.data.Store
10883  * @extends Roo.util.Observable
10884  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10885  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10886  * <p>
10887  * 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
10888  * has no knowledge of the format of the data returned by the Proxy.<br>
10889  * <p>
10890  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10891  * instances from the data object. These records are cached and made available through accessor functions.
10892  * @constructor
10893  * Creates a new Store.
10894  * @param {Object} config A config object containing the objects needed for the Store to access data,
10895  * and read the data into Records.
10896  */
10897 Roo.data.Store = function(config){
10898     this.data = new Roo.util.MixedCollection(false);
10899     this.data.getKey = function(o){
10900         return o.id;
10901     };
10902     this.baseParams = {};
10903     // private
10904     this.paramNames = {
10905         "start" : "start",
10906         "limit" : "limit",
10907         "sort" : "sort",
10908         "dir" : "dir",
10909         "multisort" : "_multisort"
10910     };
10911
10912     if(config && config.data){
10913         this.inlineData = config.data;
10914         delete config.data;
10915     }
10916
10917     Roo.apply(this, config);
10918     
10919     if(this.reader){ // reader passed
10920         this.reader = Roo.factory(this.reader, Roo.data);
10921         this.reader.xmodule = this.xmodule || false;
10922         if(!this.recordType){
10923             this.recordType = this.reader.recordType;
10924         }
10925         if(this.reader.onMetaChange){
10926             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10927         }
10928     }
10929
10930     if(this.recordType){
10931         this.fields = this.recordType.prototype.fields;
10932     }
10933     this.modified = [];
10934
10935     this.addEvents({
10936         /**
10937          * @event datachanged
10938          * Fires when the data cache has changed, and a widget which is using this Store
10939          * as a Record cache should refresh its view.
10940          * @param {Store} this
10941          */
10942         datachanged : true,
10943         /**
10944          * @event metachange
10945          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10946          * @param {Store} this
10947          * @param {Object} meta The JSON metadata
10948          */
10949         metachange : true,
10950         /**
10951          * @event add
10952          * Fires when Records have been added to the Store
10953          * @param {Store} this
10954          * @param {Roo.data.Record[]} records The array of Records added
10955          * @param {Number} index The index at which the record(s) were added
10956          */
10957         add : true,
10958         /**
10959          * @event remove
10960          * Fires when a Record has been removed from the Store
10961          * @param {Store} this
10962          * @param {Roo.data.Record} record The Record that was removed
10963          * @param {Number} index The index at which the record was removed
10964          */
10965         remove : true,
10966         /**
10967          * @event update
10968          * Fires when a Record has been updated
10969          * @param {Store} this
10970          * @param {Roo.data.Record} record The Record that was updated
10971          * @param {String} operation The update operation being performed.  Value may be one of:
10972          * <pre><code>
10973  Roo.data.Record.EDIT
10974  Roo.data.Record.REJECT
10975  Roo.data.Record.COMMIT
10976          * </code></pre>
10977          */
10978         update : true,
10979         /**
10980          * @event clear
10981          * Fires when the data cache has been cleared.
10982          * @param {Store} this
10983          */
10984         clear : true,
10985         /**
10986          * @event beforeload
10987          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10988          * the load action will be canceled.
10989          * @param {Store} this
10990          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10991          */
10992         beforeload : true,
10993         /**
10994          * @event beforeloadadd
10995          * Fires after a new set of Records has been loaded.
10996          * @param {Store} this
10997          * @param {Roo.data.Record[]} records The Records that were loaded
10998          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10999          */
11000         beforeloadadd : true,
11001         /**
11002          * @event load
11003          * Fires after a new set of Records has been loaded, before they are added to the store.
11004          * @param {Store} this
11005          * @param {Roo.data.Record[]} records The Records that were loaded
11006          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11007          * @params {Object} return from reader
11008          */
11009         load : true,
11010         /**
11011          * @event loadexception
11012          * Fires if an exception occurs in the Proxy during loading.
11013          * Called with the signature of the Proxy's "loadexception" event.
11014          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11015          * 
11016          * @param {Proxy} 
11017          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11018          * @param {Object} load options 
11019          * @param {Object} jsonData from your request (normally this contains the Exception)
11020          */
11021         loadexception : true
11022     });
11023     
11024     if(this.proxy){
11025         this.proxy = Roo.factory(this.proxy, Roo.data);
11026         this.proxy.xmodule = this.xmodule || false;
11027         this.relayEvents(this.proxy,  ["loadexception"]);
11028     }
11029     this.sortToggle = {};
11030     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11031
11032     Roo.data.Store.superclass.constructor.call(this);
11033
11034     if(this.inlineData){
11035         this.loadData(this.inlineData);
11036         delete this.inlineData;
11037     }
11038 };
11039
11040 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11041      /**
11042     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11043     * without a remote query - used by combo/forms at present.
11044     */
11045     
11046     /**
11047     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11048     */
11049     /**
11050     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11051     */
11052     /**
11053     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11054     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11055     */
11056     /**
11057     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11058     * on any HTTP request
11059     */
11060     /**
11061     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11062     */
11063     /**
11064     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11065     */
11066     multiSort: false,
11067     /**
11068     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11069     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11070     */
11071     remoteSort : false,
11072
11073     /**
11074     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11075      * loaded or when a record is removed. (defaults to false).
11076     */
11077     pruneModifiedRecords : false,
11078
11079     // private
11080     lastOptions : null,
11081
11082     /**
11083      * Add Records to the Store and fires the add event.
11084      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11085      */
11086     add : function(records){
11087         records = [].concat(records);
11088         for(var i = 0, len = records.length; i < len; i++){
11089             records[i].join(this);
11090         }
11091         var index = this.data.length;
11092         this.data.addAll(records);
11093         this.fireEvent("add", this, records, index);
11094     },
11095
11096     /**
11097      * Remove a Record from the Store and fires the remove event.
11098      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11099      */
11100     remove : function(record){
11101         var index = this.data.indexOf(record);
11102         this.data.removeAt(index);
11103         if(this.pruneModifiedRecords){
11104             this.modified.remove(record);
11105         }
11106         this.fireEvent("remove", this, record, index);
11107     },
11108
11109     /**
11110      * Remove all Records from the Store and fires the clear event.
11111      */
11112     removeAll : function(){
11113         this.data.clear();
11114         if(this.pruneModifiedRecords){
11115             this.modified = [];
11116         }
11117         this.fireEvent("clear", this);
11118     },
11119
11120     /**
11121      * Inserts Records to the Store at the given index and fires the add event.
11122      * @param {Number} index The start index at which to insert the passed Records.
11123      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11124      */
11125     insert : function(index, records){
11126         records = [].concat(records);
11127         for(var i = 0, len = records.length; i < len; i++){
11128             this.data.insert(index, records[i]);
11129             records[i].join(this);
11130         }
11131         this.fireEvent("add", this, records, index);
11132     },
11133
11134     /**
11135      * Get the index within the cache of the passed Record.
11136      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11137      * @return {Number} The index of the passed Record. Returns -1 if not found.
11138      */
11139     indexOf : function(record){
11140         return this.data.indexOf(record);
11141     },
11142
11143     /**
11144      * Get the index within the cache of the Record with the passed id.
11145      * @param {String} id The id of the Record to find.
11146      * @return {Number} The index of the Record. Returns -1 if not found.
11147      */
11148     indexOfId : function(id){
11149         return this.data.indexOfKey(id);
11150     },
11151
11152     /**
11153      * Get the Record with the specified id.
11154      * @param {String} id The id of the Record to find.
11155      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11156      */
11157     getById : function(id){
11158         return this.data.key(id);
11159     },
11160
11161     /**
11162      * Get the Record at the specified index.
11163      * @param {Number} index The index of the Record to find.
11164      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11165      */
11166     getAt : function(index){
11167         return this.data.itemAt(index);
11168     },
11169
11170     /**
11171      * Returns a range of Records between specified indices.
11172      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11173      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11174      * @return {Roo.data.Record[]} An array of Records
11175      */
11176     getRange : function(start, end){
11177         return this.data.getRange(start, end);
11178     },
11179
11180     // private
11181     storeOptions : function(o){
11182         o = Roo.apply({}, o);
11183         delete o.callback;
11184         delete o.scope;
11185         this.lastOptions = o;
11186     },
11187
11188     /**
11189      * Loads the Record cache from the configured Proxy using the configured Reader.
11190      * <p>
11191      * If using remote paging, then the first load call must specify the <em>start</em>
11192      * and <em>limit</em> properties in the options.params property to establish the initial
11193      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11194      * <p>
11195      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11196      * and this call will return before the new data has been loaded. Perform any post-processing
11197      * in a callback function, or in a "load" event handler.</strong>
11198      * <p>
11199      * @param {Object} options An object containing properties which control loading options:<ul>
11200      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11201      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11202      * passed the following arguments:<ul>
11203      * <li>r : Roo.data.Record[]</li>
11204      * <li>options: Options object from the load call</li>
11205      * <li>success: Boolean success indicator</li></ul></li>
11206      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11207      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11208      * </ul>
11209      */
11210     load : function(options){
11211         options = options || {};
11212         if(this.fireEvent("beforeload", this, options) !== false){
11213             this.storeOptions(options);
11214             var p = Roo.apply(options.params || {}, this.baseParams);
11215             // if meta was not loaded from remote source.. try requesting it.
11216             if (!this.reader.metaFromRemote) {
11217                 p._requestMeta = 1;
11218             }
11219             if(this.sortInfo && this.remoteSort){
11220                 var pn = this.paramNames;
11221                 p[pn["sort"]] = this.sortInfo.field;
11222                 p[pn["dir"]] = this.sortInfo.direction;
11223             }
11224             if (this.multiSort) {
11225                 var pn = this.paramNames;
11226                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11227             }
11228             
11229             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11230         }
11231     },
11232
11233     /**
11234      * Reloads the Record cache from the configured Proxy using the configured Reader and
11235      * the options from the last load operation performed.
11236      * @param {Object} options (optional) An object containing properties which may override the options
11237      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11238      * the most recently used options are reused).
11239      */
11240     reload : function(options){
11241         this.load(Roo.applyIf(options||{}, this.lastOptions));
11242     },
11243
11244     // private
11245     // Called as a callback by the Reader during a load operation.
11246     loadRecords : function(o, options, success){
11247         if(!o || success === false){
11248             if(success !== false){
11249                 this.fireEvent("load", this, [], options, o);
11250             }
11251             if(options.callback){
11252                 options.callback.call(options.scope || this, [], options, false);
11253             }
11254             return;
11255         }
11256         // if data returned failure - throw an exception.
11257         if (o.success === false) {
11258             // show a message if no listener is registered.
11259             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11260                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11261             }
11262             // loadmask wil be hooked into this..
11263             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11264             return;
11265         }
11266         var r = o.records, t = o.totalRecords || r.length;
11267         
11268         this.fireEvent("beforeloadadd", this, r, options, o);
11269         
11270         if(!options || options.add !== true){
11271             if(this.pruneModifiedRecords){
11272                 this.modified = [];
11273             }
11274             for(var i = 0, len = r.length; i < len; i++){
11275                 r[i].join(this);
11276             }
11277             if(this.snapshot){
11278                 this.data = this.snapshot;
11279                 delete this.snapshot;
11280             }
11281             this.data.clear();
11282             this.data.addAll(r);
11283             this.totalLength = t;
11284             this.applySort();
11285             this.fireEvent("datachanged", this);
11286         }else{
11287             this.totalLength = Math.max(t, this.data.length+r.length);
11288             this.add(r);
11289         }
11290         
11291         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11292                 
11293             var e = new Roo.data.Record({});
11294
11295             e.set(this.parent.displayField, this.parent.emptyTitle);
11296             e.set(this.parent.valueField, '');
11297
11298             this.insert(0, e);
11299         }
11300             
11301         this.fireEvent("load", this, r, options, o);
11302         if(options.callback){
11303             options.callback.call(options.scope || this, r, options, true);
11304         }
11305     },
11306
11307
11308     /**
11309      * Loads data from a passed data block. A Reader which understands the format of the data
11310      * must have been configured in the constructor.
11311      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11312      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11313      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11314      */
11315     loadData : function(o, append){
11316         var r = this.reader.readRecords(o);
11317         this.loadRecords(r, {add: append}, true);
11318     },
11319
11320     /**
11321      * Gets the number of cached records.
11322      * <p>
11323      * <em>If using paging, this may not be the total size of the dataset. If the data object
11324      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11325      * the data set size</em>
11326      */
11327     getCount : function(){
11328         return this.data.length || 0;
11329     },
11330
11331     /**
11332      * Gets the total number of records in the dataset as returned by the server.
11333      * <p>
11334      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11335      * the dataset size</em>
11336      */
11337     getTotalCount : function(){
11338         return this.totalLength || 0;
11339     },
11340
11341     /**
11342      * Returns the sort state of the Store as an object with two properties:
11343      * <pre><code>
11344  field {String} The name of the field by which the Records are sorted
11345  direction {String} The sort order, "ASC" or "DESC"
11346      * </code></pre>
11347      */
11348     getSortState : function(){
11349         return this.sortInfo;
11350     },
11351
11352     // private
11353     applySort : function(){
11354         if(this.sortInfo && !this.remoteSort){
11355             var s = this.sortInfo, f = s.field;
11356             var st = this.fields.get(f).sortType;
11357             var fn = function(r1, r2){
11358                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11359                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11360             };
11361             this.data.sort(s.direction, fn);
11362             if(this.snapshot && this.snapshot != this.data){
11363                 this.snapshot.sort(s.direction, fn);
11364             }
11365         }
11366     },
11367
11368     /**
11369      * Sets the default sort column and order to be used by the next load operation.
11370      * @param {String} fieldName The name of the field to sort by.
11371      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11372      */
11373     setDefaultSort : function(field, dir){
11374         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11375     },
11376
11377     /**
11378      * Sort the Records.
11379      * If remote sorting is used, the sort is performed on the server, and the cache is
11380      * reloaded. If local sorting is used, the cache is sorted internally.
11381      * @param {String} fieldName The name of the field to sort by.
11382      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11383      */
11384     sort : function(fieldName, dir){
11385         var f = this.fields.get(fieldName);
11386         if(!dir){
11387             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11388             
11389             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11390                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11391             }else{
11392                 dir = f.sortDir;
11393             }
11394         }
11395         this.sortToggle[f.name] = dir;
11396         this.sortInfo = {field: f.name, direction: dir};
11397         if(!this.remoteSort){
11398             this.applySort();
11399             this.fireEvent("datachanged", this);
11400         }else{
11401             this.load(this.lastOptions);
11402         }
11403     },
11404
11405     /**
11406      * Calls the specified function for each of the Records in the cache.
11407      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11408      * Returning <em>false</em> aborts and exits the iteration.
11409      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11410      */
11411     each : function(fn, scope){
11412         this.data.each(fn, scope);
11413     },
11414
11415     /**
11416      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11417      * (e.g., during paging).
11418      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11419      */
11420     getModifiedRecords : function(){
11421         return this.modified;
11422     },
11423
11424     // private
11425     createFilterFn : function(property, value, anyMatch){
11426         if(!value.exec){ // not a regex
11427             value = String(value);
11428             if(value.length == 0){
11429                 return false;
11430             }
11431             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11432         }
11433         return function(r){
11434             return value.test(r.data[property]);
11435         };
11436     },
11437
11438     /**
11439      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11440      * @param {String} property A field on your records
11441      * @param {Number} start The record index to start at (defaults to 0)
11442      * @param {Number} end The last record index to include (defaults to length - 1)
11443      * @return {Number} The sum
11444      */
11445     sum : function(property, start, end){
11446         var rs = this.data.items, v = 0;
11447         start = start || 0;
11448         end = (end || end === 0) ? end : rs.length-1;
11449
11450         for(var i = start; i <= end; i++){
11451             v += (rs[i].data[property] || 0);
11452         }
11453         return v;
11454     },
11455
11456     /**
11457      * Filter the records by a specified property.
11458      * @param {String} field A field on your records
11459      * @param {String/RegExp} value Either a string that the field
11460      * should start with or a RegExp to test against the field
11461      * @param {Boolean} anyMatch True to match any part not just the beginning
11462      */
11463     filter : function(property, value, anyMatch){
11464         var fn = this.createFilterFn(property, value, anyMatch);
11465         return fn ? this.filterBy(fn) : this.clearFilter();
11466     },
11467
11468     /**
11469      * Filter by a function. The specified function will be called with each
11470      * record in this data source. If the function returns true the record is included,
11471      * otherwise it is filtered.
11472      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11473      * @param {Object} scope (optional) The scope of the function (defaults to this)
11474      */
11475     filterBy : function(fn, scope){
11476         this.snapshot = this.snapshot || this.data;
11477         this.data = this.queryBy(fn, scope||this);
11478         this.fireEvent("datachanged", this);
11479     },
11480
11481     /**
11482      * Query the records by a specified property.
11483      * @param {String} field A field on your records
11484      * @param {String/RegExp} value Either a string that the field
11485      * should start with or a RegExp to test against the field
11486      * @param {Boolean} anyMatch True to match any part not just the beginning
11487      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11488      */
11489     query : function(property, value, anyMatch){
11490         var fn = this.createFilterFn(property, value, anyMatch);
11491         return fn ? this.queryBy(fn) : this.data.clone();
11492     },
11493
11494     /**
11495      * Query by a function. The specified function will be called with each
11496      * record in this data source. If the function returns true the record is included
11497      * in the results.
11498      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11499      * @param {Object} scope (optional) The scope of the function (defaults to this)
11500       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11501      **/
11502     queryBy : function(fn, scope){
11503         var data = this.snapshot || this.data;
11504         return data.filterBy(fn, scope||this);
11505     },
11506
11507     /**
11508      * Collects unique values for a particular dataIndex from this store.
11509      * @param {String} dataIndex The property to collect
11510      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11511      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11512      * @return {Array} An array of the unique values
11513      **/
11514     collect : function(dataIndex, allowNull, bypassFilter){
11515         var d = (bypassFilter === true && this.snapshot) ?
11516                 this.snapshot.items : this.data.items;
11517         var v, sv, r = [], l = {};
11518         for(var i = 0, len = d.length; i < len; i++){
11519             v = d[i].data[dataIndex];
11520             sv = String(v);
11521             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11522                 l[sv] = true;
11523                 r[r.length] = v;
11524             }
11525         }
11526         return r;
11527     },
11528
11529     /**
11530      * Revert to a view of the Record cache with no filtering applied.
11531      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11532      */
11533     clearFilter : function(suppressEvent){
11534         if(this.snapshot && this.snapshot != this.data){
11535             this.data = this.snapshot;
11536             delete this.snapshot;
11537             if(suppressEvent !== true){
11538                 this.fireEvent("datachanged", this);
11539             }
11540         }
11541     },
11542
11543     // private
11544     afterEdit : function(record){
11545         if(this.modified.indexOf(record) == -1){
11546             this.modified.push(record);
11547         }
11548         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11549     },
11550     
11551     // private
11552     afterReject : function(record){
11553         this.modified.remove(record);
11554         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11555     },
11556
11557     // private
11558     afterCommit : function(record){
11559         this.modified.remove(record);
11560         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11561     },
11562
11563     /**
11564      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11565      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11566      */
11567     commitChanges : function(){
11568         var m = this.modified.slice(0);
11569         this.modified = [];
11570         for(var i = 0, len = m.length; i < len; i++){
11571             m[i].commit();
11572         }
11573     },
11574
11575     /**
11576      * Cancel outstanding changes on all changed records.
11577      */
11578     rejectChanges : function(){
11579         var m = this.modified.slice(0);
11580         this.modified = [];
11581         for(var i = 0, len = m.length; i < len; i++){
11582             m[i].reject();
11583         }
11584     },
11585
11586     onMetaChange : function(meta, rtype, o){
11587         this.recordType = rtype;
11588         this.fields = rtype.prototype.fields;
11589         delete this.snapshot;
11590         this.sortInfo = meta.sortInfo || this.sortInfo;
11591         this.modified = [];
11592         this.fireEvent('metachange', this, this.reader.meta);
11593     },
11594     
11595     moveIndex : function(data, type)
11596     {
11597         var index = this.indexOf(data);
11598         
11599         var newIndex = index + type;
11600         
11601         this.remove(data);
11602         
11603         this.insert(newIndex, data);
11604         
11605     }
11606 });/*
11607  * Based on:
11608  * Ext JS Library 1.1.1
11609  * Copyright(c) 2006-2007, Ext JS, LLC.
11610  *
11611  * Originally Released Under LGPL - original licence link has changed is not relivant.
11612  *
11613  * Fork - LGPL
11614  * <script type="text/javascript">
11615  */
11616
11617 /**
11618  * @class Roo.data.SimpleStore
11619  * @extends Roo.data.Store
11620  * Small helper class to make creating Stores from Array data easier.
11621  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11622  * @cfg {Array} fields An array of field definition objects, or field name strings.
11623  * @cfg {Array} data The multi-dimensional array of data
11624  * @constructor
11625  * @param {Object} config
11626  */
11627 Roo.data.SimpleStore = function(config){
11628     Roo.data.SimpleStore.superclass.constructor.call(this, {
11629         isLocal : true,
11630         reader: new Roo.data.ArrayReader({
11631                 id: config.id
11632             },
11633             Roo.data.Record.create(config.fields)
11634         ),
11635         proxy : new Roo.data.MemoryProxy(config.data)
11636     });
11637     this.load();
11638 };
11639 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11640  * Based on:
11641  * Ext JS Library 1.1.1
11642  * Copyright(c) 2006-2007, Ext JS, LLC.
11643  *
11644  * Originally Released Under LGPL - original licence link has changed is not relivant.
11645  *
11646  * Fork - LGPL
11647  * <script type="text/javascript">
11648  */
11649
11650 /**
11651 /**
11652  * @extends Roo.data.Store
11653  * @class Roo.data.JsonStore
11654  * Small helper class to make creating Stores for JSON data easier. <br/>
11655 <pre><code>
11656 var store = new Roo.data.JsonStore({
11657     url: 'get-images.php',
11658     root: 'images',
11659     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11660 });
11661 </code></pre>
11662  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11663  * JsonReader and HttpProxy (unless inline data is provided).</b>
11664  * @cfg {Array} fields An array of field definition objects, or field name strings.
11665  * @constructor
11666  * @param {Object} config
11667  */
11668 Roo.data.JsonStore = function(c){
11669     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11670         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11671         reader: new Roo.data.JsonReader(c, c.fields)
11672     }));
11673 };
11674 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11675  * Based on:
11676  * Ext JS Library 1.1.1
11677  * Copyright(c) 2006-2007, Ext JS, LLC.
11678  *
11679  * Originally Released Under LGPL - original licence link has changed is not relivant.
11680  *
11681  * Fork - LGPL
11682  * <script type="text/javascript">
11683  */
11684
11685  
11686 Roo.data.Field = function(config){
11687     if(typeof config == "string"){
11688         config = {name: config};
11689     }
11690     Roo.apply(this, config);
11691     
11692     if(!this.type){
11693         this.type = "auto";
11694     }
11695     
11696     var st = Roo.data.SortTypes;
11697     // named sortTypes are supported, here we look them up
11698     if(typeof this.sortType == "string"){
11699         this.sortType = st[this.sortType];
11700     }
11701     
11702     // set default sortType for strings and dates
11703     if(!this.sortType){
11704         switch(this.type){
11705             case "string":
11706                 this.sortType = st.asUCString;
11707                 break;
11708             case "date":
11709                 this.sortType = st.asDate;
11710                 break;
11711             default:
11712                 this.sortType = st.none;
11713         }
11714     }
11715
11716     // define once
11717     var stripRe = /[\$,%]/g;
11718
11719     // prebuilt conversion function for this field, instead of
11720     // switching every time we're reading a value
11721     if(!this.convert){
11722         var cv, dateFormat = this.dateFormat;
11723         switch(this.type){
11724             case "":
11725             case "auto":
11726             case undefined:
11727                 cv = function(v){ return v; };
11728                 break;
11729             case "string":
11730                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11731                 break;
11732             case "int":
11733                 cv = function(v){
11734                     return v !== undefined && v !== null && v !== '' ?
11735                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11736                     };
11737                 break;
11738             case "float":
11739                 cv = function(v){
11740                     return v !== undefined && v !== null && v !== '' ?
11741                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11742                     };
11743                 break;
11744             case "bool":
11745             case "boolean":
11746                 cv = function(v){ return v === true || v === "true" || v == 1; };
11747                 break;
11748             case "date":
11749                 cv = function(v){
11750                     if(!v){
11751                         return '';
11752                     }
11753                     if(v instanceof Date){
11754                         return v;
11755                     }
11756                     if(dateFormat){
11757                         if(dateFormat == "timestamp"){
11758                             return new Date(v*1000);
11759                         }
11760                         return Date.parseDate(v, dateFormat);
11761                     }
11762                     var parsed = Date.parse(v);
11763                     return parsed ? new Date(parsed) : null;
11764                 };
11765              break;
11766             
11767         }
11768         this.convert = cv;
11769     }
11770 };
11771
11772 Roo.data.Field.prototype = {
11773     dateFormat: null,
11774     defaultValue: "",
11775     mapping: null,
11776     sortType : null,
11777     sortDir : "ASC"
11778 };/*
11779  * Based on:
11780  * Ext JS Library 1.1.1
11781  * Copyright(c) 2006-2007, Ext JS, LLC.
11782  *
11783  * Originally Released Under LGPL - original licence link has changed is not relivant.
11784  *
11785  * Fork - LGPL
11786  * <script type="text/javascript">
11787  */
11788  
11789 // Base class for reading structured data from a data source.  This class is intended to be
11790 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11791
11792 /**
11793  * @class Roo.data.DataReader
11794  * Base class for reading structured data from a data source.  This class is intended to be
11795  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11796  */
11797
11798 Roo.data.DataReader = function(meta, recordType){
11799     
11800     this.meta = meta;
11801     
11802     this.recordType = recordType instanceof Array ? 
11803         Roo.data.Record.create(recordType) : recordType;
11804 };
11805
11806 Roo.data.DataReader.prototype = {
11807      /**
11808      * Create an empty record
11809      * @param {Object} data (optional) - overlay some values
11810      * @return {Roo.data.Record} record created.
11811      */
11812     newRow :  function(d) {
11813         var da =  {};
11814         this.recordType.prototype.fields.each(function(c) {
11815             switch( c.type) {
11816                 case 'int' : da[c.name] = 0; break;
11817                 case 'date' : da[c.name] = new Date(); break;
11818                 case 'float' : da[c.name] = 0.0; break;
11819                 case 'boolean' : da[c.name] = false; break;
11820                 default : da[c.name] = ""; break;
11821             }
11822             
11823         });
11824         return new this.recordType(Roo.apply(da, d));
11825     }
11826     
11827 };/*
11828  * Based on:
11829  * Ext JS Library 1.1.1
11830  * Copyright(c) 2006-2007, Ext JS, LLC.
11831  *
11832  * Originally Released Under LGPL - original licence link has changed is not relivant.
11833  *
11834  * Fork - LGPL
11835  * <script type="text/javascript">
11836  */
11837
11838 /**
11839  * @class Roo.data.DataProxy
11840  * @extends Roo.data.Observable
11841  * This class is an abstract base class for implementations which provide retrieval of
11842  * unformatted data objects.<br>
11843  * <p>
11844  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11845  * (of the appropriate type which knows how to parse the data object) to provide a block of
11846  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11847  * <p>
11848  * Custom implementations must implement the load method as described in
11849  * {@link Roo.data.HttpProxy#load}.
11850  */
11851 Roo.data.DataProxy = function(){
11852     this.addEvents({
11853         /**
11854          * @event beforeload
11855          * Fires before a network request is made to retrieve a data object.
11856          * @param {Object} This DataProxy object.
11857          * @param {Object} params The params parameter to the load function.
11858          */
11859         beforeload : true,
11860         /**
11861          * @event load
11862          * Fires before the load method's callback is called.
11863          * @param {Object} This DataProxy object.
11864          * @param {Object} o The data object.
11865          * @param {Object} arg The callback argument object passed to the load function.
11866          */
11867         load : true,
11868         /**
11869          * @event loadexception
11870          * Fires if an Exception occurs during data retrieval.
11871          * @param {Object} This DataProxy object.
11872          * @param {Object} o The data object.
11873          * @param {Object} arg The callback argument object passed to the load function.
11874          * @param {Object} e The Exception.
11875          */
11876         loadexception : true
11877     });
11878     Roo.data.DataProxy.superclass.constructor.call(this);
11879 };
11880
11881 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11882
11883     /**
11884      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11885      */
11886 /*
11887  * Based on:
11888  * Ext JS Library 1.1.1
11889  * Copyright(c) 2006-2007, Ext JS, LLC.
11890  *
11891  * Originally Released Under LGPL - original licence link has changed is not relivant.
11892  *
11893  * Fork - LGPL
11894  * <script type="text/javascript">
11895  */
11896 /**
11897  * @class Roo.data.MemoryProxy
11898  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11899  * to the Reader when its load method is called.
11900  * @constructor
11901  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11902  */
11903 Roo.data.MemoryProxy = function(data){
11904     if (data.data) {
11905         data = data.data;
11906     }
11907     Roo.data.MemoryProxy.superclass.constructor.call(this);
11908     this.data = data;
11909 };
11910
11911 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11912     
11913     /**
11914      * Load data from the requested source (in this case an in-memory
11915      * data object passed to the constructor), read the data object into
11916      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11917      * process that block using the passed callback.
11918      * @param {Object} params This parameter is not used by the MemoryProxy class.
11919      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11920      * object into a block of Roo.data.Records.
11921      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11922      * The function must be passed <ul>
11923      * <li>The Record block object</li>
11924      * <li>The "arg" argument from the load function</li>
11925      * <li>A boolean success indicator</li>
11926      * </ul>
11927      * @param {Object} scope The scope in which to call the callback
11928      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11929      */
11930     load : function(params, reader, callback, scope, arg){
11931         params = params || {};
11932         var result;
11933         try {
11934             result = reader.readRecords(this.data);
11935         }catch(e){
11936             this.fireEvent("loadexception", this, arg, null, e);
11937             callback.call(scope, null, arg, false);
11938             return;
11939         }
11940         callback.call(scope, result, arg, true);
11941     },
11942     
11943     // private
11944     update : function(params, records){
11945         
11946     }
11947 });/*
11948  * Based on:
11949  * Ext JS Library 1.1.1
11950  * Copyright(c) 2006-2007, Ext JS, LLC.
11951  *
11952  * Originally Released Under LGPL - original licence link has changed is not relivant.
11953  *
11954  * Fork - LGPL
11955  * <script type="text/javascript">
11956  */
11957 /**
11958  * @class Roo.data.HttpProxy
11959  * @extends Roo.data.DataProxy
11960  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11961  * configured to reference a certain URL.<br><br>
11962  * <p>
11963  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11964  * from which the running page was served.<br><br>
11965  * <p>
11966  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11967  * <p>
11968  * Be aware that to enable the browser to parse an XML document, the server must set
11969  * the Content-Type header in the HTTP response to "text/xml".
11970  * @constructor
11971  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11972  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11973  * will be used to make the request.
11974  */
11975 Roo.data.HttpProxy = function(conn){
11976     Roo.data.HttpProxy.superclass.constructor.call(this);
11977     // is conn a conn config or a real conn?
11978     this.conn = conn;
11979     this.useAjax = !conn || !conn.events;
11980   
11981 };
11982
11983 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11984     // thse are take from connection...
11985     
11986     /**
11987      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11988      */
11989     /**
11990      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11991      * extra parameters to each request made by this object. (defaults to undefined)
11992      */
11993     /**
11994      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11995      *  to each request made by this object. (defaults to undefined)
11996      */
11997     /**
11998      * @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)
11999      */
12000     /**
12001      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12002      */
12003      /**
12004      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12005      * @type Boolean
12006      */
12007   
12008
12009     /**
12010      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12011      * @type Boolean
12012      */
12013     /**
12014      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12015      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12016      * a finer-grained basis than the DataProxy events.
12017      */
12018     getConnection : function(){
12019         return this.useAjax ? Roo.Ajax : this.conn;
12020     },
12021
12022     /**
12023      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12024      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12025      * process that block using the passed callback.
12026      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12027      * for the request to the remote server.
12028      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12029      * object into a block of Roo.data.Records.
12030      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12031      * The function must be passed <ul>
12032      * <li>The Record block object</li>
12033      * <li>The "arg" argument from the load function</li>
12034      * <li>A boolean success indicator</li>
12035      * </ul>
12036      * @param {Object} scope The scope in which to call the callback
12037      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12038      */
12039     load : function(params, reader, callback, scope, arg){
12040         if(this.fireEvent("beforeload", this, params) !== false){
12041             var  o = {
12042                 params : params || {},
12043                 request: {
12044                     callback : callback,
12045                     scope : scope,
12046                     arg : arg
12047                 },
12048                 reader: reader,
12049                 callback : this.loadResponse,
12050                 scope: this
12051             };
12052             if(this.useAjax){
12053                 Roo.applyIf(o, this.conn);
12054                 if(this.activeRequest){
12055                     Roo.Ajax.abort(this.activeRequest);
12056                 }
12057                 this.activeRequest = Roo.Ajax.request(o);
12058             }else{
12059                 this.conn.request(o);
12060             }
12061         }else{
12062             callback.call(scope||this, null, arg, false);
12063         }
12064     },
12065
12066     // private
12067     loadResponse : function(o, success, response){
12068         delete this.activeRequest;
12069         if(!success){
12070             this.fireEvent("loadexception", this, o, response);
12071             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12072             return;
12073         }
12074         var result;
12075         try {
12076             result = o.reader.read(response);
12077         }catch(e){
12078             this.fireEvent("loadexception", this, o, response, e);
12079             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12080             return;
12081         }
12082         
12083         this.fireEvent("load", this, o, o.request.arg);
12084         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12085     },
12086
12087     // private
12088     update : function(dataSet){
12089
12090     },
12091
12092     // private
12093     updateResponse : function(dataSet){
12094
12095     }
12096 });/*
12097  * Based on:
12098  * Ext JS Library 1.1.1
12099  * Copyright(c) 2006-2007, Ext JS, LLC.
12100  *
12101  * Originally Released Under LGPL - original licence link has changed is not relivant.
12102  *
12103  * Fork - LGPL
12104  * <script type="text/javascript">
12105  */
12106
12107 /**
12108  * @class Roo.data.ScriptTagProxy
12109  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12110  * other than the originating domain of the running page.<br><br>
12111  * <p>
12112  * <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
12113  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12114  * <p>
12115  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12116  * source code that is used as the source inside a &lt;script> tag.<br><br>
12117  * <p>
12118  * In order for the browser to process the returned data, the server must wrap the data object
12119  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12120  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12121  * depending on whether the callback name was passed:
12122  * <p>
12123  * <pre><code>
12124 boolean scriptTag = false;
12125 String cb = request.getParameter("callback");
12126 if (cb != null) {
12127     scriptTag = true;
12128     response.setContentType("text/javascript");
12129 } else {
12130     response.setContentType("application/x-json");
12131 }
12132 Writer out = response.getWriter();
12133 if (scriptTag) {
12134     out.write(cb + "(");
12135 }
12136 out.print(dataBlock.toJsonString());
12137 if (scriptTag) {
12138     out.write(");");
12139 }
12140 </pre></code>
12141  *
12142  * @constructor
12143  * @param {Object} config A configuration object.
12144  */
12145 Roo.data.ScriptTagProxy = function(config){
12146     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12147     Roo.apply(this, config);
12148     this.head = document.getElementsByTagName("head")[0];
12149 };
12150
12151 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12152
12153 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12154     /**
12155      * @cfg {String} url The URL from which to request the data object.
12156      */
12157     /**
12158      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12159      */
12160     timeout : 30000,
12161     /**
12162      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12163      * the server the name of the callback function set up by the load call to process the returned data object.
12164      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12165      * javascript output which calls this named function passing the data object as its only parameter.
12166      */
12167     callbackParam : "callback",
12168     /**
12169      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12170      * name to the request.
12171      */
12172     nocache : true,
12173
12174     /**
12175      * Load data from the configured URL, read the data object into
12176      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12177      * process that block using the passed callback.
12178      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12179      * for the request to the remote server.
12180      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12181      * object into a block of Roo.data.Records.
12182      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12183      * The function must be passed <ul>
12184      * <li>The Record block object</li>
12185      * <li>The "arg" argument from the load function</li>
12186      * <li>A boolean success indicator</li>
12187      * </ul>
12188      * @param {Object} scope The scope in which to call the callback
12189      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12190      */
12191     load : function(params, reader, callback, scope, arg){
12192         if(this.fireEvent("beforeload", this, params) !== false){
12193
12194             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12195
12196             var url = this.url;
12197             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12198             if(this.nocache){
12199                 url += "&_dc=" + (new Date().getTime());
12200             }
12201             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12202             var trans = {
12203                 id : transId,
12204                 cb : "stcCallback"+transId,
12205                 scriptId : "stcScript"+transId,
12206                 params : params,
12207                 arg : arg,
12208                 url : url,
12209                 callback : callback,
12210                 scope : scope,
12211                 reader : reader
12212             };
12213             var conn = this;
12214
12215             window[trans.cb] = function(o){
12216                 conn.handleResponse(o, trans);
12217             };
12218
12219             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12220
12221             if(this.autoAbort !== false){
12222                 this.abort();
12223             }
12224
12225             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12226
12227             var script = document.createElement("script");
12228             script.setAttribute("src", url);
12229             script.setAttribute("type", "text/javascript");
12230             script.setAttribute("id", trans.scriptId);
12231             this.head.appendChild(script);
12232
12233             this.trans = trans;
12234         }else{
12235             callback.call(scope||this, null, arg, false);
12236         }
12237     },
12238
12239     // private
12240     isLoading : function(){
12241         return this.trans ? true : false;
12242     },
12243
12244     /**
12245      * Abort the current server request.
12246      */
12247     abort : function(){
12248         if(this.isLoading()){
12249             this.destroyTrans(this.trans);
12250         }
12251     },
12252
12253     // private
12254     destroyTrans : function(trans, isLoaded){
12255         this.head.removeChild(document.getElementById(trans.scriptId));
12256         clearTimeout(trans.timeoutId);
12257         if(isLoaded){
12258             window[trans.cb] = undefined;
12259             try{
12260                 delete window[trans.cb];
12261             }catch(e){}
12262         }else{
12263             // if hasn't been loaded, wait for load to remove it to prevent script error
12264             window[trans.cb] = function(){
12265                 window[trans.cb] = undefined;
12266                 try{
12267                     delete window[trans.cb];
12268                 }catch(e){}
12269             };
12270         }
12271     },
12272
12273     // private
12274     handleResponse : function(o, trans){
12275         this.trans = false;
12276         this.destroyTrans(trans, true);
12277         var result;
12278         try {
12279             result = trans.reader.readRecords(o);
12280         }catch(e){
12281             this.fireEvent("loadexception", this, o, trans.arg, e);
12282             trans.callback.call(trans.scope||window, null, trans.arg, false);
12283             return;
12284         }
12285         this.fireEvent("load", this, o, trans.arg);
12286         trans.callback.call(trans.scope||window, result, trans.arg, true);
12287     },
12288
12289     // private
12290     handleFailure : function(trans){
12291         this.trans = false;
12292         this.destroyTrans(trans, false);
12293         this.fireEvent("loadexception", this, null, trans.arg);
12294         trans.callback.call(trans.scope||window, null, trans.arg, false);
12295     }
12296 });/*
12297  * Based on:
12298  * Ext JS Library 1.1.1
12299  * Copyright(c) 2006-2007, Ext JS, LLC.
12300  *
12301  * Originally Released Under LGPL - original licence link has changed is not relivant.
12302  *
12303  * Fork - LGPL
12304  * <script type="text/javascript">
12305  */
12306
12307 /**
12308  * @class Roo.data.JsonReader
12309  * @extends Roo.data.DataReader
12310  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12311  * based on mappings in a provided Roo.data.Record constructor.
12312  * 
12313  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12314  * in the reply previously. 
12315  * 
12316  * <p>
12317  * Example code:
12318  * <pre><code>
12319 var RecordDef = Roo.data.Record.create([
12320     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12321     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12322 ]);
12323 var myReader = new Roo.data.JsonReader({
12324     totalProperty: "results",    // The property which contains the total dataset size (optional)
12325     root: "rows",                // The property which contains an Array of row objects
12326     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12327 }, RecordDef);
12328 </code></pre>
12329  * <p>
12330  * This would consume a JSON file like this:
12331  * <pre><code>
12332 { 'results': 2, 'rows': [
12333     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12334     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12335 }
12336 </code></pre>
12337  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12338  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12339  * paged from the remote server.
12340  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12341  * @cfg {String} root name of the property which contains the Array of row objects.
12342  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12343  * @cfg {Array} fields Array of field definition objects
12344  * @constructor
12345  * Create a new JsonReader
12346  * @param {Object} meta Metadata configuration options
12347  * @param {Object} recordType Either an Array of field definition objects,
12348  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12349  */
12350 Roo.data.JsonReader = function(meta, recordType){
12351     
12352     meta = meta || {};
12353     // set some defaults:
12354     Roo.applyIf(meta, {
12355         totalProperty: 'total',
12356         successProperty : 'success',
12357         root : 'data',
12358         id : 'id'
12359     });
12360     
12361     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12362 };
12363 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12364     
12365     /**
12366      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12367      * Used by Store query builder to append _requestMeta to params.
12368      * 
12369      */
12370     metaFromRemote : false,
12371     /**
12372      * This method is only used by a DataProxy which has retrieved data from a remote server.
12373      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12374      * @return {Object} data A data block which is used by an Roo.data.Store object as
12375      * a cache of Roo.data.Records.
12376      */
12377     read : function(response){
12378         var json = response.responseText;
12379        
12380         var o = /* eval:var:o */ eval("("+json+")");
12381         if(!o) {
12382             throw {message: "JsonReader.read: Json object not found"};
12383         }
12384         
12385         if(o.metaData){
12386             
12387             delete this.ef;
12388             this.metaFromRemote = true;
12389             this.meta = o.metaData;
12390             this.recordType = Roo.data.Record.create(o.metaData.fields);
12391             this.onMetaChange(this.meta, this.recordType, o);
12392         }
12393         return this.readRecords(o);
12394     },
12395
12396     // private function a store will implement
12397     onMetaChange : function(meta, recordType, o){
12398
12399     },
12400
12401     /**
12402          * @ignore
12403          */
12404     simpleAccess: function(obj, subsc) {
12405         return obj[subsc];
12406     },
12407
12408         /**
12409          * @ignore
12410          */
12411     getJsonAccessor: function(){
12412         var re = /[\[\.]/;
12413         return function(expr) {
12414             try {
12415                 return(re.test(expr))
12416                     ? new Function("obj", "return obj." + expr)
12417                     : function(obj){
12418                         return obj[expr];
12419                     };
12420             } catch(e){}
12421             return Roo.emptyFn;
12422         };
12423     }(),
12424
12425     /**
12426      * Create a data block containing Roo.data.Records from an XML document.
12427      * @param {Object} o An object which contains an Array of row objects in the property specified
12428      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12429      * which contains the total size of the dataset.
12430      * @return {Object} data A data block which is used by an Roo.data.Store object as
12431      * a cache of Roo.data.Records.
12432      */
12433     readRecords : function(o){
12434         /**
12435          * After any data loads, the raw JSON data is available for further custom processing.
12436          * @type Object
12437          */
12438         this.o = o;
12439         var s = this.meta, Record = this.recordType,
12440             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12441
12442 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12443         if (!this.ef) {
12444             if(s.totalProperty) {
12445                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12446                 }
12447                 if(s.successProperty) {
12448                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12449                 }
12450                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12451                 if (s.id) {
12452                         var g = this.getJsonAccessor(s.id);
12453                         this.getId = function(rec) {
12454                                 var r = g(rec);  
12455                                 return (r === undefined || r === "") ? null : r;
12456                         };
12457                 } else {
12458                         this.getId = function(){return null;};
12459                 }
12460             this.ef = [];
12461             for(var jj = 0; jj < fl; jj++){
12462                 f = fi[jj];
12463                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12464                 this.ef[jj] = this.getJsonAccessor(map);
12465             }
12466         }
12467
12468         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12469         if(s.totalProperty){
12470             var vt = parseInt(this.getTotal(o), 10);
12471             if(!isNaN(vt)){
12472                 totalRecords = vt;
12473             }
12474         }
12475         if(s.successProperty){
12476             var vs = this.getSuccess(o);
12477             if(vs === false || vs === 'false'){
12478                 success = false;
12479             }
12480         }
12481         var records = [];
12482         for(var i = 0; i < c; i++){
12483                 var n = root[i];
12484             var values = {};
12485             var id = this.getId(n);
12486             for(var j = 0; j < fl; j++){
12487                 f = fi[j];
12488             var v = this.ef[j](n);
12489             if (!f.convert) {
12490                 Roo.log('missing convert for ' + f.name);
12491                 Roo.log(f);
12492                 continue;
12493             }
12494             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12495             }
12496             var record = new Record(values, id);
12497             record.json = n;
12498             records[i] = record;
12499         }
12500         return {
12501             raw : o,
12502             success : success,
12503             records : records,
12504             totalRecords : totalRecords
12505         };
12506     }
12507 });/*
12508  * Based on:
12509  * Ext JS Library 1.1.1
12510  * Copyright(c) 2006-2007, Ext JS, LLC.
12511  *
12512  * Originally Released Under LGPL - original licence link has changed is not relivant.
12513  *
12514  * Fork - LGPL
12515  * <script type="text/javascript">
12516  */
12517
12518 /**
12519  * @class Roo.data.ArrayReader
12520  * @extends Roo.data.DataReader
12521  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12522  * Each element of that Array represents a row of data fields. The
12523  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12524  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12525  * <p>
12526  * Example code:.
12527  * <pre><code>
12528 var RecordDef = Roo.data.Record.create([
12529     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12530     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12531 ]);
12532 var myReader = new Roo.data.ArrayReader({
12533     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12534 }, RecordDef);
12535 </code></pre>
12536  * <p>
12537  * This would consume an Array like this:
12538  * <pre><code>
12539 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12540   </code></pre>
12541  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12542  * @constructor
12543  * Create a new JsonReader
12544  * @param {Object} meta Metadata configuration options.
12545  * @param {Object} recordType Either an Array of field definition objects
12546  * as specified to {@link Roo.data.Record#create},
12547  * or an {@link Roo.data.Record} object
12548  * created using {@link Roo.data.Record#create}.
12549  */
12550 Roo.data.ArrayReader = function(meta, recordType){
12551     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12552 };
12553
12554 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12555     /**
12556      * Create a data block containing Roo.data.Records from an XML document.
12557      * @param {Object} o An Array of row objects which represents the dataset.
12558      * @return {Object} data A data block which is used by an Roo.data.Store object as
12559      * a cache of Roo.data.Records.
12560      */
12561     readRecords : function(o){
12562         var sid = this.meta ? this.meta.id : null;
12563         var recordType = this.recordType, fields = recordType.prototype.fields;
12564         var records = [];
12565         var root = o;
12566             for(var i = 0; i < root.length; i++){
12567                     var n = root[i];
12568                 var values = {};
12569                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12570                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12571                 var f = fields.items[j];
12572                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12573                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12574                 v = f.convert(v);
12575                 values[f.name] = v;
12576             }
12577                 var record = new recordType(values, id);
12578                 record.json = n;
12579                 records[records.length] = record;
12580             }
12581             return {
12582                 records : records,
12583                 totalRecords : records.length
12584             };
12585     }
12586 });/*
12587  * - LGPL
12588  * * 
12589  */
12590
12591 /**
12592  * @class Roo.bootstrap.ComboBox
12593  * @extends Roo.bootstrap.TriggerField
12594  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12595  * @cfg {Boolean} append (true|false) default false
12596  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12597  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12598  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12599  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12600  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12601  * @cfg {Boolean} animate default true
12602  * @cfg {Boolean} emptyResultText only for touch device
12603  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12604  * @cfg {String} emptyTitle default ''
12605  * @constructor
12606  * Create a new ComboBox.
12607  * @param {Object} config Configuration options
12608  */
12609 Roo.bootstrap.ComboBox = function(config){
12610     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12611     this.addEvents({
12612         /**
12613          * @event expand
12614          * Fires when the dropdown list is expanded
12615         * @param {Roo.bootstrap.ComboBox} combo This combo box
12616         */
12617         'expand' : true,
12618         /**
12619          * @event collapse
12620          * Fires when the dropdown list is collapsed
12621         * @param {Roo.bootstrap.ComboBox} combo This combo box
12622         */
12623         'collapse' : true,
12624         /**
12625          * @event beforeselect
12626          * Fires before a list item is selected. Return false to cancel the selection.
12627         * @param {Roo.bootstrap.ComboBox} combo This combo box
12628         * @param {Roo.data.Record} record The data record returned from the underlying store
12629         * @param {Number} index The index of the selected item in the dropdown list
12630         */
12631         'beforeselect' : true,
12632         /**
12633          * @event select
12634          * Fires when a list item is selected
12635         * @param {Roo.bootstrap.ComboBox} combo This combo box
12636         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12637         * @param {Number} index The index of the selected item in the dropdown list
12638         */
12639         'select' : true,
12640         /**
12641          * @event beforequery
12642          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12643          * The event object passed has these properties:
12644         * @param {Roo.bootstrap.ComboBox} combo This combo box
12645         * @param {String} query The query
12646         * @param {Boolean} forceAll true to force "all" query
12647         * @param {Boolean} cancel true to cancel the query
12648         * @param {Object} e The query event object
12649         */
12650         'beforequery': true,
12651          /**
12652          * @event add
12653          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12654         * @param {Roo.bootstrap.ComboBox} combo This combo box
12655         */
12656         'add' : true,
12657         /**
12658          * @event edit
12659          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12660         * @param {Roo.bootstrap.ComboBox} combo This combo box
12661         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12662         */
12663         'edit' : true,
12664         /**
12665          * @event remove
12666          * Fires when the remove value from the combobox array
12667         * @param {Roo.bootstrap.ComboBox} combo This combo box
12668         */
12669         'remove' : true,
12670         /**
12671          * @event afterremove
12672          * Fires when the remove value from the combobox array
12673         * @param {Roo.bootstrap.ComboBox} combo This combo box
12674         */
12675         'afterremove' : true,
12676         /**
12677          * @event specialfilter
12678          * Fires when specialfilter
12679             * @param {Roo.bootstrap.ComboBox} combo This combo box
12680             */
12681         'specialfilter' : true,
12682         /**
12683          * @event tick
12684          * Fires when tick the element
12685             * @param {Roo.bootstrap.ComboBox} combo This combo box
12686             */
12687         'tick' : true,
12688         /**
12689          * @event touchviewdisplay
12690          * Fires when touch view require special display (default is using displayField)
12691             * @param {Roo.bootstrap.ComboBox} combo This combo box
12692             * @param {Object} cfg set html .
12693             */
12694         'touchviewdisplay' : true
12695         
12696     });
12697     
12698     this.item = [];
12699     this.tickItems = [];
12700     
12701     this.selectedIndex = -1;
12702     if(this.mode == 'local'){
12703         if(config.queryDelay === undefined){
12704             this.queryDelay = 10;
12705         }
12706         if(config.minChars === undefined){
12707             this.minChars = 0;
12708         }
12709     }
12710 };
12711
12712 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12713      
12714     /**
12715      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12716      * rendering into an Roo.Editor, defaults to false)
12717      */
12718     /**
12719      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12720      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12721      */
12722     /**
12723      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12724      */
12725     /**
12726      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12727      * the dropdown list (defaults to undefined, with no header element)
12728      */
12729
12730      /**
12731      * @cfg {String/Roo.Template} tpl The template to use to render the output
12732      */
12733      
12734      /**
12735      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12736      */
12737     listWidth: undefined,
12738     /**
12739      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12740      * mode = 'remote' or 'text' if mode = 'local')
12741      */
12742     displayField: undefined,
12743     
12744     /**
12745      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12746      * mode = 'remote' or 'value' if mode = 'local'). 
12747      * Note: use of a valueField requires the user make a selection
12748      * in order for a value to be mapped.
12749      */
12750     valueField: undefined,
12751     /**
12752      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12753      */
12754     modalTitle : '',
12755     
12756     /**
12757      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12758      * field's data value (defaults to the underlying DOM element's name)
12759      */
12760     hiddenName: undefined,
12761     /**
12762      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12763      */
12764     listClass: '',
12765     /**
12766      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12767      */
12768     selectedClass: 'active',
12769     
12770     /**
12771      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12772      */
12773     shadow:'sides',
12774     /**
12775      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12776      * anchor positions (defaults to 'tl-bl')
12777      */
12778     listAlign: 'tl-bl?',
12779     /**
12780      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12781      */
12782     maxHeight: 300,
12783     /**
12784      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12785      * query specified by the allQuery config option (defaults to 'query')
12786      */
12787     triggerAction: 'query',
12788     /**
12789      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12790      * (defaults to 4, does not apply if editable = false)
12791      */
12792     minChars : 4,
12793     /**
12794      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12795      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12796      */
12797     typeAhead: false,
12798     /**
12799      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12800      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12801      */
12802     queryDelay: 500,
12803     /**
12804      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12805      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12806      */
12807     pageSize: 0,
12808     /**
12809      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12810      * when editable = true (defaults to false)
12811      */
12812     selectOnFocus:false,
12813     /**
12814      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12815      */
12816     queryParam: 'query',
12817     /**
12818      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12819      * when mode = 'remote' (defaults to 'Loading...')
12820      */
12821     loadingText: 'Loading...',
12822     /**
12823      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12824      */
12825     resizable: false,
12826     /**
12827      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12828      */
12829     handleHeight : 8,
12830     /**
12831      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12832      * traditional select (defaults to true)
12833      */
12834     editable: true,
12835     /**
12836      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12837      */
12838     allQuery: '',
12839     /**
12840      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12841      */
12842     mode: 'remote',
12843     /**
12844      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12845      * listWidth has a higher value)
12846      */
12847     minListWidth : 70,
12848     /**
12849      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12850      * allow the user to set arbitrary text into the field (defaults to false)
12851      */
12852     forceSelection:false,
12853     /**
12854      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12855      * if typeAhead = true (defaults to 250)
12856      */
12857     typeAheadDelay : 250,
12858     /**
12859      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12860      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12861      */
12862     valueNotFoundText : undefined,
12863     /**
12864      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12865      */
12866     blockFocus : false,
12867     
12868     /**
12869      * @cfg {Boolean} disableClear Disable showing of clear button.
12870      */
12871     disableClear : false,
12872     /**
12873      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12874      */
12875     alwaysQuery : false,
12876     
12877     /**
12878      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12879      */
12880     multiple : false,
12881     
12882     /**
12883      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12884      */
12885     invalidClass : "has-warning",
12886     
12887     /**
12888      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12889      */
12890     validClass : "has-success",
12891     
12892     /**
12893      * @cfg {Boolean} specialFilter (true|false) special filter default false
12894      */
12895     specialFilter : false,
12896     
12897     /**
12898      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12899      */
12900     mobileTouchView : true,
12901     
12902     /**
12903      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12904      */
12905     useNativeIOS : false,
12906     
12907     ios_options : false,
12908     
12909     //private
12910     addicon : false,
12911     editicon: false,
12912     
12913     page: 0,
12914     hasQuery: false,
12915     append: false,
12916     loadNext: false,
12917     autoFocus : true,
12918     tickable : false,
12919     btnPosition : 'right',
12920     triggerList : true,
12921     showToggleBtn : true,
12922     animate : true,
12923     emptyResultText: 'Empty',
12924     triggerText : 'Select',
12925     emptyTitle : '',
12926     
12927     // element that contains real text value.. (when hidden is used..)
12928     
12929     getAutoCreate : function()
12930     {   
12931         var cfg = false;
12932         //render
12933         /*
12934          * Render classic select for iso
12935          */
12936         
12937         if(Roo.isIOS && this.useNativeIOS){
12938             cfg = this.getAutoCreateNativeIOS();
12939             return cfg;
12940         }
12941         
12942         /*
12943          * Touch Devices
12944          */
12945         
12946         if(Roo.isTouch && this.mobileTouchView){
12947             cfg = this.getAutoCreateTouchView();
12948             return cfg;;
12949         }
12950         
12951         /*
12952          *  Normal ComboBox
12953          */
12954         if(!this.tickable){
12955             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12956             return cfg;
12957         }
12958         
12959         /*
12960          *  ComboBox with tickable selections
12961          */
12962              
12963         var align = this.labelAlign || this.parentLabelAlign();
12964         
12965         cfg = {
12966             cls : 'form-group roo-combobox-tickable' //input-group
12967         };
12968         
12969         var btn_text_select = '';
12970         var btn_text_done = '';
12971         var btn_text_cancel = '';
12972         
12973         if (this.btn_text_show) {
12974             btn_text_select = 'Select';
12975             btn_text_done = 'Done';
12976             btn_text_cancel = 'Cancel'; 
12977         }
12978         
12979         var buttons = {
12980             tag : 'div',
12981             cls : 'tickable-buttons',
12982             cn : [
12983                 {
12984                     tag : 'button',
12985                     type : 'button',
12986                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12987                     //html : this.triggerText
12988                     html: btn_text_select
12989                 },
12990                 {
12991                     tag : 'button',
12992                     type : 'button',
12993                     name : 'ok',
12994                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12995                     //html : 'Done'
12996                     html: btn_text_done
12997                 },
12998                 {
12999                     tag : 'button',
13000                     type : 'button',
13001                     name : 'cancel',
13002                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13003                     //html : 'Cancel'
13004                     html: btn_text_cancel
13005                 }
13006             ]
13007         };
13008         
13009         if(this.editable){
13010             buttons.cn.unshift({
13011                 tag: 'input',
13012                 cls: 'roo-select2-search-field-input'
13013             });
13014         }
13015         
13016         var _this = this;
13017         
13018         Roo.each(buttons.cn, function(c){
13019             if (_this.size) {
13020                 c.cls += ' btn-' + _this.size;
13021             }
13022
13023             if (_this.disabled) {
13024                 c.disabled = true;
13025             }
13026         });
13027         
13028         var box = {
13029             tag: 'div',
13030             cn: [
13031                 {
13032                     tag: 'input',
13033                     type : 'hidden',
13034                     cls: 'form-hidden-field'
13035                 },
13036                 {
13037                     tag: 'ul',
13038                     cls: 'roo-select2-choices',
13039                     cn:[
13040                         {
13041                             tag: 'li',
13042                             cls: 'roo-select2-search-field',
13043                             cn: [
13044                                 buttons
13045                             ]
13046                         }
13047                     ]
13048                 }
13049             ]
13050         };
13051         
13052         var combobox = {
13053             cls: 'roo-select2-container input-group roo-select2-container-multi',
13054             cn: [
13055                 box
13056 //                {
13057 //                    tag: 'ul',
13058 //                    cls: 'typeahead typeahead-long dropdown-menu',
13059 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13060 //                }
13061             ]
13062         };
13063         
13064         if(this.hasFeedback && !this.allowBlank){
13065             
13066             var feedback = {
13067                 tag: 'span',
13068                 cls: 'glyphicon form-control-feedback'
13069             };
13070
13071             combobox.cn.push(feedback);
13072         }
13073         
13074         
13075         if (align ==='left' && this.fieldLabel.length) {
13076             
13077             cfg.cls += ' roo-form-group-label-left';
13078             
13079             cfg.cn = [
13080                 {
13081                     tag : 'i',
13082                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13083                     tooltip : 'This field is required'
13084                 },
13085                 {
13086                     tag: 'label',
13087                     'for' :  id,
13088                     cls : 'control-label',
13089                     html : this.fieldLabel
13090
13091                 },
13092                 {
13093                     cls : "", 
13094                     cn: [
13095                         combobox
13096                     ]
13097                 }
13098
13099             ];
13100             
13101             var labelCfg = cfg.cn[1];
13102             var contentCfg = cfg.cn[2];
13103             
13104
13105             if(this.indicatorpos == 'right'){
13106                 
13107                 cfg.cn = [
13108                     {
13109                         tag: 'label',
13110                         'for' :  id,
13111                         cls : 'control-label',
13112                         cn : [
13113                             {
13114                                 tag : 'span',
13115                                 html : this.fieldLabel
13116                             },
13117                             {
13118                                 tag : 'i',
13119                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13120                                 tooltip : 'This field is required'
13121                             }
13122                         ]
13123                     },
13124                     {
13125                         cls : "",
13126                         cn: [
13127                             combobox
13128                         ]
13129                     }
13130
13131                 ];
13132                 
13133                 
13134                 
13135                 labelCfg = cfg.cn[0];
13136                 contentCfg = cfg.cn[1];
13137             
13138             }
13139             
13140             if(this.labelWidth > 12){
13141                 labelCfg.style = "width: " + this.labelWidth + 'px';
13142             }
13143             
13144             if(this.labelWidth < 13 && this.labelmd == 0){
13145                 this.labelmd = this.labelWidth;
13146             }
13147             
13148             if(this.labellg > 0){
13149                 labelCfg.cls += ' col-lg-' + this.labellg;
13150                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13151             }
13152             
13153             if(this.labelmd > 0){
13154                 labelCfg.cls += ' col-md-' + this.labelmd;
13155                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13156             }
13157             
13158             if(this.labelsm > 0){
13159                 labelCfg.cls += ' col-sm-' + this.labelsm;
13160                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13161             }
13162             
13163             if(this.labelxs > 0){
13164                 labelCfg.cls += ' col-xs-' + this.labelxs;
13165                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13166             }
13167                 
13168                 
13169         } else if ( this.fieldLabel.length) {
13170 //                Roo.log(" label");
13171                  cfg.cn = [
13172                     {
13173                         tag : 'i',
13174                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13175                         tooltip : 'This field is required'
13176                     },
13177                     {
13178                         tag: 'label',
13179                         //cls : 'input-group-addon',
13180                         html : this.fieldLabel
13181                     },
13182                     combobox
13183                 ];
13184                 
13185                 if(this.indicatorpos == 'right'){
13186                     cfg.cn = [
13187                         {
13188                             tag: 'label',
13189                             //cls : 'input-group-addon',
13190                             html : this.fieldLabel
13191                         },
13192                         {
13193                             tag : 'i',
13194                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13195                             tooltip : 'This field is required'
13196                         },
13197                         combobox
13198                     ];
13199                     
13200                 }
13201
13202         } else {
13203             
13204 //                Roo.log(" no label && no align");
13205                 cfg = combobox
13206                      
13207                 
13208         }
13209          
13210         var settings=this;
13211         ['xs','sm','md','lg'].map(function(size){
13212             if (settings[size]) {
13213                 cfg.cls += ' col-' + size + '-' + settings[size];
13214             }
13215         });
13216         
13217         return cfg;
13218         
13219     },
13220     
13221     _initEventsCalled : false,
13222     
13223     // private
13224     initEvents: function()
13225     {   
13226         if (this._initEventsCalled) { // as we call render... prevent looping...
13227             return;
13228         }
13229         this._initEventsCalled = true;
13230         
13231         if (!this.store) {
13232             throw "can not find store for combo";
13233         }
13234         
13235         this.indicator = this.indicatorEl();
13236         
13237         this.store = Roo.factory(this.store, Roo.data);
13238         this.store.parent = this;
13239         
13240         // if we are building from html. then this element is so complex, that we can not really
13241         // use the rendered HTML.
13242         // so we have to trash and replace the previous code.
13243         if (Roo.XComponent.build_from_html) {
13244             // remove this element....
13245             var e = this.el.dom, k=0;
13246             while (e ) { e = e.previousSibling;  ++k;}
13247
13248             this.el.remove();
13249             
13250             this.el=false;
13251             this.rendered = false;
13252             
13253             this.render(this.parent().getChildContainer(true), k);
13254         }
13255         
13256         if(Roo.isIOS && this.useNativeIOS){
13257             this.initIOSView();
13258             return;
13259         }
13260         
13261         /*
13262          * Touch Devices
13263          */
13264         
13265         if(Roo.isTouch && this.mobileTouchView){
13266             this.initTouchView();
13267             return;
13268         }
13269         
13270         if(this.tickable){
13271             this.initTickableEvents();
13272             return;
13273         }
13274         
13275         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13276         
13277         if(this.hiddenName){
13278             
13279             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13280             
13281             this.hiddenField.dom.value =
13282                 this.hiddenValue !== undefined ? this.hiddenValue :
13283                 this.value !== undefined ? this.value : '';
13284
13285             // prevent input submission
13286             this.el.dom.removeAttribute('name');
13287             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13288              
13289              
13290         }
13291         //if(Roo.isGecko){
13292         //    this.el.dom.setAttribute('autocomplete', 'off');
13293         //}
13294         
13295         var cls = 'x-combo-list';
13296         
13297         //this.list = new Roo.Layer({
13298         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13299         //});
13300         
13301         var _this = this;
13302         
13303         (function(){
13304             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13305             _this.list.setWidth(lw);
13306         }).defer(100);
13307         
13308         this.list.on('mouseover', this.onViewOver, this);
13309         this.list.on('mousemove', this.onViewMove, this);
13310         this.list.on('scroll', this.onViewScroll, this);
13311         
13312         /*
13313         this.list.swallowEvent('mousewheel');
13314         this.assetHeight = 0;
13315
13316         if(this.title){
13317             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13318             this.assetHeight += this.header.getHeight();
13319         }
13320
13321         this.innerList = this.list.createChild({cls:cls+'-inner'});
13322         this.innerList.on('mouseover', this.onViewOver, this);
13323         this.innerList.on('mousemove', this.onViewMove, this);
13324         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13325         
13326         if(this.allowBlank && !this.pageSize && !this.disableClear){
13327             this.footer = this.list.createChild({cls:cls+'-ft'});
13328             this.pageTb = new Roo.Toolbar(this.footer);
13329            
13330         }
13331         if(this.pageSize){
13332             this.footer = this.list.createChild({cls:cls+'-ft'});
13333             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13334                     {pageSize: this.pageSize});
13335             
13336         }
13337         
13338         if (this.pageTb && this.allowBlank && !this.disableClear) {
13339             var _this = this;
13340             this.pageTb.add(new Roo.Toolbar.Fill(), {
13341                 cls: 'x-btn-icon x-btn-clear',
13342                 text: '&#160;',
13343                 handler: function()
13344                 {
13345                     _this.collapse();
13346                     _this.clearValue();
13347                     _this.onSelect(false, -1);
13348                 }
13349             });
13350         }
13351         if (this.footer) {
13352             this.assetHeight += this.footer.getHeight();
13353         }
13354         */
13355             
13356         if(!this.tpl){
13357             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13358         }
13359
13360         this.view = new Roo.View(this.list, this.tpl, {
13361             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13362         });
13363         //this.view.wrapEl.setDisplayed(false);
13364         this.view.on('click', this.onViewClick, this);
13365         
13366         
13367         this.store.on('beforeload', this.onBeforeLoad, this);
13368         this.store.on('load', this.onLoad, this);
13369         this.store.on('loadexception', this.onLoadException, this);
13370         /*
13371         if(this.resizable){
13372             this.resizer = new Roo.Resizable(this.list,  {
13373                pinned:true, handles:'se'
13374             });
13375             this.resizer.on('resize', function(r, w, h){
13376                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13377                 this.listWidth = w;
13378                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13379                 this.restrictHeight();
13380             }, this);
13381             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13382         }
13383         */
13384         if(!this.editable){
13385             this.editable = true;
13386             this.setEditable(false);
13387         }
13388         
13389         /*
13390         
13391         if (typeof(this.events.add.listeners) != 'undefined') {
13392             
13393             this.addicon = this.wrap.createChild(
13394                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13395        
13396             this.addicon.on('click', function(e) {
13397                 this.fireEvent('add', this);
13398             }, this);
13399         }
13400         if (typeof(this.events.edit.listeners) != 'undefined') {
13401             
13402             this.editicon = this.wrap.createChild(
13403                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13404             if (this.addicon) {
13405                 this.editicon.setStyle('margin-left', '40px');
13406             }
13407             this.editicon.on('click', function(e) {
13408                 
13409                 // we fire even  if inothing is selected..
13410                 this.fireEvent('edit', this, this.lastData );
13411                 
13412             }, this);
13413         }
13414         */
13415         
13416         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13417             "up" : function(e){
13418                 this.inKeyMode = true;
13419                 this.selectPrev();
13420             },
13421
13422             "down" : function(e){
13423                 if(!this.isExpanded()){
13424                     this.onTriggerClick();
13425                 }else{
13426                     this.inKeyMode = true;
13427                     this.selectNext();
13428                 }
13429             },
13430
13431             "enter" : function(e){
13432 //                this.onViewClick();
13433                 //return true;
13434                 this.collapse();
13435                 
13436                 if(this.fireEvent("specialkey", this, e)){
13437                     this.onViewClick(false);
13438                 }
13439                 
13440                 return true;
13441             },
13442
13443             "esc" : function(e){
13444                 this.collapse();
13445             },
13446
13447             "tab" : function(e){
13448                 this.collapse();
13449                 
13450                 if(this.fireEvent("specialkey", this, e)){
13451                     this.onViewClick(false);
13452                 }
13453                 
13454                 return true;
13455             },
13456
13457             scope : this,
13458
13459             doRelay : function(foo, bar, hname){
13460                 if(hname == 'down' || this.scope.isExpanded()){
13461                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13462                 }
13463                 return true;
13464             },
13465
13466             forceKeyDown: true
13467         });
13468         
13469         
13470         this.queryDelay = Math.max(this.queryDelay || 10,
13471                 this.mode == 'local' ? 10 : 250);
13472         
13473         
13474         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13475         
13476         if(this.typeAhead){
13477             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13478         }
13479         if(this.editable !== false){
13480             this.inputEl().on("keyup", this.onKeyUp, this);
13481         }
13482         if(this.forceSelection){
13483             this.inputEl().on('blur', this.doForce, this);
13484         }
13485         
13486         if(this.multiple){
13487             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13488             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13489         }
13490     },
13491     
13492     initTickableEvents: function()
13493     {   
13494         this.createList();
13495         
13496         if(this.hiddenName){
13497             
13498             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13499             
13500             this.hiddenField.dom.value =
13501                 this.hiddenValue !== undefined ? this.hiddenValue :
13502                 this.value !== undefined ? this.value : '';
13503
13504             // prevent input submission
13505             this.el.dom.removeAttribute('name');
13506             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13507              
13508              
13509         }
13510         
13511 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13512         
13513         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13514         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13515         if(this.triggerList){
13516             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13517         }
13518          
13519         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13520         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13521         
13522         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13523         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13524         
13525         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13526         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13527         
13528         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13529         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13530         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13531         
13532         this.okBtn.hide();
13533         this.cancelBtn.hide();
13534         
13535         var _this = this;
13536         
13537         (function(){
13538             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13539             _this.list.setWidth(lw);
13540         }).defer(100);
13541         
13542         this.list.on('mouseover', this.onViewOver, this);
13543         this.list.on('mousemove', this.onViewMove, this);
13544         
13545         this.list.on('scroll', this.onViewScroll, this);
13546         
13547         if(!this.tpl){
13548             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>';
13549         }
13550
13551         this.view = new Roo.View(this.list, this.tpl, {
13552             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13553         });
13554         
13555         //this.view.wrapEl.setDisplayed(false);
13556         this.view.on('click', this.onViewClick, this);
13557         
13558         
13559         
13560         this.store.on('beforeload', this.onBeforeLoad, this);
13561         this.store.on('load', this.onLoad, this);
13562         this.store.on('loadexception', this.onLoadException, this);
13563         
13564         if(this.editable){
13565             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13566                 "up" : function(e){
13567                     this.inKeyMode = true;
13568                     this.selectPrev();
13569                 },
13570
13571                 "down" : function(e){
13572                     this.inKeyMode = true;
13573                     this.selectNext();
13574                 },
13575
13576                 "enter" : function(e){
13577                     if(this.fireEvent("specialkey", this, e)){
13578                         this.onViewClick(false);
13579                     }
13580                     
13581                     return true;
13582                 },
13583
13584                 "esc" : function(e){
13585                     this.onTickableFooterButtonClick(e, false, false);
13586                 },
13587
13588                 "tab" : function(e){
13589                     this.fireEvent("specialkey", this, e);
13590                     
13591                     this.onTickableFooterButtonClick(e, false, false);
13592                     
13593                     return true;
13594                 },
13595
13596                 scope : this,
13597
13598                 doRelay : function(e, fn, key){
13599                     if(this.scope.isExpanded()){
13600                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13601                     }
13602                     return true;
13603                 },
13604
13605                 forceKeyDown: true
13606             });
13607         }
13608         
13609         this.queryDelay = Math.max(this.queryDelay || 10,
13610                 this.mode == 'local' ? 10 : 250);
13611         
13612         
13613         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13614         
13615         if(this.typeAhead){
13616             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13617         }
13618         
13619         if(this.editable !== false){
13620             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13621         }
13622         
13623         this.indicator = this.indicatorEl();
13624         
13625         if(this.indicator){
13626             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13627             this.indicator.hide();
13628         }
13629         
13630     },
13631
13632     onDestroy : function(){
13633         if(this.view){
13634             this.view.setStore(null);
13635             this.view.el.removeAllListeners();
13636             this.view.el.remove();
13637             this.view.purgeListeners();
13638         }
13639         if(this.list){
13640             this.list.dom.innerHTML  = '';
13641         }
13642         
13643         if(this.store){
13644             this.store.un('beforeload', this.onBeforeLoad, this);
13645             this.store.un('load', this.onLoad, this);
13646             this.store.un('loadexception', this.onLoadException, this);
13647         }
13648         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13649     },
13650
13651     // private
13652     fireKey : function(e){
13653         if(e.isNavKeyPress() && !this.list.isVisible()){
13654             this.fireEvent("specialkey", this, e);
13655         }
13656     },
13657
13658     // private
13659     onResize: function(w, h){
13660 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13661 //        
13662 //        if(typeof w != 'number'){
13663 //            // we do not handle it!?!?
13664 //            return;
13665 //        }
13666 //        var tw = this.trigger.getWidth();
13667 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13668 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13669 //        var x = w - tw;
13670 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13671 //            
13672 //        //this.trigger.setStyle('left', x+'px');
13673 //        
13674 //        if(this.list && this.listWidth === undefined){
13675 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13676 //            this.list.setWidth(lw);
13677 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13678 //        }
13679         
13680     
13681         
13682     },
13683
13684     /**
13685      * Allow or prevent the user from directly editing the field text.  If false is passed,
13686      * the user will only be able to select from the items defined in the dropdown list.  This method
13687      * is the runtime equivalent of setting the 'editable' config option at config time.
13688      * @param {Boolean} value True to allow the user to directly edit the field text
13689      */
13690     setEditable : function(value){
13691         if(value == this.editable){
13692             return;
13693         }
13694         this.editable = value;
13695         if(!value){
13696             this.inputEl().dom.setAttribute('readOnly', true);
13697             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13698             this.inputEl().addClass('x-combo-noedit');
13699         }else{
13700             this.inputEl().dom.setAttribute('readOnly', false);
13701             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13702             this.inputEl().removeClass('x-combo-noedit');
13703         }
13704     },
13705
13706     // private
13707     
13708     onBeforeLoad : function(combo,opts){
13709         if(!this.hasFocus){
13710             return;
13711         }
13712          if (!opts.add) {
13713             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13714          }
13715         this.restrictHeight();
13716         this.selectedIndex = -1;
13717     },
13718
13719     // private
13720     onLoad : function(){
13721         
13722         this.hasQuery = false;
13723         
13724         if(!this.hasFocus){
13725             return;
13726         }
13727         
13728         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13729             this.loading.hide();
13730         }
13731         
13732         if(this.store.getCount() > 0){
13733             
13734             this.expand();
13735             this.restrictHeight();
13736             if(this.lastQuery == this.allQuery){
13737                 if(this.editable && !this.tickable){
13738                     this.inputEl().dom.select();
13739                 }
13740                 
13741                 if(
13742                     !this.selectByValue(this.value, true) &&
13743                     this.autoFocus && 
13744                     (
13745                         !this.store.lastOptions ||
13746                         typeof(this.store.lastOptions.add) == 'undefined' || 
13747                         this.store.lastOptions.add != true
13748                     )
13749                 ){
13750                     this.select(0, true);
13751                 }
13752             }else{
13753                 if(this.autoFocus){
13754                     this.selectNext();
13755                 }
13756                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13757                     this.taTask.delay(this.typeAheadDelay);
13758                 }
13759             }
13760         }else{
13761             this.onEmptyResults();
13762         }
13763         
13764         //this.el.focus();
13765     },
13766     // private
13767     onLoadException : function()
13768     {
13769         this.hasQuery = false;
13770         
13771         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13772             this.loading.hide();
13773         }
13774         
13775         if(this.tickable && this.editable){
13776             return;
13777         }
13778         
13779         this.collapse();
13780         // only causes errors at present
13781         //Roo.log(this.store.reader.jsonData);
13782         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13783             // fixme
13784             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13785         //}
13786         
13787         
13788     },
13789     // private
13790     onTypeAhead : function(){
13791         if(this.store.getCount() > 0){
13792             var r = this.store.getAt(0);
13793             var newValue = r.data[this.displayField];
13794             var len = newValue.length;
13795             var selStart = this.getRawValue().length;
13796             
13797             if(selStart != len){
13798                 this.setRawValue(newValue);
13799                 this.selectText(selStart, newValue.length);
13800             }
13801         }
13802     },
13803
13804     // private
13805     onSelect : function(record, index){
13806         
13807         if(this.fireEvent('beforeselect', this, record, index) !== false){
13808         
13809             this.setFromData(index > -1 ? record.data : false);
13810             
13811             this.collapse();
13812             this.fireEvent('select', this, record, index);
13813         }
13814     },
13815
13816     /**
13817      * Returns the currently selected field value or empty string if no value is set.
13818      * @return {String} value The selected value
13819      */
13820     getValue : function()
13821     {
13822         if(Roo.isIOS && this.useNativeIOS){
13823             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13824         }
13825         
13826         if(this.multiple){
13827             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13828         }
13829         
13830         if(this.valueField){
13831             return typeof this.value != 'undefined' ? this.value : '';
13832         }else{
13833             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13834         }
13835     },
13836     
13837     getRawValue : function()
13838     {
13839         if(Roo.isIOS && this.useNativeIOS){
13840             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13841         }
13842         
13843         var v = this.inputEl().getValue();
13844         
13845         return v;
13846     },
13847
13848     /**
13849      * Clears any text/value currently set in the field
13850      */
13851     clearValue : function(){
13852         
13853         if(this.hiddenField){
13854             this.hiddenField.dom.value = '';
13855         }
13856         this.value = '';
13857         this.setRawValue('');
13858         this.lastSelectionText = '';
13859         this.lastData = false;
13860         
13861         var close = this.closeTriggerEl();
13862         
13863         if(close){
13864             close.hide();
13865         }
13866         
13867         this.validate();
13868         
13869     },
13870
13871     /**
13872      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13873      * will be displayed in the field.  If the value does not match the data value of an existing item,
13874      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13875      * Otherwise the field will be blank (although the value will still be set).
13876      * @param {String} value The value to match
13877      */
13878     setValue : function(v)
13879     {
13880         if(Roo.isIOS && this.useNativeIOS){
13881             this.setIOSValue(v);
13882             return;
13883         }
13884         
13885         if(this.multiple){
13886             this.syncValue();
13887             return;
13888         }
13889         
13890         var text = v;
13891         if(this.valueField){
13892             var r = this.findRecord(this.valueField, v);
13893             if(r){
13894                 text = r.data[this.displayField];
13895             }else if(this.valueNotFoundText !== undefined){
13896                 text = this.valueNotFoundText;
13897             }
13898         }
13899         this.lastSelectionText = text;
13900         if(this.hiddenField){
13901             this.hiddenField.dom.value = v;
13902         }
13903         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13904         this.value = v;
13905         
13906         var close = this.closeTriggerEl();
13907         
13908         if(close){
13909             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13910         }
13911         
13912         this.validate();
13913     },
13914     /**
13915      * @property {Object} the last set data for the element
13916      */
13917     
13918     lastData : false,
13919     /**
13920      * Sets the value of the field based on a object which is related to the record format for the store.
13921      * @param {Object} value the value to set as. or false on reset?
13922      */
13923     setFromData : function(o){
13924         
13925         if(this.multiple){
13926             this.addItem(o);
13927             return;
13928         }
13929             
13930         var dv = ''; // display value
13931         var vv = ''; // value value..
13932         this.lastData = o;
13933         if (this.displayField) {
13934             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13935         } else {
13936             // this is an error condition!!!
13937             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13938         }
13939         
13940         if(this.valueField){
13941             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13942         }
13943         
13944         var close = this.closeTriggerEl();
13945         
13946         if(close){
13947             if(dv.length || vv * 1 > 0){
13948                 close.show() ;
13949                 this.blockFocus=true;
13950             } else {
13951                 close.hide();
13952             }             
13953         }
13954         
13955         if(this.hiddenField){
13956             this.hiddenField.dom.value = vv;
13957             
13958             this.lastSelectionText = dv;
13959             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13960             this.value = vv;
13961             return;
13962         }
13963         // no hidden field.. - we store the value in 'value', but still display
13964         // display field!!!!
13965         this.lastSelectionText = dv;
13966         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13967         this.value = vv;
13968         
13969         
13970         
13971     },
13972     // private
13973     reset : function(){
13974         // overridden so that last data is reset..
13975         
13976         if(this.multiple){
13977             this.clearItem();
13978             return;
13979         }
13980         
13981         this.setValue(this.originalValue);
13982         //this.clearInvalid();
13983         this.lastData = false;
13984         if (this.view) {
13985             this.view.clearSelections();
13986         }
13987         
13988         this.validate();
13989     },
13990     // private
13991     findRecord : function(prop, value){
13992         var record;
13993         if(this.store.getCount() > 0){
13994             this.store.each(function(r){
13995                 if(r.data[prop] == value){
13996                     record = r;
13997                     return false;
13998                 }
13999                 return true;
14000             });
14001         }
14002         return record;
14003     },
14004     
14005     getName: function()
14006     {
14007         // returns hidden if it's set..
14008         if (!this.rendered) {return ''};
14009         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14010         
14011     },
14012     // private
14013     onViewMove : function(e, t){
14014         this.inKeyMode = false;
14015     },
14016
14017     // private
14018     onViewOver : function(e, t){
14019         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14020             return;
14021         }
14022         var item = this.view.findItemFromChild(t);
14023         
14024         if(item){
14025             var index = this.view.indexOf(item);
14026             this.select(index, false);
14027         }
14028     },
14029
14030     // private
14031     onViewClick : function(view, doFocus, el, e)
14032     {
14033         var index = this.view.getSelectedIndexes()[0];
14034         
14035         var r = this.store.getAt(index);
14036         
14037         if(this.tickable){
14038             
14039             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14040                 return;
14041             }
14042             
14043             var rm = false;
14044             var _this = this;
14045             
14046             Roo.each(this.tickItems, function(v,k){
14047                 
14048                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14049                     Roo.log(v);
14050                     _this.tickItems.splice(k, 1);
14051                     
14052                     if(typeof(e) == 'undefined' && view == false){
14053                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14054                     }
14055                     
14056                     rm = true;
14057                     return;
14058                 }
14059             });
14060             
14061             if(rm){
14062                 return;
14063             }
14064             
14065             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14066                 this.tickItems.push(r.data);
14067             }
14068             
14069             if(typeof(e) == 'undefined' && view == false){
14070                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14071             }
14072                     
14073             return;
14074         }
14075         
14076         if(r){
14077             this.onSelect(r, index);
14078         }
14079         if(doFocus !== false && !this.blockFocus){
14080             this.inputEl().focus();
14081         }
14082     },
14083
14084     // private
14085     restrictHeight : function(){
14086         //this.innerList.dom.style.height = '';
14087         //var inner = this.innerList.dom;
14088         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14089         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14090         //this.list.beginUpdate();
14091         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14092         this.list.alignTo(this.inputEl(), this.listAlign);
14093         this.list.alignTo(this.inputEl(), this.listAlign);
14094         //this.list.endUpdate();
14095     },
14096
14097     // private
14098     onEmptyResults : function(){
14099         
14100         if(this.tickable && this.editable){
14101             this.hasFocus = false;
14102             this.restrictHeight();
14103             return;
14104         }
14105         
14106         this.collapse();
14107     },
14108
14109     /**
14110      * Returns true if the dropdown list is expanded, else false.
14111      */
14112     isExpanded : function(){
14113         return this.list.isVisible();
14114     },
14115
14116     /**
14117      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14118      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14119      * @param {String} value The data value of the item to select
14120      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14121      * selected item if it is not currently in view (defaults to true)
14122      * @return {Boolean} True if the value matched an item in the list, else false
14123      */
14124     selectByValue : function(v, scrollIntoView){
14125         if(v !== undefined && v !== null){
14126             var r = this.findRecord(this.valueField || this.displayField, v);
14127             if(r){
14128                 this.select(this.store.indexOf(r), scrollIntoView);
14129                 return true;
14130             }
14131         }
14132         return false;
14133     },
14134
14135     /**
14136      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14137      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14138      * @param {Number} index The zero-based index of the list item to select
14139      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14140      * selected item if it is not currently in view (defaults to true)
14141      */
14142     select : function(index, scrollIntoView){
14143         this.selectedIndex = index;
14144         this.view.select(index);
14145         if(scrollIntoView !== false){
14146             var el = this.view.getNode(index);
14147             /*
14148              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14149              */
14150             if(el){
14151                 this.list.scrollChildIntoView(el, false);
14152             }
14153         }
14154     },
14155
14156     // private
14157     selectNext : function(){
14158         var ct = this.store.getCount();
14159         if(ct > 0){
14160             if(this.selectedIndex == -1){
14161                 this.select(0);
14162             }else if(this.selectedIndex < ct-1){
14163                 this.select(this.selectedIndex+1);
14164             }
14165         }
14166     },
14167
14168     // private
14169     selectPrev : function(){
14170         var ct = this.store.getCount();
14171         if(ct > 0){
14172             if(this.selectedIndex == -1){
14173                 this.select(0);
14174             }else if(this.selectedIndex != 0){
14175                 this.select(this.selectedIndex-1);
14176             }
14177         }
14178     },
14179
14180     // private
14181     onKeyUp : function(e){
14182         if(this.editable !== false && !e.isSpecialKey()){
14183             this.lastKey = e.getKey();
14184             this.dqTask.delay(this.queryDelay);
14185         }
14186     },
14187
14188     // private
14189     validateBlur : function(){
14190         return !this.list || !this.list.isVisible();   
14191     },
14192
14193     // private
14194     initQuery : function(){
14195         
14196         var v = this.getRawValue();
14197         
14198         if(this.tickable && this.editable){
14199             v = this.tickableInputEl().getValue();
14200         }
14201         
14202         this.doQuery(v);
14203     },
14204
14205     // private
14206     doForce : function(){
14207         if(this.inputEl().dom.value.length > 0){
14208             this.inputEl().dom.value =
14209                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14210              
14211         }
14212     },
14213
14214     /**
14215      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14216      * query allowing the query action to be canceled if needed.
14217      * @param {String} query The SQL query to execute
14218      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14219      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14220      * saved in the current store (defaults to false)
14221      */
14222     doQuery : function(q, forceAll){
14223         
14224         if(q === undefined || q === null){
14225             q = '';
14226         }
14227         var qe = {
14228             query: q,
14229             forceAll: forceAll,
14230             combo: this,
14231             cancel:false
14232         };
14233         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14234             return false;
14235         }
14236         q = qe.query;
14237         
14238         forceAll = qe.forceAll;
14239         if(forceAll === true || (q.length >= this.minChars)){
14240             
14241             this.hasQuery = true;
14242             
14243             if(this.lastQuery != q || this.alwaysQuery){
14244                 this.lastQuery = q;
14245                 if(this.mode == 'local'){
14246                     this.selectedIndex = -1;
14247                     if(forceAll){
14248                         this.store.clearFilter();
14249                     }else{
14250                         
14251                         if(this.specialFilter){
14252                             this.fireEvent('specialfilter', this);
14253                             this.onLoad();
14254                             return;
14255                         }
14256                         
14257                         this.store.filter(this.displayField, q);
14258                     }
14259                     
14260                     this.store.fireEvent("datachanged", this.store);
14261                     
14262                     this.onLoad();
14263                     
14264                     
14265                 }else{
14266                     
14267                     this.store.baseParams[this.queryParam] = q;
14268                     
14269                     var options = {params : this.getParams(q)};
14270                     
14271                     if(this.loadNext){
14272                         options.add = true;
14273                         options.params.start = this.page * this.pageSize;
14274                     }
14275                     
14276                     this.store.load(options);
14277                     
14278                     /*
14279                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14280                      *  we should expand the list on onLoad
14281                      *  so command out it
14282                      */
14283 //                    this.expand();
14284                 }
14285             }else{
14286                 this.selectedIndex = -1;
14287                 this.onLoad();   
14288             }
14289         }
14290         
14291         this.loadNext = false;
14292     },
14293     
14294     // private
14295     getParams : function(q){
14296         var p = {};
14297         //p[this.queryParam] = q;
14298         
14299         if(this.pageSize){
14300             p.start = 0;
14301             p.limit = this.pageSize;
14302         }
14303         return p;
14304     },
14305
14306     /**
14307      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14308      */
14309     collapse : function(){
14310         if(!this.isExpanded()){
14311             return;
14312         }
14313         
14314         this.list.hide();
14315         
14316         this.hasFocus = false;
14317         
14318         if(this.tickable){
14319             this.okBtn.hide();
14320             this.cancelBtn.hide();
14321             this.trigger.show();
14322             
14323             if(this.editable){
14324                 this.tickableInputEl().dom.value = '';
14325                 this.tickableInputEl().blur();
14326             }
14327             
14328         }
14329         
14330         Roo.get(document).un('mousedown', this.collapseIf, this);
14331         Roo.get(document).un('mousewheel', this.collapseIf, this);
14332         if (!this.editable) {
14333             Roo.get(document).un('keydown', this.listKeyPress, this);
14334         }
14335         this.fireEvent('collapse', this);
14336         
14337         this.validate();
14338     },
14339
14340     // private
14341     collapseIf : function(e){
14342         var in_combo  = e.within(this.el);
14343         var in_list =  e.within(this.list);
14344         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14345         
14346         if (in_combo || in_list || is_list) {
14347             //e.stopPropagation();
14348             return;
14349         }
14350         
14351         if(this.tickable){
14352             this.onTickableFooterButtonClick(e, false, false);
14353         }
14354
14355         this.collapse();
14356         
14357     },
14358
14359     /**
14360      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14361      */
14362     expand : function(){
14363        
14364         if(this.isExpanded() || !this.hasFocus){
14365             return;
14366         }
14367         
14368         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14369         this.list.setWidth(lw);
14370         
14371         Roo.log('expand');
14372         
14373         this.list.show();
14374         
14375         this.restrictHeight();
14376         
14377         if(this.tickable){
14378             
14379             this.tickItems = Roo.apply([], this.item);
14380             
14381             this.okBtn.show();
14382             this.cancelBtn.show();
14383             this.trigger.hide();
14384             
14385             if(this.editable){
14386                 this.tickableInputEl().focus();
14387             }
14388             
14389         }
14390         
14391         Roo.get(document).on('mousedown', this.collapseIf, this);
14392         Roo.get(document).on('mousewheel', this.collapseIf, this);
14393         if (!this.editable) {
14394             Roo.get(document).on('keydown', this.listKeyPress, this);
14395         }
14396         
14397         this.fireEvent('expand', this);
14398     },
14399
14400     // private
14401     // Implements the default empty TriggerField.onTriggerClick function
14402     onTriggerClick : function(e)
14403     {
14404         Roo.log('trigger click');
14405         
14406         if(this.disabled || !this.triggerList){
14407             return;
14408         }
14409         
14410         this.page = 0;
14411         this.loadNext = false;
14412         
14413         if(this.isExpanded()){
14414             this.collapse();
14415             if (!this.blockFocus) {
14416                 this.inputEl().focus();
14417             }
14418             
14419         }else {
14420             this.hasFocus = true;
14421             if(this.triggerAction == 'all') {
14422                 this.doQuery(this.allQuery, true);
14423             } else {
14424                 this.doQuery(this.getRawValue());
14425             }
14426             if (!this.blockFocus) {
14427                 this.inputEl().focus();
14428             }
14429         }
14430     },
14431     
14432     onTickableTriggerClick : function(e)
14433     {
14434         if(this.disabled){
14435             return;
14436         }
14437         
14438         this.page = 0;
14439         this.loadNext = false;
14440         this.hasFocus = true;
14441         
14442         if(this.triggerAction == 'all') {
14443             this.doQuery(this.allQuery, true);
14444         } else {
14445             this.doQuery(this.getRawValue());
14446         }
14447     },
14448     
14449     onSearchFieldClick : function(e)
14450     {
14451         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14452             this.onTickableFooterButtonClick(e, false, false);
14453             return;
14454         }
14455         
14456         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14457             return;
14458         }
14459         
14460         this.page = 0;
14461         this.loadNext = false;
14462         this.hasFocus = true;
14463         
14464         if(this.triggerAction == 'all') {
14465             this.doQuery(this.allQuery, true);
14466         } else {
14467             this.doQuery(this.getRawValue());
14468         }
14469     },
14470     
14471     listKeyPress : function(e)
14472     {
14473         //Roo.log('listkeypress');
14474         // scroll to first matching element based on key pres..
14475         if (e.isSpecialKey()) {
14476             return false;
14477         }
14478         var k = String.fromCharCode(e.getKey()).toUpperCase();
14479         //Roo.log(k);
14480         var match  = false;
14481         var csel = this.view.getSelectedNodes();
14482         var cselitem = false;
14483         if (csel.length) {
14484             var ix = this.view.indexOf(csel[0]);
14485             cselitem  = this.store.getAt(ix);
14486             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14487                 cselitem = false;
14488             }
14489             
14490         }
14491         
14492         this.store.each(function(v) { 
14493             if (cselitem) {
14494                 // start at existing selection.
14495                 if (cselitem.id == v.id) {
14496                     cselitem = false;
14497                 }
14498                 return true;
14499             }
14500                 
14501             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14502                 match = this.store.indexOf(v);
14503                 return false;
14504             }
14505             return true;
14506         }, this);
14507         
14508         if (match === false) {
14509             return true; // no more action?
14510         }
14511         // scroll to?
14512         this.view.select(match);
14513         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14514         sn.scrollIntoView(sn.dom.parentNode, false);
14515     },
14516     
14517     onViewScroll : function(e, t){
14518         
14519         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){
14520             return;
14521         }
14522         
14523         this.hasQuery = true;
14524         
14525         this.loading = this.list.select('.loading', true).first();
14526         
14527         if(this.loading === null){
14528             this.list.createChild({
14529                 tag: 'div',
14530                 cls: 'loading roo-select2-more-results roo-select2-active',
14531                 html: 'Loading more results...'
14532             });
14533             
14534             this.loading = this.list.select('.loading', true).first();
14535             
14536             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14537             
14538             this.loading.hide();
14539         }
14540         
14541         this.loading.show();
14542         
14543         var _combo = this;
14544         
14545         this.page++;
14546         this.loadNext = true;
14547         
14548         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14549         
14550         return;
14551     },
14552     
14553     addItem : function(o)
14554     {   
14555         var dv = ''; // display value
14556         
14557         if (this.displayField) {
14558             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14559         } else {
14560             // this is an error condition!!!
14561             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14562         }
14563         
14564         if(!dv.length){
14565             return;
14566         }
14567         
14568         var choice = this.choices.createChild({
14569             tag: 'li',
14570             cls: 'roo-select2-search-choice',
14571             cn: [
14572                 {
14573                     tag: 'div',
14574                     html: dv
14575                 },
14576                 {
14577                     tag: 'a',
14578                     href: '#',
14579                     cls: 'roo-select2-search-choice-close fa fa-times',
14580                     tabindex: '-1'
14581                 }
14582             ]
14583             
14584         }, this.searchField);
14585         
14586         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14587         
14588         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14589         
14590         this.item.push(o);
14591         
14592         this.lastData = o;
14593         
14594         this.syncValue();
14595         
14596         this.inputEl().dom.value = '';
14597         
14598         this.validate();
14599     },
14600     
14601     onRemoveItem : function(e, _self, o)
14602     {
14603         e.preventDefault();
14604         
14605         this.lastItem = Roo.apply([], this.item);
14606         
14607         var index = this.item.indexOf(o.data) * 1;
14608         
14609         if( index < 0){
14610             Roo.log('not this item?!');
14611             return;
14612         }
14613         
14614         this.item.splice(index, 1);
14615         o.item.remove();
14616         
14617         this.syncValue();
14618         
14619         this.fireEvent('remove', this, e);
14620         
14621         this.validate();
14622         
14623     },
14624     
14625     syncValue : function()
14626     {
14627         if(!this.item.length){
14628             this.clearValue();
14629             return;
14630         }
14631             
14632         var value = [];
14633         var _this = this;
14634         Roo.each(this.item, function(i){
14635             if(_this.valueField){
14636                 value.push(i[_this.valueField]);
14637                 return;
14638             }
14639
14640             value.push(i);
14641         });
14642
14643         this.value = value.join(',');
14644
14645         if(this.hiddenField){
14646             this.hiddenField.dom.value = this.value;
14647         }
14648         
14649         this.store.fireEvent("datachanged", this.store);
14650         
14651         this.validate();
14652     },
14653     
14654     clearItem : function()
14655     {
14656         if(!this.multiple){
14657             return;
14658         }
14659         
14660         this.item = [];
14661         
14662         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14663            c.remove();
14664         });
14665         
14666         this.syncValue();
14667         
14668         this.validate();
14669         
14670         if(this.tickable && !Roo.isTouch){
14671             this.view.refresh();
14672         }
14673     },
14674     
14675     inputEl: function ()
14676     {
14677         if(Roo.isIOS && this.useNativeIOS){
14678             return this.el.select('select.roo-ios-select', true).first();
14679         }
14680         
14681         if(Roo.isTouch && this.mobileTouchView){
14682             return this.el.select('input.form-control',true).first();
14683         }
14684         
14685         if(this.tickable){
14686             return this.searchField;
14687         }
14688         
14689         return this.el.select('input.form-control',true).first();
14690     },
14691     
14692     onTickableFooterButtonClick : function(e, btn, el)
14693     {
14694         e.preventDefault();
14695         
14696         this.lastItem = Roo.apply([], this.item);
14697         
14698         if(btn && btn.name == 'cancel'){
14699             this.tickItems = Roo.apply([], this.item);
14700             this.collapse();
14701             return;
14702         }
14703         
14704         this.clearItem();
14705         
14706         var _this = this;
14707         
14708         Roo.each(this.tickItems, function(o){
14709             _this.addItem(o);
14710         });
14711         
14712         this.collapse();
14713         
14714     },
14715     
14716     validate : function()
14717     {
14718         if(this.getVisibilityEl().hasClass('hidden')){
14719             return true;
14720         }
14721         
14722         var v = this.getRawValue();
14723         
14724         if(this.multiple){
14725             v = this.getValue();
14726         }
14727         
14728         if(this.disabled || this.allowBlank || v.length){
14729             this.markValid();
14730             return true;
14731         }
14732         
14733         this.markInvalid();
14734         return false;
14735     },
14736     
14737     tickableInputEl : function()
14738     {
14739         if(!this.tickable || !this.editable){
14740             return this.inputEl();
14741         }
14742         
14743         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14744     },
14745     
14746     
14747     getAutoCreateTouchView : function()
14748     {
14749         var id = Roo.id();
14750         
14751         var cfg = {
14752             cls: 'form-group' //input-group
14753         };
14754         
14755         var input =  {
14756             tag: 'input',
14757             id : id,
14758             type : this.inputType,
14759             cls : 'form-control x-combo-noedit',
14760             autocomplete: 'new-password',
14761             placeholder : this.placeholder || '',
14762             readonly : true
14763         };
14764         
14765         if (this.name) {
14766             input.name = this.name;
14767         }
14768         
14769         if (this.size) {
14770             input.cls += ' input-' + this.size;
14771         }
14772         
14773         if (this.disabled) {
14774             input.disabled = true;
14775         }
14776         
14777         var inputblock = {
14778             cls : '',
14779             cn : [
14780                 input
14781             ]
14782         };
14783         
14784         if(this.before){
14785             inputblock.cls += ' input-group';
14786             
14787             inputblock.cn.unshift({
14788                 tag :'span',
14789                 cls : 'input-group-addon',
14790                 html : this.before
14791             });
14792         }
14793         
14794         if(this.removable && !this.multiple){
14795             inputblock.cls += ' roo-removable';
14796             
14797             inputblock.cn.push({
14798                 tag: 'button',
14799                 html : 'x',
14800                 cls : 'roo-combo-removable-btn close'
14801             });
14802         }
14803
14804         if(this.hasFeedback && !this.allowBlank){
14805             
14806             inputblock.cls += ' has-feedback';
14807             
14808             inputblock.cn.push({
14809                 tag: 'span',
14810                 cls: 'glyphicon form-control-feedback'
14811             });
14812             
14813         }
14814         
14815         if (this.after) {
14816             
14817             inputblock.cls += (this.before) ? '' : ' input-group';
14818             
14819             inputblock.cn.push({
14820                 tag :'span',
14821                 cls : 'input-group-addon',
14822                 html : this.after
14823             });
14824         }
14825
14826         var box = {
14827             tag: 'div',
14828             cn: [
14829                 {
14830                     tag: 'input',
14831                     type : 'hidden',
14832                     cls: 'form-hidden-field'
14833                 },
14834                 inputblock
14835             ]
14836             
14837         };
14838         
14839         if(this.multiple){
14840             box = {
14841                 tag: 'div',
14842                 cn: [
14843                     {
14844                         tag: 'input',
14845                         type : 'hidden',
14846                         cls: 'form-hidden-field'
14847                     },
14848                     {
14849                         tag: 'ul',
14850                         cls: 'roo-select2-choices',
14851                         cn:[
14852                             {
14853                                 tag: 'li',
14854                                 cls: 'roo-select2-search-field',
14855                                 cn: [
14856
14857                                     inputblock
14858                                 ]
14859                             }
14860                         ]
14861                     }
14862                 ]
14863             }
14864         };
14865         
14866         var combobox = {
14867             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14868             cn: [
14869                 box
14870             ]
14871         };
14872         
14873         if(!this.multiple && this.showToggleBtn){
14874             
14875             var caret = {
14876                         tag: 'span',
14877                         cls: 'caret'
14878             };
14879             
14880             if (this.caret != false) {
14881                 caret = {
14882                      tag: 'i',
14883                      cls: 'fa fa-' + this.caret
14884                 };
14885                 
14886             }
14887             
14888             combobox.cn.push({
14889                 tag :'span',
14890                 cls : 'input-group-addon btn dropdown-toggle',
14891                 cn : [
14892                     caret,
14893                     {
14894                         tag: 'span',
14895                         cls: 'combobox-clear',
14896                         cn  : [
14897                             {
14898                                 tag : 'i',
14899                                 cls: 'icon-remove'
14900                             }
14901                         ]
14902                     }
14903                 ]
14904
14905             })
14906         }
14907         
14908         if(this.multiple){
14909             combobox.cls += ' roo-select2-container-multi';
14910         }
14911         
14912         var align = this.labelAlign || this.parentLabelAlign();
14913         
14914         if (align ==='left' && this.fieldLabel.length) {
14915
14916             cfg.cn = [
14917                 {
14918                    tag : 'i',
14919                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14920                    tooltip : 'This field is required'
14921                 },
14922                 {
14923                     tag: 'label',
14924                     cls : 'control-label',
14925                     html : this.fieldLabel
14926
14927                 },
14928                 {
14929                     cls : '', 
14930                     cn: [
14931                         combobox
14932                     ]
14933                 }
14934             ];
14935             
14936             var labelCfg = cfg.cn[1];
14937             var contentCfg = cfg.cn[2];
14938             
14939
14940             if(this.indicatorpos == 'right'){
14941                 cfg.cn = [
14942                     {
14943                         tag: 'label',
14944                         'for' :  id,
14945                         cls : 'control-label',
14946                         cn : [
14947                             {
14948                                 tag : 'span',
14949                                 html : this.fieldLabel
14950                             },
14951                             {
14952                                 tag : 'i',
14953                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14954                                 tooltip : 'This field is required'
14955                             }
14956                         ]
14957                     },
14958                     {
14959                         cls : "",
14960                         cn: [
14961                             combobox
14962                         ]
14963                     }
14964
14965                 ];
14966                 
14967                 labelCfg = cfg.cn[0];
14968                 contentCfg = cfg.cn[1];
14969             }
14970             
14971            
14972             
14973             if(this.labelWidth > 12){
14974                 labelCfg.style = "width: " + this.labelWidth + 'px';
14975             }
14976             
14977             if(this.labelWidth < 13 && this.labelmd == 0){
14978                 this.labelmd = this.labelWidth;
14979             }
14980             
14981             if(this.labellg > 0){
14982                 labelCfg.cls += ' col-lg-' + this.labellg;
14983                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14984             }
14985             
14986             if(this.labelmd > 0){
14987                 labelCfg.cls += ' col-md-' + this.labelmd;
14988                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14989             }
14990             
14991             if(this.labelsm > 0){
14992                 labelCfg.cls += ' col-sm-' + this.labelsm;
14993                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14994             }
14995             
14996             if(this.labelxs > 0){
14997                 labelCfg.cls += ' col-xs-' + this.labelxs;
14998                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14999             }
15000                 
15001                 
15002         } else if ( this.fieldLabel.length) {
15003             cfg.cn = [
15004                 {
15005                    tag : 'i',
15006                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15007                    tooltip : 'This field is required'
15008                 },
15009                 {
15010                     tag: 'label',
15011                     cls : 'control-label',
15012                     html : this.fieldLabel
15013
15014                 },
15015                 {
15016                     cls : '', 
15017                     cn: [
15018                         combobox
15019                     ]
15020                 }
15021             ];
15022             
15023             if(this.indicatorpos == 'right'){
15024                 cfg.cn = [
15025                     {
15026                         tag: 'label',
15027                         cls : 'control-label',
15028                         html : this.fieldLabel,
15029                         cn : [
15030                             {
15031                                tag : 'i',
15032                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15033                                tooltip : 'This field is required'
15034                             }
15035                         ]
15036                     },
15037                     {
15038                         cls : '', 
15039                         cn: [
15040                             combobox
15041                         ]
15042                     }
15043                 ];
15044             }
15045         } else {
15046             cfg.cn = combobox;    
15047         }
15048         
15049         
15050         var settings = this;
15051         
15052         ['xs','sm','md','lg'].map(function(size){
15053             if (settings[size]) {
15054                 cfg.cls += ' col-' + size + '-' + settings[size];
15055             }
15056         });
15057         
15058         return cfg;
15059     },
15060     
15061     initTouchView : function()
15062     {
15063         this.renderTouchView();
15064         
15065         this.touchViewEl.on('scroll', function(){
15066             this.el.dom.scrollTop = 0;
15067         }, this);
15068         
15069         this.originalValue = this.getValue();
15070         
15071         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15072         
15073         this.inputEl().on("click", this.showTouchView, this);
15074         if (this.triggerEl) {
15075             this.triggerEl.on("click", this.showTouchView, this);
15076         }
15077         
15078         
15079         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15080         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15081         
15082         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15083         
15084         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15085         this.store.on('load', this.onTouchViewLoad, this);
15086         this.store.on('loadexception', this.onTouchViewLoadException, this);
15087         
15088         if(this.hiddenName){
15089             
15090             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15091             
15092             this.hiddenField.dom.value =
15093                 this.hiddenValue !== undefined ? this.hiddenValue :
15094                 this.value !== undefined ? this.value : '';
15095         
15096             this.el.dom.removeAttribute('name');
15097             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15098         }
15099         
15100         if(this.multiple){
15101             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15102             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15103         }
15104         
15105         if(this.removable && !this.multiple){
15106             var close = this.closeTriggerEl();
15107             if(close){
15108                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15109                 close.on('click', this.removeBtnClick, this, close);
15110             }
15111         }
15112         /*
15113          * fix the bug in Safari iOS8
15114          */
15115         this.inputEl().on("focus", function(e){
15116             document.activeElement.blur();
15117         }, this);
15118         
15119         return;
15120         
15121         
15122     },
15123     
15124     renderTouchView : function()
15125     {
15126         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15127         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15128         
15129         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15130         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15131         
15132         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15133         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15134         this.touchViewBodyEl.setStyle('overflow', 'auto');
15135         
15136         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15137         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15138         
15139         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15140         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15141         
15142     },
15143     
15144     showTouchView : function()
15145     {
15146         if(this.disabled){
15147             return;
15148         }
15149         
15150         this.touchViewHeaderEl.hide();
15151
15152         if(this.modalTitle.length){
15153             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15154             this.touchViewHeaderEl.show();
15155         }
15156
15157         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15158         this.touchViewEl.show();
15159
15160         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15161         
15162         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15163         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15164
15165         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15166
15167         if(this.modalTitle.length){
15168             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15169         }
15170         
15171         this.touchViewBodyEl.setHeight(bodyHeight);
15172
15173         if(this.animate){
15174             var _this = this;
15175             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15176         }else{
15177             this.touchViewEl.addClass('in');
15178         }
15179
15180         this.doTouchViewQuery();
15181         
15182     },
15183     
15184     hideTouchView : function()
15185     {
15186         this.touchViewEl.removeClass('in');
15187
15188         if(this.animate){
15189             var _this = this;
15190             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15191         }else{
15192             this.touchViewEl.setStyle('display', 'none');
15193         }
15194         
15195     },
15196     
15197     setTouchViewValue : function()
15198     {
15199         if(this.multiple){
15200             this.clearItem();
15201         
15202             var _this = this;
15203
15204             Roo.each(this.tickItems, function(o){
15205                 this.addItem(o);
15206             }, this);
15207         }
15208         
15209         this.hideTouchView();
15210     },
15211     
15212     doTouchViewQuery : function()
15213     {
15214         var qe = {
15215             query: '',
15216             forceAll: true,
15217             combo: this,
15218             cancel:false
15219         };
15220         
15221         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15222             return false;
15223         }
15224         
15225         if(!this.alwaysQuery || this.mode == 'local'){
15226             this.onTouchViewLoad();
15227             return;
15228         }
15229         
15230         this.store.load();
15231     },
15232     
15233     onTouchViewBeforeLoad : function(combo,opts)
15234     {
15235         return;
15236     },
15237
15238     // private
15239     onTouchViewLoad : function()
15240     {
15241         if(this.store.getCount() < 1){
15242             this.onTouchViewEmptyResults();
15243             return;
15244         }
15245         
15246         this.clearTouchView();
15247         
15248         var rawValue = this.getRawValue();
15249         
15250         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15251         
15252         this.tickItems = [];
15253         
15254         this.store.data.each(function(d, rowIndex){
15255             var row = this.touchViewListGroup.createChild(template);
15256             
15257             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15258                 row.addClass(d.data.cls);
15259             }
15260             
15261             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15262                 var cfg = {
15263                     data : d.data,
15264                     html : d.data[this.displayField]
15265                 };
15266                 
15267                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15268                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15269                 }
15270             }
15271             row.removeClass('selected');
15272             if(!this.multiple && this.valueField &&
15273                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15274             {
15275                 // radio buttons..
15276                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15277                 row.addClass('selected');
15278             }
15279             
15280             if(this.multiple && this.valueField &&
15281                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15282             {
15283                 
15284                 // checkboxes...
15285                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15286                 this.tickItems.push(d.data);
15287             }
15288             
15289             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15290             
15291         }, this);
15292         
15293         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15294         
15295         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15296
15297         if(this.modalTitle.length){
15298             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15299         }
15300
15301         var listHeight = this.touchViewListGroup.getHeight();
15302         
15303         var _this = this;
15304         
15305         if(firstChecked && listHeight > bodyHeight){
15306             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15307         }
15308         
15309     },
15310     
15311     onTouchViewLoadException : function()
15312     {
15313         this.hideTouchView();
15314     },
15315     
15316     onTouchViewEmptyResults : function()
15317     {
15318         this.clearTouchView();
15319         
15320         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15321         
15322         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15323         
15324     },
15325     
15326     clearTouchView : function()
15327     {
15328         this.touchViewListGroup.dom.innerHTML = '';
15329     },
15330     
15331     onTouchViewClick : function(e, el, o)
15332     {
15333         e.preventDefault();
15334         
15335         var row = o.row;
15336         var rowIndex = o.rowIndex;
15337         
15338         var r = this.store.getAt(rowIndex);
15339         
15340         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15341             
15342             if(!this.multiple){
15343                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15344                     c.dom.removeAttribute('checked');
15345                 }, this);
15346
15347                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15348
15349                 this.setFromData(r.data);
15350
15351                 var close = this.closeTriggerEl();
15352
15353                 if(close){
15354                     close.show();
15355                 }
15356
15357                 this.hideTouchView();
15358
15359                 this.fireEvent('select', this, r, rowIndex);
15360
15361                 return;
15362             }
15363
15364             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15365                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15366                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15367                 return;
15368             }
15369
15370             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15371             this.addItem(r.data);
15372             this.tickItems.push(r.data);
15373         }
15374     },
15375     
15376     getAutoCreateNativeIOS : function()
15377     {
15378         var cfg = {
15379             cls: 'form-group' //input-group,
15380         };
15381         
15382         var combobox =  {
15383             tag: 'select',
15384             cls : 'roo-ios-select'
15385         };
15386         
15387         if (this.name) {
15388             combobox.name = this.name;
15389         }
15390         
15391         if (this.disabled) {
15392             combobox.disabled = true;
15393         }
15394         
15395         var settings = this;
15396         
15397         ['xs','sm','md','lg'].map(function(size){
15398             if (settings[size]) {
15399                 cfg.cls += ' col-' + size + '-' + settings[size];
15400             }
15401         });
15402         
15403         cfg.cn = combobox;
15404         
15405         return cfg;
15406         
15407     },
15408     
15409     initIOSView : function()
15410     {
15411         this.store.on('load', this.onIOSViewLoad, this);
15412         
15413         return;
15414     },
15415     
15416     onIOSViewLoad : function()
15417     {
15418         if(this.store.getCount() < 1){
15419             return;
15420         }
15421         
15422         this.clearIOSView();
15423         
15424         if(this.allowBlank) {
15425             
15426             var default_text = '-- SELECT --';
15427             
15428             if(this.placeholder.length){
15429                 default_text = this.placeholder;
15430             }
15431             
15432             if(this.emptyTitle.length){
15433                 default_text += ' - ' + this.emptyTitle + ' -';
15434             }
15435             
15436             var opt = this.inputEl().createChild({
15437                 tag: 'option',
15438                 value : 0,
15439                 html : default_text
15440             });
15441             
15442             var o = {};
15443             o[this.valueField] = 0;
15444             o[this.displayField] = default_text;
15445             
15446             this.ios_options.push({
15447                 data : o,
15448                 el : opt
15449             });
15450             
15451         }
15452         
15453         this.store.data.each(function(d, rowIndex){
15454             
15455             var html = '';
15456             
15457             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15458                 html = d.data[this.displayField];
15459             }
15460             
15461             var value = '';
15462             
15463             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15464                 value = d.data[this.valueField];
15465             }
15466             
15467             var option = {
15468                 tag: 'option',
15469                 value : value,
15470                 html : html
15471             };
15472             
15473             if(this.value == d.data[this.valueField]){
15474                 option['selected'] = true;
15475             }
15476             
15477             var opt = this.inputEl().createChild(option);
15478             
15479             this.ios_options.push({
15480                 data : d.data,
15481                 el : opt
15482             });
15483             
15484         }, this);
15485         
15486         this.inputEl().on('change', function(){
15487            this.fireEvent('select', this);
15488         }, this);
15489         
15490     },
15491     
15492     clearIOSView: function()
15493     {
15494         this.inputEl().dom.innerHTML = '';
15495         
15496         this.ios_options = [];
15497     },
15498     
15499     setIOSValue: function(v)
15500     {
15501         this.value = v;
15502         
15503         if(!this.ios_options){
15504             return;
15505         }
15506         
15507         Roo.each(this.ios_options, function(opts){
15508            
15509            opts.el.dom.removeAttribute('selected');
15510            
15511            if(opts.data[this.valueField] != v){
15512                return;
15513            }
15514            
15515            opts.el.dom.setAttribute('selected', true);
15516            
15517         }, this);
15518     }
15519
15520     /** 
15521     * @cfg {Boolean} grow 
15522     * @hide 
15523     */
15524     /** 
15525     * @cfg {Number} growMin 
15526     * @hide 
15527     */
15528     /** 
15529     * @cfg {Number} growMax 
15530     * @hide 
15531     */
15532     /**
15533      * @hide
15534      * @method autoSize
15535      */
15536 });
15537
15538 Roo.apply(Roo.bootstrap.ComboBox,  {
15539     
15540     header : {
15541         tag: 'div',
15542         cls: 'modal-header',
15543         cn: [
15544             {
15545                 tag: 'h4',
15546                 cls: 'modal-title'
15547             }
15548         ]
15549     },
15550     
15551     body : {
15552         tag: 'div',
15553         cls: 'modal-body',
15554         cn: [
15555             {
15556                 tag: 'ul',
15557                 cls: 'list-group'
15558             }
15559         ]
15560     },
15561     
15562     listItemRadio : {
15563         tag: 'li',
15564         cls: 'list-group-item',
15565         cn: [
15566             {
15567                 tag: 'span',
15568                 cls: 'roo-combobox-list-group-item-value'
15569             },
15570             {
15571                 tag: 'div',
15572                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15573                 cn: [
15574                     {
15575                         tag: 'input',
15576                         type: 'radio'
15577                     },
15578                     {
15579                         tag: 'label'
15580                     }
15581                 ]
15582             }
15583         ]
15584     },
15585     
15586     listItemCheckbox : {
15587         tag: 'li',
15588         cls: 'list-group-item',
15589         cn: [
15590             {
15591                 tag: 'span',
15592                 cls: 'roo-combobox-list-group-item-value'
15593             },
15594             {
15595                 tag: 'div',
15596                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15597                 cn: [
15598                     {
15599                         tag: 'input',
15600                         type: 'checkbox'
15601                     },
15602                     {
15603                         tag: 'label'
15604                     }
15605                 ]
15606             }
15607         ]
15608     },
15609     
15610     emptyResult : {
15611         tag: 'div',
15612         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15613     },
15614     
15615     footer : {
15616         tag: 'div',
15617         cls: 'modal-footer',
15618         cn: [
15619             {
15620                 tag: 'div',
15621                 cls: 'row',
15622                 cn: [
15623                     {
15624                         tag: 'div',
15625                         cls: 'col-xs-6 text-left',
15626                         cn: {
15627                             tag: 'button',
15628                             cls: 'btn btn-danger roo-touch-view-cancel',
15629                             html: 'Cancel'
15630                         }
15631                     },
15632                     {
15633                         tag: 'div',
15634                         cls: 'col-xs-6 text-right',
15635                         cn: {
15636                             tag: 'button',
15637                             cls: 'btn btn-success roo-touch-view-ok',
15638                             html: 'OK'
15639                         }
15640                     }
15641                 ]
15642             }
15643         ]
15644         
15645     }
15646 });
15647
15648 Roo.apply(Roo.bootstrap.ComboBox,  {
15649     
15650     touchViewTemplate : {
15651         tag: 'div',
15652         cls: 'modal fade roo-combobox-touch-view',
15653         cn: [
15654             {
15655                 tag: 'div',
15656                 cls: 'modal-dialog',
15657                 style : 'position:fixed', // we have to fix position....
15658                 cn: [
15659                     {
15660                         tag: 'div',
15661                         cls: 'modal-content',
15662                         cn: [
15663                             Roo.bootstrap.ComboBox.header,
15664                             Roo.bootstrap.ComboBox.body,
15665                             Roo.bootstrap.ComboBox.footer
15666                         ]
15667                     }
15668                 ]
15669             }
15670         ]
15671     }
15672 });/*
15673  * Based on:
15674  * Ext JS Library 1.1.1
15675  * Copyright(c) 2006-2007, Ext JS, LLC.
15676  *
15677  * Originally Released Under LGPL - original licence link has changed is not relivant.
15678  *
15679  * Fork - LGPL
15680  * <script type="text/javascript">
15681  */
15682
15683 /**
15684  * @class Roo.View
15685  * @extends Roo.util.Observable
15686  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15687  * This class also supports single and multi selection modes. <br>
15688  * Create a data model bound view:
15689  <pre><code>
15690  var store = new Roo.data.Store(...);
15691
15692  var view = new Roo.View({
15693     el : "my-element",
15694     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15695  
15696     singleSelect: true,
15697     selectedClass: "ydataview-selected",
15698     store: store
15699  });
15700
15701  // listen for node click?
15702  view.on("click", function(vw, index, node, e){
15703  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15704  });
15705
15706  // load XML data
15707  dataModel.load("foobar.xml");
15708  </code></pre>
15709  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15710  * <br><br>
15711  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15712  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15713  * 
15714  * Note: old style constructor is still suported (container, template, config)
15715  * 
15716  * @constructor
15717  * Create a new View
15718  * @param {Object} config The config object
15719  * 
15720  */
15721 Roo.View = function(config, depreciated_tpl, depreciated_config){
15722     
15723     this.parent = false;
15724     
15725     if (typeof(depreciated_tpl) == 'undefined') {
15726         // new way.. - universal constructor.
15727         Roo.apply(this, config);
15728         this.el  = Roo.get(this.el);
15729     } else {
15730         // old format..
15731         this.el  = Roo.get(config);
15732         this.tpl = depreciated_tpl;
15733         Roo.apply(this, depreciated_config);
15734     }
15735     this.wrapEl  = this.el.wrap().wrap();
15736     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15737     
15738     
15739     if(typeof(this.tpl) == "string"){
15740         this.tpl = new Roo.Template(this.tpl);
15741     } else {
15742         // support xtype ctors..
15743         this.tpl = new Roo.factory(this.tpl, Roo);
15744     }
15745     
15746     
15747     this.tpl.compile();
15748     
15749     /** @private */
15750     this.addEvents({
15751         /**
15752          * @event beforeclick
15753          * Fires before a click is processed. Returns false to cancel the default action.
15754          * @param {Roo.View} this
15755          * @param {Number} index The index of the target node
15756          * @param {HTMLElement} node The target node
15757          * @param {Roo.EventObject} e The raw event object
15758          */
15759             "beforeclick" : true,
15760         /**
15761          * @event click
15762          * Fires when a template node is clicked.
15763          * @param {Roo.View} this
15764          * @param {Number} index The index of the target node
15765          * @param {HTMLElement} node The target node
15766          * @param {Roo.EventObject} e The raw event object
15767          */
15768             "click" : true,
15769         /**
15770          * @event dblclick
15771          * Fires when a template node is double clicked.
15772          * @param {Roo.View} this
15773          * @param {Number} index The index of the target node
15774          * @param {HTMLElement} node The target node
15775          * @param {Roo.EventObject} e The raw event object
15776          */
15777             "dblclick" : true,
15778         /**
15779          * @event contextmenu
15780          * Fires when a template node is right clicked.
15781          * @param {Roo.View} this
15782          * @param {Number} index The index of the target node
15783          * @param {HTMLElement} node The target node
15784          * @param {Roo.EventObject} e The raw event object
15785          */
15786             "contextmenu" : true,
15787         /**
15788          * @event selectionchange
15789          * Fires when the selected nodes change.
15790          * @param {Roo.View} this
15791          * @param {Array} selections Array of the selected nodes
15792          */
15793             "selectionchange" : true,
15794     
15795         /**
15796          * @event beforeselect
15797          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15798          * @param {Roo.View} this
15799          * @param {HTMLElement} node The node to be selected
15800          * @param {Array} selections Array of currently selected nodes
15801          */
15802             "beforeselect" : true,
15803         /**
15804          * @event preparedata
15805          * Fires on every row to render, to allow you to change the data.
15806          * @param {Roo.View} this
15807          * @param {Object} data to be rendered (change this)
15808          */
15809           "preparedata" : true
15810           
15811           
15812         });
15813
15814
15815
15816     this.el.on({
15817         "click": this.onClick,
15818         "dblclick": this.onDblClick,
15819         "contextmenu": this.onContextMenu,
15820         scope:this
15821     });
15822
15823     this.selections = [];
15824     this.nodes = [];
15825     this.cmp = new Roo.CompositeElementLite([]);
15826     if(this.store){
15827         this.store = Roo.factory(this.store, Roo.data);
15828         this.setStore(this.store, true);
15829     }
15830     
15831     if ( this.footer && this.footer.xtype) {
15832            
15833          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15834         
15835         this.footer.dataSource = this.store;
15836         this.footer.container = fctr;
15837         this.footer = Roo.factory(this.footer, Roo);
15838         fctr.insertFirst(this.el);
15839         
15840         // this is a bit insane - as the paging toolbar seems to detach the el..
15841 //        dom.parentNode.parentNode.parentNode
15842          // they get detached?
15843     }
15844     
15845     
15846     Roo.View.superclass.constructor.call(this);
15847     
15848     
15849 };
15850
15851 Roo.extend(Roo.View, Roo.util.Observable, {
15852     
15853      /**
15854      * @cfg {Roo.data.Store} store Data store to load data from.
15855      */
15856     store : false,
15857     
15858     /**
15859      * @cfg {String|Roo.Element} el The container element.
15860      */
15861     el : '',
15862     
15863     /**
15864      * @cfg {String|Roo.Template} tpl The template used by this View 
15865      */
15866     tpl : false,
15867     /**
15868      * @cfg {String} dataName the named area of the template to use as the data area
15869      *                          Works with domtemplates roo-name="name"
15870      */
15871     dataName: false,
15872     /**
15873      * @cfg {String} selectedClass The css class to add to selected nodes
15874      */
15875     selectedClass : "x-view-selected",
15876      /**
15877      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15878      */
15879     emptyText : "",
15880     
15881     /**
15882      * @cfg {String} text to display on mask (default Loading)
15883      */
15884     mask : false,
15885     /**
15886      * @cfg {Boolean} multiSelect Allow multiple selection
15887      */
15888     multiSelect : false,
15889     /**
15890      * @cfg {Boolean} singleSelect Allow single selection
15891      */
15892     singleSelect:  false,
15893     
15894     /**
15895      * @cfg {Boolean} toggleSelect - selecting 
15896      */
15897     toggleSelect : false,
15898     
15899     /**
15900      * @cfg {Boolean} tickable - selecting 
15901      */
15902     tickable : false,
15903     
15904     /**
15905      * Returns the element this view is bound to.
15906      * @return {Roo.Element}
15907      */
15908     getEl : function(){
15909         return this.wrapEl;
15910     },
15911     
15912     
15913
15914     /**
15915      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15916      */
15917     refresh : function(){
15918         //Roo.log('refresh');
15919         var t = this.tpl;
15920         
15921         // if we are using something like 'domtemplate', then
15922         // the what gets used is:
15923         // t.applySubtemplate(NAME, data, wrapping data..)
15924         // the outer template then get' applied with
15925         //     the store 'extra data'
15926         // and the body get's added to the
15927         //      roo-name="data" node?
15928         //      <span class='roo-tpl-{name}'></span> ?????
15929         
15930         
15931         
15932         this.clearSelections();
15933         this.el.update("");
15934         var html = [];
15935         var records = this.store.getRange();
15936         if(records.length < 1) {
15937             
15938             // is this valid??  = should it render a template??
15939             
15940             this.el.update(this.emptyText);
15941             return;
15942         }
15943         var el = this.el;
15944         if (this.dataName) {
15945             this.el.update(t.apply(this.store.meta)); //????
15946             el = this.el.child('.roo-tpl-' + this.dataName);
15947         }
15948         
15949         for(var i = 0, len = records.length; i < len; i++){
15950             var data = this.prepareData(records[i].data, i, records[i]);
15951             this.fireEvent("preparedata", this, data, i, records[i]);
15952             
15953             var d = Roo.apply({}, data);
15954             
15955             if(this.tickable){
15956                 Roo.apply(d, {'roo-id' : Roo.id()});
15957                 
15958                 var _this = this;
15959             
15960                 Roo.each(this.parent.item, function(item){
15961                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15962                         return;
15963                     }
15964                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15965                 });
15966             }
15967             
15968             html[html.length] = Roo.util.Format.trim(
15969                 this.dataName ?
15970                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15971                     t.apply(d)
15972             );
15973         }
15974         
15975         
15976         
15977         el.update(html.join(""));
15978         this.nodes = el.dom.childNodes;
15979         this.updateIndexes(0);
15980     },
15981     
15982
15983     /**
15984      * Function to override to reformat the data that is sent to
15985      * the template for each node.
15986      * DEPRICATED - use the preparedata event handler.
15987      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15988      * a JSON object for an UpdateManager bound view).
15989      */
15990     prepareData : function(data, index, record)
15991     {
15992         this.fireEvent("preparedata", this, data, index, record);
15993         return data;
15994     },
15995
15996     onUpdate : function(ds, record){
15997         // Roo.log('on update');   
15998         this.clearSelections();
15999         var index = this.store.indexOf(record);
16000         var n = this.nodes[index];
16001         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16002         n.parentNode.removeChild(n);
16003         this.updateIndexes(index, index);
16004     },
16005
16006     
16007     
16008 // --------- FIXME     
16009     onAdd : function(ds, records, index)
16010     {
16011         //Roo.log(['on Add', ds, records, index] );        
16012         this.clearSelections();
16013         if(this.nodes.length == 0){
16014             this.refresh();
16015             return;
16016         }
16017         var n = this.nodes[index];
16018         for(var i = 0, len = records.length; i < len; i++){
16019             var d = this.prepareData(records[i].data, i, records[i]);
16020             if(n){
16021                 this.tpl.insertBefore(n, d);
16022             }else{
16023                 
16024                 this.tpl.append(this.el, d);
16025             }
16026         }
16027         this.updateIndexes(index);
16028     },
16029
16030     onRemove : function(ds, record, index){
16031        // Roo.log('onRemove');
16032         this.clearSelections();
16033         var el = this.dataName  ?
16034             this.el.child('.roo-tpl-' + this.dataName) :
16035             this.el; 
16036         
16037         el.dom.removeChild(this.nodes[index]);
16038         this.updateIndexes(index);
16039     },
16040
16041     /**
16042      * Refresh an individual node.
16043      * @param {Number} index
16044      */
16045     refreshNode : function(index){
16046         this.onUpdate(this.store, this.store.getAt(index));
16047     },
16048
16049     updateIndexes : function(startIndex, endIndex){
16050         var ns = this.nodes;
16051         startIndex = startIndex || 0;
16052         endIndex = endIndex || ns.length - 1;
16053         for(var i = startIndex; i <= endIndex; i++){
16054             ns[i].nodeIndex = i;
16055         }
16056     },
16057
16058     /**
16059      * Changes the data store this view uses and refresh the view.
16060      * @param {Store} store
16061      */
16062     setStore : function(store, initial){
16063         if(!initial && this.store){
16064             this.store.un("datachanged", this.refresh);
16065             this.store.un("add", this.onAdd);
16066             this.store.un("remove", this.onRemove);
16067             this.store.un("update", this.onUpdate);
16068             this.store.un("clear", this.refresh);
16069             this.store.un("beforeload", this.onBeforeLoad);
16070             this.store.un("load", this.onLoad);
16071             this.store.un("loadexception", this.onLoad);
16072         }
16073         if(store){
16074           
16075             store.on("datachanged", this.refresh, this);
16076             store.on("add", this.onAdd, this);
16077             store.on("remove", this.onRemove, this);
16078             store.on("update", this.onUpdate, this);
16079             store.on("clear", this.refresh, this);
16080             store.on("beforeload", this.onBeforeLoad, this);
16081             store.on("load", this.onLoad, this);
16082             store.on("loadexception", this.onLoad, this);
16083         }
16084         
16085         if(store){
16086             this.refresh();
16087         }
16088     },
16089     /**
16090      * onbeforeLoad - masks the loading area.
16091      *
16092      */
16093     onBeforeLoad : function(store,opts)
16094     {
16095          //Roo.log('onBeforeLoad');   
16096         if (!opts.add) {
16097             this.el.update("");
16098         }
16099         this.el.mask(this.mask ? this.mask : "Loading" ); 
16100     },
16101     onLoad : function ()
16102     {
16103         this.el.unmask();
16104     },
16105     
16106
16107     /**
16108      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16109      * @param {HTMLElement} node
16110      * @return {HTMLElement} The template node
16111      */
16112     findItemFromChild : function(node){
16113         var el = this.dataName  ?
16114             this.el.child('.roo-tpl-' + this.dataName,true) :
16115             this.el.dom; 
16116         
16117         if(!node || node.parentNode == el){
16118                     return node;
16119             }
16120             var p = node.parentNode;
16121             while(p && p != el){
16122             if(p.parentNode == el){
16123                 return p;
16124             }
16125             p = p.parentNode;
16126         }
16127             return null;
16128     },
16129
16130     /** @ignore */
16131     onClick : function(e){
16132         var item = this.findItemFromChild(e.getTarget());
16133         if(item){
16134             var index = this.indexOf(item);
16135             if(this.onItemClick(item, index, e) !== false){
16136                 this.fireEvent("click", this, index, item, e);
16137             }
16138         }else{
16139             this.clearSelections();
16140         }
16141     },
16142
16143     /** @ignore */
16144     onContextMenu : function(e){
16145         var item = this.findItemFromChild(e.getTarget());
16146         if(item){
16147             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16148         }
16149     },
16150
16151     /** @ignore */
16152     onDblClick : function(e){
16153         var item = this.findItemFromChild(e.getTarget());
16154         if(item){
16155             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16156         }
16157     },
16158
16159     onItemClick : function(item, index, e)
16160     {
16161         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16162             return false;
16163         }
16164         if (this.toggleSelect) {
16165             var m = this.isSelected(item) ? 'unselect' : 'select';
16166             //Roo.log(m);
16167             var _t = this;
16168             _t[m](item, true, false);
16169             return true;
16170         }
16171         if(this.multiSelect || this.singleSelect){
16172             if(this.multiSelect && e.shiftKey && this.lastSelection){
16173                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16174             }else{
16175                 this.select(item, this.multiSelect && e.ctrlKey);
16176                 this.lastSelection = item;
16177             }
16178             
16179             if(!this.tickable){
16180                 e.preventDefault();
16181             }
16182             
16183         }
16184         return true;
16185     },
16186
16187     /**
16188      * Get the number of selected nodes.
16189      * @return {Number}
16190      */
16191     getSelectionCount : function(){
16192         return this.selections.length;
16193     },
16194
16195     /**
16196      * Get the currently selected nodes.
16197      * @return {Array} An array of HTMLElements
16198      */
16199     getSelectedNodes : function(){
16200         return this.selections;
16201     },
16202
16203     /**
16204      * Get the indexes of the selected nodes.
16205      * @return {Array}
16206      */
16207     getSelectedIndexes : function(){
16208         var indexes = [], s = this.selections;
16209         for(var i = 0, len = s.length; i < len; i++){
16210             indexes.push(s[i].nodeIndex);
16211         }
16212         return indexes;
16213     },
16214
16215     /**
16216      * Clear all selections
16217      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16218      */
16219     clearSelections : function(suppressEvent){
16220         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16221             this.cmp.elements = this.selections;
16222             this.cmp.removeClass(this.selectedClass);
16223             this.selections = [];
16224             if(!suppressEvent){
16225                 this.fireEvent("selectionchange", this, this.selections);
16226             }
16227         }
16228     },
16229
16230     /**
16231      * Returns true if the passed node is selected
16232      * @param {HTMLElement/Number} node The node or node index
16233      * @return {Boolean}
16234      */
16235     isSelected : function(node){
16236         var s = this.selections;
16237         if(s.length < 1){
16238             return false;
16239         }
16240         node = this.getNode(node);
16241         return s.indexOf(node) !== -1;
16242     },
16243
16244     /**
16245      * Selects nodes.
16246      * @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
16247      * @param {Boolean} keepExisting (optional) true to keep existing selections
16248      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16249      */
16250     select : function(nodeInfo, keepExisting, suppressEvent){
16251         if(nodeInfo instanceof Array){
16252             if(!keepExisting){
16253                 this.clearSelections(true);
16254             }
16255             for(var i = 0, len = nodeInfo.length; i < len; i++){
16256                 this.select(nodeInfo[i], true, true);
16257             }
16258             return;
16259         } 
16260         var node = this.getNode(nodeInfo);
16261         if(!node || this.isSelected(node)){
16262             return; // already selected.
16263         }
16264         if(!keepExisting){
16265             this.clearSelections(true);
16266         }
16267         
16268         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16269             Roo.fly(node).addClass(this.selectedClass);
16270             this.selections.push(node);
16271             if(!suppressEvent){
16272                 this.fireEvent("selectionchange", this, this.selections);
16273             }
16274         }
16275         
16276         
16277     },
16278       /**
16279      * Unselects nodes.
16280      * @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
16281      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16282      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16283      */
16284     unselect : function(nodeInfo, keepExisting, suppressEvent)
16285     {
16286         if(nodeInfo instanceof Array){
16287             Roo.each(this.selections, function(s) {
16288                 this.unselect(s, nodeInfo);
16289             }, this);
16290             return;
16291         }
16292         var node = this.getNode(nodeInfo);
16293         if(!node || !this.isSelected(node)){
16294             //Roo.log("not selected");
16295             return; // not selected.
16296         }
16297         // fireevent???
16298         var ns = [];
16299         Roo.each(this.selections, function(s) {
16300             if (s == node ) {
16301                 Roo.fly(node).removeClass(this.selectedClass);
16302
16303                 return;
16304             }
16305             ns.push(s);
16306         },this);
16307         
16308         this.selections= ns;
16309         this.fireEvent("selectionchange", this, this.selections);
16310     },
16311
16312     /**
16313      * Gets a template node.
16314      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16315      * @return {HTMLElement} The node or null if it wasn't found
16316      */
16317     getNode : function(nodeInfo){
16318         if(typeof nodeInfo == "string"){
16319             return document.getElementById(nodeInfo);
16320         }else if(typeof nodeInfo == "number"){
16321             return this.nodes[nodeInfo];
16322         }
16323         return nodeInfo;
16324     },
16325
16326     /**
16327      * Gets a range template nodes.
16328      * @param {Number} startIndex
16329      * @param {Number} endIndex
16330      * @return {Array} An array of nodes
16331      */
16332     getNodes : function(start, end){
16333         var ns = this.nodes;
16334         start = start || 0;
16335         end = typeof end == "undefined" ? ns.length - 1 : end;
16336         var nodes = [];
16337         if(start <= end){
16338             for(var i = start; i <= end; i++){
16339                 nodes.push(ns[i]);
16340             }
16341         } else{
16342             for(var i = start; i >= end; i--){
16343                 nodes.push(ns[i]);
16344             }
16345         }
16346         return nodes;
16347     },
16348
16349     /**
16350      * Finds the index of the passed node
16351      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16352      * @return {Number} The index of the node or -1
16353      */
16354     indexOf : function(node){
16355         node = this.getNode(node);
16356         if(typeof node.nodeIndex == "number"){
16357             return node.nodeIndex;
16358         }
16359         var ns = this.nodes;
16360         for(var i = 0, len = ns.length; i < len; i++){
16361             if(ns[i] == node){
16362                 return i;
16363             }
16364         }
16365         return -1;
16366     }
16367 });
16368 /*
16369  * - LGPL
16370  *
16371  * based on jquery fullcalendar
16372  * 
16373  */
16374
16375 Roo.bootstrap = Roo.bootstrap || {};
16376 /**
16377  * @class Roo.bootstrap.Calendar
16378  * @extends Roo.bootstrap.Component
16379  * Bootstrap Calendar class
16380  * @cfg {Boolean} loadMask (true|false) default false
16381  * @cfg {Object} header generate the user specific header of the calendar, default false
16382
16383  * @constructor
16384  * Create a new Container
16385  * @param {Object} config The config object
16386  */
16387
16388
16389
16390 Roo.bootstrap.Calendar = function(config){
16391     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16392      this.addEvents({
16393         /**
16394              * @event select
16395              * Fires when a date is selected
16396              * @param {DatePicker} this
16397              * @param {Date} date The selected date
16398              */
16399         'select': true,
16400         /**
16401              * @event monthchange
16402              * Fires when the displayed month changes 
16403              * @param {DatePicker} this
16404              * @param {Date} date The selected month
16405              */
16406         'monthchange': true,
16407         /**
16408              * @event evententer
16409              * Fires when mouse over an event
16410              * @param {Calendar} this
16411              * @param {event} Event
16412              */
16413         'evententer': true,
16414         /**
16415              * @event eventleave
16416              * Fires when the mouse leaves an
16417              * @param {Calendar} this
16418              * @param {event}
16419              */
16420         'eventleave': true,
16421         /**
16422              * @event eventclick
16423              * Fires when the mouse click an
16424              * @param {Calendar} this
16425              * @param {event}
16426              */
16427         'eventclick': true
16428         
16429     });
16430
16431 };
16432
16433 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16434     
16435      /**
16436      * @cfg {Number} startDay
16437      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16438      */
16439     startDay : 0,
16440     
16441     loadMask : false,
16442     
16443     header : false,
16444       
16445     getAutoCreate : function(){
16446         
16447         
16448         var fc_button = function(name, corner, style, content ) {
16449             return Roo.apply({},{
16450                 tag : 'span',
16451                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16452                          (corner.length ?
16453                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16454                             ''
16455                         ),
16456                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16457                 unselectable: 'on'
16458             });
16459         };
16460         
16461         var header = {};
16462         
16463         if(!this.header){
16464             header = {
16465                 tag : 'table',
16466                 cls : 'fc-header',
16467                 style : 'width:100%',
16468                 cn : [
16469                     {
16470                         tag: 'tr',
16471                         cn : [
16472                             {
16473                                 tag : 'td',
16474                                 cls : 'fc-header-left',
16475                                 cn : [
16476                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16477                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16478                                     { tag: 'span', cls: 'fc-header-space' },
16479                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16480
16481
16482                                 ]
16483                             },
16484
16485                             {
16486                                 tag : 'td',
16487                                 cls : 'fc-header-center',
16488                                 cn : [
16489                                     {
16490                                         tag: 'span',
16491                                         cls: 'fc-header-title',
16492                                         cn : {
16493                                             tag: 'H2',
16494                                             html : 'month / year'
16495                                         }
16496                                     }
16497
16498                                 ]
16499                             },
16500                             {
16501                                 tag : 'td',
16502                                 cls : 'fc-header-right',
16503                                 cn : [
16504                               /*      fc_button('month', 'left', '', 'month' ),
16505                                     fc_button('week', '', '', 'week' ),
16506                                     fc_button('day', 'right', '', 'day' )
16507                                 */    
16508
16509                                 ]
16510                             }
16511
16512                         ]
16513                     }
16514                 ]
16515             };
16516         }
16517         
16518         header = this.header;
16519         
16520        
16521         var cal_heads = function() {
16522             var ret = [];
16523             // fixme - handle this.
16524             
16525             for (var i =0; i < Date.dayNames.length; i++) {
16526                 var d = Date.dayNames[i];
16527                 ret.push({
16528                     tag: 'th',
16529                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16530                     html : d.substring(0,3)
16531                 });
16532                 
16533             }
16534             ret[0].cls += ' fc-first';
16535             ret[6].cls += ' fc-last';
16536             return ret;
16537         };
16538         var cal_cell = function(n) {
16539             return  {
16540                 tag: 'td',
16541                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16542                 cn : [
16543                     {
16544                         cn : [
16545                             {
16546                                 cls: 'fc-day-number',
16547                                 html: 'D'
16548                             },
16549                             {
16550                                 cls: 'fc-day-content',
16551                              
16552                                 cn : [
16553                                      {
16554                                         style: 'position: relative;' // height: 17px;
16555                                     }
16556                                 ]
16557                             }
16558                             
16559                             
16560                         ]
16561                     }
16562                 ]
16563                 
16564             }
16565         };
16566         var cal_rows = function() {
16567             
16568             var ret = [];
16569             for (var r = 0; r < 6; r++) {
16570                 var row= {
16571                     tag : 'tr',
16572                     cls : 'fc-week',
16573                     cn : []
16574                 };
16575                 
16576                 for (var i =0; i < Date.dayNames.length; i++) {
16577                     var d = Date.dayNames[i];
16578                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16579
16580                 }
16581                 row.cn[0].cls+=' fc-first';
16582                 row.cn[0].cn[0].style = 'min-height:90px';
16583                 row.cn[6].cls+=' fc-last';
16584                 ret.push(row);
16585                 
16586             }
16587             ret[0].cls += ' fc-first';
16588             ret[4].cls += ' fc-prev-last';
16589             ret[5].cls += ' fc-last';
16590             return ret;
16591             
16592         };
16593         
16594         var cal_table = {
16595             tag: 'table',
16596             cls: 'fc-border-separate',
16597             style : 'width:100%',
16598             cellspacing  : 0,
16599             cn : [
16600                 { 
16601                     tag: 'thead',
16602                     cn : [
16603                         { 
16604                             tag: 'tr',
16605                             cls : 'fc-first fc-last',
16606                             cn : cal_heads()
16607                         }
16608                     ]
16609                 },
16610                 { 
16611                     tag: 'tbody',
16612                     cn : cal_rows()
16613                 }
16614                   
16615             ]
16616         };
16617          
16618          var cfg = {
16619             cls : 'fc fc-ltr',
16620             cn : [
16621                 header,
16622                 {
16623                     cls : 'fc-content',
16624                     style : "position: relative;",
16625                     cn : [
16626                         {
16627                             cls : 'fc-view fc-view-month fc-grid',
16628                             style : 'position: relative',
16629                             unselectable : 'on',
16630                             cn : [
16631                                 {
16632                                     cls : 'fc-event-container',
16633                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16634                                 },
16635                                 cal_table
16636                             ]
16637                         }
16638                     ]
16639     
16640                 }
16641            ] 
16642             
16643         };
16644         
16645          
16646         
16647         return cfg;
16648     },
16649     
16650     
16651     initEvents : function()
16652     {
16653         if(!this.store){
16654             throw "can not find store for calendar";
16655         }
16656         
16657         var mark = {
16658             tag: "div",
16659             cls:"x-dlg-mask",
16660             style: "text-align:center",
16661             cn: [
16662                 {
16663                     tag: "div",
16664                     style: "background-color:white;width:50%;margin:250 auto",
16665                     cn: [
16666                         {
16667                             tag: "img",
16668                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16669                         },
16670                         {
16671                             tag: "span",
16672                             html: "Loading"
16673                         }
16674                         
16675                     ]
16676                 }
16677             ]
16678         };
16679         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16680         
16681         var size = this.el.select('.fc-content', true).first().getSize();
16682         this.maskEl.setSize(size.width, size.height);
16683         this.maskEl.enableDisplayMode("block");
16684         if(!this.loadMask){
16685             this.maskEl.hide();
16686         }
16687         
16688         this.store = Roo.factory(this.store, Roo.data);
16689         this.store.on('load', this.onLoad, this);
16690         this.store.on('beforeload', this.onBeforeLoad, this);
16691         
16692         this.resize();
16693         
16694         this.cells = this.el.select('.fc-day',true);
16695         //Roo.log(this.cells);
16696         this.textNodes = this.el.query('.fc-day-number');
16697         this.cells.addClassOnOver('fc-state-hover');
16698         
16699         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16700         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16701         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16702         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16703         
16704         this.on('monthchange', this.onMonthChange, this);
16705         
16706         this.update(new Date().clearTime());
16707     },
16708     
16709     resize : function() {
16710         var sz  = this.el.getSize();
16711         
16712         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16713         this.el.select('.fc-day-content div',true).setHeight(34);
16714     },
16715     
16716     
16717     // private
16718     showPrevMonth : function(e){
16719         this.update(this.activeDate.add("mo", -1));
16720     },
16721     showToday : function(e){
16722         this.update(new Date().clearTime());
16723     },
16724     // private
16725     showNextMonth : function(e){
16726         this.update(this.activeDate.add("mo", 1));
16727     },
16728
16729     // private
16730     showPrevYear : function(){
16731         this.update(this.activeDate.add("y", -1));
16732     },
16733
16734     // private
16735     showNextYear : function(){
16736         this.update(this.activeDate.add("y", 1));
16737     },
16738
16739     
16740    // private
16741     update : function(date)
16742     {
16743         var vd = this.activeDate;
16744         this.activeDate = date;
16745 //        if(vd && this.el){
16746 //            var t = date.getTime();
16747 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16748 //                Roo.log('using add remove');
16749 //                
16750 //                this.fireEvent('monthchange', this, date);
16751 //                
16752 //                this.cells.removeClass("fc-state-highlight");
16753 //                this.cells.each(function(c){
16754 //                   if(c.dateValue == t){
16755 //                       c.addClass("fc-state-highlight");
16756 //                       setTimeout(function(){
16757 //                            try{c.dom.firstChild.focus();}catch(e){}
16758 //                       }, 50);
16759 //                       return false;
16760 //                   }
16761 //                   return true;
16762 //                });
16763 //                return;
16764 //            }
16765 //        }
16766         
16767         var days = date.getDaysInMonth();
16768         
16769         var firstOfMonth = date.getFirstDateOfMonth();
16770         var startingPos = firstOfMonth.getDay()-this.startDay;
16771         
16772         if(startingPos < this.startDay){
16773             startingPos += 7;
16774         }
16775         
16776         var pm = date.add(Date.MONTH, -1);
16777         var prevStart = pm.getDaysInMonth()-startingPos;
16778 //        
16779         this.cells = this.el.select('.fc-day',true);
16780         this.textNodes = this.el.query('.fc-day-number');
16781         this.cells.addClassOnOver('fc-state-hover');
16782         
16783         var cells = this.cells.elements;
16784         var textEls = this.textNodes;
16785         
16786         Roo.each(cells, function(cell){
16787             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16788         });
16789         
16790         days += startingPos;
16791
16792         // convert everything to numbers so it's fast
16793         var day = 86400000;
16794         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16795         //Roo.log(d);
16796         //Roo.log(pm);
16797         //Roo.log(prevStart);
16798         
16799         var today = new Date().clearTime().getTime();
16800         var sel = date.clearTime().getTime();
16801         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16802         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16803         var ddMatch = this.disabledDatesRE;
16804         var ddText = this.disabledDatesText;
16805         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16806         var ddaysText = this.disabledDaysText;
16807         var format = this.format;
16808         
16809         var setCellClass = function(cal, cell){
16810             cell.row = 0;
16811             cell.events = [];
16812             cell.more = [];
16813             //Roo.log('set Cell Class');
16814             cell.title = "";
16815             var t = d.getTime();
16816             
16817             //Roo.log(d);
16818             
16819             cell.dateValue = t;
16820             if(t == today){
16821                 cell.className += " fc-today";
16822                 cell.className += " fc-state-highlight";
16823                 cell.title = cal.todayText;
16824             }
16825             if(t == sel){
16826                 // disable highlight in other month..
16827                 //cell.className += " fc-state-highlight";
16828                 
16829             }
16830             // disabling
16831             if(t < min) {
16832                 cell.className = " fc-state-disabled";
16833                 cell.title = cal.minText;
16834                 return;
16835             }
16836             if(t > max) {
16837                 cell.className = " fc-state-disabled";
16838                 cell.title = cal.maxText;
16839                 return;
16840             }
16841             if(ddays){
16842                 if(ddays.indexOf(d.getDay()) != -1){
16843                     cell.title = ddaysText;
16844                     cell.className = " fc-state-disabled";
16845                 }
16846             }
16847             if(ddMatch && format){
16848                 var fvalue = d.dateFormat(format);
16849                 if(ddMatch.test(fvalue)){
16850                     cell.title = ddText.replace("%0", fvalue);
16851                     cell.className = " fc-state-disabled";
16852                 }
16853             }
16854             
16855             if (!cell.initialClassName) {
16856                 cell.initialClassName = cell.dom.className;
16857             }
16858             
16859             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16860         };
16861
16862         var i = 0;
16863         
16864         for(; i < startingPos; i++) {
16865             textEls[i].innerHTML = (++prevStart);
16866             d.setDate(d.getDate()+1);
16867             
16868             cells[i].className = "fc-past fc-other-month";
16869             setCellClass(this, cells[i]);
16870         }
16871         
16872         var intDay = 0;
16873         
16874         for(; i < days; i++){
16875             intDay = i - startingPos + 1;
16876             textEls[i].innerHTML = (intDay);
16877             d.setDate(d.getDate()+1);
16878             
16879             cells[i].className = ''; // "x-date-active";
16880             setCellClass(this, cells[i]);
16881         }
16882         var extraDays = 0;
16883         
16884         for(; i < 42; i++) {
16885             textEls[i].innerHTML = (++extraDays);
16886             d.setDate(d.getDate()+1);
16887             
16888             cells[i].className = "fc-future fc-other-month";
16889             setCellClass(this, cells[i]);
16890         }
16891         
16892         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16893         
16894         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16895         
16896         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16897         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16898         
16899         if(totalRows != 6){
16900             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16901             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16902         }
16903         
16904         this.fireEvent('monthchange', this, date);
16905         
16906         
16907         /*
16908         if(!this.internalRender){
16909             var main = this.el.dom.firstChild;
16910             var w = main.offsetWidth;
16911             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16912             Roo.fly(main).setWidth(w);
16913             this.internalRender = true;
16914             // opera does not respect the auto grow header center column
16915             // then, after it gets a width opera refuses to recalculate
16916             // without a second pass
16917             if(Roo.isOpera && !this.secondPass){
16918                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16919                 this.secondPass = true;
16920                 this.update.defer(10, this, [date]);
16921             }
16922         }
16923         */
16924         
16925     },
16926     
16927     findCell : function(dt) {
16928         dt = dt.clearTime().getTime();
16929         var ret = false;
16930         this.cells.each(function(c){
16931             //Roo.log("check " +c.dateValue + '?=' + dt);
16932             if(c.dateValue == dt){
16933                 ret = c;
16934                 return false;
16935             }
16936             return true;
16937         });
16938         
16939         return ret;
16940     },
16941     
16942     findCells : function(ev) {
16943         var s = ev.start.clone().clearTime().getTime();
16944        // Roo.log(s);
16945         var e= ev.end.clone().clearTime().getTime();
16946        // Roo.log(e);
16947         var ret = [];
16948         this.cells.each(function(c){
16949              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16950             
16951             if(c.dateValue > e){
16952                 return ;
16953             }
16954             if(c.dateValue < s){
16955                 return ;
16956             }
16957             ret.push(c);
16958         });
16959         
16960         return ret;    
16961     },
16962     
16963 //    findBestRow: function(cells)
16964 //    {
16965 //        var ret = 0;
16966 //        
16967 //        for (var i =0 ; i < cells.length;i++) {
16968 //            ret  = Math.max(cells[i].rows || 0,ret);
16969 //        }
16970 //        return ret;
16971 //        
16972 //    },
16973     
16974     
16975     addItem : function(ev)
16976     {
16977         // look for vertical location slot in
16978         var cells = this.findCells(ev);
16979         
16980 //        ev.row = this.findBestRow(cells);
16981         
16982         // work out the location.
16983         
16984         var crow = false;
16985         var rows = [];
16986         for(var i =0; i < cells.length; i++) {
16987             
16988             cells[i].row = cells[0].row;
16989             
16990             if(i == 0){
16991                 cells[i].row = cells[i].row + 1;
16992             }
16993             
16994             if (!crow) {
16995                 crow = {
16996                     start : cells[i],
16997                     end :  cells[i]
16998                 };
16999                 continue;
17000             }
17001             if (crow.start.getY() == cells[i].getY()) {
17002                 // on same row.
17003                 crow.end = cells[i];
17004                 continue;
17005             }
17006             // different row.
17007             rows.push(crow);
17008             crow = {
17009                 start: cells[i],
17010                 end : cells[i]
17011             };
17012             
17013         }
17014         
17015         rows.push(crow);
17016         ev.els = [];
17017         ev.rows = rows;
17018         ev.cells = cells;
17019         
17020         cells[0].events.push(ev);
17021         
17022         this.calevents.push(ev);
17023     },
17024     
17025     clearEvents: function() {
17026         
17027         if(!this.calevents){
17028             return;
17029         }
17030         
17031         Roo.each(this.cells.elements, function(c){
17032             c.row = 0;
17033             c.events = [];
17034             c.more = [];
17035         });
17036         
17037         Roo.each(this.calevents, function(e) {
17038             Roo.each(e.els, function(el) {
17039                 el.un('mouseenter' ,this.onEventEnter, this);
17040                 el.un('mouseleave' ,this.onEventLeave, this);
17041                 el.remove();
17042             },this);
17043         },this);
17044         
17045         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17046             e.remove();
17047         });
17048         
17049     },
17050     
17051     renderEvents: function()
17052     {   
17053         var _this = this;
17054         
17055         this.cells.each(function(c) {
17056             
17057             if(c.row < 5){
17058                 return;
17059             }
17060             
17061             var ev = c.events;
17062             
17063             var r = 4;
17064             if(c.row != c.events.length){
17065                 r = 4 - (4 - (c.row - c.events.length));
17066             }
17067             
17068             c.events = ev.slice(0, r);
17069             c.more = ev.slice(r);
17070             
17071             if(c.more.length && c.more.length == 1){
17072                 c.events.push(c.more.pop());
17073             }
17074             
17075             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17076             
17077         });
17078             
17079         this.cells.each(function(c) {
17080             
17081             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17082             
17083             
17084             for (var e = 0; e < c.events.length; e++){
17085                 var ev = c.events[e];
17086                 var rows = ev.rows;
17087                 
17088                 for(var i = 0; i < rows.length; i++) {
17089                 
17090                     // how many rows should it span..
17091
17092                     var  cfg = {
17093                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17094                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17095
17096                         unselectable : "on",
17097                         cn : [
17098                             {
17099                                 cls: 'fc-event-inner',
17100                                 cn : [
17101     //                                {
17102     //                                  tag:'span',
17103     //                                  cls: 'fc-event-time',
17104     //                                  html : cells.length > 1 ? '' : ev.time
17105     //                                },
17106                                     {
17107                                       tag:'span',
17108                                       cls: 'fc-event-title',
17109                                       html : String.format('{0}', ev.title)
17110                                     }
17111
17112
17113                                 ]
17114                             },
17115                             {
17116                                 cls: 'ui-resizable-handle ui-resizable-e',
17117                                 html : '&nbsp;&nbsp;&nbsp'
17118                             }
17119
17120                         ]
17121                     };
17122
17123                     if (i == 0) {
17124                         cfg.cls += ' fc-event-start';
17125                     }
17126                     if ((i+1) == rows.length) {
17127                         cfg.cls += ' fc-event-end';
17128                     }
17129
17130                     var ctr = _this.el.select('.fc-event-container',true).first();
17131                     var cg = ctr.createChild(cfg);
17132
17133                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17134                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17135
17136                     var r = (c.more.length) ? 1 : 0;
17137                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17138                     cg.setWidth(ebox.right - sbox.x -2);
17139
17140                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17141                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17142                     cg.on('click', _this.onEventClick, _this, ev);
17143
17144                     ev.els.push(cg);
17145                     
17146                 }
17147                 
17148             }
17149             
17150             
17151             if(c.more.length){
17152                 var  cfg = {
17153                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17154                     style : 'position: absolute',
17155                     unselectable : "on",
17156                     cn : [
17157                         {
17158                             cls: 'fc-event-inner',
17159                             cn : [
17160                                 {
17161                                   tag:'span',
17162                                   cls: 'fc-event-title',
17163                                   html : 'More'
17164                                 }
17165
17166
17167                             ]
17168                         },
17169                         {
17170                             cls: 'ui-resizable-handle ui-resizable-e',
17171                             html : '&nbsp;&nbsp;&nbsp'
17172                         }
17173
17174                     ]
17175                 };
17176
17177                 var ctr = _this.el.select('.fc-event-container',true).first();
17178                 var cg = ctr.createChild(cfg);
17179
17180                 var sbox = c.select('.fc-day-content',true).first().getBox();
17181                 var ebox = c.select('.fc-day-content',true).first().getBox();
17182                 //Roo.log(cg);
17183                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17184                 cg.setWidth(ebox.right - sbox.x -2);
17185
17186                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17187                 
17188             }
17189             
17190         });
17191         
17192         
17193         
17194     },
17195     
17196     onEventEnter: function (e, el,event,d) {
17197         this.fireEvent('evententer', this, el, event);
17198     },
17199     
17200     onEventLeave: function (e, el,event,d) {
17201         this.fireEvent('eventleave', this, el, event);
17202     },
17203     
17204     onEventClick: function (e, el,event,d) {
17205         this.fireEvent('eventclick', this, el, event);
17206     },
17207     
17208     onMonthChange: function () {
17209         this.store.load();
17210     },
17211     
17212     onMoreEventClick: function(e, el, more)
17213     {
17214         var _this = this;
17215         
17216         this.calpopover.placement = 'right';
17217         this.calpopover.setTitle('More');
17218         
17219         this.calpopover.setContent('');
17220         
17221         var ctr = this.calpopover.el.select('.popover-content', true).first();
17222         
17223         Roo.each(more, function(m){
17224             var cfg = {
17225                 cls : 'fc-event-hori fc-event-draggable',
17226                 html : m.title
17227             };
17228             var cg = ctr.createChild(cfg);
17229             
17230             cg.on('click', _this.onEventClick, _this, m);
17231         });
17232         
17233         this.calpopover.show(el);
17234         
17235         
17236     },
17237     
17238     onLoad: function () 
17239     {   
17240         this.calevents = [];
17241         var cal = this;
17242         
17243         if(this.store.getCount() > 0){
17244             this.store.data.each(function(d){
17245                cal.addItem({
17246                     id : d.data.id,
17247                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17248                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17249                     time : d.data.start_time,
17250                     title : d.data.title,
17251                     description : d.data.description,
17252                     venue : d.data.venue
17253                 });
17254             });
17255         }
17256         
17257         this.renderEvents();
17258         
17259         if(this.calevents.length && this.loadMask){
17260             this.maskEl.hide();
17261         }
17262     },
17263     
17264     onBeforeLoad: function()
17265     {
17266         this.clearEvents();
17267         if(this.loadMask){
17268             this.maskEl.show();
17269         }
17270     }
17271 });
17272
17273  
17274  /*
17275  * - LGPL
17276  *
17277  * element
17278  * 
17279  */
17280
17281 /**
17282  * @class Roo.bootstrap.Popover
17283  * @extends Roo.bootstrap.Component
17284  * Bootstrap Popover class
17285  * @cfg {String} html contents of the popover   (or false to use children..)
17286  * @cfg {String} title of popover (or false to hide)
17287  * @cfg {String} placement how it is placed
17288  * @cfg {String} trigger click || hover (or false to trigger manually)
17289  * @cfg {String} over what (parent or false to trigger manually.)
17290  * @cfg {Number} delay - delay before showing
17291  
17292  * @constructor
17293  * Create a new Popover
17294  * @param {Object} config The config object
17295  */
17296
17297 Roo.bootstrap.Popover = function(config){
17298     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17299     
17300     this.addEvents({
17301         // raw events
17302          /**
17303          * @event show
17304          * After the popover show
17305          * 
17306          * @param {Roo.bootstrap.Popover} this
17307          */
17308         "show" : true,
17309         /**
17310          * @event hide
17311          * After the popover hide
17312          * 
17313          * @param {Roo.bootstrap.Popover} this
17314          */
17315         "hide" : true
17316     });
17317 };
17318
17319 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17320     
17321     title: 'Fill in a title',
17322     html: false,
17323     
17324     placement : 'right',
17325     trigger : 'hover', // hover
17326     
17327     delay : 0,
17328     
17329     over: 'parent',
17330     
17331     can_build_overlaid : false,
17332     
17333     getChildContainer : function()
17334     {
17335         return this.el.select('.popover-content',true).first();
17336     },
17337     
17338     getAutoCreate : function(){
17339          
17340         var cfg = {
17341            cls : 'popover roo-dynamic',
17342            style: 'display:block',
17343            cn : [
17344                 {
17345                     cls : 'arrow'
17346                 },
17347                 {
17348                     cls : 'popover-inner',
17349                     cn : [
17350                         {
17351                             tag: 'h3',
17352                             cls: 'popover-title',
17353                             html : this.title
17354                         },
17355                         {
17356                             cls : 'popover-content',
17357                             html : this.html
17358                         }
17359                     ]
17360                     
17361                 }
17362            ]
17363         };
17364         
17365         return cfg;
17366     },
17367     setTitle: function(str)
17368     {
17369         this.title = str;
17370         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17371     },
17372     setContent: function(str)
17373     {
17374         this.html = str;
17375         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17376     },
17377     // as it get's added to the bottom of the page.
17378     onRender : function(ct, position)
17379     {
17380         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17381         if(!this.el){
17382             var cfg = Roo.apply({},  this.getAutoCreate());
17383             cfg.id = Roo.id();
17384             
17385             if (this.cls) {
17386                 cfg.cls += ' ' + this.cls;
17387             }
17388             if (this.style) {
17389                 cfg.style = this.style;
17390             }
17391             //Roo.log("adding to ");
17392             this.el = Roo.get(document.body).createChild(cfg, position);
17393 //            Roo.log(this.el);
17394         }
17395         this.initEvents();
17396     },
17397     
17398     initEvents : function()
17399     {
17400         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17401         this.el.enableDisplayMode('block');
17402         this.el.hide();
17403         if (this.over === false) {
17404             return; 
17405         }
17406         if (this.triggers === false) {
17407             return;
17408         }
17409         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17410         var triggers = this.trigger ? this.trigger.split(' ') : [];
17411         Roo.each(triggers, function(trigger) {
17412         
17413             if (trigger == 'click') {
17414                 on_el.on('click', this.toggle, this);
17415             } else if (trigger != 'manual') {
17416                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17417                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17418       
17419                 on_el.on(eventIn  ,this.enter, this);
17420                 on_el.on(eventOut, this.leave, this);
17421             }
17422         }, this);
17423         
17424     },
17425     
17426     
17427     // private
17428     timeout : null,
17429     hoverState : null,
17430     
17431     toggle : function () {
17432         this.hoverState == 'in' ? this.leave() : this.enter();
17433     },
17434     
17435     enter : function () {
17436         
17437         clearTimeout(this.timeout);
17438     
17439         this.hoverState = 'in';
17440     
17441         if (!this.delay || !this.delay.show) {
17442             this.show();
17443             return;
17444         }
17445         var _t = this;
17446         this.timeout = setTimeout(function () {
17447             if (_t.hoverState == 'in') {
17448                 _t.show();
17449             }
17450         }, this.delay.show)
17451     },
17452     
17453     leave : function() {
17454         clearTimeout(this.timeout);
17455     
17456         this.hoverState = 'out';
17457     
17458         if (!this.delay || !this.delay.hide) {
17459             this.hide();
17460             return;
17461         }
17462         var _t = this;
17463         this.timeout = setTimeout(function () {
17464             if (_t.hoverState == 'out') {
17465                 _t.hide();
17466             }
17467         }, this.delay.hide)
17468     },
17469     
17470     show : function (on_el)
17471     {
17472         if (!on_el) {
17473             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17474         }
17475         
17476         // set content.
17477         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17478         if (this.html !== false) {
17479             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17480         }
17481         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17482         if (!this.title.length) {
17483             this.el.select('.popover-title',true).hide();
17484         }
17485         
17486         var placement = typeof this.placement == 'function' ?
17487             this.placement.call(this, this.el, on_el) :
17488             this.placement;
17489             
17490         var autoToken = /\s?auto?\s?/i;
17491         var autoPlace = autoToken.test(placement);
17492         if (autoPlace) {
17493             placement = placement.replace(autoToken, '') || 'top';
17494         }
17495         
17496         //this.el.detach()
17497         //this.el.setXY([0,0]);
17498         this.el.show();
17499         this.el.dom.style.display='block';
17500         this.el.addClass(placement);
17501         
17502         //this.el.appendTo(on_el);
17503         
17504         var p = this.getPosition();
17505         var box = this.el.getBox();
17506         
17507         if (autoPlace) {
17508             // fixme..
17509         }
17510         var align = Roo.bootstrap.Popover.alignment[placement];
17511         
17512 //        Roo.log(align);
17513         this.el.alignTo(on_el, align[0],align[1]);
17514         //var arrow = this.el.select('.arrow',true).first();
17515         //arrow.set(align[2], 
17516         
17517         this.el.addClass('in');
17518         
17519         
17520         if (this.el.hasClass('fade')) {
17521             // fade it?
17522         }
17523         
17524         this.hoverState = 'in';
17525         
17526         this.fireEvent('show', this);
17527         
17528     },
17529     hide : function()
17530     {
17531         this.el.setXY([0,0]);
17532         this.el.removeClass('in');
17533         this.el.hide();
17534         this.hoverState = null;
17535         
17536         this.fireEvent('hide', this);
17537     }
17538     
17539 });
17540
17541 Roo.bootstrap.Popover.alignment = {
17542     'left' : ['r-l', [-10,0], 'right'],
17543     'right' : ['l-r', [10,0], 'left'],
17544     'bottom' : ['t-b', [0,10], 'top'],
17545     'top' : [ 'b-t', [0,-10], 'bottom']
17546 };
17547
17548  /*
17549  * - LGPL
17550  *
17551  * Progress
17552  * 
17553  */
17554
17555 /**
17556  * @class Roo.bootstrap.Progress
17557  * @extends Roo.bootstrap.Component
17558  * Bootstrap Progress class
17559  * @cfg {Boolean} striped striped of the progress bar
17560  * @cfg {Boolean} active animated of the progress bar
17561  * 
17562  * 
17563  * @constructor
17564  * Create a new Progress
17565  * @param {Object} config The config object
17566  */
17567
17568 Roo.bootstrap.Progress = function(config){
17569     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17570 };
17571
17572 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17573     
17574     striped : false,
17575     active: false,
17576     
17577     getAutoCreate : function(){
17578         var cfg = {
17579             tag: 'div',
17580             cls: 'progress'
17581         };
17582         
17583         
17584         if(this.striped){
17585             cfg.cls += ' progress-striped';
17586         }
17587       
17588         if(this.active){
17589             cfg.cls += ' active';
17590         }
17591         
17592         
17593         return cfg;
17594     }
17595    
17596 });
17597
17598  
17599
17600  /*
17601  * - LGPL
17602  *
17603  * ProgressBar
17604  * 
17605  */
17606
17607 /**
17608  * @class Roo.bootstrap.ProgressBar
17609  * @extends Roo.bootstrap.Component
17610  * Bootstrap ProgressBar class
17611  * @cfg {Number} aria_valuenow aria-value now
17612  * @cfg {Number} aria_valuemin aria-value min
17613  * @cfg {Number} aria_valuemax aria-value max
17614  * @cfg {String} label label for the progress bar
17615  * @cfg {String} panel (success | info | warning | danger )
17616  * @cfg {String} role role of the progress bar
17617  * @cfg {String} sr_only text
17618  * 
17619  * 
17620  * @constructor
17621  * Create a new ProgressBar
17622  * @param {Object} config The config object
17623  */
17624
17625 Roo.bootstrap.ProgressBar = function(config){
17626     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17627 };
17628
17629 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17630     
17631     aria_valuenow : 0,
17632     aria_valuemin : 0,
17633     aria_valuemax : 100,
17634     label : false,
17635     panel : false,
17636     role : false,
17637     sr_only: false,
17638     
17639     getAutoCreate : function()
17640     {
17641         
17642         var cfg = {
17643             tag: 'div',
17644             cls: 'progress-bar',
17645             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17646         };
17647         
17648         if(this.sr_only){
17649             cfg.cn = {
17650                 tag: 'span',
17651                 cls: 'sr-only',
17652                 html: this.sr_only
17653             }
17654         }
17655         
17656         if(this.role){
17657             cfg.role = this.role;
17658         }
17659         
17660         if(this.aria_valuenow){
17661             cfg['aria-valuenow'] = this.aria_valuenow;
17662         }
17663         
17664         if(this.aria_valuemin){
17665             cfg['aria-valuemin'] = this.aria_valuemin;
17666         }
17667         
17668         if(this.aria_valuemax){
17669             cfg['aria-valuemax'] = this.aria_valuemax;
17670         }
17671         
17672         if(this.label && !this.sr_only){
17673             cfg.html = this.label;
17674         }
17675         
17676         if(this.panel){
17677             cfg.cls += ' progress-bar-' + this.panel;
17678         }
17679         
17680         return cfg;
17681     },
17682     
17683     update : function(aria_valuenow)
17684     {
17685         this.aria_valuenow = aria_valuenow;
17686         
17687         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17688     }
17689    
17690 });
17691
17692  
17693
17694  /*
17695  * - LGPL
17696  *
17697  * column
17698  * 
17699  */
17700
17701 /**
17702  * @class Roo.bootstrap.TabGroup
17703  * @extends Roo.bootstrap.Column
17704  * Bootstrap Column class
17705  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17706  * @cfg {Boolean} carousel true to make the group behave like a carousel
17707  * @cfg {Boolean} bullets show bullets for the panels
17708  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17709  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17710  * @cfg {Boolean} showarrow (true|false) show arrow default true
17711  * 
17712  * @constructor
17713  * Create a new TabGroup
17714  * @param {Object} config The config object
17715  */
17716
17717 Roo.bootstrap.TabGroup = function(config){
17718     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17719     if (!this.navId) {
17720         this.navId = Roo.id();
17721     }
17722     this.tabs = [];
17723     Roo.bootstrap.TabGroup.register(this);
17724     
17725 };
17726
17727 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17728     
17729     carousel : false,
17730     transition : false,
17731     bullets : 0,
17732     timer : 0,
17733     autoslide : false,
17734     slideFn : false,
17735     slideOnTouch : false,
17736     showarrow : true,
17737     
17738     getAutoCreate : function()
17739     {
17740         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17741         
17742         cfg.cls += ' tab-content';
17743         
17744         if (this.carousel) {
17745             cfg.cls += ' carousel slide';
17746             
17747             cfg.cn = [{
17748                cls : 'carousel-inner',
17749                cn : []
17750             }];
17751         
17752             if(this.bullets  && !Roo.isTouch){
17753                 
17754                 var bullets = {
17755                     cls : 'carousel-bullets',
17756                     cn : []
17757                 };
17758                
17759                 if(this.bullets_cls){
17760                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17761                 }
17762                 
17763                 bullets.cn.push({
17764                     cls : 'clear'
17765                 });
17766                 
17767                 cfg.cn[0].cn.push(bullets);
17768             }
17769             
17770             if(this.showarrow){
17771                 cfg.cn[0].cn.push({
17772                     tag : 'div',
17773                     class : 'carousel-arrow',
17774                     cn : [
17775                         {
17776                             tag : 'div',
17777                             class : 'carousel-prev',
17778                             cn : [
17779                                 {
17780                                     tag : 'i',
17781                                     class : 'fa fa-chevron-left'
17782                                 }
17783                             ]
17784                         },
17785                         {
17786                             tag : 'div',
17787                             class : 'carousel-next',
17788                             cn : [
17789                                 {
17790                                     tag : 'i',
17791                                     class : 'fa fa-chevron-right'
17792                                 }
17793                             ]
17794                         }
17795                     ]
17796                 });
17797             }
17798             
17799         }
17800         
17801         return cfg;
17802     },
17803     
17804     initEvents:  function()
17805     {
17806 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17807 //            this.el.on("touchstart", this.onTouchStart, this);
17808 //        }
17809         
17810         if(this.autoslide){
17811             var _this = this;
17812             
17813             this.slideFn = window.setInterval(function() {
17814                 _this.showPanelNext();
17815             }, this.timer);
17816         }
17817         
17818         if(this.showarrow){
17819             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17820             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17821         }
17822         
17823         
17824     },
17825     
17826 //    onTouchStart : function(e, el, o)
17827 //    {
17828 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17829 //            return;
17830 //        }
17831 //        
17832 //        this.showPanelNext();
17833 //    },
17834     
17835     
17836     getChildContainer : function()
17837     {
17838         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17839     },
17840     
17841     /**
17842     * register a Navigation item
17843     * @param {Roo.bootstrap.NavItem} the navitem to add
17844     */
17845     register : function(item)
17846     {
17847         this.tabs.push( item);
17848         item.navId = this.navId; // not really needed..
17849         this.addBullet();
17850     
17851     },
17852     
17853     getActivePanel : function()
17854     {
17855         var r = false;
17856         Roo.each(this.tabs, function(t) {
17857             if (t.active) {
17858                 r = t;
17859                 return false;
17860             }
17861             return null;
17862         });
17863         return r;
17864         
17865     },
17866     getPanelByName : function(n)
17867     {
17868         var r = false;
17869         Roo.each(this.tabs, function(t) {
17870             if (t.tabId == n) {
17871                 r = t;
17872                 return false;
17873             }
17874             return null;
17875         });
17876         return r;
17877     },
17878     indexOfPanel : function(p)
17879     {
17880         var r = false;
17881         Roo.each(this.tabs, function(t,i) {
17882             if (t.tabId == p.tabId) {
17883                 r = i;
17884                 return false;
17885             }
17886             return null;
17887         });
17888         return r;
17889     },
17890     /**
17891      * show a specific panel
17892      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17893      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17894      */
17895     showPanel : function (pan)
17896     {
17897         if(this.transition || typeof(pan) == 'undefined'){
17898             Roo.log("waiting for the transitionend");
17899             return;
17900         }
17901         
17902         if (typeof(pan) == 'number') {
17903             pan = this.tabs[pan];
17904         }
17905         
17906         if (typeof(pan) == 'string') {
17907             pan = this.getPanelByName(pan);
17908         }
17909         
17910         var cur = this.getActivePanel();
17911         
17912         if(!pan || !cur){
17913             Roo.log('pan or acitve pan is undefined');
17914             return false;
17915         }
17916         
17917         if (pan.tabId == this.getActivePanel().tabId) {
17918             return true;
17919         }
17920         
17921         if (false === cur.fireEvent('beforedeactivate')) {
17922             return false;
17923         }
17924         
17925         if(this.bullets > 0 && !Roo.isTouch){
17926             this.setActiveBullet(this.indexOfPanel(pan));
17927         }
17928         
17929         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17930             
17931             this.transition = true;
17932             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17933             var lr = dir == 'next' ? 'left' : 'right';
17934             pan.el.addClass(dir); // or prev
17935             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17936             cur.el.addClass(lr); // or right
17937             pan.el.addClass(lr);
17938             
17939             var _this = this;
17940             cur.el.on('transitionend', function() {
17941                 Roo.log("trans end?");
17942                 
17943                 pan.el.removeClass([lr,dir]);
17944                 pan.setActive(true);
17945                 
17946                 cur.el.removeClass([lr]);
17947                 cur.setActive(false);
17948                 
17949                 _this.transition = false;
17950                 
17951             }, this, { single:  true } );
17952             
17953             return true;
17954         }
17955         
17956         cur.setActive(false);
17957         pan.setActive(true);
17958         
17959         return true;
17960         
17961     },
17962     showPanelNext : function()
17963     {
17964         var i = this.indexOfPanel(this.getActivePanel());
17965         
17966         if (i >= this.tabs.length - 1 && !this.autoslide) {
17967             return;
17968         }
17969         
17970         if (i >= this.tabs.length - 1 && this.autoslide) {
17971             i = -1;
17972         }
17973         
17974         this.showPanel(this.tabs[i+1]);
17975     },
17976     
17977     showPanelPrev : function()
17978     {
17979         var i = this.indexOfPanel(this.getActivePanel());
17980         
17981         if (i  < 1 && !this.autoslide) {
17982             return;
17983         }
17984         
17985         if (i < 1 && this.autoslide) {
17986             i = this.tabs.length;
17987         }
17988         
17989         this.showPanel(this.tabs[i-1]);
17990     },
17991     
17992     
17993     addBullet: function()
17994     {
17995         if(!this.bullets || Roo.isTouch){
17996             return;
17997         }
17998         var ctr = this.el.select('.carousel-bullets',true).first();
17999         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18000         var bullet = ctr.createChild({
18001             cls : 'bullet bullet-' + i
18002         },ctr.dom.lastChild);
18003         
18004         
18005         var _this = this;
18006         
18007         bullet.on('click', (function(e, el, o, ii, t){
18008
18009             e.preventDefault();
18010
18011             this.showPanel(ii);
18012
18013             if(this.autoslide && this.slideFn){
18014                 clearInterval(this.slideFn);
18015                 this.slideFn = window.setInterval(function() {
18016                     _this.showPanelNext();
18017                 }, this.timer);
18018             }
18019
18020         }).createDelegate(this, [i, bullet], true));
18021                 
18022         
18023     },
18024      
18025     setActiveBullet : function(i)
18026     {
18027         if(Roo.isTouch){
18028             return;
18029         }
18030         
18031         Roo.each(this.el.select('.bullet', true).elements, function(el){
18032             el.removeClass('selected');
18033         });
18034
18035         var bullet = this.el.select('.bullet-' + i, true).first();
18036         
18037         if(!bullet){
18038             return;
18039         }
18040         
18041         bullet.addClass('selected');
18042     }
18043     
18044     
18045   
18046 });
18047
18048  
18049
18050  
18051  
18052 Roo.apply(Roo.bootstrap.TabGroup, {
18053     
18054     groups: {},
18055      /**
18056     * register a Navigation Group
18057     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18058     */
18059     register : function(navgrp)
18060     {
18061         this.groups[navgrp.navId] = navgrp;
18062         
18063     },
18064     /**
18065     * fetch a Navigation Group based on the navigation ID
18066     * if one does not exist , it will get created.
18067     * @param {string} the navgroup to add
18068     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18069     */
18070     get: function(navId) {
18071         if (typeof(this.groups[navId]) == 'undefined') {
18072             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18073         }
18074         return this.groups[navId] ;
18075     }
18076     
18077     
18078     
18079 });
18080
18081  /*
18082  * - LGPL
18083  *
18084  * TabPanel
18085  * 
18086  */
18087
18088 /**
18089  * @class Roo.bootstrap.TabPanel
18090  * @extends Roo.bootstrap.Component
18091  * Bootstrap TabPanel class
18092  * @cfg {Boolean} active panel active
18093  * @cfg {String} html panel content
18094  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18095  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18096  * @cfg {String} href click to link..
18097  * 
18098  * 
18099  * @constructor
18100  * Create a new TabPanel
18101  * @param {Object} config The config object
18102  */
18103
18104 Roo.bootstrap.TabPanel = function(config){
18105     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18106     this.addEvents({
18107         /**
18108              * @event changed
18109              * Fires when the active status changes
18110              * @param {Roo.bootstrap.TabPanel} this
18111              * @param {Boolean} state the new state
18112             
18113          */
18114         'changed': true,
18115         /**
18116              * @event beforedeactivate
18117              * Fires before a tab is de-activated - can be used to do validation on a form.
18118              * @param {Roo.bootstrap.TabPanel} this
18119              * @return {Boolean} false if there is an error
18120             
18121          */
18122         'beforedeactivate': true
18123      });
18124     
18125     this.tabId = this.tabId || Roo.id();
18126   
18127 };
18128
18129 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18130     
18131     active: false,
18132     html: false,
18133     tabId: false,
18134     navId : false,
18135     href : '',
18136     
18137     getAutoCreate : function(){
18138         var cfg = {
18139             tag: 'div',
18140             // item is needed for carousel - not sure if it has any effect otherwise
18141             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18142             html: this.html || ''
18143         };
18144         
18145         if(this.active){
18146             cfg.cls += ' active';
18147         }
18148         
18149         if(this.tabId){
18150             cfg.tabId = this.tabId;
18151         }
18152         
18153         
18154         return cfg;
18155     },
18156     
18157     initEvents:  function()
18158     {
18159         var p = this.parent();
18160         
18161         this.navId = this.navId || p.navId;
18162         
18163         if (typeof(this.navId) != 'undefined') {
18164             // not really needed.. but just in case.. parent should be a NavGroup.
18165             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18166             
18167             tg.register(this);
18168             
18169             var i = tg.tabs.length - 1;
18170             
18171             if(this.active && tg.bullets > 0 && i < tg.bullets){
18172                 tg.setActiveBullet(i);
18173             }
18174         }
18175         
18176         this.el.on('click', this.onClick, this);
18177         
18178         if(Roo.isTouch){
18179             this.el.on("touchstart", this.onTouchStart, this);
18180             this.el.on("touchmove", this.onTouchMove, this);
18181             this.el.on("touchend", this.onTouchEnd, this);
18182         }
18183         
18184     },
18185     
18186     onRender : function(ct, position)
18187     {
18188         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18189     },
18190     
18191     setActive : function(state)
18192     {
18193         Roo.log("panel - set active " + this.tabId + "=" + state);
18194         
18195         this.active = state;
18196         if (!state) {
18197             this.el.removeClass('active');
18198             
18199         } else  if (!this.el.hasClass('active')) {
18200             this.el.addClass('active');
18201         }
18202         
18203         this.fireEvent('changed', this, state);
18204     },
18205     
18206     onClick : function(e)
18207     {
18208         e.preventDefault();
18209         
18210         if(!this.href.length){
18211             return;
18212         }
18213         
18214         window.location.href = this.href;
18215     },
18216     
18217     startX : 0,
18218     startY : 0,
18219     endX : 0,
18220     endY : 0,
18221     swiping : false,
18222     
18223     onTouchStart : function(e)
18224     {
18225         this.swiping = false;
18226         
18227         this.startX = e.browserEvent.touches[0].clientX;
18228         this.startY = e.browserEvent.touches[0].clientY;
18229     },
18230     
18231     onTouchMove : function(e)
18232     {
18233         this.swiping = true;
18234         
18235         this.endX = e.browserEvent.touches[0].clientX;
18236         this.endY = e.browserEvent.touches[0].clientY;
18237     },
18238     
18239     onTouchEnd : function(e)
18240     {
18241         if(!this.swiping){
18242             this.onClick(e);
18243             return;
18244         }
18245         
18246         var tabGroup = this.parent();
18247         
18248         if(this.endX > this.startX){ // swiping right
18249             tabGroup.showPanelPrev();
18250             return;
18251         }
18252         
18253         if(this.startX > this.endX){ // swiping left
18254             tabGroup.showPanelNext();
18255             return;
18256         }
18257     }
18258     
18259     
18260 });
18261  
18262
18263  
18264
18265  /*
18266  * - LGPL
18267  *
18268  * DateField
18269  * 
18270  */
18271
18272 /**
18273  * @class Roo.bootstrap.DateField
18274  * @extends Roo.bootstrap.Input
18275  * Bootstrap DateField class
18276  * @cfg {Number} weekStart default 0
18277  * @cfg {String} viewMode default empty, (months|years)
18278  * @cfg {String} minViewMode default empty, (months|years)
18279  * @cfg {Number} startDate default -Infinity
18280  * @cfg {Number} endDate default Infinity
18281  * @cfg {Boolean} todayHighlight default false
18282  * @cfg {Boolean} todayBtn default false
18283  * @cfg {Boolean} calendarWeeks default false
18284  * @cfg {Object} daysOfWeekDisabled default empty
18285  * @cfg {Boolean} singleMode default false (true | false)
18286  * 
18287  * @cfg {Boolean} keyboardNavigation default true
18288  * @cfg {String} language default en
18289  * 
18290  * @constructor
18291  * Create a new DateField
18292  * @param {Object} config The config object
18293  */
18294
18295 Roo.bootstrap.DateField = function(config){
18296     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18297      this.addEvents({
18298             /**
18299              * @event show
18300              * Fires when this field show.
18301              * @param {Roo.bootstrap.DateField} this
18302              * @param {Mixed} date The date value
18303              */
18304             show : true,
18305             /**
18306              * @event show
18307              * Fires when this field hide.
18308              * @param {Roo.bootstrap.DateField} this
18309              * @param {Mixed} date The date value
18310              */
18311             hide : true,
18312             /**
18313              * @event select
18314              * Fires when select a date.
18315              * @param {Roo.bootstrap.DateField} this
18316              * @param {Mixed} date The date value
18317              */
18318             select : true,
18319             /**
18320              * @event beforeselect
18321              * Fires when before select a date.
18322              * @param {Roo.bootstrap.DateField} this
18323              * @param {Mixed} date The date value
18324              */
18325             beforeselect : true
18326         });
18327 };
18328
18329 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18330     
18331     /**
18332      * @cfg {String} format
18333      * The default date format string which can be overriden for localization support.  The format must be
18334      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18335      */
18336     format : "m/d/y",
18337     /**
18338      * @cfg {String} altFormats
18339      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18340      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18341      */
18342     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18343     
18344     weekStart : 0,
18345     
18346     viewMode : '',
18347     
18348     minViewMode : '',
18349     
18350     todayHighlight : false,
18351     
18352     todayBtn: false,
18353     
18354     language: 'en',
18355     
18356     keyboardNavigation: true,
18357     
18358     calendarWeeks: false,
18359     
18360     startDate: -Infinity,
18361     
18362     endDate: Infinity,
18363     
18364     daysOfWeekDisabled: [],
18365     
18366     _events: [],
18367     
18368     singleMode : false,
18369     
18370     UTCDate: function()
18371     {
18372         return new Date(Date.UTC.apply(Date, arguments));
18373     },
18374     
18375     UTCToday: function()
18376     {
18377         var today = new Date();
18378         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18379     },
18380     
18381     getDate: function() {
18382             var d = this.getUTCDate();
18383             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18384     },
18385     
18386     getUTCDate: function() {
18387             return this.date;
18388     },
18389     
18390     setDate: function(d) {
18391             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18392     },
18393     
18394     setUTCDate: function(d) {
18395             this.date = d;
18396             this.setValue(this.formatDate(this.date));
18397     },
18398         
18399     onRender: function(ct, position)
18400     {
18401         
18402         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18403         
18404         this.language = this.language || 'en';
18405         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18406         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18407         
18408         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18409         this.format = this.format || 'm/d/y';
18410         this.isInline = false;
18411         this.isInput = true;
18412         this.component = this.el.select('.add-on', true).first() || false;
18413         this.component = (this.component && this.component.length === 0) ? false : this.component;
18414         this.hasInput = this.component && this.inputEl().length;
18415         
18416         if (typeof(this.minViewMode === 'string')) {
18417             switch (this.minViewMode) {
18418                 case 'months':
18419                     this.minViewMode = 1;
18420                     break;
18421                 case 'years':
18422                     this.minViewMode = 2;
18423                     break;
18424                 default:
18425                     this.minViewMode = 0;
18426                     break;
18427             }
18428         }
18429         
18430         if (typeof(this.viewMode === 'string')) {
18431             switch (this.viewMode) {
18432                 case 'months':
18433                     this.viewMode = 1;
18434                     break;
18435                 case 'years':
18436                     this.viewMode = 2;
18437                     break;
18438                 default:
18439                     this.viewMode = 0;
18440                     break;
18441             }
18442         }
18443                 
18444         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18445         
18446 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18447         
18448         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18449         
18450         this.picker().on('mousedown', this.onMousedown, this);
18451         this.picker().on('click', this.onClick, this);
18452         
18453         this.picker().addClass('datepicker-dropdown');
18454         
18455         this.startViewMode = this.viewMode;
18456         
18457         if(this.singleMode){
18458             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18459                 v.setVisibilityMode(Roo.Element.DISPLAY);
18460                 v.hide();
18461             });
18462             
18463             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18464                 v.setStyle('width', '189px');
18465             });
18466         }
18467         
18468         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18469             if(!this.calendarWeeks){
18470                 v.remove();
18471                 return;
18472             }
18473             
18474             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18475             v.attr('colspan', function(i, val){
18476                 return parseInt(val) + 1;
18477             });
18478         });
18479                         
18480         
18481         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18482         
18483         this.setStartDate(this.startDate);
18484         this.setEndDate(this.endDate);
18485         
18486         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18487         
18488         this.fillDow();
18489         this.fillMonths();
18490         this.update();
18491         this.showMode();
18492         
18493         if(this.isInline) {
18494             this.show();
18495         }
18496     },
18497     
18498     picker : function()
18499     {
18500         return this.pickerEl;
18501 //        return this.el.select('.datepicker', true).first();
18502     },
18503     
18504     fillDow: function()
18505     {
18506         var dowCnt = this.weekStart;
18507         
18508         var dow = {
18509             tag: 'tr',
18510             cn: [
18511                 
18512             ]
18513         };
18514         
18515         if(this.calendarWeeks){
18516             dow.cn.push({
18517                 tag: 'th',
18518                 cls: 'cw',
18519                 html: '&nbsp;'
18520             })
18521         }
18522         
18523         while (dowCnt < this.weekStart + 7) {
18524             dow.cn.push({
18525                 tag: 'th',
18526                 cls: 'dow',
18527                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18528             });
18529         }
18530         
18531         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18532     },
18533     
18534     fillMonths: function()
18535     {    
18536         var i = 0;
18537         var months = this.picker().select('>.datepicker-months td', true).first();
18538         
18539         months.dom.innerHTML = '';
18540         
18541         while (i < 12) {
18542             var month = {
18543                 tag: 'span',
18544                 cls: 'month',
18545                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18546             };
18547             
18548             months.createChild(month);
18549         }
18550         
18551     },
18552     
18553     update: function()
18554     {
18555         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;
18556         
18557         if (this.date < this.startDate) {
18558             this.viewDate = new Date(this.startDate);
18559         } else if (this.date > this.endDate) {
18560             this.viewDate = new Date(this.endDate);
18561         } else {
18562             this.viewDate = new Date(this.date);
18563         }
18564         
18565         this.fill();
18566     },
18567     
18568     fill: function() 
18569     {
18570         var d = new Date(this.viewDate),
18571                 year = d.getUTCFullYear(),
18572                 month = d.getUTCMonth(),
18573                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18574                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18575                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18576                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18577                 currentDate = this.date && this.date.valueOf(),
18578                 today = this.UTCToday();
18579         
18580         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18581         
18582 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18583         
18584 //        this.picker.select('>tfoot th.today').
18585 //                                              .text(dates[this.language].today)
18586 //                                              .toggle(this.todayBtn !== false);
18587     
18588         this.updateNavArrows();
18589         this.fillMonths();
18590                                                 
18591         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18592         
18593         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18594          
18595         prevMonth.setUTCDate(day);
18596         
18597         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18598         
18599         var nextMonth = new Date(prevMonth);
18600         
18601         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18602         
18603         nextMonth = nextMonth.valueOf();
18604         
18605         var fillMonths = false;
18606         
18607         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18608         
18609         while(prevMonth.valueOf() < nextMonth) {
18610             var clsName = '';
18611             
18612             if (prevMonth.getUTCDay() === this.weekStart) {
18613                 if(fillMonths){
18614                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18615                 }
18616                     
18617                 fillMonths = {
18618                     tag: 'tr',
18619                     cn: []
18620                 };
18621                 
18622                 if(this.calendarWeeks){
18623                     // ISO 8601: First week contains first thursday.
18624                     // ISO also states week starts on Monday, but we can be more abstract here.
18625                     var
18626                     // Start of current week: based on weekstart/current date
18627                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18628                     // Thursday of this week
18629                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18630                     // First Thursday of year, year from thursday
18631                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18632                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18633                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18634                     
18635                     fillMonths.cn.push({
18636                         tag: 'td',
18637                         cls: 'cw',
18638                         html: calWeek
18639                     });
18640                 }
18641             }
18642             
18643             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18644                 clsName += ' old';
18645             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18646                 clsName += ' new';
18647             }
18648             if (this.todayHighlight &&
18649                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18650                 prevMonth.getUTCMonth() == today.getMonth() &&
18651                 prevMonth.getUTCDate() == today.getDate()) {
18652                 clsName += ' today';
18653             }
18654             
18655             if (currentDate && prevMonth.valueOf() === currentDate) {
18656                 clsName += ' active';
18657             }
18658             
18659             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18660                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18661                     clsName += ' disabled';
18662             }
18663             
18664             fillMonths.cn.push({
18665                 tag: 'td',
18666                 cls: 'day ' + clsName,
18667                 html: prevMonth.getDate()
18668             });
18669             
18670             prevMonth.setDate(prevMonth.getDate()+1);
18671         }
18672           
18673         var currentYear = this.date && this.date.getUTCFullYear();
18674         var currentMonth = this.date && this.date.getUTCMonth();
18675         
18676         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18677         
18678         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18679             v.removeClass('active');
18680             
18681             if(currentYear === year && k === currentMonth){
18682                 v.addClass('active');
18683             }
18684             
18685             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18686                 v.addClass('disabled');
18687             }
18688             
18689         });
18690         
18691         
18692         year = parseInt(year/10, 10) * 10;
18693         
18694         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18695         
18696         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18697         
18698         year -= 1;
18699         for (var i = -1; i < 11; i++) {
18700             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18701                 tag: 'span',
18702                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18703                 html: year
18704             });
18705             
18706             year += 1;
18707         }
18708     },
18709     
18710     showMode: function(dir) 
18711     {
18712         if (dir) {
18713             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18714         }
18715         
18716         Roo.each(this.picker().select('>div',true).elements, function(v){
18717             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18718             v.hide();
18719         });
18720         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18721     },
18722     
18723     place: function()
18724     {
18725         if(this.isInline) {
18726             return;
18727         }
18728         
18729         this.picker().removeClass(['bottom', 'top']);
18730         
18731         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18732             /*
18733              * place to the top of element!
18734              *
18735              */
18736             
18737             this.picker().addClass('top');
18738             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18739             
18740             return;
18741         }
18742         
18743         this.picker().addClass('bottom');
18744         
18745         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18746     },
18747     
18748     parseDate : function(value)
18749     {
18750         if(!value || value instanceof Date){
18751             return value;
18752         }
18753         var v = Date.parseDate(value, this.format);
18754         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18755             v = Date.parseDate(value, 'Y-m-d');
18756         }
18757         if(!v && this.altFormats){
18758             if(!this.altFormatsArray){
18759                 this.altFormatsArray = this.altFormats.split("|");
18760             }
18761             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18762                 v = Date.parseDate(value, this.altFormatsArray[i]);
18763             }
18764         }
18765         return v;
18766     },
18767     
18768     formatDate : function(date, fmt)
18769     {   
18770         return (!date || !(date instanceof Date)) ?
18771         date : date.dateFormat(fmt || this.format);
18772     },
18773     
18774     onFocus : function()
18775     {
18776         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18777         this.show();
18778     },
18779     
18780     onBlur : function()
18781     {
18782         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18783         
18784         var d = this.inputEl().getValue();
18785         
18786         this.setValue(d);
18787                 
18788         this.hide();
18789     },
18790     
18791     show : function()
18792     {
18793         this.picker().show();
18794         this.update();
18795         this.place();
18796         
18797         this.fireEvent('show', this, this.date);
18798     },
18799     
18800     hide : function()
18801     {
18802         if(this.isInline) {
18803             return;
18804         }
18805         this.picker().hide();
18806         this.viewMode = this.startViewMode;
18807         this.showMode();
18808         
18809         this.fireEvent('hide', this, this.date);
18810         
18811     },
18812     
18813     onMousedown: function(e)
18814     {
18815         e.stopPropagation();
18816         e.preventDefault();
18817     },
18818     
18819     keyup: function(e)
18820     {
18821         Roo.bootstrap.DateField.superclass.keyup.call(this);
18822         this.update();
18823     },
18824
18825     setValue: function(v)
18826     {
18827         if(this.fireEvent('beforeselect', this, v) !== false){
18828             var d = new Date(this.parseDate(v) ).clearTime();
18829         
18830             if(isNaN(d.getTime())){
18831                 this.date = this.viewDate = '';
18832                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18833                 return;
18834             }
18835
18836             v = this.formatDate(d);
18837
18838             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18839
18840             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18841
18842             this.update();
18843
18844             this.fireEvent('select', this, this.date);
18845         }
18846     },
18847     
18848     getValue: function()
18849     {
18850         return this.formatDate(this.date);
18851     },
18852     
18853     fireKey: function(e)
18854     {
18855         if (!this.picker().isVisible()){
18856             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18857                 this.show();
18858             }
18859             return;
18860         }
18861         
18862         var dateChanged = false,
18863         dir, day, month,
18864         newDate, newViewDate;
18865         
18866         switch(e.keyCode){
18867             case 27: // escape
18868                 this.hide();
18869                 e.preventDefault();
18870                 break;
18871             case 37: // left
18872             case 39: // right
18873                 if (!this.keyboardNavigation) {
18874                     break;
18875                 }
18876                 dir = e.keyCode == 37 ? -1 : 1;
18877                 
18878                 if (e.ctrlKey){
18879                     newDate = this.moveYear(this.date, dir);
18880                     newViewDate = this.moveYear(this.viewDate, dir);
18881                 } else if (e.shiftKey){
18882                     newDate = this.moveMonth(this.date, dir);
18883                     newViewDate = this.moveMonth(this.viewDate, dir);
18884                 } else {
18885                     newDate = new Date(this.date);
18886                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18887                     newViewDate = new Date(this.viewDate);
18888                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18889                 }
18890                 if (this.dateWithinRange(newDate)){
18891                     this.date = newDate;
18892                     this.viewDate = newViewDate;
18893                     this.setValue(this.formatDate(this.date));
18894 //                    this.update();
18895                     e.preventDefault();
18896                     dateChanged = true;
18897                 }
18898                 break;
18899             case 38: // up
18900             case 40: // down
18901                 if (!this.keyboardNavigation) {
18902                     break;
18903                 }
18904                 dir = e.keyCode == 38 ? -1 : 1;
18905                 if (e.ctrlKey){
18906                     newDate = this.moveYear(this.date, dir);
18907                     newViewDate = this.moveYear(this.viewDate, dir);
18908                 } else if (e.shiftKey){
18909                     newDate = this.moveMonth(this.date, dir);
18910                     newViewDate = this.moveMonth(this.viewDate, dir);
18911                 } else {
18912                     newDate = new Date(this.date);
18913                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18914                     newViewDate = new Date(this.viewDate);
18915                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18916                 }
18917                 if (this.dateWithinRange(newDate)){
18918                     this.date = newDate;
18919                     this.viewDate = newViewDate;
18920                     this.setValue(this.formatDate(this.date));
18921 //                    this.update();
18922                     e.preventDefault();
18923                     dateChanged = true;
18924                 }
18925                 break;
18926             case 13: // enter
18927                 this.setValue(this.formatDate(this.date));
18928                 this.hide();
18929                 e.preventDefault();
18930                 break;
18931             case 9: // tab
18932                 this.setValue(this.formatDate(this.date));
18933                 this.hide();
18934                 break;
18935             case 16: // shift
18936             case 17: // ctrl
18937             case 18: // alt
18938                 break;
18939             default :
18940                 this.hide();
18941                 
18942         }
18943     },
18944     
18945     
18946     onClick: function(e) 
18947     {
18948         e.stopPropagation();
18949         e.preventDefault();
18950         
18951         var target = e.getTarget();
18952         
18953         if(target.nodeName.toLowerCase() === 'i'){
18954             target = Roo.get(target).dom.parentNode;
18955         }
18956         
18957         var nodeName = target.nodeName;
18958         var className = target.className;
18959         var html = target.innerHTML;
18960         //Roo.log(nodeName);
18961         
18962         switch(nodeName.toLowerCase()) {
18963             case 'th':
18964                 switch(className) {
18965                     case 'switch':
18966                         this.showMode(1);
18967                         break;
18968                     case 'prev':
18969                     case 'next':
18970                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18971                         switch(this.viewMode){
18972                                 case 0:
18973                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18974                                         break;
18975                                 case 1:
18976                                 case 2:
18977                                         this.viewDate = this.moveYear(this.viewDate, dir);
18978                                         break;
18979                         }
18980                         this.fill();
18981                         break;
18982                     case 'today':
18983                         var date = new Date();
18984                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18985 //                        this.fill()
18986                         this.setValue(this.formatDate(this.date));
18987                         
18988                         this.hide();
18989                         break;
18990                 }
18991                 break;
18992             case 'span':
18993                 if (className.indexOf('disabled') < 0) {
18994                     this.viewDate.setUTCDate(1);
18995                     if (className.indexOf('month') > -1) {
18996                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18997                     } else {
18998                         var year = parseInt(html, 10) || 0;
18999                         this.viewDate.setUTCFullYear(year);
19000                         
19001                     }
19002                     
19003                     if(this.singleMode){
19004                         this.setValue(this.formatDate(this.viewDate));
19005                         this.hide();
19006                         return;
19007                     }
19008                     
19009                     this.showMode(-1);
19010                     this.fill();
19011                 }
19012                 break;
19013                 
19014             case 'td':
19015                 //Roo.log(className);
19016                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19017                     var day = parseInt(html, 10) || 1;
19018                     var year = this.viewDate.getUTCFullYear(),
19019                         month = this.viewDate.getUTCMonth();
19020
19021                     if (className.indexOf('old') > -1) {
19022                         if(month === 0 ){
19023                             month = 11;
19024                             year -= 1;
19025                         }else{
19026                             month -= 1;
19027                         }
19028                     } else if (className.indexOf('new') > -1) {
19029                         if (month == 11) {
19030                             month = 0;
19031                             year += 1;
19032                         } else {
19033                             month += 1;
19034                         }
19035                     }
19036                     //Roo.log([year,month,day]);
19037                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19038                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19039 //                    this.fill();
19040                     //Roo.log(this.formatDate(this.date));
19041                     this.setValue(this.formatDate(this.date));
19042                     this.hide();
19043                 }
19044                 break;
19045         }
19046     },
19047     
19048     setStartDate: function(startDate)
19049     {
19050         this.startDate = startDate || -Infinity;
19051         if (this.startDate !== -Infinity) {
19052             this.startDate = this.parseDate(this.startDate);
19053         }
19054         this.update();
19055         this.updateNavArrows();
19056     },
19057
19058     setEndDate: function(endDate)
19059     {
19060         this.endDate = endDate || Infinity;
19061         if (this.endDate !== Infinity) {
19062             this.endDate = this.parseDate(this.endDate);
19063         }
19064         this.update();
19065         this.updateNavArrows();
19066     },
19067     
19068     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19069     {
19070         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19071         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19072             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19073         }
19074         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19075             return parseInt(d, 10);
19076         });
19077         this.update();
19078         this.updateNavArrows();
19079     },
19080     
19081     updateNavArrows: function() 
19082     {
19083         if(this.singleMode){
19084             return;
19085         }
19086         
19087         var d = new Date(this.viewDate),
19088         year = d.getUTCFullYear(),
19089         month = d.getUTCMonth();
19090         
19091         Roo.each(this.picker().select('.prev', true).elements, function(v){
19092             v.show();
19093             switch (this.viewMode) {
19094                 case 0:
19095
19096                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19097                         v.hide();
19098                     }
19099                     break;
19100                 case 1:
19101                 case 2:
19102                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19103                         v.hide();
19104                     }
19105                     break;
19106             }
19107         });
19108         
19109         Roo.each(this.picker().select('.next', true).elements, function(v){
19110             v.show();
19111             switch (this.viewMode) {
19112                 case 0:
19113
19114                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19115                         v.hide();
19116                     }
19117                     break;
19118                 case 1:
19119                 case 2:
19120                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19121                         v.hide();
19122                     }
19123                     break;
19124             }
19125         })
19126     },
19127     
19128     moveMonth: function(date, dir)
19129     {
19130         if (!dir) {
19131             return date;
19132         }
19133         var new_date = new Date(date.valueOf()),
19134         day = new_date.getUTCDate(),
19135         month = new_date.getUTCMonth(),
19136         mag = Math.abs(dir),
19137         new_month, test;
19138         dir = dir > 0 ? 1 : -1;
19139         if (mag == 1){
19140             test = dir == -1
19141             // If going back one month, make sure month is not current month
19142             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19143             ? function(){
19144                 return new_date.getUTCMonth() == month;
19145             }
19146             // If going forward one month, make sure month is as expected
19147             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19148             : function(){
19149                 return new_date.getUTCMonth() != new_month;
19150             };
19151             new_month = month + dir;
19152             new_date.setUTCMonth(new_month);
19153             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19154             if (new_month < 0 || new_month > 11) {
19155                 new_month = (new_month + 12) % 12;
19156             }
19157         } else {
19158             // For magnitudes >1, move one month at a time...
19159             for (var i=0; i<mag; i++) {
19160                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19161                 new_date = this.moveMonth(new_date, dir);
19162             }
19163             // ...then reset the day, keeping it in the new month
19164             new_month = new_date.getUTCMonth();
19165             new_date.setUTCDate(day);
19166             test = function(){
19167                 return new_month != new_date.getUTCMonth();
19168             };
19169         }
19170         // Common date-resetting loop -- if date is beyond end of month, make it
19171         // end of month
19172         while (test()){
19173             new_date.setUTCDate(--day);
19174             new_date.setUTCMonth(new_month);
19175         }
19176         return new_date;
19177     },
19178
19179     moveYear: function(date, dir)
19180     {
19181         return this.moveMonth(date, dir*12);
19182     },
19183
19184     dateWithinRange: function(date)
19185     {
19186         return date >= this.startDate && date <= this.endDate;
19187     },
19188
19189     
19190     remove: function() 
19191     {
19192         this.picker().remove();
19193     },
19194     
19195     validateValue : function(value)
19196     {
19197         if(this.getVisibilityEl().hasClass('hidden')){
19198             return true;
19199         }
19200         
19201         if(value.length < 1)  {
19202             if(this.allowBlank){
19203                 return true;
19204             }
19205             return false;
19206         }
19207         
19208         if(value.length < this.minLength){
19209             return false;
19210         }
19211         if(value.length > this.maxLength){
19212             return false;
19213         }
19214         if(this.vtype){
19215             var vt = Roo.form.VTypes;
19216             if(!vt[this.vtype](value, this)){
19217                 return false;
19218             }
19219         }
19220         if(typeof this.validator == "function"){
19221             var msg = this.validator(value);
19222             if(msg !== true){
19223                 return false;
19224             }
19225         }
19226         
19227         if(this.regex && !this.regex.test(value)){
19228             return false;
19229         }
19230         
19231         if(typeof(this.parseDate(value)) == 'undefined'){
19232             return false;
19233         }
19234         
19235         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19236             return false;
19237         }      
19238         
19239         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19240             return false;
19241         } 
19242         
19243         
19244         return true;
19245     },
19246     
19247     setVisible : function(visible)
19248     {
19249         if(!this.getEl()){
19250             return;
19251         }
19252         
19253         this.getEl().removeClass('hidden');
19254         
19255         if(visible){
19256             return;
19257         }
19258         
19259         this.getEl().addClass('hidden');
19260     }
19261    
19262 });
19263
19264 Roo.apply(Roo.bootstrap.DateField,  {
19265     
19266     head : {
19267         tag: 'thead',
19268         cn: [
19269         {
19270             tag: 'tr',
19271             cn: [
19272             {
19273                 tag: 'th',
19274                 cls: 'prev',
19275                 html: '<i class="fa fa-arrow-left"/>'
19276             },
19277             {
19278                 tag: 'th',
19279                 cls: 'switch',
19280                 colspan: '5'
19281             },
19282             {
19283                 tag: 'th',
19284                 cls: 'next',
19285                 html: '<i class="fa fa-arrow-right"/>'
19286             }
19287
19288             ]
19289         }
19290         ]
19291     },
19292     
19293     content : {
19294         tag: 'tbody',
19295         cn: [
19296         {
19297             tag: 'tr',
19298             cn: [
19299             {
19300                 tag: 'td',
19301                 colspan: '7'
19302             }
19303             ]
19304         }
19305         ]
19306     },
19307     
19308     footer : {
19309         tag: 'tfoot',
19310         cn: [
19311         {
19312             tag: 'tr',
19313             cn: [
19314             {
19315                 tag: 'th',
19316                 colspan: '7',
19317                 cls: 'today'
19318             }
19319                     
19320             ]
19321         }
19322         ]
19323     },
19324     
19325     dates:{
19326         en: {
19327             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19328             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19329             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19330             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19331             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19332             today: "Today"
19333         }
19334     },
19335     
19336     modes: [
19337     {
19338         clsName: 'days',
19339         navFnc: 'Month',
19340         navStep: 1
19341     },
19342     {
19343         clsName: 'months',
19344         navFnc: 'FullYear',
19345         navStep: 1
19346     },
19347     {
19348         clsName: 'years',
19349         navFnc: 'FullYear',
19350         navStep: 10
19351     }]
19352 });
19353
19354 Roo.apply(Roo.bootstrap.DateField,  {
19355   
19356     template : {
19357         tag: 'div',
19358         cls: 'datepicker dropdown-menu roo-dynamic',
19359         cn: [
19360         {
19361             tag: 'div',
19362             cls: 'datepicker-days',
19363             cn: [
19364             {
19365                 tag: 'table',
19366                 cls: 'table-condensed',
19367                 cn:[
19368                 Roo.bootstrap.DateField.head,
19369                 {
19370                     tag: 'tbody'
19371                 },
19372                 Roo.bootstrap.DateField.footer
19373                 ]
19374             }
19375             ]
19376         },
19377         {
19378             tag: 'div',
19379             cls: 'datepicker-months',
19380             cn: [
19381             {
19382                 tag: 'table',
19383                 cls: 'table-condensed',
19384                 cn:[
19385                 Roo.bootstrap.DateField.head,
19386                 Roo.bootstrap.DateField.content,
19387                 Roo.bootstrap.DateField.footer
19388                 ]
19389             }
19390             ]
19391         },
19392         {
19393             tag: 'div',
19394             cls: 'datepicker-years',
19395             cn: [
19396             {
19397                 tag: 'table',
19398                 cls: 'table-condensed',
19399                 cn:[
19400                 Roo.bootstrap.DateField.head,
19401                 Roo.bootstrap.DateField.content,
19402                 Roo.bootstrap.DateField.footer
19403                 ]
19404             }
19405             ]
19406         }
19407         ]
19408     }
19409 });
19410
19411  
19412
19413  /*
19414  * - LGPL
19415  *
19416  * TimeField
19417  * 
19418  */
19419
19420 /**
19421  * @class Roo.bootstrap.TimeField
19422  * @extends Roo.bootstrap.Input
19423  * Bootstrap DateField class
19424  * 
19425  * 
19426  * @constructor
19427  * Create a new TimeField
19428  * @param {Object} config The config object
19429  */
19430
19431 Roo.bootstrap.TimeField = function(config){
19432     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19433     this.addEvents({
19434             /**
19435              * @event show
19436              * Fires when this field show.
19437              * @param {Roo.bootstrap.DateField} thisthis
19438              * @param {Mixed} date The date value
19439              */
19440             show : true,
19441             /**
19442              * @event show
19443              * Fires when this field hide.
19444              * @param {Roo.bootstrap.DateField} this
19445              * @param {Mixed} date The date value
19446              */
19447             hide : true,
19448             /**
19449              * @event select
19450              * Fires when select a date.
19451              * @param {Roo.bootstrap.DateField} this
19452              * @param {Mixed} date The date value
19453              */
19454             select : true
19455         });
19456 };
19457
19458 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19459     
19460     /**
19461      * @cfg {String} format
19462      * The default time format string which can be overriden for localization support.  The format must be
19463      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19464      */
19465     format : "H:i",
19466        
19467     onRender: function(ct, position)
19468     {
19469         
19470         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19471                 
19472         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19473         
19474         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19475         
19476         this.pop = this.picker().select('>.datepicker-time',true).first();
19477         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19478         
19479         this.picker().on('mousedown', this.onMousedown, this);
19480         this.picker().on('click', this.onClick, this);
19481         
19482         this.picker().addClass('datepicker-dropdown');
19483     
19484         this.fillTime();
19485         this.update();
19486             
19487         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19488         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19489         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19490         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19491         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19492         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19493
19494     },
19495     
19496     fireKey: function(e){
19497         if (!this.picker().isVisible()){
19498             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19499                 this.show();
19500             }
19501             return;
19502         }
19503
19504         e.preventDefault();
19505         
19506         switch(e.keyCode){
19507             case 27: // escape
19508                 this.hide();
19509                 break;
19510             case 37: // left
19511             case 39: // right
19512                 this.onTogglePeriod();
19513                 break;
19514             case 38: // up
19515                 this.onIncrementMinutes();
19516                 break;
19517             case 40: // down
19518                 this.onDecrementMinutes();
19519                 break;
19520             case 13: // enter
19521             case 9: // tab
19522                 this.setTime();
19523                 break;
19524         }
19525     },
19526     
19527     onClick: function(e) {
19528         e.stopPropagation();
19529         e.preventDefault();
19530     },
19531     
19532     picker : function()
19533     {
19534         return this.el.select('.datepicker', true).first();
19535     },
19536     
19537     fillTime: function()
19538     {    
19539         var time = this.pop.select('tbody', true).first();
19540         
19541         time.dom.innerHTML = '';
19542         
19543         time.createChild({
19544             tag: 'tr',
19545             cn: [
19546                 {
19547                     tag: 'td',
19548                     cn: [
19549                         {
19550                             tag: 'a',
19551                             href: '#',
19552                             cls: 'btn',
19553                             cn: [
19554                                 {
19555                                     tag: 'span',
19556                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19557                                 }
19558                             ]
19559                         } 
19560                     ]
19561                 },
19562                 {
19563                     tag: 'td',
19564                     cls: 'separator'
19565                 },
19566                 {
19567                     tag: 'td',
19568                     cn: [
19569                         {
19570                             tag: 'a',
19571                             href: '#',
19572                             cls: 'btn',
19573                             cn: [
19574                                 {
19575                                     tag: 'span',
19576                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19577                                 }
19578                             ]
19579                         }
19580                     ]
19581                 },
19582                 {
19583                     tag: 'td',
19584                     cls: 'separator'
19585                 }
19586             ]
19587         });
19588         
19589         time.createChild({
19590             tag: 'tr',
19591             cn: [
19592                 {
19593                     tag: 'td',
19594                     cn: [
19595                         {
19596                             tag: 'span',
19597                             cls: 'timepicker-hour',
19598                             html: '00'
19599                         }  
19600                     ]
19601                 },
19602                 {
19603                     tag: 'td',
19604                     cls: 'separator',
19605                     html: ':'
19606                 },
19607                 {
19608                     tag: 'td',
19609                     cn: [
19610                         {
19611                             tag: 'span',
19612                             cls: 'timepicker-minute',
19613                             html: '00'
19614                         }  
19615                     ]
19616                 },
19617                 {
19618                     tag: 'td',
19619                     cls: 'separator'
19620                 },
19621                 {
19622                     tag: 'td',
19623                     cn: [
19624                         {
19625                             tag: 'button',
19626                             type: 'button',
19627                             cls: 'btn btn-primary period',
19628                             html: 'AM'
19629                             
19630                         }
19631                     ]
19632                 }
19633             ]
19634         });
19635         
19636         time.createChild({
19637             tag: 'tr',
19638             cn: [
19639                 {
19640                     tag: 'td',
19641                     cn: [
19642                         {
19643                             tag: 'a',
19644                             href: '#',
19645                             cls: 'btn',
19646                             cn: [
19647                                 {
19648                                     tag: 'span',
19649                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19650                                 }
19651                             ]
19652                         }
19653                     ]
19654                 },
19655                 {
19656                     tag: 'td',
19657                     cls: 'separator'
19658                 },
19659                 {
19660                     tag: 'td',
19661                     cn: [
19662                         {
19663                             tag: 'a',
19664                             href: '#',
19665                             cls: 'btn',
19666                             cn: [
19667                                 {
19668                                     tag: 'span',
19669                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19670                                 }
19671                             ]
19672                         }
19673                     ]
19674                 },
19675                 {
19676                     tag: 'td',
19677                     cls: 'separator'
19678                 }
19679             ]
19680         });
19681         
19682     },
19683     
19684     update: function()
19685     {
19686         
19687         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19688         
19689         this.fill();
19690     },
19691     
19692     fill: function() 
19693     {
19694         var hours = this.time.getHours();
19695         var minutes = this.time.getMinutes();
19696         var period = 'AM';
19697         
19698         if(hours > 11){
19699             period = 'PM';
19700         }
19701         
19702         if(hours == 0){
19703             hours = 12;
19704         }
19705         
19706         
19707         if(hours > 12){
19708             hours = hours - 12;
19709         }
19710         
19711         if(hours < 10){
19712             hours = '0' + hours;
19713         }
19714         
19715         if(minutes < 10){
19716             minutes = '0' + minutes;
19717         }
19718         
19719         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19720         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19721         this.pop.select('button', true).first().dom.innerHTML = period;
19722         
19723     },
19724     
19725     place: function()
19726     {   
19727         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19728         
19729         var cls = ['bottom'];
19730         
19731         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19732             cls.pop();
19733             cls.push('top');
19734         }
19735         
19736         cls.push('right');
19737         
19738         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19739             cls.pop();
19740             cls.push('left');
19741         }
19742         
19743         this.picker().addClass(cls.join('-'));
19744         
19745         var _this = this;
19746         
19747         Roo.each(cls, function(c){
19748             if(c == 'bottom'){
19749                 _this.picker().setTop(_this.inputEl().getHeight());
19750                 return;
19751             }
19752             if(c == 'top'){
19753                 _this.picker().setTop(0 - _this.picker().getHeight());
19754                 return;
19755             }
19756             
19757             if(c == 'left'){
19758                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19759                 return;
19760             }
19761             if(c == 'right'){
19762                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19763                 return;
19764             }
19765         });
19766         
19767     },
19768   
19769     onFocus : function()
19770     {
19771         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19772         this.show();
19773     },
19774     
19775     onBlur : function()
19776     {
19777         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19778         this.hide();
19779     },
19780     
19781     show : function()
19782     {
19783         this.picker().show();
19784         this.pop.show();
19785         this.update();
19786         this.place();
19787         
19788         this.fireEvent('show', this, this.date);
19789     },
19790     
19791     hide : function()
19792     {
19793         this.picker().hide();
19794         this.pop.hide();
19795         
19796         this.fireEvent('hide', this, this.date);
19797     },
19798     
19799     setTime : function()
19800     {
19801         this.hide();
19802         this.setValue(this.time.format(this.format));
19803         
19804         this.fireEvent('select', this, this.date);
19805         
19806         
19807     },
19808     
19809     onMousedown: function(e){
19810         e.stopPropagation();
19811         e.preventDefault();
19812     },
19813     
19814     onIncrementHours: function()
19815     {
19816         Roo.log('onIncrementHours');
19817         this.time = this.time.add(Date.HOUR, 1);
19818         this.update();
19819         
19820     },
19821     
19822     onDecrementHours: function()
19823     {
19824         Roo.log('onDecrementHours');
19825         this.time = this.time.add(Date.HOUR, -1);
19826         this.update();
19827     },
19828     
19829     onIncrementMinutes: function()
19830     {
19831         Roo.log('onIncrementMinutes');
19832         this.time = this.time.add(Date.MINUTE, 1);
19833         this.update();
19834     },
19835     
19836     onDecrementMinutes: function()
19837     {
19838         Roo.log('onDecrementMinutes');
19839         this.time = this.time.add(Date.MINUTE, -1);
19840         this.update();
19841     },
19842     
19843     onTogglePeriod: function()
19844     {
19845         Roo.log('onTogglePeriod');
19846         this.time = this.time.add(Date.HOUR, 12);
19847         this.update();
19848     }
19849     
19850    
19851 });
19852
19853 Roo.apply(Roo.bootstrap.TimeField,  {
19854     
19855     content : {
19856         tag: 'tbody',
19857         cn: [
19858             {
19859                 tag: 'tr',
19860                 cn: [
19861                 {
19862                     tag: 'td',
19863                     colspan: '7'
19864                 }
19865                 ]
19866             }
19867         ]
19868     },
19869     
19870     footer : {
19871         tag: 'tfoot',
19872         cn: [
19873             {
19874                 tag: 'tr',
19875                 cn: [
19876                 {
19877                     tag: 'th',
19878                     colspan: '7',
19879                     cls: '',
19880                     cn: [
19881                         {
19882                             tag: 'button',
19883                             cls: 'btn btn-info ok',
19884                             html: 'OK'
19885                         }
19886                     ]
19887                 }
19888
19889                 ]
19890             }
19891         ]
19892     }
19893 });
19894
19895 Roo.apply(Roo.bootstrap.TimeField,  {
19896   
19897     template : {
19898         tag: 'div',
19899         cls: 'datepicker dropdown-menu',
19900         cn: [
19901             {
19902                 tag: 'div',
19903                 cls: 'datepicker-time',
19904                 cn: [
19905                 {
19906                     tag: 'table',
19907                     cls: 'table-condensed',
19908                     cn:[
19909                     Roo.bootstrap.TimeField.content,
19910                     Roo.bootstrap.TimeField.footer
19911                     ]
19912                 }
19913                 ]
19914             }
19915         ]
19916     }
19917 });
19918
19919  
19920
19921  /*
19922  * - LGPL
19923  *
19924  * MonthField
19925  * 
19926  */
19927
19928 /**
19929  * @class Roo.bootstrap.MonthField
19930  * @extends Roo.bootstrap.Input
19931  * Bootstrap MonthField class
19932  * 
19933  * @cfg {String} language default en
19934  * 
19935  * @constructor
19936  * Create a new MonthField
19937  * @param {Object} config The config object
19938  */
19939
19940 Roo.bootstrap.MonthField = function(config){
19941     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19942     
19943     this.addEvents({
19944         /**
19945          * @event show
19946          * Fires when this field show.
19947          * @param {Roo.bootstrap.MonthField} this
19948          * @param {Mixed} date The date value
19949          */
19950         show : true,
19951         /**
19952          * @event show
19953          * Fires when this field hide.
19954          * @param {Roo.bootstrap.MonthField} this
19955          * @param {Mixed} date The date value
19956          */
19957         hide : true,
19958         /**
19959          * @event select
19960          * Fires when select a date.
19961          * @param {Roo.bootstrap.MonthField} this
19962          * @param {String} oldvalue The old value
19963          * @param {String} newvalue The new value
19964          */
19965         select : true
19966     });
19967 };
19968
19969 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19970     
19971     onRender: function(ct, position)
19972     {
19973         
19974         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19975         
19976         this.language = this.language || 'en';
19977         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19978         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19979         
19980         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19981         this.isInline = false;
19982         this.isInput = true;
19983         this.component = this.el.select('.add-on', true).first() || false;
19984         this.component = (this.component && this.component.length === 0) ? false : this.component;
19985         this.hasInput = this.component && this.inputEL().length;
19986         
19987         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19988         
19989         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19990         
19991         this.picker().on('mousedown', this.onMousedown, this);
19992         this.picker().on('click', this.onClick, this);
19993         
19994         this.picker().addClass('datepicker-dropdown');
19995         
19996         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19997             v.setStyle('width', '189px');
19998         });
19999         
20000         this.fillMonths();
20001         
20002         this.update();
20003         
20004         if(this.isInline) {
20005             this.show();
20006         }
20007         
20008     },
20009     
20010     setValue: function(v, suppressEvent)
20011     {   
20012         var o = this.getValue();
20013         
20014         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20015         
20016         this.update();
20017
20018         if(suppressEvent !== true){
20019             this.fireEvent('select', this, o, v);
20020         }
20021         
20022     },
20023     
20024     getValue: function()
20025     {
20026         return this.value;
20027     },
20028     
20029     onClick: function(e) 
20030     {
20031         e.stopPropagation();
20032         e.preventDefault();
20033         
20034         var target = e.getTarget();
20035         
20036         if(target.nodeName.toLowerCase() === 'i'){
20037             target = Roo.get(target).dom.parentNode;
20038         }
20039         
20040         var nodeName = target.nodeName;
20041         var className = target.className;
20042         var html = target.innerHTML;
20043         
20044         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20045             return;
20046         }
20047         
20048         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20049         
20050         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20051         
20052         this.hide();
20053                         
20054     },
20055     
20056     picker : function()
20057     {
20058         return this.pickerEl;
20059     },
20060     
20061     fillMonths: function()
20062     {    
20063         var i = 0;
20064         var months = this.picker().select('>.datepicker-months td', true).first();
20065         
20066         months.dom.innerHTML = '';
20067         
20068         while (i < 12) {
20069             var month = {
20070                 tag: 'span',
20071                 cls: 'month',
20072                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20073             };
20074             
20075             months.createChild(month);
20076         }
20077         
20078     },
20079     
20080     update: function()
20081     {
20082         var _this = this;
20083         
20084         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20085             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20086         }
20087         
20088         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20089             e.removeClass('active');
20090             
20091             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20092                 e.addClass('active');
20093             }
20094         })
20095     },
20096     
20097     place: function()
20098     {
20099         if(this.isInline) {
20100             return;
20101         }
20102         
20103         this.picker().removeClass(['bottom', 'top']);
20104         
20105         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20106             /*
20107              * place to the top of element!
20108              *
20109              */
20110             
20111             this.picker().addClass('top');
20112             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20113             
20114             return;
20115         }
20116         
20117         this.picker().addClass('bottom');
20118         
20119         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20120     },
20121     
20122     onFocus : function()
20123     {
20124         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20125         this.show();
20126     },
20127     
20128     onBlur : function()
20129     {
20130         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20131         
20132         var d = this.inputEl().getValue();
20133         
20134         this.setValue(d);
20135                 
20136         this.hide();
20137     },
20138     
20139     show : function()
20140     {
20141         this.picker().show();
20142         this.picker().select('>.datepicker-months', true).first().show();
20143         this.update();
20144         this.place();
20145         
20146         this.fireEvent('show', this, this.date);
20147     },
20148     
20149     hide : function()
20150     {
20151         if(this.isInline) {
20152             return;
20153         }
20154         this.picker().hide();
20155         this.fireEvent('hide', this, this.date);
20156         
20157     },
20158     
20159     onMousedown: function(e)
20160     {
20161         e.stopPropagation();
20162         e.preventDefault();
20163     },
20164     
20165     keyup: function(e)
20166     {
20167         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20168         this.update();
20169     },
20170
20171     fireKey: function(e)
20172     {
20173         if (!this.picker().isVisible()){
20174             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20175                 this.show();
20176             }
20177             return;
20178         }
20179         
20180         var dir;
20181         
20182         switch(e.keyCode){
20183             case 27: // escape
20184                 this.hide();
20185                 e.preventDefault();
20186                 break;
20187             case 37: // left
20188             case 39: // right
20189                 dir = e.keyCode == 37 ? -1 : 1;
20190                 
20191                 this.vIndex = this.vIndex + dir;
20192                 
20193                 if(this.vIndex < 0){
20194                     this.vIndex = 0;
20195                 }
20196                 
20197                 if(this.vIndex > 11){
20198                     this.vIndex = 11;
20199                 }
20200                 
20201                 if(isNaN(this.vIndex)){
20202                     this.vIndex = 0;
20203                 }
20204                 
20205                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20206                 
20207                 break;
20208             case 38: // up
20209             case 40: // down
20210                 
20211                 dir = e.keyCode == 38 ? -1 : 1;
20212                 
20213                 this.vIndex = this.vIndex + dir * 4;
20214                 
20215                 if(this.vIndex < 0){
20216                     this.vIndex = 0;
20217                 }
20218                 
20219                 if(this.vIndex > 11){
20220                     this.vIndex = 11;
20221                 }
20222                 
20223                 if(isNaN(this.vIndex)){
20224                     this.vIndex = 0;
20225                 }
20226                 
20227                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20228                 break;
20229                 
20230             case 13: // enter
20231                 
20232                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20233                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20234                 }
20235                 
20236                 this.hide();
20237                 e.preventDefault();
20238                 break;
20239             case 9: // tab
20240                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20241                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20242                 }
20243                 this.hide();
20244                 break;
20245             case 16: // shift
20246             case 17: // ctrl
20247             case 18: // alt
20248                 break;
20249             default :
20250                 this.hide();
20251                 
20252         }
20253     },
20254     
20255     remove: function() 
20256     {
20257         this.picker().remove();
20258     }
20259    
20260 });
20261
20262 Roo.apply(Roo.bootstrap.MonthField,  {
20263     
20264     content : {
20265         tag: 'tbody',
20266         cn: [
20267         {
20268             tag: 'tr',
20269             cn: [
20270             {
20271                 tag: 'td',
20272                 colspan: '7'
20273             }
20274             ]
20275         }
20276         ]
20277     },
20278     
20279     dates:{
20280         en: {
20281             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20282             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20283         }
20284     }
20285 });
20286
20287 Roo.apply(Roo.bootstrap.MonthField,  {
20288   
20289     template : {
20290         tag: 'div',
20291         cls: 'datepicker dropdown-menu roo-dynamic',
20292         cn: [
20293             {
20294                 tag: 'div',
20295                 cls: 'datepicker-months',
20296                 cn: [
20297                 {
20298                     tag: 'table',
20299                     cls: 'table-condensed',
20300                     cn:[
20301                         Roo.bootstrap.DateField.content
20302                     ]
20303                 }
20304                 ]
20305             }
20306         ]
20307     }
20308 });
20309
20310  
20311
20312  
20313  /*
20314  * - LGPL
20315  *
20316  * CheckBox
20317  * 
20318  */
20319
20320 /**
20321  * @class Roo.bootstrap.CheckBox
20322  * @extends Roo.bootstrap.Input
20323  * Bootstrap CheckBox class
20324  * 
20325  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20326  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20327  * @cfg {String} boxLabel The text that appears beside the checkbox
20328  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20329  * @cfg {Boolean} checked initnal the element
20330  * @cfg {Boolean} inline inline the element (default false)
20331  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20332  * @cfg {String} tooltip label tooltip
20333  * 
20334  * @constructor
20335  * Create a new CheckBox
20336  * @param {Object} config The config object
20337  */
20338
20339 Roo.bootstrap.CheckBox = function(config){
20340     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20341    
20342     this.addEvents({
20343         /**
20344         * @event check
20345         * Fires when the element is checked or unchecked.
20346         * @param {Roo.bootstrap.CheckBox} this This input
20347         * @param {Boolean} checked The new checked value
20348         */
20349        check : true,
20350        /**
20351         * @event click
20352         * Fires when the element is click.
20353         * @param {Roo.bootstrap.CheckBox} this This input
20354         */
20355        click : true
20356     });
20357     
20358 };
20359
20360 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20361   
20362     inputType: 'checkbox',
20363     inputValue: 1,
20364     valueOff: 0,
20365     boxLabel: false,
20366     checked: false,
20367     weight : false,
20368     inline: false,
20369     tooltip : '',
20370     
20371     getAutoCreate : function()
20372     {
20373         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20374         
20375         var id = Roo.id();
20376         
20377         var cfg = {};
20378         
20379         cfg.cls = 'form-group ' + this.inputType; //input-group
20380         
20381         if(this.inline){
20382             cfg.cls += ' ' + this.inputType + '-inline';
20383         }
20384         
20385         var input =  {
20386             tag: 'input',
20387             id : id,
20388             type : this.inputType,
20389             value : this.inputValue,
20390             cls : 'roo-' + this.inputType, //'form-box',
20391             placeholder : this.placeholder || ''
20392             
20393         };
20394         
20395         if(this.inputType != 'radio'){
20396             var hidden =  {
20397                 tag: 'input',
20398                 type : 'hidden',
20399                 cls : 'roo-hidden-value',
20400                 value : this.checked ? this.inputValue : this.valueOff
20401             };
20402         }
20403         
20404             
20405         if (this.weight) { // Validity check?
20406             cfg.cls += " " + this.inputType + "-" + this.weight;
20407         }
20408         
20409         if (this.disabled) {
20410             input.disabled=true;
20411         }
20412         
20413         if(this.checked){
20414             input.checked = this.checked;
20415         }
20416         
20417         if (this.name) {
20418             
20419             input.name = this.name;
20420             
20421             if(this.inputType != 'radio'){
20422                 hidden.name = this.name;
20423                 input.name = '_hidden_' + this.name;
20424             }
20425         }
20426         
20427         if (this.size) {
20428             input.cls += ' input-' + this.size;
20429         }
20430         
20431         var settings=this;
20432         
20433         ['xs','sm','md','lg'].map(function(size){
20434             if (settings[size]) {
20435                 cfg.cls += ' col-' + size + '-' + settings[size];
20436             }
20437         });
20438         
20439         var inputblock = input;
20440          
20441         if (this.before || this.after) {
20442             
20443             inputblock = {
20444                 cls : 'input-group',
20445                 cn :  [] 
20446             };
20447             
20448             if (this.before) {
20449                 inputblock.cn.push({
20450                     tag :'span',
20451                     cls : 'input-group-addon',
20452                     html : this.before
20453                 });
20454             }
20455             
20456             inputblock.cn.push(input);
20457             
20458             if(this.inputType != 'radio'){
20459                 inputblock.cn.push(hidden);
20460             }
20461             
20462             if (this.after) {
20463                 inputblock.cn.push({
20464                     tag :'span',
20465                     cls : 'input-group-addon',
20466                     html : this.after
20467                 });
20468             }
20469             
20470         }
20471         
20472         if (align ==='left' && this.fieldLabel.length) {
20473 //                Roo.log("left and has label");
20474             cfg.cn = [
20475                 {
20476                     tag: 'label',
20477                     'for' :  id,
20478                     cls : 'control-label',
20479                     html : this.fieldLabel
20480                 },
20481                 {
20482                     cls : "", 
20483                     cn: [
20484                         inputblock
20485                     ]
20486                 }
20487             ];
20488             
20489             if(this.labelWidth > 12){
20490                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20491             }
20492             
20493             if(this.labelWidth < 13 && this.labelmd == 0){
20494                 this.labelmd = this.labelWidth;
20495             }
20496             
20497             if(this.labellg > 0){
20498                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20499                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20500             }
20501             
20502             if(this.labelmd > 0){
20503                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20504                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20505             }
20506             
20507             if(this.labelsm > 0){
20508                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20509                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20510             }
20511             
20512             if(this.labelxs > 0){
20513                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20514                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20515             }
20516             
20517         } else if ( this.fieldLabel.length) {
20518 //                Roo.log(" label");
20519                 cfg.cn = [
20520                    
20521                     {
20522                         tag: this.boxLabel ? 'span' : 'label',
20523                         'for': id,
20524                         cls: 'control-label box-input-label',
20525                         //cls : 'input-group-addon',
20526                         html : this.fieldLabel
20527                     },
20528                     
20529                     inputblock
20530                     
20531                 ];
20532
20533         } else {
20534             
20535 //                Roo.log(" no label && no align");
20536                 cfg.cn = [  inputblock ] ;
20537                 
20538                 
20539         }
20540         
20541         if(this.boxLabel){
20542              var boxLabelCfg = {
20543                 tag: 'label',
20544                 //'for': id, // box label is handled by onclick - so no for...
20545                 cls: 'box-label',
20546                 html: this.boxLabel
20547             };
20548             
20549             if(this.tooltip){
20550                 boxLabelCfg.tooltip = this.tooltip;
20551             }
20552              
20553             cfg.cn.push(boxLabelCfg);
20554         }
20555         
20556         if(this.inputType != 'radio'){
20557             cfg.cn.push(hidden);
20558         }
20559         
20560         return cfg;
20561         
20562     },
20563     
20564     /**
20565      * return the real input element.
20566      */
20567     inputEl: function ()
20568     {
20569         return this.el.select('input.roo-' + this.inputType,true).first();
20570     },
20571     hiddenEl: function ()
20572     {
20573         return this.el.select('input.roo-hidden-value',true).first();
20574     },
20575     
20576     labelEl: function()
20577     {
20578         return this.el.select('label.control-label',true).first();
20579     },
20580     /* depricated... */
20581     
20582     label: function()
20583     {
20584         return this.labelEl();
20585     },
20586     
20587     boxLabelEl: function()
20588     {
20589         return this.el.select('label.box-label',true).first();
20590     },
20591     
20592     initEvents : function()
20593     {
20594 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20595         
20596         this.inputEl().on('click', this.onClick,  this);
20597         
20598         if (this.boxLabel) { 
20599             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20600         }
20601         
20602         this.startValue = this.getValue();
20603         
20604         if(this.groupId){
20605             Roo.bootstrap.CheckBox.register(this);
20606         }
20607     },
20608     
20609     onClick : function(e)
20610     {   
20611         if(this.fireEvent('click', this, e) !== false){
20612             this.setChecked(!this.checked);
20613         }
20614         
20615     },
20616     
20617     setChecked : function(state,suppressEvent)
20618     {
20619         this.startValue = this.getValue();
20620
20621         if(this.inputType == 'radio'){
20622             
20623             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20624                 e.dom.checked = false;
20625             });
20626             
20627             this.inputEl().dom.checked = true;
20628             
20629             this.inputEl().dom.value = this.inputValue;
20630             
20631             if(suppressEvent !== true){
20632                 this.fireEvent('check', this, true);
20633             }
20634             
20635             this.validate();
20636             
20637             return;
20638         }
20639         
20640         this.checked = state;
20641         
20642         this.inputEl().dom.checked = state;
20643         
20644         
20645         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20646         
20647         if(suppressEvent !== true){
20648             this.fireEvent('check', this, state);
20649         }
20650         
20651         this.validate();
20652     },
20653     
20654     getValue : function()
20655     {
20656         if(this.inputType == 'radio'){
20657             return this.getGroupValue();
20658         }
20659         
20660         return this.hiddenEl().dom.value;
20661         
20662     },
20663     
20664     getGroupValue : function()
20665     {
20666         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20667             return '';
20668         }
20669         
20670         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20671     },
20672     
20673     setValue : function(v,suppressEvent)
20674     {
20675         if(this.inputType == 'radio'){
20676             this.setGroupValue(v, suppressEvent);
20677             return;
20678         }
20679         
20680         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20681         
20682         this.validate();
20683     },
20684     
20685     setGroupValue : function(v, suppressEvent)
20686     {
20687         this.startValue = this.getValue();
20688         
20689         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20690             e.dom.checked = false;
20691             
20692             if(e.dom.value == v){
20693                 e.dom.checked = true;
20694             }
20695         });
20696         
20697         if(suppressEvent !== true){
20698             this.fireEvent('check', this, true);
20699         }
20700
20701         this.validate();
20702         
20703         return;
20704     },
20705     
20706     validate : function()
20707     {
20708         if(this.getVisibilityEl().hasClass('hidden')){
20709             return true;
20710         }
20711         
20712         if(
20713                 this.disabled || 
20714                 (this.inputType == 'radio' && this.validateRadio()) ||
20715                 (this.inputType == 'checkbox' && this.validateCheckbox())
20716         ){
20717             this.markValid();
20718             return true;
20719         }
20720         
20721         this.markInvalid();
20722         return false;
20723     },
20724     
20725     validateRadio : function()
20726     {
20727         if(this.getVisibilityEl().hasClass('hidden')){
20728             return true;
20729         }
20730         
20731         if(this.allowBlank){
20732             return true;
20733         }
20734         
20735         var valid = false;
20736         
20737         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20738             if(!e.dom.checked){
20739                 return;
20740             }
20741             
20742             valid = true;
20743             
20744             return false;
20745         });
20746         
20747         return valid;
20748     },
20749     
20750     validateCheckbox : function()
20751     {
20752         if(!this.groupId){
20753             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20754             //return (this.getValue() == this.inputValue) ? true : false;
20755         }
20756         
20757         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20758         
20759         if(!group){
20760             return false;
20761         }
20762         
20763         var r = false;
20764         
20765         for(var i in group){
20766             if(group[i].el.isVisible(true)){
20767                 r = false;
20768                 break;
20769             }
20770             
20771             r = true;
20772         }
20773         
20774         for(var i in group){
20775             if(r){
20776                 break;
20777             }
20778             
20779             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20780         }
20781         
20782         return r;
20783     },
20784     
20785     /**
20786      * Mark this field as valid
20787      */
20788     markValid : function()
20789     {
20790         var _this = this;
20791         
20792         this.fireEvent('valid', this);
20793         
20794         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20795         
20796         if(this.groupId){
20797             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20798         }
20799         
20800         if(label){
20801             label.markValid();
20802         }
20803
20804         if(this.inputType == 'radio'){
20805             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20806                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20807                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20808             });
20809             
20810             return;
20811         }
20812
20813         if(!this.groupId){
20814             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20815             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20816             return;
20817         }
20818         
20819         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20820         
20821         if(!group){
20822             return;
20823         }
20824         
20825         for(var i in group){
20826             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20827             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20828         }
20829     },
20830     
20831      /**
20832      * Mark this field as invalid
20833      * @param {String} msg The validation message
20834      */
20835     markInvalid : function(msg)
20836     {
20837         if(this.allowBlank){
20838             return;
20839         }
20840         
20841         var _this = this;
20842         
20843         this.fireEvent('invalid', this, msg);
20844         
20845         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20846         
20847         if(this.groupId){
20848             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20849         }
20850         
20851         if(label){
20852             label.markInvalid();
20853         }
20854             
20855         if(this.inputType == 'radio'){
20856             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20857                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20858                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20859             });
20860             
20861             return;
20862         }
20863         
20864         if(!this.groupId){
20865             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20866             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20867             return;
20868         }
20869         
20870         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20871         
20872         if(!group){
20873             return;
20874         }
20875         
20876         for(var i in group){
20877             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20878             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20879         }
20880         
20881     },
20882     
20883     clearInvalid : function()
20884     {
20885         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20886         
20887         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20888         
20889         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20890         
20891         if (label && label.iconEl) {
20892             label.iconEl.removeClass(label.validClass);
20893             label.iconEl.removeClass(label.invalidClass);
20894         }
20895     },
20896     
20897     disable : function()
20898     {
20899         if(this.inputType != 'radio'){
20900             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20901             return;
20902         }
20903         
20904         var _this = this;
20905         
20906         if(this.rendered){
20907             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20908                 _this.getActionEl().addClass(this.disabledClass);
20909                 e.dom.disabled = true;
20910             });
20911         }
20912         
20913         this.disabled = true;
20914         this.fireEvent("disable", this);
20915         return this;
20916     },
20917
20918     enable : function()
20919     {
20920         if(this.inputType != 'radio'){
20921             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20922             return;
20923         }
20924         
20925         var _this = this;
20926         
20927         if(this.rendered){
20928             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20929                 _this.getActionEl().removeClass(this.disabledClass);
20930                 e.dom.disabled = false;
20931             });
20932         }
20933         
20934         this.disabled = false;
20935         this.fireEvent("enable", this);
20936         return this;
20937     },
20938     
20939     setBoxLabel : function(v)
20940     {
20941         this.boxLabel = v;
20942         
20943         if(this.rendered){
20944             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20945         }
20946     }
20947
20948 });
20949
20950 Roo.apply(Roo.bootstrap.CheckBox, {
20951     
20952     groups: {},
20953     
20954      /**
20955     * register a CheckBox Group
20956     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20957     */
20958     register : function(checkbox)
20959     {
20960         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20961             this.groups[checkbox.groupId] = {};
20962         }
20963         
20964         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20965             return;
20966         }
20967         
20968         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20969         
20970     },
20971     /**
20972     * fetch a CheckBox Group based on the group ID
20973     * @param {string} the group ID
20974     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20975     */
20976     get: function(groupId) {
20977         if (typeof(this.groups[groupId]) == 'undefined') {
20978             return false;
20979         }
20980         
20981         return this.groups[groupId] ;
20982     }
20983     
20984     
20985 });
20986 /*
20987  * - LGPL
20988  *
20989  * RadioItem
20990  * 
20991  */
20992
20993 /**
20994  * @class Roo.bootstrap.Radio
20995  * @extends Roo.bootstrap.Component
20996  * Bootstrap Radio class
20997  * @cfg {String} boxLabel - the label associated
20998  * @cfg {String} value - the value of radio
20999  * 
21000  * @constructor
21001  * Create a new Radio
21002  * @param {Object} config The config object
21003  */
21004 Roo.bootstrap.Radio = function(config){
21005     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21006     
21007 };
21008
21009 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21010     
21011     boxLabel : '',
21012     
21013     value : '',
21014     
21015     getAutoCreate : function()
21016     {
21017         var cfg = {
21018             tag : 'div',
21019             cls : 'form-group radio',
21020             cn : [
21021                 {
21022                     tag : 'label',
21023                     cls : 'box-label',
21024                     html : this.boxLabel
21025                 }
21026             ]
21027         };
21028         
21029         return cfg;
21030     },
21031     
21032     initEvents : function() 
21033     {
21034         this.parent().register(this);
21035         
21036         this.el.on('click', this.onClick, this);
21037         
21038     },
21039     
21040     onClick : function(e)
21041     {
21042         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21043             this.setChecked(true);
21044         }
21045     },
21046     
21047     setChecked : function(state, suppressEvent)
21048     {
21049         this.parent().setValue(this.value, suppressEvent);
21050         
21051     },
21052     
21053     setBoxLabel : function(v)
21054     {
21055         this.boxLabel = v;
21056         
21057         if(this.rendered){
21058             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21059         }
21060     }
21061     
21062 });
21063  
21064
21065  /*
21066  * - LGPL
21067  *
21068  * Input
21069  * 
21070  */
21071
21072 /**
21073  * @class Roo.bootstrap.SecurePass
21074  * @extends Roo.bootstrap.Input
21075  * Bootstrap SecurePass class
21076  *
21077  * 
21078  * @constructor
21079  * Create a new SecurePass
21080  * @param {Object} config The config object
21081  */
21082  
21083 Roo.bootstrap.SecurePass = function (config) {
21084     // these go here, so the translation tool can replace them..
21085     this.errors = {
21086         PwdEmpty: "Please type a password, and then retype it to confirm.",
21087         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21088         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21089         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21090         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21091         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21092         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21093         TooWeak: "Your password is Too Weak."
21094     },
21095     this.meterLabel = "Password strength:";
21096     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21097     this.meterClass = [
21098         "roo-password-meter-tooweak", 
21099         "roo-password-meter-weak", 
21100         "roo-password-meter-medium", 
21101         "roo-password-meter-strong", 
21102         "roo-password-meter-grey"
21103     ];
21104     
21105     this.errors = {};
21106     
21107     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21108 }
21109
21110 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21111     /**
21112      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21113      * {
21114      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21115      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21116      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21117      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21118      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21119      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21120      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21121      * })
21122      */
21123     // private
21124     
21125     meterWidth: 300,
21126     errorMsg :'',    
21127     errors: false,
21128     imageRoot: '/',
21129     /**
21130      * @cfg {String/Object} Label for the strength meter (defaults to
21131      * 'Password strength:')
21132      */
21133     // private
21134     meterLabel: '',
21135     /**
21136      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21137      * ['Weak', 'Medium', 'Strong'])
21138      */
21139     // private    
21140     pwdStrengths: false,    
21141     // private
21142     strength: 0,
21143     // private
21144     _lastPwd: null,
21145     // private
21146     kCapitalLetter: 0,
21147     kSmallLetter: 1,
21148     kDigit: 2,
21149     kPunctuation: 3,
21150     
21151     insecure: false,
21152     // private
21153     initEvents: function ()
21154     {
21155         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21156
21157         if (this.el.is('input[type=password]') && Roo.isSafari) {
21158             this.el.on('keydown', this.SafariOnKeyDown, this);
21159         }
21160
21161         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21162     },
21163     // private
21164     onRender: function (ct, position)
21165     {
21166         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21167         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21168         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21169
21170         this.trigger.createChild({
21171                    cn: [
21172                     {
21173                     //id: 'PwdMeter',
21174                     tag: 'div',
21175                     cls: 'roo-password-meter-grey col-xs-12',
21176                     style: {
21177                         //width: 0,
21178                         //width: this.meterWidth + 'px'                                                
21179                         }
21180                     },
21181                     {                            
21182                          cls: 'roo-password-meter-text'                          
21183                     }
21184                 ]            
21185         });
21186
21187          
21188         if (this.hideTrigger) {
21189             this.trigger.setDisplayed(false);
21190         }
21191         this.setSize(this.width || '', this.height || '');
21192     },
21193     // private
21194     onDestroy: function ()
21195     {
21196         if (this.trigger) {
21197             this.trigger.removeAllListeners();
21198             this.trigger.remove();
21199         }
21200         if (this.wrap) {
21201             this.wrap.remove();
21202         }
21203         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21204     },
21205     // private
21206     checkStrength: function ()
21207     {
21208         var pwd = this.inputEl().getValue();
21209         if (pwd == this._lastPwd) {
21210             return;
21211         }
21212
21213         var strength;
21214         if (this.ClientSideStrongPassword(pwd)) {
21215             strength = 3;
21216         } else if (this.ClientSideMediumPassword(pwd)) {
21217             strength = 2;
21218         } else if (this.ClientSideWeakPassword(pwd)) {
21219             strength = 1;
21220         } else {
21221             strength = 0;
21222         }
21223         
21224         Roo.log('strength1: ' + strength);
21225         
21226         //var pm = this.trigger.child('div/div/div').dom;
21227         var pm = this.trigger.child('div/div');
21228         pm.removeClass(this.meterClass);
21229         pm.addClass(this.meterClass[strength]);
21230                 
21231         
21232         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21233                 
21234         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21235         
21236         this._lastPwd = pwd;
21237     },
21238     reset: function ()
21239     {
21240         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21241         
21242         this._lastPwd = '';
21243         
21244         var pm = this.trigger.child('div/div');
21245         pm.removeClass(this.meterClass);
21246         pm.addClass('roo-password-meter-grey');        
21247         
21248         
21249         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21250         
21251         pt.innerHTML = '';
21252         this.inputEl().dom.type='password';
21253     },
21254     // private
21255     validateValue: function (value)
21256     {
21257         
21258         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21259             return false;
21260         }
21261         if (value.length == 0) {
21262             if (this.allowBlank) {
21263                 this.clearInvalid();
21264                 return true;
21265             }
21266
21267             this.markInvalid(this.errors.PwdEmpty);
21268             this.errorMsg = this.errors.PwdEmpty;
21269             return false;
21270         }
21271         
21272         if(this.insecure){
21273             return true;
21274         }
21275         
21276         if ('[\x21-\x7e]*'.match(value)) {
21277             this.markInvalid(this.errors.PwdBadChar);
21278             this.errorMsg = this.errors.PwdBadChar;
21279             return false;
21280         }
21281         if (value.length < 6) {
21282             this.markInvalid(this.errors.PwdShort);
21283             this.errorMsg = this.errors.PwdShort;
21284             return false;
21285         }
21286         if (value.length > 16) {
21287             this.markInvalid(this.errors.PwdLong);
21288             this.errorMsg = this.errors.PwdLong;
21289             return false;
21290         }
21291         var strength;
21292         if (this.ClientSideStrongPassword(value)) {
21293             strength = 3;
21294         } else if (this.ClientSideMediumPassword(value)) {
21295             strength = 2;
21296         } else if (this.ClientSideWeakPassword(value)) {
21297             strength = 1;
21298         } else {
21299             strength = 0;
21300         }
21301
21302         
21303         if (strength < 2) {
21304             //this.markInvalid(this.errors.TooWeak);
21305             this.errorMsg = this.errors.TooWeak;
21306             //return false;
21307         }
21308         
21309         
21310         console.log('strength2: ' + strength);
21311         
21312         //var pm = this.trigger.child('div/div/div').dom;
21313         
21314         var pm = this.trigger.child('div/div');
21315         pm.removeClass(this.meterClass);
21316         pm.addClass(this.meterClass[strength]);
21317                 
21318         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21319                 
21320         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21321         
21322         this.errorMsg = ''; 
21323         return true;
21324     },
21325     // private
21326     CharacterSetChecks: function (type)
21327     {
21328         this.type = type;
21329         this.fResult = false;
21330     },
21331     // private
21332     isctype: function (character, type)
21333     {
21334         switch (type) {  
21335             case this.kCapitalLetter:
21336                 if (character >= 'A' && character <= 'Z') {
21337                     return true;
21338                 }
21339                 break;
21340             
21341             case this.kSmallLetter:
21342                 if (character >= 'a' && character <= 'z') {
21343                     return true;
21344                 }
21345                 break;
21346             
21347             case this.kDigit:
21348                 if (character >= '0' && character <= '9') {
21349                     return true;
21350                 }
21351                 break;
21352             
21353             case this.kPunctuation:
21354                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21355                     return true;
21356                 }
21357                 break;
21358             
21359             default:
21360                 return false;
21361         }
21362
21363     },
21364     // private
21365     IsLongEnough: function (pwd, size)
21366     {
21367         return !(pwd == null || isNaN(size) || pwd.length < size);
21368     },
21369     // private
21370     SpansEnoughCharacterSets: function (word, nb)
21371     {
21372         if (!this.IsLongEnough(word, nb))
21373         {
21374             return false;
21375         }
21376
21377         var characterSetChecks = new Array(
21378             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21379             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21380         );
21381         
21382         for (var index = 0; index < word.length; ++index) {
21383             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21384                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21385                     characterSetChecks[nCharSet].fResult = true;
21386                     break;
21387                 }
21388             }
21389         }
21390
21391         var nCharSets = 0;
21392         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21393             if (characterSetChecks[nCharSet].fResult) {
21394                 ++nCharSets;
21395             }
21396         }
21397
21398         if (nCharSets < nb) {
21399             return false;
21400         }
21401         return true;
21402     },
21403     // private
21404     ClientSideStrongPassword: function (pwd)
21405     {
21406         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21407     },
21408     // private
21409     ClientSideMediumPassword: function (pwd)
21410     {
21411         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21412     },
21413     // private
21414     ClientSideWeakPassword: function (pwd)
21415     {
21416         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21417     }
21418           
21419 })//<script type="text/javascript">
21420
21421 /*
21422  * Based  Ext JS Library 1.1.1
21423  * Copyright(c) 2006-2007, Ext JS, LLC.
21424  * LGPL
21425  *
21426  */
21427  
21428 /**
21429  * @class Roo.HtmlEditorCore
21430  * @extends Roo.Component
21431  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21432  *
21433  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21434  */
21435
21436 Roo.HtmlEditorCore = function(config){
21437     
21438     
21439     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21440     
21441     
21442     this.addEvents({
21443         /**
21444          * @event initialize
21445          * Fires when the editor is fully initialized (including the iframe)
21446          * @param {Roo.HtmlEditorCore} this
21447          */
21448         initialize: true,
21449         /**
21450          * @event activate
21451          * Fires when the editor is first receives the focus. Any insertion must wait
21452          * until after this event.
21453          * @param {Roo.HtmlEditorCore} this
21454          */
21455         activate: true,
21456          /**
21457          * @event beforesync
21458          * Fires before the textarea is updated with content from the editor iframe. Return false
21459          * to cancel the sync.
21460          * @param {Roo.HtmlEditorCore} this
21461          * @param {String} html
21462          */
21463         beforesync: true,
21464          /**
21465          * @event beforepush
21466          * Fires before the iframe editor is updated with content from the textarea. Return false
21467          * to cancel the push.
21468          * @param {Roo.HtmlEditorCore} this
21469          * @param {String} html
21470          */
21471         beforepush: true,
21472          /**
21473          * @event sync
21474          * Fires when the textarea is updated with content from the editor iframe.
21475          * @param {Roo.HtmlEditorCore} this
21476          * @param {String} html
21477          */
21478         sync: true,
21479          /**
21480          * @event push
21481          * Fires when the iframe editor is updated with content from the textarea.
21482          * @param {Roo.HtmlEditorCore} this
21483          * @param {String} html
21484          */
21485         push: true,
21486         
21487         /**
21488          * @event editorevent
21489          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21490          * @param {Roo.HtmlEditorCore} this
21491          */
21492         editorevent: true
21493         
21494     });
21495     
21496     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21497     
21498     // defaults : white / black...
21499     this.applyBlacklists();
21500     
21501     
21502     
21503 };
21504
21505
21506 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21507
21508
21509      /**
21510      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21511      */
21512     
21513     owner : false,
21514     
21515      /**
21516      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21517      *                        Roo.resizable.
21518      */
21519     resizable : false,
21520      /**
21521      * @cfg {Number} height (in pixels)
21522      */   
21523     height: 300,
21524    /**
21525      * @cfg {Number} width (in pixels)
21526      */   
21527     width: 500,
21528     
21529     /**
21530      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21531      * 
21532      */
21533     stylesheets: false,
21534     
21535     // id of frame..
21536     frameId: false,
21537     
21538     // private properties
21539     validationEvent : false,
21540     deferHeight: true,
21541     initialized : false,
21542     activated : false,
21543     sourceEditMode : false,
21544     onFocus : Roo.emptyFn,
21545     iframePad:3,
21546     hideMode:'offsets',
21547     
21548     clearUp: true,
21549     
21550     // blacklist + whitelisted elements..
21551     black: false,
21552     white: false,
21553      
21554     bodyCls : '',
21555
21556     /**
21557      * Protected method that will not generally be called directly. It
21558      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21559      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21560      */
21561     getDocMarkup : function(){
21562         // body styles..
21563         var st = '';
21564         
21565         // inherit styels from page...?? 
21566         if (this.stylesheets === false) {
21567             
21568             Roo.get(document.head).select('style').each(function(node) {
21569                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21570             });
21571             
21572             Roo.get(document.head).select('link').each(function(node) { 
21573                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21574             });
21575             
21576         } else if (!this.stylesheets.length) {
21577                 // simple..
21578                 st = '<style type="text/css">' +
21579                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21580                    '</style>';
21581         } else { 
21582             st = '<style type="text/css">' +
21583                     this.stylesheets +
21584                 '</style>';
21585         }
21586         
21587         st +=  '<style type="text/css">' +
21588             'IMG { cursor: pointer } ' +
21589         '</style>';
21590
21591         var cls = 'roo-htmleditor-body';
21592         
21593         if(this.bodyCls.length){
21594             cls += ' ' + this.bodyCls;
21595         }
21596         
21597         return '<html><head>' + st  +
21598             //<style type="text/css">' +
21599             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21600             //'</style>' +
21601             ' </head><body class="' +  cls + '"></body></html>';
21602     },
21603
21604     // private
21605     onRender : function(ct, position)
21606     {
21607         var _t = this;
21608         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21609         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21610         
21611         
21612         this.el.dom.style.border = '0 none';
21613         this.el.dom.setAttribute('tabIndex', -1);
21614         this.el.addClass('x-hidden hide');
21615         
21616         
21617         
21618         if(Roo.isIE){ // fix IE 1px bogus margin
21619             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21620         }
21621        
21622         
21623         this.frameId = Roo.id();
21624         
21625          
21626         
21627         var iframe = this.owner.wrap.createChild({
21628             tag: 'iframe',
21629             cls: 'form-control', // bootstrap..
21630             id: this.frameId,
21631             name: this.frameId,
21632             frameBorder : 'no',
21633             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21634         }, this.el
21635         );
21636         
21637         
21638         this.iframe = iframe.dom;
21639
21640          this.assignDocWin();
21641         
21642         this.doc.designMode = 'on';
21643        
21644         this.doc.open();
21645         this.doc.write(this.getDocMarkup());
21646         this.doc.close();
21647
21648         
21649         var task = { // must defer to wait for browser to be ready
21650             run : function(){
21651                 //console.log("run task?" + this.doc.readyState);
21652                 this.assignDocWin();
21653                 if(this.doc.body || this.doc.readyState == 'complete'){
21654                     try {
21655                         this.doc.designMode="on";
21656                     } catch (e) {
21657                         return;
21658                     }
21659                     Roo.TaskMgr.stop(task);
21660                     this.initEditor.defer(10, this);
21661                 }
21662             },
21663             interval : 10,
21664             duration: 10000,
21665             scope: this
21666         };
21667         Roo.TaskMgr.start(task);
21668
21669     },
21670
21671     // private
21672     onResize : function(w, h)
21673     {
21674          Roo.log('resize: ' +w + ',' + h );
21675         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21676         if(!this.iframe){
21677             return;
21678         }
21679         if(typeof w == 'number'){
21680             
21681             this.iframe.style.width = w + 'px';
21682         }
21683         if(typeof h == 'number'){
21684             
21685             this.iframe.style.height = h + 'px';
21686             if(this.doc){
21687                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21688             }
21689         }
21690         
21691     },
21692
21693     /**
21694      * Toggles the editor between standard and source edit mode.
21695      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21696      */
21697     toggleSourceEdit : function(sourceEditMode){
21698         
21699         this.sourceEditMode = sourceEditMode === true;
21700         
21701         if(this.sourceEditMode){
21702  
21703             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21704             
21705         }else{
21706             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21707             //this.iframe.className = '';
21708             this.deferFocus();
21709         }
21710         //this.setSize(this.owner.wrap.getSize());
21711         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21712     },
21713
21714     
21715   
21716
21717     /**
21718      * Protected method that will not generally be called directly. If you need/want
21719      * custom HTML cleanup, this is the method you should override.
21720      * @param {String} html The HTML to be cleaned
21721      * return {String} The cleaned HTML
21722      */
21723     cleanHtml : function(html){
21724         html = String(html);
21725         if(html.length > 5){
21726             if(Roo.isSafari){ // strip safari nonsense
21727                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21728             }
21729         }
21730         if(html == '&nbsp;'){
21731             html = '';
21732         }
21733         return html;
21734     },
21735
21736     /**
21737      * HTML Editor -> Textarea
21738      * Protected method that will not generally be called directly. Syncs the contents
21739      * of the editor iframe with the textarea.
21740      */
21741     syncValue : function(){
21742         if(this.initialized){
21743             var bd = (this.doc.body || this.doc.documentElement);
21744             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21745             var html = bd.innerHTML;
21746             if(Roo.isSafari){
21747                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21748                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21749                 if(m && m[1]){
21750                     html = '<div style="'+m[0]+'">' + html + '</div>';
21751                 }
21752             }
21753             html = this.cleanHtml(html);
21754             // fix up the special chars.. normaly like back quotes in word...
21755             // however we do not want to do this with chinese..
21756             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21757                 var cc = b.charCodeAt();
21758                 if (
21759                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21760                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21761                     (cc >= 0xf900 && cc < 0xfb00 )
21762                 ) {
21763                         return b;
21764                 }
21765                 return "&#"+cc+";" 
21766             });
21767             if(this.owner.fireEvent('beforesync', this, html) !== false){
21768                 this.el.dom.value = html;
21769                 this.owner.fireEvent('sync', this, html);
21770             }
21771         }
21772     },
21773
21774     /**
21775      * Protected method that will not generally be called directly. Pushes the value of the textarea
21776      * into the iframe editor.
21777      */
21778     pushValue : function(){
21779         if(this.initialized){
21780             var v = this.el.dom.value.trim();
21781             
21782 //            if(v.length < 1){
21783 //                v = '&#160;';
21784 //            }
21785             
21786             if(this.owner.fireEvent('beforepush', this, v) !== false){
21787                 var d = (this.doc.body || this.doc.documentElement);
21788                 d.innerHTML = v;
21789                 this.cleanUpPaste();
21790                 this.el.dom.value = d.innerHTML;
21791                 this.owner.fireEvent('push', this, v);
21792             }
21793         }
21794     },
21795
21796     // private
21797     deferFocus : function(){
21798         this.focus.defer(10, this);
21799     },
21800
21801     // doc'ed in Field
21802     focus : function(){
21803         if(this.win && !this.sourceEditMode){
21804             this.win.focus();
21805         }else{
21806             this.el.focus();
21807         }
21808     },
21809     
21810     assignDocWin: function()
21811     {
21812         var iframe = this.iframe;
21813         
21814          if(Roo.isIE){
21815             this.doc = iframe.contentWindow.document;
21816             this.win = iframe.contentWindow;
21817         } else {
21818 //            if (!Roo.get(this.frameId)) {
21819 //                return;
21820 //            }
21821 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21822 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21823             
21824             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21825                 return;
21826             }
21827             
21828             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21829             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21830         }
21831     },
21832     
21833     // private
21834     initEditor : function(){
21835         //console.log("INIT EDITOR");
21836         this.assignDocWin();
21837         
21838         
21839         
21840         this.doc.designMode="on";
21841         this.doc.open();
21842         this.doc.write(this.getDocMarkup());
21843         this.doc.close();
21844         
21845         var dbody = (this.doc.body || this.doc.documentElement);
21846         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21847         // this copies styles from the containing element into thsi one..
21848         // not sure why we need all of this..
21849         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21850         
21851         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21852         //ss['background-attachment'] = 'fixed'; // w3c
21853         dbody.bgProperties = 'fixed'; // ie
21854         //Roo.DomHelper.applyStyles(dbody, ss);
21855         Roo.EventManager.on(this.doc, {
21856             //'mousedown': this.onEditorEvent,
21857             'mouseup': this.onEditorEvent,
21858             'dblclick': this.onEditorEvent,
21859             'click': this.onEditorEvent,
21860             'keyup': this.onEditorEvent,
21861             buffer:100,
21862             scope: this
21863         });
21864         if(Roo.isGecko){
21865             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21866         }
21867         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21868             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21869         }
21870         this.initialized = true;
21871
21872         this.owner.fireEvent('initialize', this);
21873         this.pushValue();
21874     },
21875
21876     // private
21877     onDestroy : function(){
21878         
21879         
21880         
21881         if(this.rendered){
21882             
21883             //for (var i =0; i < this.toolbars.length;i++) {
21884             //    // fixme - ask toolbars for heights?
21885             //    this.toolbars[i].onDestroy();
21886            // }
21887             
21888             //this.wrap.dom.innerHTML = '';
21889             //this.wrap.remove();
21890         }
21891     },
21892
21893     // private
21894     onFirstFocus : function(){
21895         
21896         this.assignDocWin();
21897         
21898         
21899         this.activated = true;
21900          
21901     
21902         if(Roo.isGecko){ // prevent silly gecko errors
21903             this.win.focus();
21904             var s = this.win.getSelection();
21905             if(!s.focusNode || s.focusNode.nodeType != 3){
21906                 var r = s.getRangeAt(0);
21907                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21908                 r.collapse(true);
21909                 this.deferFocus();
21910             }
21911             try{
21912                 this.execCmd('useCSS', true);
21913                 this.execCmd('styleWithCSS', false);
21914             }catch(e){}
21915         }
21916         this.owner.fireEvent('activate', this);
21917     },
21918
21919     // private
21920     adjustFont: function(btn){
21921         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21922         //if(Roo.isSafari){ // safari
21923         //    adjust *= 2;
21924        // }
21925         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21926         if(Roo.isSafari){ // safari
21927             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21928             v =  (v < 10) ? 10 : v;
21929             v =  (v > 48) ? 48 : v;
21930             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21931             
21932         }
21933         
21934         
21935         v = Math.max(1, v+adjust);
21936         
21937         this.execCmd('FontSize', v  );
21938     },
21939
21940     onEditorEvent : function(e)
21941     {
21942         this.owner.fireEvent('editorevent', this, e);
21943       //  this.updateToolbar();
21944         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21945     },
21946
21947     insertTag : function(tg)
21948     {
21949         // could be a bit smarter... -> wrap the current selected tRoo..
21950         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21951             
21952             range = this.createRange(this.getSelection());
21953             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21954             wrappingNode.appendChild(range.extractContents());
21955             range.insertNode(wrappingNode);
21956
21957             return;
21958             
21959             
21960             
21961         }
21962         this.execCmd("formatblock",   tg);
21963         
21964     },
21965     
21966     insertText : function(txt)
21967     {
21968         
21969         
21970         var range = this.createRange();
21971         range.deleteContents();
21972                //alert(Sender.getAttribute('label'));
21973                
21974         range.insertNode(this.doc.createTextNode(txt));
21975     } ,
21976     
21977      
21978
21979     /**
21980      * Executes a Midas editor command on the editor document and performs necessary focus and
21981      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21982      * @param {String} cmd The Midas command
21983      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21984      */
21985     relayCmd : function(cmd, value){
21986         this.win.focus();
21987         this.execCmd(cmd, value);
21988         this.owner.fireEvent('editorevent', this);
21989         //this.updateToolbar();
21990         this.owner.deferFocus();
21991     },
21992
21993     /**
21994      * Executes a Midas editor command directly on the editor document.
21995      * For visual commands, you should use {@link #relayCmd} instead.
21996      * <b>This should only be called after the editor is initialized.</b>
21997      * @param {String} cmd The Midas command
21998      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21999      */
22000     execCmd : function(cmd, value){
22001         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22002         this.syncValue();
22003     },
22004  
22005  
22006    
22007     /**
22008      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22009      * to insert tRoo.
22010      * @param {String} text | dom node.. 
22011      */
22012     insertAtCursor : function(text)
22013     {
22014         
22015         if(!this.activated){
22016             return;
22017         }
22018         /*
22019         if(Roo.isIE){
22020             this.win.focus();
22021             var r = this.doc.selection.createRange();
22022             if(r){
22023                 r.collapse(true);
22024                 r.pasteHTML(text);
22025                 this.syncValue();
22026                 this.deferFocus();
22027             
22028             }
22029             return;
22030         }
22031         */
22032         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22033             this.win.focus();
22034             
22035             
22036             // from jquery ui (MIT licenced)
22037             var range, node;
22038             var win = this.win;
22039             
22040             if (win.getSelection && win.getSelection().getRangeAt) {
22041                 range = win.getSelection().getRangeAt(0);
22042                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22043                 range.insertNode(node);
22044             } else if (win.document.selection && win.document.selection.createRange) {
22045                 // no firefox support
22046                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22047                 win.document.selection.createRange().pasteHTML(txt);
22048             } else {
22049                 // no firefox support
22050                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22051                 this.execCmd('InsertHTML', txt);
22052             } 
22053             
22054             this.syncValue();
22055             
22056             this.deferFocus();
22057         }
22058     },
22059  // private
22060     mozKeyPress : function(e){
22061         if(e.ctrlKey){
22062             var c = e.getCharCode(), cmd;
22063           
22064             if(c > 0){
22065                 c = String.fromCharCode(c).toLowerCase();
22066                 switch(c){
22067                     case 'b':
22068                         cmd = 'bold';
22069                         break;
22070                     case 'i':
22071                         cmd = 'italic';
22072                         break;
22073                     
22074                     case 'u':
22075                         cmd = 'underline';
22076                         break;
22077                     
22078                     case 'v':
22079                         this.cleanUpPaste.defer(100, this);
22080                         return;
22081                         
22082                 }
22083                 if(cmd){
22084                     this.win.focus();
22085                     this.execCmd(cmd);
22086                     this.deferFocus();
22087                     e.preventDefault();
22088                 }
22089                 
22090             }
22091         }
22092     },
22093
22094     // private
22095     fixKeys : function(){ // load time branching for fastest keydown performance
22096         if(Roo.isIE){
22097             return function(e){
22098                 var k = e.getKey(), r;
22099                 if(k == e.TAB){
22100                     e.stopEvent();
22101                     r = this.doc.selection.createRange();
22102                     if(r){
22103                         r.collapse(true);
22104                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22105                         this.deferFocus();
22106                     }
22107                     return;
22108                 }
22109                 
22110                 if(k == e.ENTER){
22111                     r = this.doc.selection.createRange();
22112                     if(r){
22113                         var target = r.parentElement();
22114                         if(!target || target.tagName.toLowerCase() != 'li'){
22115                             e.stopEvent();
22116                             r.pasteHTML('<br />');
22117                             r.collapse(false);
22118                             r.select();
22119                         }
22120                     }
22121                 }
22122                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22123                     this.cleanUpPaste.defer(100, this);
22124                     return;
22125                 }
22126                 
22127                 
22128             };
22129         }else if(Roo.isOpera){
22130             return function(e){
22131                 var k = e.getKey();
22132                 if(k == e.TAB){
22133                     e.stopEvent();
22134                     this.win.focus();
22135                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22136                     this.deferFocus();
22137                 }
22138                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22139                     this.cleanUpPaste.defer(100, this);
22140                     return;
22141                 }
22142                 
22143             };
22144         }else if(Roo.isSafari){
22145             return function(e){
22146                 var k = e.getKey();
22147                 
22148                 if(k == e.TAB){
22149                     e.stopEvent();
22150                     this.execCmd('InsertText','\t');
22151                     this.deferFocus();
22152                     return;
22153                 }
22154                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22155                     this.cleanUpPaste.defer(100, this);
22156                     return;
22157                 }
22158                 
22159              };
22160         }
22161     }(),
22162     
22163     getAllAncestors: function()
22164     {
22165         var p = this.getSelectedNode();
22166         var a = [];
22167         if (!p) {
22168             a.push(p); // push blank onto stack..
22169             p = this.getParentElement();
22170         }
22171         
22172         
22173         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22174             a.push(p);
22175             p = p.parentNode;
22176         }
22177         a.push(this.doc.body);
22178         return a;
22179     },
22180     lastSel : false,
22181     lastSelNode : false,
22182     
22183     
22184     getSelection : function() 
22185     {
22186         this.assignDocWin();
22187         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22188     },
22189     
22190     getSelectedNode: function() 
22191     {
22192         // this may only work on Gecko!!!
22193         
22194         // should we cache this!!!!
22195         
22196         
22197         
22198          
22199         var range = this.createRange(this.getSelection()).cloneRange();
22200         
22201         if (Roo.isIE) {
22202             var parent = range.parentElement();
22203             while (true) {
22204                 var testRange = range.duplicate();
22205                 testRange.moveToElementText(parent);
22206                 if (testRange.inRange(range)) {
22207                     break;
22208                 }
22209                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22210                     break;
22211                 }
22212                 parent = parent.parentElement;
22213             }
22214             return parent;
22215         }
22216         
22217         // is ancestor a text element.
22218         var ac =  range.commonAncestorContainer;
22219         if (ac.nodeType == 3) {
22220             ac = ac.parentNode;
22221         }
22222         
22223         var ar = ac.childNodes;
22224          
22225         var nodes = [];
22226         var other_nodes = [];
22227         var has_other_nodes = false;
22228         for (var i=0;i<ar.length;i++) {
22229             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22230                 continue;
22231             }
22232             // fullly contained node.
22233             
22234             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22235                 nodes.push(ar[i]);
22236                 continue;
22237             }
22238             
22239             // probably selected..
22240             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22241                 other_nodes.push(ar[i]);
22242                 continue;
22243             }
22244             // outer..
22245             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22246                 continue;
22247             }
22248             
22249             
22250             has_other_nodes = true;
22251         }
22252         if (!nodes.length && other_nodes.length) {
22253             nodes= other_nodes;
22254         }
22255         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22256             return false;
22257         }
22258         
22259         return nodes[0];
22260     },
22261     createRange: function(sel)
22262     {
22263         // this has strange effects when using with 
22264         // top toolbar - not sure if it's a great idea.
22265         //this.editor.contentWindow.focus();
22266         if (typeof sel != "undefined") {
22267             try {
22268                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22269             } catch(e) {
22270                 return this.doc.createRange();
22271             }
22272         } else {
22273             return this.doc.createRange();
22274         }
22275     },
22276     getParentElement: function()
22277     {
22278         
22279         this.assignDocWin();
22280         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22281         
22282         var range = this.createRange(sel);
22283          
22284         try {
22285             var p = range.commonAncestorContainer;
22286             while (p.nodeType == 3) { // text node
22287                 p = p.parentNode;
22288             }
22289             return p;
22290         } catch (e) {
22291             return null;
22292         }
22293     
22294     },
22295     /***
22296      *
22297      * Range intersection.. the hard stuff...
22298      *  '-1' = before
22299      *  '0' = hits..
22300      *  '1' = after.
22301      *         [ -- selected range --- ]
22302      *   [fail]                        [fail]
22303      *
22304      *    basically..
22305      *      if end is before start or  hits it. fail.
22306      *      if start is after end or hits it fail.
22307      *
22308      *   if either hits (but other is outside. - then it's not 
22309      *   
22310      *    
22311      **/
22312     
22313     
22314     // @see http://www.thismuchiknow.co.uk/?p=64.
22315     rangeIntersectsNode : function(range, node)
22316     {
22317         var nodeRange = node.ownerDocument.createRange();
22318         try {
22319             nodeRange.selectNode(node);
22320         } catch (e) {
22321             nodeRange.selectNodeContents(node);
22322         }
22323     
22324         var rangeStartRange = range.cloneRange();
22325         rangeStartRange.collapse(true);
22326     
22327         var rangeEndRange = range.cloneRange();
22328         rangeEndRange.collapse(false);
22329     
22330         var nodeStartRange = nodeRange.cloneRange();
22331         nodeStartRange.collapse(true);
22332     
22333         var nodeEndRange = nodeRange.cloneRange();
22334         nodeEndRange.collapse(false);
22335     
22336         return rangeStartRange.compareBoundaryPoints(
22337                  Range.START_TO_START, nodeEndRange) == -1 &&
22338                rangeEndRange.compareBoundaryPoints(
22339                  Range.START_TO_START, nodeStartRange) == 1;
22340         
22341          
22342     },
22343     rangeCompareNode : function(range, node)
22344     {
22345         var nodeRange = node.ownerDocument.createRange();
22346         try {
22347             nodeRange.selectNode(node);
22348         } catch (e) {
22349             nodeRange.selectNodeContents(node);
22350         }
22351         
22352         
22353         range.collapse(true);
22354     
22355         nodeRange.collapse(true);
22356      
22357         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22358         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22359          
22360         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22361         
22362         var nodeIsBefore   =  ss == 1;
22363         var nodeIsAfter    = ee == -1;
22364         
22365         if (nodeIsBefore && nodeIsAfter) {
22366             return 0; // outer
22367         }
22368         if (!nodeIsBefore && nodeIsAfter) {
22369             return 1; //right trailed.
22370         }
22371         
22372         if (nodeIsBefore && !nodeIsAfter) {
22373             return 2;  // left trailed.
22374         }
22375         // fully contined.
22376         return 3;
22377     },
22378
22379     // private? - in a new class?
22380     cleanUpPaste :  function()
22381     {
22382         // cleans up the whole document..
22383         Roo.log('cleanuppaste');
22384         
22385         this.cleanUpChildren(this.doc.body);
22386         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22387         if (clean != this.doc.body.innerHTML) {
22388             this.doc.body.innerHTML = clean;
22389         }
22390         
22391     },
22392     
22393     cleanWordChars : function(input) {// change the chars to hex code
22394         var he = Roo.HtmlEditorCore;
22395         
22396         var output = input;
22397         Roo.each(he.swapCodes, function(sw) { 
22398             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22399             
22400             output = output.replace(swapper, sw[1]);
22401         });
22402         
22403         return output;
22404     },
22405     
22406     
22407     cleanUpChildren : function (n)
22408     {
22409         if (!n.childNodes.length) {
22410             return;
22411         }
22412         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22413            this.cleanUpChild(n.childNodes[i]);
22414         }
22415     },
22416     
22417     
22418         
22419     
22420     cleanUpChild : function (node)
22421     {
22422         var ed = this;
22423         //console.log(node);
22424         if (node.nodeName == "#text") {
22425             // clean up silly Windows -- stuff?
22426             return; 
22427         }
22428         if (node.nodeName == "#comment") {
22429             node.parentNode.removeChild(node);
22430             // clean up silly Windows -- stuff?
22431             return; 
22432         }
22433         var lcname = node.tagName.toLowerCase();
22434         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22435         // whitelist of tags..
22436         
22437         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22438             // remove node.
22439             node.parentNode.removeChild(node);
22440             return;
22441             
22442         }
22443         
22444         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22445         
22446         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22447         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22448         
22449         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22450         //    remove_keep_children = true;
22451         //}
22452         
22453         if (remove_keep_children) {
22454             this.cleanUpChildren(node);
22455             // inserts everything just before this node...
22456             while (node.childNodes.length) {
22457                 var cn = node.childNodes[0];
22458                 node.removeChild(cn);
22459                 node.parentNode.insertBefore(cn, node);
22460             }
22461             node.parentNode.removeChild(node);
22462             return;
22463         }
22464         
22465         if (!node.attributes || !node.attributes.length) {
22466             this.cleanUpChildren(node);
22467             return;
22468         }
22469         
22470         function cleanAttr(n,v)
22471         {
22472             
22473             if (v.match(/^\./) || v.match(/^\//)) {
22474                 return;
22475             }
22476             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22477                 return;
22478             }
22479             if (v.match(/^#/)) {
22480                 return;
22481             }
22482 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22483             node.removeAttribute(n);
22484             
22485         }
22486         
22487         var cwhite = this.cwhite;
22488         var cblack = this.cblack;
22489             
22490         function cleanStyle(n,v)
22491         {
22492             if (v.match(/expression/)) { //XSS?? should we even bother..
22493                 node.removeAttribute(n);
22494                 return;
22495             }
22496             
22497             var parts = v.split(/;/);
22498             var clean = [];
22499             
22500             Roo.each(parts, function(p) {
22501                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22502                 if (!p.length) {
22503                     return true;
22504                 }
22505                 var l = p.split(':').shift().replace(/\s+/g,'');
22506                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22507                 
22508                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22509 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22510                     //node.removeAttribute(n);
22511                     return true;
22512                 }
22513                 //Roo.log()
22514                 // only allow 'c whitelisted system attributes'
22515                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22516 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22517                     //node.removeAttribute(n);
22518                     return true;
22519                 }
22520                 
22521                 
22522                  
22523                 
22524                 clean.push(p);
22525                 return true;
22526             });
22527             if (clean.length) { 
22528                 node.setAttribute(n, clean.join(';'));
22529             } else {
22530                 node.removeAttribute(n);
22531             }
22532             
22533         }
22534         
22535         
22536         for (var i = node.attributes.length-1; i > -1 ; i--) {
22537             var a = node.attributes[i];
22538             //console.log(a);
22539             
22540             if (a.name.toLowerCase().substr(0,2)=='on')  {
22541                 node.removeAttribute(a.name);
22542                 continue;
22543             }
22544             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22545                 node.removeAttribute(a.name);
22546                 continue;
22547             }
22548             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22549                 cleanAttr(a.name,a.value); // fixme..
22550                 continue;
22551             }
22552             if (a.name == 'style') {
22553                 cleanStyle(a.name,a.value);
22554                 continue;
22555             }
22556             /// clean up MS crap..
22557             // tecnically this should be a list of valid class'es..
22558             
22559             
22560             if (a.name == 'class') {
22561                 if (a.value.match(/^Mso/)) {
22562                     node.className = '';
22563                 }
22564                 
22565                 if (a.value.match(/^body$/)) {
22566                     node.className = '';
22567                 }
22568                 continue;
22569             }
22570             
22571             // style cleanup!?
22572             // class cleanup?
22573             
22574         }
22575         
22576         
22577         this.cleanUpChildren(node);
22578         
22579         
22580     },
22581     
22582     /**
22583      * Clean up MS wordisms...
22584      */
22585     cleanWord : function(node)
22586     {
22587         
22588         
22589         if (!node) {
22590             this.cleanWord(this.doc.body);
22591             return;
22592         }
22593         if (node.nodeName == "#text") {
22594             // clean up silly Windows -- stuff?
22595             return; 
22596         }
22597         if (node.nodeName == "#comment") {
22598             node.parentNode.removeChild(node);
22599             // clean up silly Windows -- stuff?
22600             return; 
22601         }
22602         
22603         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22604             node.parentNode.removeChild(node);
22605             return;
22606         }
22607         
22608         // remove - but keep children..
22609         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22610             while (node.childNodes.length) {
22611                 var cn = node.childNodes[0];
22612                 node.removeChild(cn);
22613                 node.parentNode.insertBefore(cn, node);
22614             }
22615             node.parentNode.removeChild(node);
22616             this.iterateChildren(node, this.cleanWord);
22617             return;
22618         }
22619         // clean styles
22620         if (node.className.length) {
22621             
22622             var cn = node.className.split(/\W+/);
22623             var cna = [];
22624             Roo.each(cn, function(cls) {
22625                 if (cls.match(/Mso[a-zA-Z]+/)) {
22626                     return;
22627                 }
22628                 cna.push(cls);
22629             });
22630             node.className = cna.length ? cna.join(' ') : '';
22631             if (!cna.length) {
22632                 node.removeAttribute("class");
22633             }
22634         }
22635         
22636         if (node.hasAttribute("lang")) {
22637             node.removeAttribute("lang");
22638         }
22639         
22640         if (node.hasAttribute("style")) {
22641             
22642             var styles = node.getAttribute("style").split(";");
22643             var nstyle = [];
22644             Roo.each(styles, function(s) {
22645                 if (!s.match(/:/)) {
22646                     return;
22647                 }
22648                 var kv = s.split(":");
22649                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22650                     return;
22651                 }
22652                 // what ever is left... we allow.
22653                 nstyle.push(s);
22654             });
22655             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22656             if (!nstyle.length) {
22657                 node.removeAttribute('style');
22658             }
22659         }
22660         this.iterateChildren(node, this.cleanWord);
22661         
22662         
22663         
22664     },
22665     /**
22666      * iterateChildren of a Node, calling fn each time, using this as the scole..
22667      * @param {DomNode} node node to iterate children of.
22668      * @param {Function} fn method of this class to call on each item.
22669      */
22670     iterateChildren : function(node, fn)
22671     {
22672         if (!node.childNodes.length) {
22673                 return;
22674         }
22675         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22676            fn.call(this, node.childNodes[i])
22677         }
22678     },
22679     
22680     
22681     /**
22682      * cleanTableWidths.
22683      *
22684      * Quite often pasting from word etc.. results in tables with column and widths.
22685      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22686      *
22687      */
22688     cleanTableWidths : function(node)
22689     {
22690          
22691          
22692         if (!node) {
22693             this.cleanTableWidths(this.doc.body);
22694             return;
22695         }
22696         
22697         // ignore list...
22698         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22699             return; 
22700         }
22701         Roo.log(node.tagName);
22702         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22703             this.iterateChildren(node, this.cleanTableWidths);
22704             return;
22705         }
22706         if (node.hasAttribute('width')) {
22707             node.removeAttribute('width');
22708         }
22709         
22710          
22711         if (node.hasAttribute("style")) {
22712             // pretty basic...
22713             
22714             var styles = node.getAttribute("style").split(";");
22715             var nstyle = [];
22716             Roo.each(styles, function(s) {
22717                 if (!s.match(/:/)) {
22718                     return;
22719                 }
22720                 var kv = s.split(":");
22721                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22722                     return;
22723                 }
22724                 // what ever is left... we allow.
22725                 nstyle.push(s);
22726             });
22727             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22728             if (!nstyle.length) {
22729                 node.removeAttribute('style');
22730             }
22731         }
22732         
22733         this.iterateChildren(node, this.cleanTableWidths);
22734         
22735         
22736     },
22737     
22738     
22739     
22740     
22741     domToHTML : function(currentElement, depth, nopadtext) {
22742         
22743         depth = depth || 0;
22744         nopadtext = nopadtext || false;
22745     
22746         if (!currentElement) {
22747             return this.domToHTML(this.doc.body);
22748         }
22749         
22750         //Roo.log(currentElement);
22751         var j;
22752         var allText = false;
22753         var nodeName = currentElement.nodeName;
22754         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22755         
22756         if  (nodeName == '#text') {
22757             
22758             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22759         }
22760         
22761         
22762         var ret = '';
22763         if (nodeName != 'BODY') {
22764              
22765             var i = 0;
22766             // Prints the node tagName, such as <A>, <IMG>, etc
22767             if (tagName) {
22768                 var attr = [];
22769                 for(i = 0; i < currentElement.attributes.length;i++) {
22770                     // quoting?
22771                     var aname = currentElement.attributes.item(i).name;
22772                     if (!currentElement.attributes.item(i).value.length) {
22773                         continue;
22774                     }
22775                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22776                 }
22777                 
22778                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22779             } 
22780             else {
22781                 
22782                 // eack
22783             }
22784         } else {
22785             tagName = false;
22786         }
22787         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22788             return ret;
22789         }
22790         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22791             nopadtext = true;
22792         }
22793         
22794         
22795         // Traverse the tree
22796         i = 0;
22797         var currentElementChild = currentElement.childNodes.item(i);
22798         var allText = true;
22799         var innerHTML  = '';
22800         lastnode = '';
22801         while (currentElementChild) {
22802             // Formatting code (indent the tree so it looks nice on the screen)
22803             var nopad = nopadtext;
22804             if (lastnode == 'SPAN') {
22805                 nopad  = true;
22806             }
22807             // text
22808             if  (currentElementChild.nodeName == '#text') {
22809                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22810                 toadd = nopadtext ? toadd : toadd.trim();
22811                 if (!nopad && toadd.length > 80) {
22812                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22813                 }
22814                 innerHTML  += toadd;
22815                 
22816                 i++;
22817                 currentElementChild = currentElement.childNodes.item(i);
22818                 lastNode = '';
22819                 continue;
22820             }
22821             allText = false;
22822             
22823             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22824                 
22825             // Recursively traverse the tree structure of the child node
22826             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22827             lastnode = currentElementChild.nodeName;
22828             i++;
22829             currentElementChild=currentElement.childNodes.item(i);
22830         }
22831         
22832         ret += innerHTML;
22833         
22834         if (!allText) {
22835                 // The remaining code is mostly for formatting the tree
22836             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22837         }
22838         
22839         
22840         if (tagName) {
22841             ret+= "</"+tagName+">";
22842         }
22843         return ret;
22844         
22845     },
22846         
22847     applyBlacklists : function()
22848     {
22849         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22850         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22851         
22852         this.white = [];
22853         this.black = [];
22854         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22855             if (b.indexOf(tag) > -1) {
22856                 return;
22857             }
22858             this.white.push(tag);
22859             
22860         }, this);
22861         
22862         Roo.each(w, function(tag) {
22863             if (b.indexOf(tag) > -1) {
22864                 return;
22865             }
22866             if (this.white.indexOf(tag) > -1) {
22867                 return;
22868             }
22869             this.white.push(tag);
22870             
22871         }, this);
22872         
22873         
22874         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22875             if (w.indexOf(tag) > -1) {
22876                 return;
22877             }
22878             this.black.push(tag);
22879             
22880         }, this);
22881         
22882         Roo.each(b, function(tag) {
22883             if (w.indexOf(tag) > -1) {
22884                 return;
22885             }
22886             if (this.black.indexOf(tag) > -1) {
22887                 return;
22888             }
22889             this.black.push(tag);
22890             
22891         }, this);
22892         
22893         
22894         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22895         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22896         
22897         this.cwhite = [];
22898         this.cblack = [];
22899         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22900             if (b.indexOf(tag) > -1) {
22901                 return;
22902             }
22903             this.cwhite.push(tag);
22904             
22905         }, this);
22906         
22907         Roo.each(w, function(tag) {
22908             if (b.indexOf(tag) > -1) {
22909                 return;
22910             }
22911             if (this.cwhite.indexOf(tag) > -1) {
22912                 return;
22913             }
22914             this.cwhite.push(tag);
22915             
22916         }, this);
22917         
22918         
22919         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22920             if (w.indexOf(tag) > -1) {
22921                 return;
22922             }
22923             this.cblack.push(tag);
22924             
22925         }, this);
22926         
22927         Roo.each(b, function(tag) {
22928             if (w.indexOf(tag) > -1) {
22929                 return;
22930             }
22931             if (this.cblack.indexOf(tag) > -1) {
22932                 return;
22933             }
22934             this.cblack.push(tag);
22935             
22936         }, this);
22937     },
22938     
22939     setStylesheets : function(stylesheets)
22940     {
22941         if(typeof(stylesheets) == 'string'){
22942             Roo.get(this.iframe.contentDocument.head).createChild({
22943                 tag : 'link',
22944                 rel : 'stylesheet',
22945                 type : 'text/css',
22946                 href : stylesheets
22947             });
22948             
22949             return;
22950         }
22951         var _this = this;
22952      
22953         Roo.each(stylesheets, function(s) {
22954             if(!s.length){
22955                 return;
22956             }
22957             
22958             Roo.get(_this.iframe.contentDocument.head).createChild({
22959                 tag : 'link',
22960                 rel : 'stylesheet',
22961                 type : 'text/css',
22962                 href : s
22963             });
22964         });
22965
22966         
22967     },
22968     
22969     removeStylesheets : function()
22970     {
22971         var _this = this;
22972         
22973         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22974             s.remove();
22975         });
22976     },
22977     
22978     setStyle : function(style)
22979     {
22980         Roo.get(this.iframe.contentDocument.head).createChild({
22981             tag : 'style',
22982             type : 'text/css',
22983             html : style
22984         });
22985
22986         return;
22987     }
22988     
22989     // hide stuff that is not compatible
22990     /**
22991      * @event blur
22992      * @hide
22993      */
22994     /**
22995      * @event change
22996      * @hide
22997      */
22998     /**
22999      * @event focus
23000      * @hide
23001      */
23002     /**
23003      * @event specialkey
23004      * @hide
23005      */
23006     /**
23007      * @cfg {String} fieldClass @hide
23008      */
23009     /**
23010      * @cfg {String} focusClass @hide
23011      */
23012     /**
23013      * @cfg {String} autoCreate @hide
23014      */
23015     /**
23016      * @cfg {String} inputType @hide
23017      */
23018     /**
23019      * @cfg {String} invalidClass @hide
23020      */
23021     /**
23022      * @cfg {String} invalidText @hide
23023      */
23024     /**
23025      * @cfg {String} msgFx @hide
23026      */
23027     /**
23028      * @cfg {String} validateOnBlur @hide
23029      */
23030 });
23031
23032 Roo.HtmlEditorCore.white = [
23033         'area', 'br', 'img', 'input', 'hr', 'wbr',
23034         
23035        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23036        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23037        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23038        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23039        'table',   'ul',         'xmp', 
23040        
23041        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23042       'thead',   'tr', 
23043      
23044       'dir', 'menu', 'ol', 'ul', 'dl',
23045        
23046       'embed',  'object'
23047 ];
23048
23049
23050 Roo.HtmlEditorCore.black = [
23051     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23052         'applet', // 
23053         'base',   'basefont', 'bgsound', 'blink',  'body', 
23054         'frame',  'frameset', 'head',    'html',   'ilayer', 
23055         'iframe', 'layer',  'link',     'meta',    'object',   
23056         'script', 'style' ,'title',  'xml' // clean later..
23057 ];
23058 Roo.HtmlEditorCore.clean = [
23059     'script', 'style', 'title', 'xml'
23060 ];
23061 Roo.HtmlEditorCore.remove = [
23062     'font'
23063 ];
23064 // attributes..
23065
23066 Roo.HtmlEditorCore.ablack = [
23067     'on'
23068 ];
23069     
23070 Roo.HtmlEditorCore.aclean = [ 
23071     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23072 ];
23073
23074 // protocols..
23075 Roo.HtmlEditorCore.pwhite= [
23076         'http',  'https',  'mailto'
23077 ];
23078
23079 // white listed style attributes.
23080 Roo.HtmlEditorCore.cwhite= [
23081       //  'text-align', /// default is to allow most things..
23082       
23083          
23084 //        'font-size'//??
23085 ];
23086
23087 // black listed style attributes.
23088 Roo.HtmlEditorCore.cblack= [
23089       //  'font-size' -- this can be set by the project 
23090 ];
23091
23092
23093 Roo.HtmlEditorCore.swapCodes   =[ 
23094     [    8211, "--" ], 
23095     [    8212, "--" ], 
23096     [    8216,  "'" ],  
23097     [    8217, "'" ],  
23098     [    8220, '"' ],  
23099     [    8221, '"' ],  
23100     [    8226, "*" ],  
23101     [    8230, "..." ]
23102 ]; 
23103
23104     /*
23105  * - LGPL
23106  *
23107  * HtmlEditor
23108  * 
23109  */
23110
23111 /**
23112  * @class Roo.bootstrap.HtmlEditor
23113  * @extends Roo.bootstrap.TextArea
23114  * Bootstrap HtmlEditor class
23115
23116  * @constructor
23117  * Create a new HtmlEditor
23118  * @param {Object} config The config object
23119  */
23120
23121 Roo.bootstrap.HtmlEditor = function(config){
23122     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23123     if (!this.toolbars) {
23124         this.toolbars = [];
23125     }
23126     
23127     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23128     this.addEvents({
23129             /**
23130              * @event initialize
23131              * Fires when the editor is fully initialized (including the iframe)
23132              * @param {HtmlEditor} this
23133              */
23134             initialize: true,
23135             /**
23136              * @event activate
23137              * Fires when the editor is first receives the focus. Any insertion must wait
23138              * until after this event.
23139              * @param {HtmlEditor} this
23140              */
23141             activate: true,
23142              /**
23143              * @event beforesync
23144              * Fires before the textarea is updated with content from the editor iframe. Return false
23145              * to cancel the sync.
23146              * @param {HtmlEditor} this
23147              * @param {String} html
23148              */
23149             beforesync: true,
23150              /**
23151              * @event beforepush
23152              * Fires before the iframe editor is updated with content from the textarea. Return false
23153              * to cancel the push.
23154              * @param {HtmlEditor} this
23155              * @param {String} html
23156              */
23157             beforepush: true,
23158              /**
23159              * @event sync
23160              * Fires when the textarea is updated with content from the editor iframe.
23161              * @param {HtmlEditor} this
23162              * @param {String} html
23163              */
23164             sync: true,
23165              /**
23166              * @event push
23167              * Fires when the iframe editor is updated with content from the textarea.
23168              * @param {HtmlEditor} this
23169              * @param {String} html
23170              */
23171             push: true,
23172              /**
23173              * @event editmodechange
23174              * Fires when the editor switches edit modes
23175              * @param {HtmlEditor} this
23176              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23177              */
23178             editmodechange: true,
23179             /**
23180              * @event editorevent
23181              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23182              * @param {HtmlEditor} this
23183              */
23184             editorevent: true,
23185             /**
23186              * @event firstfocus
23187              * Fires when on first focus - needed by toolbars..
23188              * @param {HtmlEditor} this
23189              */
23190             firstfocus: true,
23191             /**
23192              * @event autosave
23193              * Auto save the htmlEditor value as a file into Events
23194              * @param {HtmlEditor} this
23195              */
23196             autosave: true,
23197             /**
23198              * @event savedpreview
23199              * preview the saved version of htmlEditor
23200              * @param {HtmlEditor} this
23201              */
23202             savedpreview: true
23203         });
23204 };
23205
23206
23207 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23208     
23209     
23210       /**
23211      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23212      */
23213     toolbars : false,
23214     
23215      /**
23216     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23217     */
23218     btns : [],
23219    
23220      /**
23221      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23222      *                        Roo.resizable.
23223      */
23224     resizable : false,
23225      /**
23226      * @cfg {Number} height (in pixels)
23227      */   
23228     height: 300,
23229    /**
23230      * @cfg {Number} width (in pixels)
23231      */   
23232     width: false,
23233     
23234     /**
23235      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23236      * 
23237      */
23238     stylesheets: false,
23239     
23240     // id of frame..
23241     frameId: false,
23242     
23243     // private properties
23244     validationEvent : false,
23245     deferHeight: true,
23246     initialized : false,
23247     activated : false,
23248     
23249     onFocus : Roo.emptyFn,
23250     iframePad:3,
23251     hideMode:'offsets',
23252     
23253     tbContainer : false,
23254     
23255     bodyCls : '',
23256     
23257     toolbarContainer :function() {
23258         return this.wrap.select('.x-html-editor-tb',true).first();
23259     },
23260
23261     /**
23262      * Protected method that will not generally be called directly. It
23263      * is called when the editor creates its toolbar. Override this method if you need to
23264      * add custom toolbar buttons.
23265      * @param {HtmlEditor} editor
23266      */
23267     createToolbar : function(){
23268         Roo.log('renewing');
23269         Roo.log("create toolbars");
23270         
23271         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23272         this.toolbars[0].render(this.toolbarContainer());
23273         
23274         return;
23275         
23276 //        if (!editor.toolbars || !editor.toolbars.length) {
23277 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23278 //        }
23279 //        
23280 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23281 //            editor.toolbars[i] = Roo.factory(
23282 //                    typeof(editor.toolbars[i]) == 'string' ?
23283 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23284 //                Roo.bootstrap.HtmlEditor);
23285 //            editor.toolbars[i].init(editor);
23286 //        }
23287     },
23288
23289      
23290     // private
23291     onRender : function(ct, position)
23292     {
23293        // Roo.log("Call onRender: " + this.xtype);
23294         var _t = this;
23295         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23296       
23297         this.wrap = this.inputEl().wrap({
23298             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23299         });
23300         
23301         this.editorcore.onRender(ct, position);
23302          
23303         if (this.resizable) {
23304             this.resizeEl = new Roo.Resizable(this.wrap, {
23305                 pinned : true,
23306                 wrap: true,
23307                 dynamic : true,
23308                 minHeight : this.height,
23309                 height: this.height,
23310                 handles : this.resizable,
23311                 width: this.width,
23312                 listeners : {
23313                     resize : function(r, w, h) {
23314                         _t.onResize(w,h); // -something
23315                     }
23316                 }
23317             });
23318             
23319         }
23320         this.createToolbar(this);
23321        
23322         
23323         if(!this.width && this.resizable){
23324             this.setSize(this.wrap.getSize());
23325         }
23326         if (this.resizeEl) {
23327             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23328             // should trigger onReize..
23329         }
23330         
23331     },
23332
23333     // private
23334     onResize : function(w, h)
23335     {
23336         Roo.log('resize: ' +w + ',' + h );
23337         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23338         var ew = false;
23339         var eh = false;
23340         
23341         if(this.inputEl() ){
23342             if(typeof w == 'number'){
23343                 var aw = w - this.wrap.getFrameWidth('lr');
23344                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23345                 ew = aw;
23346             }
23347             if(typeof h == 'number'){
23348                  var tbh = -11;  // fixme it needs to tool bar size!
23349                 for (var i =0; i < this.toolbars.length;i++) {
23350                     // fixme - ask toolbars for heights?
23351                     tbh += this.toolbars[i].el.getHeight();
23352                     //if (this.toolbars[i].footer) {
23353                     //    tbh += this.toolbars[i].footer.el.getHeight();
23354                     //}
23355                 }
23356               
23357                 
23358                 
23359                 
23360                 
23361                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23362                 ah -= 5; // knock a few pixes off for look..
23363                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23364                 var eh = ah;
23365             }
23366         }
23367         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23368         this.editorcore.onResize(ew,eh);
23369         
23370     },
23371
23372     /**
23373      * Toggles the editor between standard and source edit mode.
23374      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23375      */
23376     toggleSourceEdit : function(sourceEditMode)
23377     {
23378         this.editorcore.toggleSourceEdit(sourceEditMode);
23379         
23380         if(this.editorcore.sourceEditMode){
23381             Roo.log('editor - showing textarea');
23382             
23383 //            Roo.log('in');
23384 //            Roo.log(this.syncValue());
23385             this.syncValue();
23386             this.inputEl().removeClass(['hide', 'x-hidden']);
23387             this.inputEl().dom.removeAttribute('tabIndex');
23388             this.inputEl().focus();
23389         }else{
23390             Roo.log('editor - hiding textarea');
23391 //            Roo.log('out')
23392 //            Roo.log(this.pushValue()); 
23393             this.pushValue();
23394             
23395             this.inputEl().addClass(['hide', 'x-hidden']);
23396             this.inputEl().dom.setAttribute('tabIndex', -1);
23397             //this.deferFocus();
23398         }
23399          
23400         if(this.resizable){
23401             this.setSize(this.wrap.getSize());
23402         }
23403         
23404         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23405     },
23406  
23407     // private (for BoxComponent)
23408     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23409
23410     // private (for BoxComponent)
23411     getResizeEl : function(){
23412         return this.wrap;
23413     },
23414
23415     // private (for BoxComponent)
23416     getPositionEl : function(){
23417         return this.wrap;
23418     },
23419
23420     // private
23421     initEvents : function(){
23422         this.originalValue = this.getValue();
23423     },
23424
23425 //    /**
23426 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23427 //     * @method
23428 //     */
23429 //    markInvalid : Roo.emptyFn,
23430 //    /**
23431 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23432 //     * @method
23433 //     */
23434 //    clearInvalid : Roo.emptyFn,
23435
23436     setValue : function(v){
23437         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23438         this.editorcore.pushValue();
23439     },
23440
23441      
23442     // private
23443     deferFocus : function(){
23444         this.focus.defer(10, this);
23445     },
23446
23447     // doc'ed in Field
23448     focus : function(){
23449         this.editorcore.focus();
23450         
23451     },
23452       
23453
23454     // private
23455     onDestroy : function(){
23456         
23457         
23458         
23459         if(this.rendered){
23460             
23461             for (var i =0; i < this.toolbars.length;i++) {
23462                 // fixme - ask toolbars for heights?
23463                 this.toolbars[i].onDestroy();
23464             }
23465             
23466             this.wrap.dom.innerHTML = '';
23467             this.wrap.remove();
23468         }
23469     },
23470
23471     // private
23472     onFirstFocus : function(){
23473         //Roo.log("onFirstFocus");
23474         this.editorcore.onFirstFocus();
23475          for (var i =0; i < this.toolbars.length;i++) {
23476             this.toolbars[i].onFirstFocus();
23477         }
23478         
23479     },
23480     
23481     // private
23482     syncValue : function()
23483     {   
23484         this.editorcore.syncValue();
23485     },
23486     
23487     pushValue : function()
23488     {   
23489         this.editorcore.pushValue();
23490     }
23491      
23492     
23493     // hide stuff that is not compatible
23494     /**
23495      * @event blur
23496      * @hide
23497      */
23498     /**
23499      * @event change
23500      * @hide
23501      */
23502     /**
23503      * @event focus
23504      * @hide
23505      */
23506     /**
23507      * @event specialkey
23508      * @hide
23509      */
23510     /**
23511      * @cfg {String} fieldClass @hide
23512      */
23513     /**
23514      * @cfg {String} focusClass @hide
23515      */
23516     /**
23517      * @cfg {String} autoCreate @hide
23518      */
23519     /**
23520      * @cfg {String} inputType @hide
23521      */
23522     /**
23523      * @cfg {String} invalidClass @hide
23524      */
23525     /**
23526      * @cfg {String} invalidText @hide
23527      */
23528     /**
23529      * @cfg {String} msgFx @hide
23530      */
23531     /**
23532      * @cfg {String} validateOnBlur @hide
23533      */
23534 });
23535  
23536     
23537    
23538    
23539    
23540       
23541 Roo.namespace('Roo.bootstrap.htmleditor');
23542 /**
23543  * @class Roo.bootstrap.HtmlEditorToolbar1
23544  * Basic Toolbar
23545  * 
23546  * Usage:
23547  *
23548  new Roo.bootstrap.HtmlEditor({
23549     ....
23550     toolbars : [
23551         new Roo.bootstrap.HtmlEditorToolbar1({
23552             disable : { fonts: 1 , format: 1, ..., ... , ...],
23553             btns : [ .... ]
23554         })
23555     }
23556      
23557  * 
23558  * @cfg {Object} disable List of elements to disable..
23559  * @cfg {Array} btns List of additional buttons.
23560  * 
23561  * 
23562  * NEEDS Extra CSS? 
23563  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23564  */
23565  
23566 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23567 {
23568     
23569     Roo.apply(this, config);
23570     
23571     // default disabled, based on 'good practice'..
23572     this.disable = this.disable || {};
23573     Roo.applyIf(this.disable, {
23574         fontSize : true,
23575         colors : true,
23576         specialElements : true
23577     });
23578     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23579     
23580     this.editor = config.editor;
23581     this.editorcore = config.editor.editorcore;
23582     
23583     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23584     
23585     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23586     // dont call parent... till later.
23587 }
23588 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23589      
23590     bar : true,
23591     
23592     editor : false,
23593     editorcore : false,
23594     
23595     
23596     formats : [
23597         "p" ,  
23598         "h1","h2","h3","h4","h5","h6", 
23599         "pre", "code", 
23600         "abbr", "acronym", "address", "cite", "samp", "var",
23601         'div','span'
23602     ],
23603     
23604     onRender : function(ct, position)
23605     {
23606        // Roo.log("Call onRender: " + this.xtype);
23607         
23608        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23609        Roo.log(this.el);
23610        this.el.dom.style.marginBottom = '0';
23611        var _this = this;
23612        var editorcore = this.editorcore;
23613        var editor= this.editor;
23614        
23615        var children = [];
23616        var btn = function(id,cmd , toggle, handler, html){
23617        
23618             var  event = toggle ? 'toggle' : 'click';
23619        
23620             var a = {
23621                 size : 'sm',
23622                 xtype: 'Button',
23623                 xns: Roo.bootstrap,
23624                 glyphicon : id,
23625                 cmd : id || cmd,
23626                 enableToggle:toggle !== false,
23627                 html : html || '',
23628                 pressed : toggle ? false : null,
23629                 listeners : {}
23630             };
23631             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23632                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23633             };
23634             children.push(a);
23635             return a;
23636        }
23637        
23638     //    var cb_box = function...
23639         
23640         var style = {
23641                 xtype: 'Button',
23642                 size : 'sm',
23643                 xns: Roo.bootstrap,
23644                 glyphicon : 'font',
23645                 //html : 'submit'
23646                 menu : {
23647                     xtype: 'Menu',
23648                     xns: Roo.bootstrap,
23649                     items:  []
23650                 }
23651         };
23652         Roo.each(this.formats, function(f) {
23653             style.menu.items.push({
23654                 xtype :'MenuItem',
23655                 xns: Roo.bootstrap,
23656                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23657                 tagname : f,
23658                 listeners : {
23659                     click : function()
23660                     {
23661                         editorcore.insertTag(this.tagname);
23662                         editor.focus();
23663                     }
23664                 }
23665                 
23666             });
23667         });
23668         children.push(style);   
23669         
23670         btn('bold',false,true);
23671         btn('italic',false,true);
23672         btn('align-left', 'justifyleft',true);
23673         btn('align-center', 'justifycenter',true);
23674         btn('align-right' , 'justifyright',true);
23675         btn('link', false, false, function(btn) {
23676             //Roo.log("create link?");
23677             var url = prompt(this.createLinkText, this.defaultLinkValue);
23678             if(url && url != 'http:/'+'/'){
23679                 this.editorcore.relayCmd('createlink', url);
23680             }
23681         }),
23682         btn('list','insertunorderedlist',true);
23683         btn('pencil', false,true, function(btn){
23684                 Roo.log(this);
23685                 this.toggleSourceEdit(btn.pressed);
23686         });
23687         
23688         if (this.editor.btns.length > 0) {
23689             for (var i = 0; i<this.editor.btns.length; i++) {
23690                 children.push(this.editor.btns[i]);
23691             }
23692         }
23693         
23694         /*
23695         var cog = {
23696                 xtype: 'Button',
23697                 size : 'sm',
23698                 xns: Roo.bootstrap,
23699                 glyphicon : 'cog',
23700                 //html : 'submit'
23701                 menu : {
23702                     xtype: 'Menu',
23703                     xns: Roo.bootstrap,
23704                     items:  []
23705                 }
23706         };
23707         
23708         cog.menu.items.push({
23709             xtype :'MenuItem',
23710             xns: Roo.bootstrap,
23711             html : Clean styles,
23712             tagname : f,
23713             listeners : {
23714                 click : function()
23715                 {
23716                     editorcore.insertTag(this.tagname);
23717                     editor.focus();
23718                 }
23719             }
23720             
23721         });
23722        */
23723         
23724          
23725        this.xtype = 'NavSimplebar';
23726         
23727         for(var i=0;i< children.length;i++) {
23728             
23729             this.buttons.add(this.addxtypeChild(children[i]));
23730             
23731         }
23732         
23733         editor.on('editorevent', this.updateToolbar, this);
23734     },
23735     onBtnClick : function(id)
23736     {
23737        this.editorcore.relayCmd(id);
23738        this.editorcore.focus();
23739     },
23740     
23741     /**
23742      * Protected method that will not generally be called directly. It triggers
23743      * a toolbar update by reading the markup state of the current selection in the editor.
23744      */
23745     updateToolbar: function(){
23746
23747         if(!this.editorcore.activated){
23748             this.editor.onFirstFocus(); // is this neeed?
23749             return;
23750         }
23751
23752         var btns = this.buttons; 
23753         var doc = this.editorcore.doc;
23754         btns.get('bold').setActive(doc.queryCommandState('bold'));
23755         btns.get('italic').setActive(doc.queryCommandState('italic'));
23756         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23757         
23758         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23759         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23760         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23761         
23762         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23763         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23764          /*
23765         
23766         var ans = this.editorcore.getAllAncestors();
23767         if (this.formatCombo) {
23768             
23769             
23770             var store = this.formatCombo.store;
23771             this.formatCombo.setValue("");
23772             for (var i =0; i < ans.length;i++) {
23773                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23774                     // select it..
23775                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23776                     break;
23777                 }
23778             }
23779         }
23780         
23781         
23782         
23783         // hides menus... - so this cant be on a menu...
23784         Roo.bootstrap.MenuMgr.hideAll();
23785         */
23786         Roo.bootstrap.MenuMgr.hideAll();
23787         //this.editorsyncValue();
23788     },
23789     onFirstFocus: function() {
23790         this.buttons.each(function(item){
23791            item.enable();
23792         });
23793     },
23794     toggleSourceEdit : function(sourceEditMode){
23795         
23796           
23797         if(sourceEditMode){
23798             Roo.log("disabling buttons");
23799            this.buttons.each( function(item){
23800                 if(item.cmd != 'pencil'){
23801                     item.disable();
23802                 }
23803             });
23804           
23805         }else{
23806             Roo.log("enabling buttons");
23807             if(this.editorcore.initialized){
23808                 this.buttons.each( function(item){
23809                     item.enable();
23810                 });
23811             }
23812             
23813         }
23814         Roo.log("calling toggole on editor");
23815         // tell the editor that it's been pressed..
23816         this.editor.toggleSourceEdit(sourceEditMode);
23817        
23818     }
23819 });
23820
23821
23822
23823
23824
23825 /**
23826  * @class Roo.bootstrap.Table.AbstractSelectionModel
23827  * @extends Roo.util.Observable
23828  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23829  * implemented by descendant classes.  This class should not be directly instantiated.
23830  * @constructor
23831  */
23832 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23833     this.locked = false;
23834     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23835 };
23836
23837
23838 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23839     /** @ignore Called by the grid automatically. Do not call directly. */
23840     init : function(grid){
23841         this.grid = grid;
23842         this.initEvents();
23843     },
23844
23845     /**
23846      * Locks the selections.
23847      */
23848     lock : function(){
23849         this.locked = true;
23850     },
23851
23852     /**
23853      * Unlocks the selections.
23854      */
23855     unlock : function(){
23856         this.locked = false;
23857     },
23858
23859     /**
23860      * Returns true if the selections are locked.
23861      * @return {Boolean}
23862      */
23863     isLocked : function(){
23864         return this.locked;
23865     }
23866 });
23867 /**
23868  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23869  * @class Roo.bootstrap.Table.RowSelectionModel
23870  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23871  * It supports multiple selections and keyboard selection/navigation. 
23872  * @constructor
23873  * @param {Object} config
23874  */
23875
23876 Roo.bootstrap.Table.RowSelectionModel = function(config){
23877     Roo.apply(this, config);
23878     this.selections = new Roo.util.MixedCollection(false, function(o){
23879         return o.id;
23880     });
23881
23882     this.last = false;
23883     this.lastActive = false;
23884
23885     this.addEvents({
23886         /**
23887              * @event selectionchange
23888              * Fires when the selection changes
23889              * @param {SelectionModel} this
23890              */
23891             "selectionchange" : true,
23892         /**
23893              * @event afterselectionchange
23894              * Fires after the selection changes (eg. by key press or clicking)
23895              * @param {SelectionModel} this
23896              */
23897             "afterselectionchange" : true,
23898         /**
23899              * @event beforerowselect
23900              * Fires when a row is selected being selected, return false to cancel.
23901              * @param {SelectionModel} this
23902              * @param {Number} rowIndex The selected index
23903              * @param {Boolean} keepExisting False if other selections will be cleared
23904              */
23905             "beforerowselect" : true,
23906         /**
23907              * @event rowselect
23908              * Fires when a row is selected.
23909              * @param {SelectionModel} this
23910              * @param {Number} rowIndex The selected index
23911              * @param {Roo.data.Record} r The record
23912              */
23913             "rowselect" : true,
23914         /**
23915              * @event rowdeselect
23916              * Fires when a row is deselected.
23917              * @param {SelectionModel} this
23918              * @param {Number} rowIndex The selected index
23919              */
23920         "rowdeselect" : true
23921     });
23922     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23923     this.locked = false;
23924  };
23925
23926 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23927     /**
23928      * @cfg {Boolean} singleSelect
23929      * True to allow selection of only one row at a time (defaults to false)
23930      */
23931     singleSelect : false,
23932
23933     // private
23934     initEvents : function()
23935     {
23936
23937         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23938         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23939         //}else{ // allow click to work like normal
23940          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23941         //}
23942         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23943         this.grid.on("rowclick", this.handleMouseDown, this);
23944         
23945         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23946             "up" : function(e){
23947                 if(!e.shiftKey){
23948                     this.selectPrevious(e.shiftKey);
23949                 }else if(this.last !== false && this.lastActive !== false){
23950                     var last = this.last;
23951                     this.selectRange(this.last,  this.lastActive-1);
23952                     this.grid.getView().focusRow(this.lastActive);
23953                     if(last !== false){
23954                         this.last = last;
23955                     }
23956                 }else{
23957                     this.selectFirstRow();
23958                 }
23959                 this.fireEvent("afterselectionchange", this);
23960             },
23961             "down" : function(e){
23962                 if(!e.shiftKey){
23963                     this.selectNext(e.shiftKey);
23964                 }else if(this.last !== false && this.lastActive !== false){
23965                     var last = this.last;
23966                     this.selectRange(this.last,  this.lastActive+1);
23967                     this.grid.getView().focusRow(this.lastActive);
23968                     if(last !== false){
23969                         this.last = last;
23970                     }
23971                 }else{
23972                     this.selectFirstRow();
23973                 }
23974                 this.fireEvent("afterselectionchange", this);
23975             },
23976             scope: this
23977         });
23978         this.grid.store.on('load', function(){
23979             this.selections.clear();
23980         },this);
23981         /*
23982         var view = this.grid.view;
23983         view.on("refresh", this.onRefresh, this);
23984         view.on("rowupdated", this.onRowUpdated, this);
23985         view.on("rowremoved", this.onRemove, this);
23986         */
23987     },
23988
23989     // private
23990     onRefresh : function()
23991     {
23992         var ds = this.grid.store, i, v = this.grid.view;
23993         var s = this.selections;
23994         s.each(function(r){
23995             if((i = ds.indexOfId(r.id)) != -1){
23996                 v.onRowSelect(i);
23997             }else{
23998                 s.remove(r);
23999             }
24000         });
24001     },
24002
24003     // private
24004     onRemove : function(v, index, r){
24005         this.selections.remove(r);
24006     },
24007
24008     // private
24009     onRowUpdated : function(v, index, r){
24010         if(this.isSelected(r)){
24011             v.onRowSelect(index);
24012         }
24013     },
24014
24015     /**
24016      * Select records.
24017      * @param {Array} records The records to select
24018      * @param {Boolean} keepExisting (optional) True to keep existing selections
24019      */
24020     selectRecords : function(records, keepExisting)
24021     {
24022         if(!keepExisting){
24023             this.clearSelections();
24024         }
24025             var ds = this.grid.store;
24026         for(var i = 0, len = records.length; i < len; i++){
24027             this.selectRow(ds.indexOf(records[i]), true);
24028         }
24029     },
24030
24031     /**
24032      * Gets the number of selected rows.
24033      * @return {Number}
24034      */
24035     getCount : function(){
24036         return this.selections.length;
24037     },
24038
24039     /**
24040      * Selects the first row in the grid.
24041      */
24042     selectFirstRow : function(){
24043         this.selectRow(0);
24044     },
24045
24046     /**
24047      * Select the last row.
24048      * @param {Boolean} keepExisting (optional) True to keep existing selections
24049      */
24050     selectLastRow : function(keepExisting){
24051         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24052         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24053     },
24054
24055     /**
24056      * Selects the row immediately following the last selected row.
24057      * @param {Boolean} keepExisting (optional) True to keep existing selections
24058      */
24059     selectNext : function(keepExisting)
24060     {
24061             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24062             this.selectRow(this.last+1, keepExisting);
24063             this.grid.getView().focusRow(this.last);
24064         }
24065     },
24066
24067     /**
24068      * Selects the row that precedes the last selected row.
24069      * @param {Boolean} keepExisting (optional) True to keep existing selections
24070      */
24071     selectPrevious : function(keepExisting){
24072         if(this.last){
24073             this.selectRow(this.last-1, keepExisting);
24074             this.grid.getView().focusRow(this.last);
24075         }
24076     },
24077
24078     /**
24079      * Returns the selected records
24080      * @return {Array} Array of selected records
24081      */
24082     getSelections : function(){
24083         return [].concat(this.selections.items);
24084     },
24085
24086     /**
24087      * Returns the first selected record.
24088      * @return {Record}
24089      */
24090     getSelected : function(){
24091         return this.selections.itemAt(0);
24092     },
24093
24094
24095     /**
24096      * Clears all selections.
24097      */
24098     clearSelections : function(fast)
24099     {
24100         if(this.locked) {
24101             return;
24102         }
24103         if(fast !== true){
24104                 var ds = this.grid.store;
24105             var s = this.selections;
24106             s.each(function(r){
24107                 this.deselectRow(ds.indexOfId(r.id));
24108             }, this);
24109             s.clear();
24110         }else{
24111             this.selections.clear();
24112         }
24113         this.last = false;
24114     },
24115
24116
24117     /**
24118      * Selects all rows.
24119      */
24120     selectAll : function(){
24121         if(this.locked) {
24122             return;
24123         }
24124         this.selections.clear();
24125         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24126             this.selectRow(i, true);
24127         }
24128     },
24129
24130     /**
24131      * Returns True if there is a selection.
24132      * @return {Boolean}
24133      */
24134     hasSelection : function(){
24135         return this.selections.length > 0;
24136     },
24137
24138     /**
24139      * Returns True if the specified row is selected.
24140      * @param {Number/Record} record The record or index of the record to check
24141      * @return {Boolean}
24142      */
24143     isSelected : function(index){
24144             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24145         return (r && this.selections.key(r.id) ? true : false);
24146     },
24147
24148     /**
24149      * Returns True if the specified record id is selected.
24150      * @param {String} id The id of record to check
24151      * @return {Boolean}
24152      */
24153     isIdSelected : function(id){
24154         return (this.selections.key(id) ? true : false);
24155     },
24156
24157
24158     // private
24159     handleMouseDBClick : function(e, t){
24160         
24161     },
24162     // private
24163     handleMouseDown : function(e, t)
24164     {
24165             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24166         if(this.isLocked() || rowIndex < 0 ){
24167             return;
24168         };
24169         if(e.shiftKey && this.last !== false){
24170             var last = this.last;
24171             this.selectRange(last, rowIndex, e.ctrlKey);
24172             this.last = last; // reset the last
24173             t.focus();
24174     
24175         }else{
24176             var isSelected = this.isSelected(rowIndex);
24177             //Roo.log("select row:" + rowIndex);
24178             if(isSelected){
24179                 this.deselectRow(rowIndex);
24180             } else {
24181                         this.selectRow(rowIndex, true);
24182             }
24183     
24184             /*
24185                 if(e.button !== 0 && isSelected){
24186                 alert('rowIndex 2: ' + rowIndex);
24187                     view.focusRow(rowIndex);
24188                 }else if(e.ctrlKey && isSelected){
24189                     this.deselectRow(rowIndex);
24190                 }else if(!isSelected){
24191                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24192                     view.focusRow(rowIndex);
24193                 }
24194             */
24195         }
24196         this.fireEvent("afterselectionchange", this);
24197     },
24198     // private
24199     handleDragableRowClick :  function(grid, rowIndex, e) 
24200     {
24201         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24202             this.selectRow(rowIndex, false);
24203             grid.view.focusRow(rowIndex);
24204              this.fireEvent("afterselectionchange", this);
24205         }
24206     },
24207     
24208     /**
24209      * Selects multiple rows.
24210      * @param {Array} rows Array of the indexes of the row to select
24211      * @param {Boolean} keepExisting (optional) True to keep existing selections
24212      */
24213     selectRows : function(rows, keepExisting){
24214         if(!keepExisting){
24215             this.clearSelections();
24216         }
24217         for(var i = 0, len = rows.length; i < len; i++){
24218             this.selectRow(rows[i], true);
24219         }
24220     },
24221
24222     /**
24223      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24224      * @param {Number} startRow The index of the first row in the range
24225      * @param {Number} endRow The index of the last row in the range
24226      * @param {Boolean} keepExisting (optional) True to retain existing selections
24227      */
24228     selectRange : function(startRow, endRow, keepExisting){
24229         if(this.locked) {
24230             return;
24231         }
24232         if(!keepExisting){
24233             this.clearSelections();
24234         }
24235         if(startRow <= endRow){
24236             for(var i = startRow; i <= endRow; i++){
24237                 this.selectRow(i, true);
24238             }
24239         }else{
24240             for(var i = startRow; i >= endRow; i--){
24241                 this.selectRow(i, true);
24242             }
24243         }
24244     },
24245
24246     /**
24247      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24248      * @param {Number} startRow The index of the first row in the range
24249      * @param {Number} endRow The index of the last row in the range
24250      */
24251     deselectRange : function(startRow, endRow, preventViewNotify){
24252         if(this.locked) {
24253             return;
24254         }
24255         for(var i = startRow; i <= endRow; i++){
24256             this.deselectRow(i, preventViewNotify);
24257         }
24258     },
24259
24260     /**
24261      * Selects a row.
24262      * @param {Number} row The index of the row to select
24263      * @param {Boolean} keepExisting (optional) True to keep existing selections
24264      */
24265     selectRow : function(index, keepExisting, preventViewNotify)
24266     {
24267             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24268             return;
24269         }
24270         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24271             if(!keepExisting || this.singleSelect){
24272                 this.clearSelections();
24273             }
24274             
24275             var r = this.grid.store.getAt(index);
24276             //console.log('selectRow - record id :' + r.id);
24277             
24278             this.selections.add(r);
24279             this.last = this.lastActive = index;
24280             if(!preventViewNotify){
24281                 var proxy = new Roo.Element(
24282                                 this.grid.getRowDom(index)
24283                 );
24284                 proxy.addClass('bg-info info');
24285             }
24286             this.fireEvent("rowselect", this, index, r);
24287             this.fireEvent("selectionchange", this);
24288         }
24289     },
24290
24291     /**
24292      * Deselects a row.
24293      * @param {Number} row The index of the row to deselect
24294      */
24295     deselectRow : function(index, preventViewNotify)
24296     {
24297         if(this.locked) {
24298             return;
24299         }
24300         if(this.last == index){
24301             this.last = false;
24302         }
24303         if(this.lastActive == index){
24304             this.lastActive = false;
24305         }
24306         
24307         var r = this.grid.store.getAt(index);
24308         if (!r) {
24309             return;
24310         }
24311         
24312         this.selections.remove(r);
24313         //.console.log('deselectRow - record id :' + r.id);
24314         if(!preventViewNotify){
24315         
24316             var proxy = new Roo.Element(
24317                 this.grid.getRowDom(index)
24318             );
24319             proxy.removeClass('bg-info info');
24320         }
24321         this.fireEvent("rowdeselect", this, index);
24322         this.fireEvent("selectionchange", this);
24323     },
24324
24325     // private
24326     restoreLast : function(){
24327         if(this._last){
24328             this.last = this._last;
24329         }
24330     },
24331
24332     // private
24333     acceptsNav : function(row, col, cm){
24334         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24335     },
24336
24337     // private
24338     onEditorKey : function(field, e){
24339         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24340         if(k == e.TAB){
24341             e.stopEvent();
24342             ed.completeEdit();
24343             if(e.shiftKey){
24344                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24345             }else{
24346                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24347             }
24348         }else if(k == e.ENTER && !e.ctrlKey){
24349             e.stopEvent();
24350             ed.completeEdit();
24351             if(e.shiftKey){
24352                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24353             }else{
24354                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24355             }
24356         }else if(k == e.ESC){
24357             ed.cancelEdit();
24358         }
24359         if(newCell){
24360             g.startEditing(newCell[0], newCell[1]);
24361         }
24362     }
24363 });
24364 /*
24365  * Based on:
24366  * Ext JS Library 1.1.1
24367  * Copyright(c) 2006-2007, Ext JS, LLC.
24368  *
24369  * Originally Released Under LGPL - original licence link has changed is not relivant.
24370  *
24371  * Fork - LGPL
24372  * <script type="text/javascript">
24373  */
24374  
24375 /**
24376  * @class Roo.bootstrap.PagingToolbar
24377  * @extends Roo.bootstrap.NavSimplebar
24378  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24379  * @constructor
24380  * Create a new PagingToolbar
24381  * @param {Object} config The config object
24382  * @param {Roo.data.Store} store
24383  */
24384 Roo.bootstrap.PagingToolbar = function(config)
24385 {
24386     // old args format still supported... - xtype is prefered..
24387         // created from xtype...
24388     
24389     this.ds = config.dataSource;
24390     
24391     if (config.store && !this.ds) {
24392         this.store= Roo.factory(config.store, Roo.data);
24393         this.ds = this.store;
24394         this.ds.xmodule = this.xmodule || false;
24395     }
24396     
24397     this.toolbarItems = [];
24398     if (config.items) {
24399         this.toolbarItems = config.items;
24400     }
24401     
24402     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24403     
24404     this.cursor = 0;
24405     
24406     if (this.ds) { 
24407         this.bind(this.ds);
24408     }
24409     
24410     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24411     
24412 };
24413
24414 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24415     /**
24416      * @cfg {Roo.data.Store} dataSource
24417      * The underlying data store providing the paged data
24418      */
24419     /**
24420      * @cfg {String/HTMLElement/Element} container
24421      * container The id or element that will contain the toolbar
24422      */
24423     /**
24424      * @cfg {Boolean} displayInfo
24425      * True to display the displayMsg (defaults to false)
24426      */
24427     /**
24428      * @cfg {Number} pageSize
24429      * The number of records to display per page (defaults to 20)
24430      */
24431     pageSize: 20,
24432     /**
24433      * @cfg {String} displayMsg
24434      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24435      */
24436     displayMsg : 'Displaying {0} - {1} of {2}',
24437     /**
24438      * @cfg {String} emptyMsg
24439      * The message to display when no records are found (defaults to "No data to display")
24440      */
24441     emptyMsg : 'No data to display',
24442     /**
24443      * Customizable piece of the default paging text (defaults to "Page")
24444      * @type String
24445      */
24446     beforePageText : "Page",
24447     /**
24448      * Customizable piece of the default paging text (defaults to "of %0")
24449      * @type String
24450      */
24451     afterPageText : "of {0}",
24452     /**
24453      * Customizable piece of the default paging text (defaults to "First Page")
24454      * @type String
24455      */
24456     firstText : "First Page",
24457     /**
24458      * Customizable piece of the default paging text (defaults to "Previous Page")
24459      * @type String
24460      */
24461     prevText : "Previous Page",
24462     /**
24463      * Customizable piece of the default paging text (defaults to "Next Page")
24464      * @type String
24465      */
24466     nextText : "Next Page",
24467     /**
24468      * Customizable piece of the default paging text (defaults to "Last Page")
24469      * @type String
24470      */
24471     lastText : "Last Page",
24472     /**
24473      * Customizable piece of the default paging text (defaults to "Refresh")
24474      * @type String
24475      */
24476     refreshText : "Refresh",
24477
24478     buttons : false,
24479     // private
24480     onRender : function(ct, position) 
24481     {
24482         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24483         this.navgroup.parentId = this.id;
24484         this.navgroup.onRender(this.el, null);
24485         // add the buttons to the navgroup
24486         
24487         if(this.displayInfo){
24488             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24489             this.displayEl = this.el.select('.x-paging-info', true).first();
24490 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24491 //            this.displayEl = navel.el.select('span',true).first();
24492         }
24493         
24494         var _this = this;
24495         
24496         if(this.buttons){
24497             Roo.each(_this.buttons, function(e){ // this might need to use render????
24498                Roo.factory(e).onRender(_this.el, null);
24499             });
24500         }
24501             
24502         Roo.each(_this.toolbarItems, function(e) {
24503             _this.navgroup.addItem(e);
24504         });
24505         
24506         
24507         this.first = this.navgroup.addItem({
24508             tooltip: this.firstText,
24509             cls: "prev",
24510             icon : 'fa fa-backward',
24511             disabled: true,
24512             preventDefault: true,
24513             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24514         });
24515         
24516         this.prev =  this.navgroup.addItem({
24517             tooltip: this.prevText,
24518             cls: "prev",
24519             icon : 'fa fa-step-backward',
24520             disabled: true,
24521             preventDefault: true,
24522             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24523         });
24524     //this.addSeparator();
24525         
24526         
24527         var field = this.navgroup.addItem( {
24528             tagtype : 'span',
24529             cls : 'x-paging-position',
24530             
24531             html : this.beforePageText  +
24532                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24533                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24534          } ); //?? escaped?
24535         
24536         this.field = field.el.select('input', true).first();
24537         this.field.on("keydown", this.onPagingKeydown, this);
24538         this.field.on("focus", function(){this.dom.select();});
24539     
24540     
24541         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24542         //this.field.setHeight(18);
24543         //this.addSeparator();
24544         this.next = this.navgroup.addItem({
24545             tooltip: this.nextText,
24546             cls: "next",
24547             html : ' <i class="fa fa-step-forward">',
24548             disabled: true,
24549             preventDefault: true,
24550             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24551         });
24552         this.last = this.navgroup.addItem({
24553             tooltip: this.lastText,
24554             icon : 'fa fa-forward',
24555             cls: "next",
24556             disabled: true,
24557             preventDefault: true,
24558             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24559         });
24560     //this.addSeparator();
24561         this.loading = this.navgroup.addItem({
24562             tooltip: this.refreshText,
24563             icon: 'fa fa-refresh',
24564             preventDefault: true,
24565             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24566         });
24567         
24568     },
24569
24570     // private
24571     updateInfo : function(){
24572         if(this.displayEl){
24573             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24574             var msg = count == 0 ?
24575                 this.emptyMsg :
24576                 String.format(
24577                     this.displayMsg,
24578                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24579                 );
24580             this.displayEl.update(msg);
24581         }
24582     },
24583
24584     // private
24585     onLoad : function(ds, r, o)
24586     {
24587         this.cursor = o.params.start ? o.params.start : 0;
24588         
24589         var d = this.getPageData(),
24590             ap = d.activePage,
24591             ps = d.pages;
24592         
24593         
24594         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24595         this.field.dom.value = ap;
24596         this.first.setDisabled(ap == 1);
24597         this.prev.setDisabled(ap == 1);
24598         this.next.setDisabled(ap == ps);
24599         this.last.setDisabled(ap == ps);
24600         this.loading.enable();
24601         this.updateInfo();
24602     },
24603
24604     // private
24605     getPageData : function(){
24606         var total = this.ds.getTotalCount();
24607         return {
24608             total : total,
24609             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24610             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24611         };
24612     },
24613
24614     // private
24615     onLoadError : function(){
24616         this.loading.enable();
24617     },
24618
24619     // private
24620     onPagingKeydown : function(e){
24621         var k = e.getKey();
24622         var d = this.getPageData();
24623         if(k == e.RETURN){
24624             var v = this.field.dom.value, pageNum;
24625             if(!v || isNaN(pageNum = parseInt(v, 10))){
24626                 this.field.dom.value = d.activePage;
24627                 return;
24628             }
24629             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24630             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24631             e.stopEvent();
24632         }
24633         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))
24634         {
24635           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24636           this.field.dom.value = pageNum;
24637           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24638           e.stopEvent();
24639         }
24640         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24641         {
24642           var v = this.field.dom.value, pageNum; 
24643           var increment = (e.shiftKey) ? 10 : 1;
24644           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24645                 increment *= -1;
24646           }
24647           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24648             this.field.dom.value = d.activePage;
24649             return;
24650           }
24651           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24652           {
24653             this.field.dom.value = parseInt(v, 10) + increment;
24654             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24655             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24656           }
24657           e.stopEvent();
24658         }
24659     },
24660
24661     // private
24662     beforeLoad : function(){
24663         if(this.loading){
24664             this.loading.disable();
24665         }
24666     },
24667
24668     // private
24669     onClick : function(which){
24670         
24671         var ds = this.ds;
24672         if (!ds) {
24673             return;
24674         }
24675         
24676         switch(which){
24677             case "first":
24678                 ds.load({params:{start: 0, limit: this.pageSize}});
24679             break;
24680             case "prev":
24681                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24682             break;
24683             case "next":
24684                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24685             break;
24686             case "last":
24687                 var total = ds.getTotalCount();
24688                 var extra = total % this.pageSize;
24689                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24690                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24691             break;
24692             case "refresh":
24693                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24694             break;
24695         }
24696     },
24697
24698     /**
24699      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24700      * @param {Roo.data.Store} store The data store to unbind
24701      */
24702     unbind : function(ds){
24703         ds.un("beforeload", this.beforeLoad, this);
24704         ds.un("load", this.onLoad, this);
24705         ds.un("loadexception", this.onLoadError, this);
24706         ds.un("remove", this.updateInfo, this);
24707         ds.un("add", this.updateInfo, this);
24708         this.ds = undefined;
24709     },
24710
24711     /**
24712      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24713      * @param {Roo.data.Store} store The data store to bind
24714      */
24715     bind : function(ds){
24716         ds.on("beforeload", this.beforeLoad, this);
24717         ds.on("load", this.onLoad, this);
24718         ds.on("loadexception", this.onLoadError, this);
24719         ds.on("remove", this.updateInfo, this);
24720         ds.on("add", this.updateInfo, this);
24721         this.ds = ds;
24722     }
24723 });/*
24724  * - LGPL
24725  *
24726  * element
24727  * 
24728  */
24729
24730 /**
24731  * @class Roo.bootstrap.MessageBar
24732  * @extends Roo.bootstrap.Component
24733  * Bootstrap MessageBar class
24734  * @cfg {String} html contents of the MessageBar
24735  * @cfg {String} weight (info | success | warning | danger) default info
24736  * @cfg {String} beforeClass insert the bar before the given class
24737  * @cfg {Boolean} closable (true | false) default false
24738  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24739  * 
24740  * @constructor
24741  * Create a new Element
24742  * @param {Object} config The config object
24743  */
24744
24745 Roo.bootstrap.MessageBar = function(config){
24746     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24747 };
24748
24749 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24750     
24751     html: '',
24752     weight: 'info',
24753     closable: false,
24754     fixed: false,
24755     beforeClass: 'bootstrap-sticky-wrap',
24756     
24757     getAutoCreate : function(){
24758         
24759         var cfg = {
24760             tag: 'div',
24761             cls: 'alert alert-dismissable alert-' + this.weight,
24762             cn: [
24763                 {
24764                     tag: 'span',
24765                     cls: 'message',
24766                     html: this.html || ''
24767                 }
24768             ]
24769         };
24770         
24771         if(this.fixed){
24772             cfg.cls += ' alert-messages-fixed';
24773         }
24774         
24775         if(this.closable){
24776             cfg.cn.push({
24777                 tag: 'button',
24778                 cls: 'close',
24779                 html: 'x'
24780             });
24781         }
24782         
24783         return cfg;
24784     },
24785     
24786     onRender : function(ct, position)
24787     {
24788         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24789         
24790         if(!this.el){
24791             var cfg = Roo.apply({},  this.getAutoCreate());
24792             cfg.id = Roo.id();
24793             
24794             if (this.cls) {
24795                 cfg.cls += ' ' + this.cls;
24796             }
24797             if (this.style) {
24798                 cfg.style = this.style;
24799             }
24800             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24801             
24802             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24803         }
24804         
24805         this.el.select('>button.close').on('click', this.hide, this);
24806         
24807     },
24808     
24809     show : function()
24810     {
24811         if (!this.rendered) {
24812             this.render();
24813         }
24814         
24815         this.el.show();
24816         
24817         this.fireEvent('show', this);
24818         
24819     },
24820     
24821     hide : function()
24822     {
24823         if (!this.rendered) {
24824             this.render();
24825         }
24826         
24827         this.el.hide();
24828         
24829         this.fireEvent('hide', this);
24830     },
24831     
24832     update : function()
24833     {
24834 //        var e = this.el.dom.firstChild;
24835 //        
24836 //        if(this.closable){
24837 //            e = e.nextSibling;
24838 //        }
24839 //        
24840 //        e.data = this.html || '';
24841
24842         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24843     }
24844    
24845 });
24846
24847  
24848
24849      /*
24850  * - LGPL
24851  *
24852  * Graph
24853  * 
24854  */
24855
24856
24857 /**
24858  * @class Roo.bootstrap.Graph
24859  * @extends Roo.bootstrap.Component
24860  * Bootstrap Graph class
24861 > Prameters
24862  -sm {number} sm 4
24863  -md {number} md 5
24864  @cfg {String} graphtype  bar | vbar | pie
24865  @cfg {number} g_x coodinator | centre x (pie)
24866  @cfg {number} g_y coodinator | centre y (pie)
24867  @cfg {number} g_r radius (pie)
24868  @cfg {number} g_height height of the chart (respected by all elements in the set)
24869  @cfg {number} g_width width of the chart (respected by all elements in the set)
24870  @cfg {Object} title The title of the chart
24871     
24872  -{Array}  values
24873  -opts (object) options for the chart 
24874      o {
24875      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24876      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24877      o vgutter (number)
24878      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.
24879      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24880      o to
24881      o stretch (boolean)
24882      o }
24883  -opts (object) options for the pie
24884      o{
24885      o cut
24886      o startAngle (number)
24887      o endAngle (number)
24888      } 
24889  *
24890  * @constructor
24891  * Create a new Input
24892  * @param {Object} config The config object
24893  */
24894
24895 Roo.bootstrap.Graph = function(config){
24896     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24897     
24898     this.addEvents({
24899         // img events
24900         /**
24901          * @event click
24902          * The img click event for the img.
24903          * @param {Roo.EventObject} e
24904          */
24905         "click" : true
24906     });
24907 };
24908
24909 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24910     
24911     sm: 4,
24912     md: 5,
24913     graphtype: 'bar',
24914     g_height: 250,
24915     g_width: 400,
24916     g_x: 50,
24917     g_y: 50,
24918     g_r: 30,
24919     opts:{
24920         //g_colors: this.colors,
24921         g_type: 'soft',
24922         g_gutter: '20%'
24923
24924     },
24925     title : false,
24926
24927     getAutoCreate : function(){
24928         
24929         var cfg = {
24930             tag: 'div',
24931             html : null
24932         };
24933         
24934         
24935         return  cfg;
24936     },
24937
24938     onRender : function(ct,position){
24939         
24940         
24941         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24942         
24943         if (typeof(Raphael) == 'undefined') {
24944             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24945             return;
24946         }
24947         
24948         this.raphael = Raphael(this.el.dom);
24949         
24950                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24951                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24952                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24953                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24954                 /*
24955                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24956                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24957                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24958                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24959                 
24960                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24961                 r.barchart(330, 10, 300, 220, data1);
24962                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24963                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24964                 */
24965                 
24966                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24967                 // r.barchart(30, 30, 560, 250,  xdata, {
24968                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24969                 //     axis : "0 0 1 1",
24970                 //     axisxlabels :  xdata
24971                 //     //yvalues : cols,
24972                    
24973                 // });
24974 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24975 //        
24976 //        this.load(null,xdata,{
24977 //                axis : "0 0 1 1",
24978 //                axisxlabels :  xdata
24979 //                });
24980
24981     },
24982
24983     load : function(graphtype,xdata,opts)
24984     {
24985         this.raphael.clear();
24986         if(!graphtype) {
24987             graphtype = this.graphtype;
24988         }
24989         if(!opts){
24990             opts = this.opts;
24991         }
24992         var r = this.raphael,
24993             fin = function () {
24994                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24995             },
24996             fout = function () {
24997                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24998             },
24999             pfin = function() {
25000                 this.sector.stop();
25001                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25002
25003                 if (this.label) {
25004                     this.label[0].stop();
25005                     this.label[0].attr({ r: 7.5 });
25006                     this.label[1].attr({ "font-weight": 800 });
25007                 }
25008             },
25009             pfout = function() {
25010                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25011
25012                 if (this.label) {
25013                     this.label[0].animate({ r: 5 }, 500, "bounce");
25014                     this.label[1].attr({ "font-weight": 400 });
25015                 }
25016             };
25017
25018         switch(graphtype){
25019             case 'bar':
25020                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25021                 break;
25022             case 'hbar':
25023                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25024                 break;
25025             case 'pie':
25026 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25027 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25028 //            
25029                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25030                 
25031                 break;
25032
25033         }
25034         
25035         if(this.title){
25036             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25037         }
25038         
25039     },
25040     
25041     setTitle: function(o)
25042     {
25043         this.title = o;
25044     },
25045     
25046     initEvents: function() {
25047         
25048         if(!this.href){
25049             this.el.on('click', this.onClick, this);
25050         }
25051     },
25052     
25053     onClick : function(e)
25054     {
25055         Roo.log('img onclick');
25056         this.fireEvent('click', this, e);
25057     }
25058    
25059 });
25060
25061  
25062 /*
25063  * - LGPL
25064  *
25065  * numberBox
25066  * 
25067  */
25068 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25069
25070 /**
25071  * @class Roo.bootstrap.dash.NumberBox
25072  * @extends Roo.bootstrap.Component
25073  * Bootstrap NumberBox class
25074  * @cfg {String} headline Box headline
25075  * @cfg {String} content Box content
25076  * @cfg {String} icon Box icon
25077  * @cfg {String} footer Footer text
25078  * @cfg {String} fhref Footer href
25079  * 
25080  * @constructor
25081  * Create a new NumberBox
25082  * @param {Object} config The config object
25083  */
25084
25085
25086 Roo.bootstrap.dash.NumberBox = function(config){
25087     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25088     
25089 };
25090
25091 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25092     
25093     headline : '',
25094     content : '',
25095     icon : '',
25096     footer : '',
25097     fhref : '',
25098     ficon : '',
25099     
25100     getAutoCreate : function(){
25101         
25102         var cfg = {
25103             tag : 'div',
25104             cls : 'small-box ',
25105             cn : [
25106                 {
25107                     tag : 'div',
25108                     cls : 'inner',
25109                     cn :[
25110                         {
25111                             tag : 'h3',
25112                             cls : 'roo-headline',
25113                             html : this.headline
25114                         },
25115                         {
25116                             tag : 'p',
25117                             cls : 'roo-content',
25118                             html : this.content
25119                         }
25120                     ]
25121                 }
25122             ]
25123         };
25124         
25125         if(this.icon){
25126             cfg.cn.push({
25127                 tag : 'div',
25128                 cls : 'icon',
25129                 cn :[
25130                     {
25131                         tag : 'i',
25132                         cls : 'ion ' + this.icon
25133                     }
25134                 ]
25135             });
25136         }
25137         
25138         if(this.footer){
25139             var footer = {
25140                 tag : 'a',
25141                 cls : 'small-box-footer',
25142                 href : this.fhref || '#',
25143                 html : this.footer
25144             };
25145             
25146             cfg.cn.push(footer);
25147             
25148         }
25149         
25150         return  cfg;
25151     },
25152
25153     onRender : function(ct,position){
25154         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25155
25156
25157        
25158                 
25159     },
25160
25161     setHeadline: function (value)
25162     {
25163         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25164     },
25165     
25166     setFooter: function (value, href)
25167     {
25168         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25169         
25170         if(href){
25171             this.el.select('a.small-box-footer',true).first().attr('href', href);
25172         }
25173         
25174     },
25175
25176     setContent: function (value)
25177     {
25178         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25179     },
25180
25181     initEvents: function() 
25182     {   
25183         
25184     }
25185     
25186 });
25187
25188  
25189 /*
25190  * - LGPL
25191  *
25192  * TabBox
25193  * 
25194  */
25195 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25196
25197 /**
25198  * @class Roo.bootstrap.dash.TabBox
25199  * @extends Roo.bootstrap.Component
25200  * Bootstrap TabBox class
25201  * @cfg {String} title Title of the TabBox
25202  * @cfg {String} icon Icon of the TabBox
25203  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25204  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25205  * 
25206  * @constructor
25207  * Create a new TabBox
25208  * @param {Object} config The config object
25209  */
25210
25211
25212 Roo.bootstrap.dash.TabBox = function(config){
25213     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25214     this.addEvents({
25215         // raw events
25216         /**
25217          * @event addpane
25218          * When a pane is added
25219          * @param {Roo.bootstrap.dash.TabPane} pane
25220          */
25221         "addpane" : true,
25222         /**
25223          * @event activatepane
25224          * When a pane is activated
25225          * @param {Roo.bootstrap.dash.TabPane} pane
25226          */
25227         "activatepane" : true
25228         
25229          
25230     });
25231     
25232     this.panes = [];
25233 };
25234
25235 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25236
25237     title : '',
25238     icon : false,
25239     showtabs : true,
25240     tabScrollable : false,
25241     
25242     getChildContainer : function()
25243     {
25244         return this.el.select('.tab-content', true).first();
25245     },
25246     
25247     getAutoCreate : function(){
25248         
25249         var header = {
25250             tag: 'li',
25251             cls: 'pull-left header',
25252             html: this.title,
25253             cn : []
25254         };
25255         
25256         if(this.icon){
25257             header.cn.push({
25258                 tag: 'i',
25259                 cls: 'fa ' + this.icon
25260             });
25261         }
25262         
25263         var h = {
25264             tag: 'ul',
25265             cls: 'nav nav-tabs pull-right',
25266             cn: [
25267                 header
25268             ]
25269         };
25270         
25271         if(this.tabScrollable){
25272             h = {
25273                 tag: 'div',
25274                 cls: 'tab-header',
25275                 cn: [
25276                     {
25277                         tag: 'ul',
25278                         cls: 'nav nav-tabs pull-right',
25279                         cn: [
25280                             header
25281                         ]
25282                     }
25283                 ]
25284             };
25285         }
25286         
25287         var cfg = {
25288             tag: 'div',
25289             cls: 'nav-tabs-custom',
25290             cn: [
25291                 h,
25292                 {
25293                     tag: 'div',
25294                     cls: 'tab-content no-padding',
25295                     cn: []
25296                 }
25297             ]
25298         };
25299
25300         return  cfg;
25301     },
25302     initEvents : function()
25303     {
25304         //Roo.log('add add pane handler');
25305         this.on('addpane', this.onAddPane, this);
25306     },
25307      /**
25308      * Updates the box title
25309      * @param {String} html to set the title to.
25310      */
25311     setTitle : function(value)
25312     {
25313         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25314     },
25315     onAddPane : function(pane)
25316     {
25317         this.panes.push(pane);
25318         //Roo.log('addpane');
25319         //Roo.log(pane);
25320         // tabs are rendere left to right..
25321         if(!this.showtabs){
25322             return;
25323         }
25324         
25325         var ctr = this.el.select('.nav-tabs', true).first();
25326          
25327          
25328         var existing = ctr.select('.nav-tab',true);
25329         var qty = existing.getCount();;
25330         
25331         
25332         var tab = ctr.createChild({
25333             tag : 'li',
25334             cls : 'nav-tab' + (qty ? '' : ' active'),
25335             cn : [
25336                 {
25337                     tag : 'a',
25338                     href:'#',
25339                     html : pane.title
25340                 }
25341             ]
25342         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25343         pane.tab = tab;
25344         
25345         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25346         if (!qty) {
25347             pane.el.addClass('active');
25348         }
25349         
25350                 
25351     },
25352     onTabClick : function(ev,un,ob,pane)
25353     {
25354         //Roo.log('tab - prev default');
25355         ev.preventDefault();
25356         
25357         
25358         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25359         pane.tab.addClass('active');
25360         //Roo.log(pane.title);
25361         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25362         // technically we should have a deactivate event.. but maybe add later.
25363         // and it should not de-activate the selected tab...
25364         this.fireEvent('activatepane', pane);
25365         pane.el.addClass('active');
25366         pane.fireEvent('activate');
25367         
25368         
25369     },
25370     
25371     getActivePane : function()
25372     {
25373         var r = false;
25374         Roo.each(this.panes, function(p) {
25375             if(p.el.hasClass('active')){
25376                 r = p;
25377                 return false;
25378             }
25379             
25380             return;
25381         });
25382         
25383         return r;
25384     }
25385     
25386     
25387 });
25388
25389  
25390 /*
25391  * - LGPL
25392  *
25393  * Tab pane
25394  * 
25395  */
25396 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25397 /**
25398  * @class Roo.bootstrap.TabPane
25399  * @extends Roo.bootstrap.Component
25400  * Bootstrap TabPane class
25401  * @cfg {Boolean} active (false | true) Default false
25402  * @cfg {String} title title of panel
25403
25404  * 
25405  * @constructor
25406  * Create a new TabPane
25407  * @param {Object} config The config object
25408  */
25409
25410 Roo.bootstrap.dash.TabPane = function(config){
25411     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25412     
25413     this.addEvents({
25414         // raw events
25415         /**
25416          * @event activate
25417          * When a pane is activated
25418          * @param {Roo.bootstrap.dash.TabPane} pane
25419          */
25420         "activate" : true
25421          
25422     });
25423 };
25424
25425 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25426     
25427     active : false,
25428     title : '',
25429     
25430     // the tabBox that this is attached to.
25431     tab : false,
25432      
25433     getAutoCreate : function() 
25434     {
25435         var cfg = {
25436             tag: 'div',
25437             cls: 'tab-pane'
25438         };
25439         
25440         if(this.active){
25441             cfg.cls += ' active';
25442         }
25443         
25444         return cfg;
25445     },
25446     initEvents  : function()
25447     {
25448         //Roo.log('trigger add pane handler');
25449         this.parent().fireEvent('addpane', this)
25450     },
25451     
25452      /**
25453      * Updates the tab title 
25454      * @param {String} html to set the title to.
25455      */
25456     setTitle: function(str)
25457     {
25458         if (!this.tab) {
25459             return;
25460         }
25461         this.title = str;
25462         this.tab.select('a', true).first().dom.innerHTML = str;
25463         
25464     }
25465     
25466     
25467     
25468 });
25469
25470  
25471
25472
25473  /*
25474  * - LGPL
25475  *
25476  * menu
25477  * 
25478  */
25479 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25480
25481 /**
25482  * @class Roo.bootstrap.menu.Menu
25483  * @extends Roo.bootstrap.Component
25484  * Bootstrap Menu class - container for Menu
25485  * @cfg {String} html Text of the menu
25486  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25487  * @cfg {String} icon Font awesome icon
25488  * @cfg {String} pos Menu align to (top | bottom) default bottom
25489  * 
25490  * 
25491  * @constructor
25492  * Create a new Menu
25493  * @param {Object} config The config object
25494  */
25495
25496
25497 Roo.bootstrap.menu.Menu = function(config){
25498     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25499     
25500     this.addEvents({
25501         /**
25502          * @event beforeshow
25503          * Fires before this menu is displayed
25504          * @param {Roo.bootstrap.menu.Menu} this
25505          */
25506         beforeshow : true,
25507         /**
25508          * @event beforehide
25509          * Fires before this menu is hidden
25510          * @param {Roo.bootstrap.menu.Menu} this
25511          */
25512         beforehide : true,
25513         /**
25514          * @event show
25515          * Fires after this menu is displayed
25516          * @param {Roo.bootstrap.menu.Menu} this
25517          */
25518         show : true,
25519         /**
25520          * @event hide
25521          * Fires after this menu is hidden
25522          * @param {Roo.bootstrap.menu.Menu} this
25523          */
25524         hide : true,
25525         /**
25526          * @event click
25527          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25528          * @param {Roo.bootstrap.menu.Menu} this
25529          * @param {Roo.EventObject} e
25530          */
25531         click : true
25532     });
25533     
25534 };
25535
25536 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25537     
25538     submenu : false,
25539     html : '',
25540     weight : 'default',
25541     icon : false,
25542     pos : 'bottom',
25543     
25544     
25545     getChildContainer : function() {
25546         if(this.isSubMenu){
25547             return this.el;
25548         }
25549         
25550         return this.el.select('ul.dropdown-menu', true).first();  
25551     },
25552     
25553     getAutoCreate : function()
25554     {
25555         var text = [
25556             {
25557                 tag : 'span',
25558                 cls : 'roo-menu-text',
25559                 html : this.html
25560             }
25561         ];
25562         
25563         if(this.icon){
25564             text.unshift({
25565                 tag : 'i',
25566                 cls : 'fa ' + this.icon
25567             })
25568         }
25569         
25570         
25571         var cfg = {
25572             tag : 'div',
25573             cls : 'btn-group',
25574             cn : [
25575                 {
25576                     tag : 'button',
25577                     cls : 'dropdown-button btn btn-' + this.weight,
25578                     cn : text
25579                 },
25580                 {
25581                     tag : 'button',
25582                     cls : 'dropdown-toggle btn btn-' + this.weight,
25583                     cn : [
25584                         {
25585                             tag : 'span',
25586                             cls : 'caret'
25587                         }
25588                     ]
25589                 },
25590                 {
25591                     tag : 'ul',
25592                     cls : 'dropdown-menu'
25593                 }
25594             ]
25595             
25596         };
25597         
25598         if(this.pos == 'top'){
25599             cfg.cls += ' dropup';
25600         }
25601         
25602         if(this.isSubMenu){
25603             cfg = {
25604                 tag : 'ul',
25605                 cls : 'dropdown-menu'
25606             }
25607         }
25608         
25609         return cfg;
25610     },
25611     
25612     onRender : function(ct, position)
25613     {
25614         this.isSubMenu = ct.hasClass('dropdown-submenu');
25615         
25616         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25617     },
25618     
25619     initEvents : function() 
25620     {
25621         if(this.isSubMenu){
25622             return;
25623         }
25624         
25625         this.hidden = true;
25626         
25627         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25628         this.triggerEl.on('click', this.onTriggerPress, this);
25629         
25630         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25631         this.buttonEl.on('click', this.onClick, this);
25632         
25633     },
25634     
25635     list : function()
25636     {
25637         if(this.isSubMenu){
25638             return this.el;
25639         }
25640         
25641         return this.el.select('ul.dropdown-menu', true).first();
25642     },
25643     
25644     onClick : function(e)
25645     {
25646         this.fireEvent("click", this, e);
25647     },
25648     
25649     onTriggerPress  : function(e)
25650     {   
25651         if (this.isVisible()) {
25652             this.hide();
25653         } else {
25654             this.show();
25655         }
25656     },
25657     
25658     isVisible : function(){
25659         return !this.hidden;
25660     },
25661     
25662     show : function()
25663     {
25664         this.fireEvent("beforeshow", this);
25665         
25666         this.hidden = false;
25667         this.el.addClass('open');
25668         
25669         Roo.get(document).on("mouseup", this.onMouseUp, this);
25670         
25671         this.fireEvent("show", this);
25672         
25673         
25674     },
25675     
25676     hide : function()
25677     {
25678         this.fireEvent("beforehide", this);
25679         
25680         this.hidden = true;
25681         this.el.removeClass('open');
25682         
25683         Roo.get(document).un("mouseup", this.onMouseUp);
25684         
25685         this.fireEvent("hide", this);
25686     },
25687     
25688     onMouseUp : function()
25689     {
25690         this.hide();
25691     }
25692     
25693 });
25694
25695  
25696  /*
25697  * - LGPL
25698  *
25699  * menu item
25700  * 
25701  */
25702 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25703
25704 /**
25705  * @class Roo.bootstrap.menu.Item
25706  * @extends Roo.bootstrap.Component
25707  * Bootstrap MenuItem class
25708  * @cfg {Boolean} submenu (true | false) default false
25709  * @cfg {String} html text of the item
25710  * @cfg {String} href the link
25711  * @cfg {Boolean} disable (true | false) default false
25712  * @cfg {Boolean} preventDefault (true | false) default true
25713  * @cfg {String} icon Font awesome icon
25714  * @cfg {String} pos Submenu align to (left | right) default right 
25715  * 
25716  * 
25717  * @constructor
25718  * Create a new Item
25719  * @param {Object} config The config object
25720  */
25721
25722
25723 Roo.bootstrap.menu.Item = function(config){
25724     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25725     this.addEvents({
25726         /**
25727          * @event mouseover
25728          * Fires when the mouse is hovering over this menu
25729          * @param {Roo.bootstrap.menu.Item} this
25730          * @param {Roo.EventObject} e
25731          */
25732         mouseover : true,
25733         /**
25734          * @event mouseout
25735          * Fires when the mouse exits this menu
25736          * @param {Roo.bootstrap.menu.Item} this
25737          * @param {Roo.EventObject} e
25738          */
25739         mouseout : true,
25740         // raw events
25741         /**
25742          * @event click
25743          * The raw click event for the entire grid.
25744          * @param {Roo.EventObject} e
25745          */
25746         click : true
25747     });
25748 };
25749
25750 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25751     
25752     submenu : false,
25753     href : '',
25754     html : '',
25755     preventDefault: true,
25756     disable : false,
25757     icon : false,
25758     pos : 'right',
25759     
25760     getAutoCreate : function()
25761     {
25762         var text = [
25763             {
25764                 tag : 'span',
25765                 cls : 'roo-menu-item-text',
25766                 html : this.html
25767             }
25768         ];
25769         
25770         if(this.icon){
25771             text.unshift({
25772                 tag : 'i',
25773                 cls : 'fa ' + this.icon
25774             })
25775         }
25776         
25777         var cfg = {
25778             tag : 'li',
25779             cn : [
25780                 {
25781                     tag : 'a',
25782                     href : this.href || '#',
25783                     cn : text
25784                 }
25785             ]
25786         };
25787         
25788         if(this.disable){
25789             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25790         }
25791         
25792         if(this.submenu){
25793             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25794             
25795             if(this.pos == 'left'){
25796                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25797             }
25798         }
25799         
25800         return cfg;
25801     },
25802     
25803     initEvents : function() 
25804     {
25805         this.el.on('mouseover', this.onMouseOver, this);
25806         this.el.on('mouseout', this.onMouseOut, this);
25807         
25808         this.el.select('a', true).first().on('click', this.onClick, this);
25809         
25810     },
25811     
25812     onClick : function(e)
25813     {
25814         if(this.preventDefault){
25815             e.preventDefault();
25816         }
25817         
25818         this.fireEvent("click", this, e);
25819     },
25820     
25821     onMouseOver : function(e)
25822     {
25823         if(this.submenu && this.pos == 'left'){
25824             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25825         }
25826         
25827         this.fireEvent("mouseover", this, e);
25828     },
25829     
25830     onMouseOut : function(e)
25831     {
25832         this.fireEvent("mouseout", this, e);
25833     }
25834 });
25835
25836  
25837
25838  /*
25839  * - LGPL
25840  *
25841  * menu separator
25842  * 
25843  */
25844 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25845
25846 /**
25847  * @class Roo.bootstrap.menu.Separator
25848  * @extends Roo.bootstrap.Component
25849  * Bootstrap Separator class
25850  * 
25851  * @constructor
25852  * Create a new Separator
25853  * @param {Object} config The config object
25854  */
25855
25856
25857 Roo.bootstrap.menu.Separator = function(config){
25858     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25859 };
25860
25861 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25862     
25863     getAutoCreate : function(){
25864         var cfg = {
25865             tag : 'li',
25866             cls: 'divider'
25867         };
25868         
25869         return cfg;
25870     }
25871    
25872 });
25873
25874  
25875
25876  /*
25877  * - LGPL
25878  *
25879  * Tooltip
25880  * 
25881  */
25882
25883 /**
25884  * @class Roo.bootstrap.Tooltip
25885  * Bootstrap Tooltip class
25886  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25887  * to determine which dom element triggers the tooltip.
25888  * 
25889  * It needs to add support for additional attributes like tooltip-position
25890  * 
25891  * @constructor
25892  * Create a new Toolti
25893  * @param {Object} config The config object
25894  */
25895
25896 Roo.bootstrap.Tooltip = function(config){
25897     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25898     
25899     this.alignment = Roo.bootstrap.Tooltip.alignment;
25900     
25901     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25902         this.alignment = config.alignment;
25903     }
25904     
25905 };
25906
25907 Roo.apply(Roo.bootstrap.Tooltip, {
25908     /**
25909      * @function init initialize tooltip monitoring.
25910      * @static
25911      */
25912     currentEl : false,
25913     currentTip : false,
25914     currentRegion : false,
25915     
25916     //  init : delay?
25917     
25918     init : function()
25919     {
25920         Roo.get(document).on('mouseover', this.enter ,this);
25921         Roo.get(document).on('mouseout', this.leave, this);
25922          
25923         
25924         this.currentTip = new Roo.bootstrap.Tooltip();
25925     },
25926     
25927     enter : function(ev)
25928     {
25929         var dom = ev.getTarget();
25930         
25931         //Roo.log(['enter',dom]);
25932         var el = Roo.fly(dom);
25933         if (this.currentEl) {
25934             //Roo.log(dom);
25935             //Roo.log(this.currentEl);
25936             //Roo.log(this.currentEl.contains(dom));
25937             if (this.currentEl == el) {
25938                 return;
25939             }
25940             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25941                 return;
25942             }
25943
25944         }
25945         
25946         if (this.currentTip.el) {
25947             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25948         }    
25949         //Roo.log(ev);
25950         
25951         if(!el || el.dom == document){
25952             return;
25953         }
25954         
25955         var bindEl = el;
25956         
25957         // you can not look for children, as if el is the body.. then everythign is the child..
25958         if (!el.attr('tooltip')) { //
25959             if (!el.select("[tooltip]").elements.length) {
25960                 return;
25961             }
25962             // is the mouse over this child...?
25963             bindEl = el.select("[tooltip]").first();
25964             var xy = ev.getXY();
25965             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25966                 //Roo.log("not in region.");
25967                 return;
25968             }
25969             //Roo.log("child element over..");
25970             
25971         }
25972         this.currentEl = bindEl;
25973         this.currentTip.bind(bindEl);
25974         this.currentRegion = Roo.lib.Region.getRegion(dom);
25975         this.currentTip.enter();
25976         
25977     },
25978     leave : function(ev)
25979     {
25980         var dom = ev.getTarget();
25981         //Roo.log(['leave',dom]);
25982         if (!this.currentEl) {
25983             return;
25984         }
25985         
25986         
25987         if (dom != this.currentEl.dom) {
25988             return;
25989         }
25990         var xy = ev.getXY();
25991         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25992             return;
25993         }
25994         // only activate leave if mouse cursor is outside... bounding box..
25995         
25996         
25997         
25998         
25999         if (this.currentTip) {
26000             this.currentTip.leave();
26001         }
26002         //Roo.log('clear currentEl');
26003         this.currentEl = false;
26004         
26005         
26006     },
26007     alignment : {
26008         'left' : ['r-l', [-2,0], 'right'],
26009         'right' : ['l-r', [2,0], 'left'],
26010         'bottom' : ['t-b', [0,2], 'top'],
26011         'top' : [ 'b-t', [0,-2], 'bottom']
26012     }
26013     
26014 });
26015
26016
26017 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26018     
26019     
26020     bindEl : false,
26021     
26022     delay : null, // can be { show : 300 , hide: 500}
26023     
26024     timeout : null,
26025     
26026     hoverState : null, //???
26027     
26028     placement : 'bottom', 
26029     
26030     alignment : false,
26031     
26032     getAutoCreate : function(){
26033     
26034         var cfg = {
26035            cls : 'tooltip',
26036            role : 'tooltip',
26037            cn : [
26038                 {
26039                     cls : 'tooltip-arrow'
26040                 },
26041                 {
26042                     cls : 'tooltip-inner'
26043                 }
26044            ]
26045         };
26046         
26047         return cfg;
26048     },
26049     bind : function(el)
26050     {
26051         this.bindEl = el;
26052     },
26053       
26054     
26055     enter : function () {
26056        
26057         if (this.timeout != null) {
26058             clearTimeout(this.timeout);
26059         }
26060         
26061         this.hoverState = 'in';
26062          //Roo.log("enter - show");
26063         if (!this.delay || !this.delay.show) {
26064             this.show();
26065             return;
26066         }
26067         var _t = this;
26068         this.timeout = setTimeout(function () {
26069             if (_t.hoverState == 'in') {
26070                 _t.show();
26071             }
26072         }, this.delay.show);
26073     },
26074     leave : function()
26075     {
26076         clearTimeout(this.timeout);
26077     
26078         this.hoverState = 'out';
26079          if (!this.delay || !this.delay.hide) {
26080             this.hide();
26081             return;
26082         }
26083        
26084         var _t = this;
26085         this.timeout = setTimeout(function () {
26086             //Roo.log("leave - timeout");
26087             
26088             if (_t.hoverState == 'out') {
26089                 _t.hide();
26090                 Roo.bootstrap.Tooltip.currentEl = false;
26091             }
26092         }, delay);
26093     },
26094     
26095     show : function (msg)
26096     {
26097         if (!this.el) {
26098             this.render(document.body);
26099         }
26100         // set content.
26101         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26102         
26103         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26104         
26105         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26106         
26107         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26108         
26109         var placement = typeof this.placement == 'function' ?
26110             this.placement.call(this, this.el, on_el) :
26111             this.placement;
26112             
26113         var autoToken = /\s?auto?\s?/i;
26114         var autoPlace = autoToken.test(placement);
26115         if (autoPlace) {
26116             placement = placement.replace(autoToken, '') || 'top';
26117         }
26118         
26119         //this.el.detach()
26120         //this.el.setXY([0,0]);
26121         this.el.show();
26122         //this.el.dom.style.display='block';
26123         
26124         //this.el.appendTo(on_el);
26125         
26126         var p = this.getPosition();
26127         var box = this.el.getBox();
26128         
26129         if (autoPlace) {
26130             // fixme..
26131         }
26132         
26133         var align = this.alignment[placement];
26134         
26135         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26136         
26137         if(placement == 'top' || placement == 'bottom'){
26138             if(xy[0] < 0){
26139                 placement = 'right';
26140             }
26141             
26142             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26143                 placement = 'left';
26144             }
26145             
26146             var scroll = Roo.select('body', true).first().getScroll();
26147             
26148             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26149                 placement = 'top';
26150             }
26151             
26152         }
26153         
26154         this.el.alignTo(this.bindEl, align[0],align[1]);
26155         //var arrow = this.el.select('.arrow',true).first();
26156         //arrow.set(align[2], 
26157         
26158         this.el.addClass(placement);
26159         
26160         this.el.addClass('in fade');
26161         
26162         this.hoverState = null;
26163         
26164         if (this.el.hasClass('fade')) {
26165             // fade it?
26166         }
26167         
26168     },
26169     hide : function()
26170     {
26171          
26172         if (!this.el) {
26173             return;
26174         }
26175         //this.el.setXY([0,0]);
26176         this.el.removeClass('in');
26177         //this.el.hide();
26178         
26179     }
26180     
26181 });
26182  
26183
26184  /*
26185  * - LGPL
26186  *
26187  * Location Picker
26188  * 
26189  */
26190
26191 /**
26192  * @class Roo.bootstrap.LocationPicker
26193  * @extends Roo.bootstrap.Component
26194  * Bootstrap LocationPicker class
26195  * @cfg {Number} latitude Position when init default 0
26196  * @cfg {Number} longitude Position when init default 0
26197  * @cfg {Number} zoom default 15
26198  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26199  * @cfg {Boolean} mapTypeControl default false
26200  * @cfg {Boolean} disableDoubleClickZoom default false
26201  * @cfg {Boolean} scrollwheel default true
26202  * @cfg {Boolean} streetViewControl default false
26203  * @cfg {Number} radius default 0
26204  * @cfg {String} locationName
26205  * @cfg {Boolean} draggable default true
26206  * @cfg {Boolean} enableAutocomplete default false
26207  * @cfg {Boolean} enableReverseGeocode default true
26208  * @cfg {String} markerTitle
26209  * 
26210  * @constructor
26211  * Create a new LocationPicker
26212  * @param {Object} config The config object
26213  */
26214
26215
26216 Roo.bootstrap.LocationPicker = function(config){
26217     
26218     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26219     
26220     this.addEvents({
26221         /**
26222          * @event initial
26223          * Fires when the picker initialized.
26224          * @param {Roo.bootstrap.LocationPicker} this
26225          * @param {Google Location} location
26226          */
26227         initial : true,
26228         /**
26229          * @event positionchanged
26230          * Fires when the picker position changed.
26231          * @param {Roo.bootstrap.LocationPicker} this
26232          * @param {Google Location} location
26233          */
26234         positionchanged : true,
26235         /**
26236          * @event resize
26237          * Fires when the map resize.
26238          * @param {Roo.bootstrap.LocationPicker} this
26239          */
26240         resize : true,
26241         /**
26242          * @event show
26243          * Fires when the map show.
26244          * @param {Roo.bootstrap.LocationPicker} this
26245          */
26246         show : true,
26247         /**
26248          * @event hide
26249          * Fires when the map hide.
26250          * @param {Roo.bootstrap.LocationPicker} this
26251          */
26252         hide : true,
26253         /**
26254          * @event mapClick
26255          * Fires when click the map.
26256          * @param {Roo.bootstrap.LocationPicker} this
26257          * @param {Map event} e
26258          */
26259         mapClick : true,
26260         /**
26261          * @event mapRightClick
26262          * Fires when right click the map.
26263          * @param {Roo.bootstrap.LocationPicker} this
26264          * @param {Map event} e
26265          */
26266         mapRightClick : true,
26267         /**
26268          * @event markerClick
26269          * Fires when click the marker.
26270          * @param {Roo.bootstrap.LocationPicker} this
26271          * @param {Map event} e
26272          */
26273         markerClick : true,
26274         /**
26275          * @event markerRightClick
26276          * Fires when right click the marker.
26277          * @param {Roo.bootstrap.LocationPicker} this
26278          * @param {Map event} e
26279          */
26280         markerRightClick : true,
26281         /**
26282          * @event OverlayViewDraw
26283          * Fires when OverlayView Draw
26284          * @param {Roo.bootstrap.LocationPicker} this
26285          */
26286         OverlayViewDraw : true,
26287         /**
26288          * @event OverlayViewOnAdd
26289          * Fires when OverlayView Draw
26290          * @param {Roo.bootstrap.LocationPicker} this
26291          */
26292         OverlayViewOnAdd : true,
26293         /**
26294          * @event OverlayViewOnRemove
26295          * Fires when OverlayView Draw
26296          * @param {Roo.bootstrap.LocationPicker} this
26297          */
26298         OverlayViewOnRemove : true,
26299         /**
26300          * @event OverlayViewShow
26301          * Fires when OverlayView Draw
26302          * @param {Roo.bootstrap.LocationPicker} this
26303          * @param {Pixel} cpx
26304          */
26305         OverlayViewShow : true,
26306         /**
26307          * @event OverlayViewHide
26308          * Fires when OverlayView Draw
26309          * @param {Roo.bootstrap.LocationPicker} this
26310          */
26311         OverlayViewHide : true,
26312         /**
26313          * @event loadexception
26314          * Fires when load google lib failed.
26315          * @param {Roo.bootstrap.LocationPicker} this
26316          */
26317         loadexception : true
26318     });
26319         
26320 };
26321
26322 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26323     
26324     gMapContext: false,
26325     
26326     latitude: 0,
26327     longitude: 0,
26328     zoom: 15,
26329     mapTypeId: false,
26330     mapTypeControl: false,
26331     disableDoubleClickZoom: false,
26332     scrollwheel: true,
26333     streetViewControl: false,
26334     radius: 0,
26335     locationName: '',
26336     draggable: true,
26337     enableAutocomplete: false,
26338     enableReverseGeocode: true,
26339     markerTitle: '',
26340     
26341     getAutoCreate: function()
26342     {
26343
26344         var cfg = {
26345             tag: 'div',
26346             cls: 'roo-location-picker'
26347         };
26348         
26349         return cfg
26350     },
26351     
26352     initEvents: function(ct, position)
26353     {       
26354         if(!this.el.getWidth() || this.isApplied()){
26355             return;
26356         }
26357         
26358         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26359         
26360         this.initial();
26361     },
26362     
26363     initial: function()
26364     {
26365         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26366             this.fireEvent('loadexception', this);
26367             return;
26368         }
26369         
26370         if(!this.mapTypeId){
26371             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26372         }
26373         
26374         this.gMapContext = this.GMapContext();
26375         
26376         this.initOverlayView();
26377         
26378         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26379         
26380         var _this = this;
26381                 
26382         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26383             _this.setPosition(_this.gMapContext.marker.position);
26384         });
26385         
26386         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26387             _this.fireEvent('mapClick', this, event);
26388             
26389         });
26390
26391         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26392             _this.fireEvent('mapRightClick', this, event);
26393             
26394         });
26395         
26396         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26397             _this.fireEvent('markerClick', this, event);
26398             
26399         });
26400
26401         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26402             _this.fireEvent('markerRightClick', this, event);
26403             
26404         });
26405         
26406         this.setPosition(this.gMapContext.location);
26407         
26408         this.fireEvent('initial', this, this.gMapContext.location);
26409     },
26410     
26411     initOverlayView: function()
26412     {
26413         var _this = this;
26414         
26415         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26416             
26417             draw: function()
26418             {
26419                 _this.fireEvent('OverlayViewDraw', _this);
26420             },
26421             
26422             onAdd: function()
26423             {
26424                 _this.fireEvent('OverlayViewOnAdd', _this);
26425             },
26426             
26427             onRemove: function()
26428             {
26429                 _this.fireEvent('OverlayViewOnRemove', _this);
26430             },
26431             
26432             show: function(cpx)
26433             {
26434                 _this.fireEvent('OverlayViewShow', _this, cpx);
26435             },
26436             
26437             hide: function()
26438             {
26439                 _this.fireEvent('OverlayViewHide', _this);
26440             }
26441             
26442         });
26443     },
26444     
26445     fromLatLngToContainerPixel: function(event)
26446     {
26447         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26448     },
26449     
26450     isApplied: function() 
26451     {
26452         return this.getGmapContext() == false ? false : true;
26453     },
26454     
26455     getGmapContext: function() 
26456     {
26457         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26458     },
26459     
26460     GMapContext: function() 
26461     {
26462         var position = new google.maps.LatLng(this.latitude, this.longitude);
26463         
26464         var _map = new google.maps.Map(this.el.dom, {
26465             center: position,
26466             zoom: this.zoom,
26467             mapTypeId: this.mapTypeId,
26468             mapTypeControl: this.mapTypeControl,
26469             disableDoubleClickZoom: this.disableDoubleClickZoom,
26470             scrollwheel: this.scrollwheel,
26471             streetViewControl: this.streetViewControl,
26472             locationName: this.locationName,
26473             draggable: this.draggable,
26474             enableAutocomplete: this.enableAutocomplete,
26475             enableReverseGeocode: this.enableReverseGeocode
26476         });
26477         
26478         var _marker = new google.maps.Marker({
26479             position: position,
26480             map: _map,
26481             title: this.markerTitle,
26482             draggable: this.draggable
26483         });
26484         
26485         return {
26486             map: _map,
26487             marker: _marker,
26488             circle: null,
26489             location: position,
26490             radius: this.radius,
26491             locationName: this.locationName,
26492             addressComponents: {
26493                 formatted_address: null,
26494                 addressLine1: null,
26495                 addressLine2: null,
26496                 streetName: null,
26497                 streetNumber: null,
26498                 city: null,
26499                 district: null,
26500                 state: null,
26501                 stateOrProvince: null
26502             },
26503             settings: this,
26504             domContainer: this.el.dom,
26505             geodecoder: new google.maps.Geocoder()
26506         };
26507     },
26508     
26509     drawCircle: function(center, radius, options) 
26510     {
26511         if (this.gMapContext.circle != null) {
26512             this.gMapContext.circle.setMap(null);
26513         }
26514         if (radius > 0) {
26515             radius *= 1;
26516             options = Roo.apply({}, options, {
26517                 strokeColor: "#0000FF",
26518                 strokeOpacity: .35,
26519                 strokeWeight: 2,
26520                 fillColor: "#0000FF",
26521                 fillOpacity: .2
26522             });
26523             
26524             options.map = this.gMapContext.map;
26525             options.radius = radius;
26526             options.center = center;
26527             this.gMapContext.circle = new google.maps.Circle(options);
26528             return this.gMapContext.circle;
26529         }
26530         
26531         return null;
26532     },
26533     
26534     setPosition: function(location) 
26535     {
26536         this.gMapContext.location = location;
26537         this.gMapContext.marker.setPosition(location);
26538         this.gMapContext.map.panTo(location);
26539         this.drawCircle(location, this.gMapContext.radius, {});
26540         
26541         var _this = this;
26542         
26543         if (this.gMapContext.settings.enableReverseGeocode) {
26544             this.gMapContext.geodecoder.geocode({
26545                 latLng: this.gMapContext.location
26546             }, function(results, status) {
26547                 
26548                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26549                     _this.gMapContext.locationName = results[0].formatted_address;
26550                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26551                     
26552                     _this.fireEvent('positionchanged', this, location);
26553                 }
26554             });
26555             
26556             return;
26557         }
26558         
26559         this.fireEvent('positionchanged', this, location);
26560     },
26561     
26562     resize: function()
26563     {
26564         google.maps.event.trigger(this.gMapContext.map, "resize");
26565         
26566         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26567         
26568         this.fireEvent('resize', this);
26569     },
26570     
26571     setPositionByLatLng: function(latitude, longitude)
26572     {
26573         this.setPosition(new google.maps.LatLng(latitude, longitude));
26574     },
26575     
26576     getCurrentPosition: function() 
26577     {
26578         return {
26579             latitude: this.gMapContext.location.lat(),
26580             longitude: this.gMapContext.location.lng()
26581         };
26582     },
26583     
26584     getAddressName: function() 
26585     {
26586         return this.gMapContext.locationName;
26587     },
26588     
26589     getAddressComponents: function() 
26590     {
26591         return this.gMapContext.addressComponents;
26592     },
26593     
26594     address_component_from_google_geocode: function(address_components) 
26595     {
26596         var result = {};
26597         
26598         for (var i = 0; i < address_components.length; i++) {
26599             var component = address_components[i];
26600             if (component.types.indexOf("postal_code") >= 0) {
26601                 result.postalCode = component.short_name;
26602             } else if (component.types.indexOf("street_number") >= 0) {
26603                 result.streetNumber = component.short_name;
26604             } else if (component.types.indexOf("route") >= 0) {
26605                 result.streetName = component.short_name;
26606             } else if (component.types.indexOf("neighborhood") >= 0) {
26607                 result.city = component.short_name;
26608             } else if (component.types.indexOf("locality") >= 0) {
26609                 result.city = component.short_name;
26610             } else if (component.types.indexOf("sublocality") >= 0) {
26611                 result.district = component.short_name;
26612             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26613                 result.stateOrProvince = component.short_name;
26614             } else if (component.types.indexOf("country") >= 0) {
26615                 result.country = component.short_name;
26616             }
26617         }
26618         
26619         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26620         result.addressLine2 = "";
26621         return result;
26622     },
26623     
26624     setZoomLevel: function(zoom)
26625     {
26626         this.gMapContext.map.setZoom(zoom);
26627     },
26628     
26629     show: function()
26630     {
26631         if(!this.el){
26632             return;
26633         }
26634         
26635         this.el.show();
26636         
26637         this.resize();
26638         
26639         this.fireEvent('show', this);
26640     },
26641     
26642     hide: function()
26643     {
26644         if(!this.el){
26645             return;
26646         }
26647         
26648         this.el.hide();
26649         
26650         this.fireEvent('hide', this);
26651     }
26652     
26653 });
26654
26655 Roo.apply(Roo.bootstrap.LocationPicker, {
26656     
26657     OverlayView : function(map, options)
26658     {
26659         options = options || {};
26660         
26661         this.setMap(map);
26662     }
26663     
26664     
26665 });/*
26666  * - LGPL
26667  *
26668  * Alert
26669  * 
26670  */
26671
26672 /**
26673  * @class Roo.bootstrap.Alert
26674  * @extends Roo.bootstrap.Component
26675  * Bootstrap Alert class
26676  * @cfg {String} title The title of alert
26677  * @cfg {String} html The content of alert
26678  * @cfg {String} weight (  success | info | warning | danger )
26679  * @cfg {String} faicon font-awesomeicon
26680  * 
26681  * @constructor
26682  * Create a new alert
26683  * @param {Object} config The config object
26684  */
26685
26686
26687 Roo.bootstrap.Alert = function(config){
26688     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26689     
26690 };
26691
26692 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26693     
26694     title: '',
26695     html: '',
26696     weight: false,
26697     faicon: false,
26698     
26699     getAutoCreate : function()
26700     {
26701         
26702         var cfg = {
26703             tag : 'div',
26704             cls : 'alert',
26705             cn : [
26706                 {
26707                     tag : 'i',
26708                     cls : 'roo-alert-icon'
26709                     
26710                 },
26711                 {
26712                     tag : 'b',
26713                     cls : 'roo-alert-title',
26714                     html : this.title
26715                 },
26716                 {
26717                     tag : 'span',
26718                     cls : 'roo-alert-text',
26719                     html : this.html
26720                 }
26721             ]
26722         };
26723         
26724         if(this.faicon){
26725             cfg.cn[0].cls += ' fa ' + this.faicon;
26726         }
26727         
26728         if(this.weight){
26729             cfg.cls += ' alert-' + this.weight;
26730         }
26731         
26732         return cfg;
26733     },
26734     
26735     initEvents: function() 
26736     {
26737         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26738     },
26739     
26740     setTitle : function(str)
26741     {
26742         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26743     },
26744     
26745     setText : function(str)
26746     {
26747         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26748     },
26749     
26750     setWeight : function(weight)
26751     {
26752         if(this.weight){
26753             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26754         }
26755         
26756         this.weight = weight;
26757         
26758         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26759     },
26760     
26761     setIcon : function(icon)
26762     {
26763         if(this.faicon){
26764             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26765         }
26766         
26767         this.faicon = icon;
26768         
26769         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26770     },
26771     
26772     hide: function() 
26773     {
26774         this.el.hide();   
26775     },
26776     
26777     show: function() 
26778     {  
26779         this.el.show();   
26780     }
26781     
26782 });
26783
26784  
26785 /*
26786 * Licence: LGPL
26787 */
26788
26789 /**
26790  * @class Roo.bootstrap.UploadCropbox
26791  * @extends Roo.bootstrap.Component
26792  * Bootstrap UploadCropbox class
26793  * @cfg {String} emptyText show when image has been loaded
26794  * @cfg {String} rotateNotify show when image too small to rotate
26795  * @cfg {Number} errorTimeout default 3000
26796  * @cfg {Number} minWidth default 300
26797  * @cfg {Number} minHeight default 300
26798  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26799  * @cfg {Boolean} isDocument (true|false) default false
26800  * @cfg {String} url action url
26801  * @cfg {String} paramName default 'imageUpload'
26802  * @cfg {String} method default POST
26803  * @cfg {Boolean} loadMask (true|false) default true
26804  * @cfg {Boolean} loadingText default 'Loading...'
26805  * 
26806  * @constructor
26807  * Create a new UploadCropbox
26808  * @param {Object} config The config object
26809  */
26810
26811 Roo.bootstrap.UploadCropbox = function(config){
26812     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26813     
26814     this.addEvents({
26815         /**
26816          * @event beforeselectfile
26817          * Fire before select file
26818          * @param {Roo.bootstrap.UploadCropbox} this
26819          */
26820         "beforeselectfile" : true,
26821         /**
26822          * @event initial
26823          * Fire after initEvent
26824          * @param {Roo.bootstrap.UploadCropbox} this
26825          */
26826         "initial" : true,
26827         /**
26828          * @event crop
26829          * Fire after initEvent
26830          * @param {Roo.bootstrap.UploadCropbox} this
26831          * @param {String} data
26832          */
26833         "crop" : true,
26834         /**
26835          * @event prepare
26836          * Fire when preparing the file data
26837          * @param {Roo.bootstrap.UploadCropbox} this
26838          * @param {Object} file
26839          */
26840         "prepare" : true,
26841         /**
26842          * @event exception
26843          * Fire when get exception
26844          * @param {Roo.bootstrap.UploadCropbox} this
26845          * @param {XMLHttpRequest} xhr
26846          */
26847         "exception" : true,
26848         /**
26849          * @event beforeloadcanvas
26850          * Fire before load the canvas
26851          * @param {Roo.bootstrap.UploadCropbox} this
26852          * @param {String} src
26853          */
26854         "beforeloadcanvas" : true,
26855         /**
26856          * @event trash
26857          * Fire when trash image
26858          * @param {Roo.bootstrap.UploadCropbox} this
26859          */
26860         "trash" : true,
26861         /**
26862          * @event download
26863          * Fire when download the image
26864          * @param {Roo.bootstrap.UploadCropbox} this
26865          */
26866         "download" : true,
26867         /**
26868          * @event footerbuttonclick
26869          * Fire when footerbuttonclick
26870          * @param {Roo.bootstrap.UploadCropbox} this
26871          * @param {String} type
26872          */
26873         "footerbuttonclick" : true,
26874         /**
26875          * @event resize
26876          * Fire when resize
26877          * @param {Roo.bootstrap.UploadCropbox} this
26878          */
26879         "resize" : true,
26880         /**
26881          * @event rotate
26882          * Fire when rotate the image
26883          * @param {Roo.bootstrap.UploadCropbox} this
26884          * @param {String} pos
26885          */
26886         "rotate" : true,
26887         /**
26888          * @event inspect
26889          * Fire when inspect the file
26890          * @param {Roo.bootstrap.UploadCropbox} this
26891          * @param {Object} file
26892          */
26893         "inspect" : true,
26894         /**
26895          * @event upload
26896          * Fire when xhr upload the file
26897          * @param {Roo.bootstrap.UploadCropbox} this
26898          * @param {Object} data
26899          */
26900         "upload" : true,
26901         /**
26902          * @event arrange
26903          * Fire when arrange the file data
26904          * @param {Roo.bootstrap.UploadCropbox} this
26905          * @param {Object} formData
26906          */
26907         "arrange" : true
26908     });
26909     
26910     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26911 };
26912
26913 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26914     
26915     emptyText : 'Click to upload image',
26916     rotateNotify : 'Image is too small to rotate',
26917     errorTimeout : 3000,
26918     scale : 0,
26919     baseScale : 1,
26920     rotate : 0,
26921     dragable : false,
26922     pinching : false,
26923     mouseX : 0,
26924     mouseY : 0,
26925     cropData : false,
26926     minWidth : 300,
26927     minHeight : 300,
26928     file : false,
26929     exif : {},
26930     baseRotate : 1,
26931     cropType : 'image/jpeg',
26932     buttons : false,
26933     canvasLoaded : false,
26934     isDocument : false,
26935     method : 'POST',
26936     paramName : 'imageUpload',
26937     loadMask : true,
26938     loadingText : 'Loading...',
26939     maskEl : false,
26940     
26941     getAutoCreate : function()
26942     {
26943         var cfg = {
26944             tag : 'div',
26945             cls : 'roo-upload-cropbox',
26946             cn : [
26947                 {
26948                     tag : 'input',
26949                     cls : 'roo-upload-cropbox-selector',
26950                     type : 'file'
26951                 },
26952                 {
26953                     tag : 'div',
26954                     cls : 'roo-upload-cropbox-body',
26955                     style : 'cursor:pointer',
26956                     cn : [
26957                         {
26958                             tag : 'div',
26959                             cls : 'roo-upload-cropbox-preview'
26960                         },
26961                         {
26962                             tag : 'div',
26963                             cls : 'roo-upload-cropbox-thumb'
26964                         },
26965                         {
26966                             tag : 'div',
26967                             cls : 'roo-upload-cropbox-empty-notify',
26968                             html : this.emptyText
26969                         },
26970                         {
26971                             tag : 'div',
26972                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26973                             html : this.rotateNotify
26974                         }
26975                     ]
26976                 },
26977                 {
26978                     tag : 'div',
26979                     cls : 'roo-upload-cropbox-footer',
26980                     cn : {
26981                         tag : 'div',
26982                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26983                         cn : []
26984                     }
26985                 }
26986             ]
26987         };
26988         
26989         return cfg;
26990     },
26991     
26992     onRender : function(ct, position)
26993     {
26994         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26995         
26996         if (this.buttons.length) {
26997             
26998             Roo.each(this.buttons, function(bb) {
26999                 
27000                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27001                 
27002                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27003                 
27004             }, this);
27005         }
27006         
27007         if(this.loadMask){
27008             this.maskEl = this.el;
27009         }
27010     },
27011     
27012     initEvents : function()
27013     {
27014         this.urlAPI = (window.createObjectURL && window) || 
27015                                 (window.URL && URL.revokeObjectURL && URL) || 
27016                                 (window.webkitURL && webkitURL);
27017                         
27018         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27019         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27020         
27021         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27022         this.selectorEl.hide();
27023         
27024         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27025         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27026         
27027         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27028         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27029         this.thumbEl.hide();
27030         
27031         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27032         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27033         
27034         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27035         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27036         this.errorEl.hide();
27037         
27038         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27039         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27040         this.footerEl.hide();
27041         
27042         this.setThumbBoxSize();
27043         
27044         this.bind();
27045         
27046         this.resize();
27047         
27048         this.fireEvent('initial', this);
27049     },
27050
27051     bind : function()
27052     {
27053         var _this = this;
27054         
27055         window.addEventListener("resize", function() { _this.resize(); } );
27056         
27057         this.bodyEl.on('click', this.beforeSelectFile, this);
27058         
27059         if(Roo.isTouch){
27060             this.bodyEl.on('touchstart', this.onTouchStart, this);
27061             this.bodyEl.on('touchmove', this.onTouchMove, this);
27062             this.bodyEl.on('touchend', this.onTouchEnd, this);
27063         }
27064         
27065         if(!Roo.isTouch){
27066             this.bodyEl.on('mousedown', this.onMouseDown, this);
27067             this.bodyEl.on('mousemove', this.onMouseMove, this);
27068             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27069             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27070             Roo.get(document).on('mouseup', this.onMouseUp, this);
27071         }
27072         
27073         this.selectorEl.on('change', this.onFileSelected, this);
27074     },
27075     
27076     reset : function()
27077     {    
27078         this.scale = 0;
27079         this.baseScale = 1;
27080         this.rotate = 0;
27081         this.baseRotate = 1;
27082         this.dragable = false;
27083         this.pinching = false;
27084         this.mouseX = 0;
27085         this.mouseY = 0;
27086         this.cropData = false;
27087         this.notifyEl.dom.innerHTML = this.emptyText;
27088         
27089         this.selectorEl.dom.value = '';
27090         
27091     },
27092     
27093     resize : function()
27094     {
27095         if(this.fireEvent('resize', this) != false){
27096             this.setThumbBoxPosition();
27097             this.setCanvasPosition();
27098         }
27099     },
27100     
27101     onFooterButtonClick : function(e, el, o, type)
27102     {
27103         switch (type) {
27104             case 'rotate-left' :
27105                 this.onRotateLeft(e);
27106                 break;
27107             case 'rotate-right' :
27108                 this.onRotateRight(e);
27109                 break;
27110             case 'picture' :
27111                 this.beforeSelectFile(e);
27112                 break;
27113             case 'trash' :
27114                 this.trash(e);
27115                 break;
27116             case 'crop' :
27117                 this.crop(e);
27118                 break;
27119             case 'download' :
27120                 this.download(e);
27121                 break;
27122             default :
27123                 break;
27124         }
27125         
27126         this.fireEvent('footerbuttonclick', this, type);
27127     },
27128     
27129     beforeSelectFile : function(e)
27130     {
27131         e.preventDefault();
27132         
27133         if(this.fireEvent('beforeselectfile', this) != false){
27134             this.selectorEl.dom.click();
27135         }
27136     },
27137     
27138     onFileSelected : function(e)
27139     {
27140         e.preventDefault();
27141         
27142         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27143             return;
27144         }
27145         
27146         var file = this.selectorEl.dom.files[0];
27147         
27148         if(this.fireEvent('inspect', this, file) != false){
27149             this.prepare(file);
27150         }
27151         
27152     },
27153     
27154     trash : function(e)
27155     {
27156         this.fireEvent('trash', this);
27157     },
27158     
27159     download : function(e)
27160     {
27161         this.fireEvent('download', this);
27162     },
27163     
27164     loadCanvas : function(src)
27165     {   
27166         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27167             
27168             this.reset();
27169             
27170             this.imageEl = document.createElement('img');
27171             
27172             var _this = this;
27173             
27174             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27175             
27176             this.imageEl.src = src;
27177         }
27178     },
27179     
27180     onLoadCanvas : function()
27181     {   
27182         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27183         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27184         
27185         this.bodyEl.un('click', this.beforeSelectFile, this);
27186         
27187         this.notifyEl.hide();
27188         this.thumbEl.show();
27189         this.footerEl.show();
27190         
27191         this.baseRotateLevel();
27192         
27193         if(this.isDocument){
27194             this.setThumbBoxSize();
27195         }
27196         
27197         this.setThumbBoxPosition();
27198         
27199         this.baseScaleLevel();
27200         
27201         this.draw();
27202         
27203         this.resize();
27204         
27205         this.canvasLoaded = true;
27206         
27207         if(this.loadMask){
27208             this.maskEl.unmask();
27209         }
27210         
27211     },
27212     
27213     setCanvasPosition : function()
27214     {   
27215         if(!this.canvasEl){
27216             return;
27217         }
27218         
27219         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27220         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27221         
27222         this.previewEl.setLeft(pw);
27223         this.previewEl.setTop(ph);
27224         
27225     },
27226     
27227     onMouseDown : function(e)
27228     {   
27229         e.stopEvent();
27230         
27231         this.dragable = true;
27232         this.pinching = false;
27233         
27234         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27235             this.dragable = false;
27236             return;
27237         }
27238         
27239         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27240         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27241         
27242     },
27243     
27244     onMouseMove : function(e)
27245     {   
27246         e.stopEvent();
27247         
27248         if(!this.canvasLoaded){
27249             return;
27250         }
27251         
27252         if (!this.dragable){
27253             return;
27254         }
27255         
27256         var minX = Math.ceil(this.thumbEl.getLeft(true));
27257         var minY = Math.ceil(this.thumbEl.getTop(true));
27258         
27259         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27260         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27261         
27262         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27263         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27264         
27265         x = x - this.mouseX;
27266         y = y - this.mouseY;
27267         
27268         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27269         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27270         
27271         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27272         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27273         
27274         this.previewEl.setLeft(bgX);
27275         this.previewEl.setTop(bgY);
27276         
27277         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27278         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27279     },
27280     
27281     onMouseUp : function(e)
27282     {   
27283         e.stopEvent();
27284         
27285         this.dragable = false;
27286     },
27287     
27288     onMouseWheel : function(e)
27289     {   
27290         e.stopEvent();
27291         
27292         this.startScale = this.scale;
27293         
27294         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27295         
27296         if(!this.zoomable()){
27297             this.scale = this.startScale;
27298             return;
27299         }
27300         
27301         this.draw();
27302         
27303         return;
27304     },
27305     
27306     zoomable : function()
27307     {
27308         var minScale = this.thumbEl.getWidth() / this.minWidth;
27309         
27310         if(this.minWidth < this.minHeight){
27311             minScale = this.thumbEl.getHeight() / this.minHeight;
27312         }
27313         
27314         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27315         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27316         
27317         if(
27318                 this.isDocument &&
27319                 (this.rotate == 0 || this.rotate == 180) && 
27320                 (
27321                     width > this.imageEl.OriginWidth || 
27322                     height > this.imageEl.OriginHeight ||
27323                     (width < this.minWidth && height < this.minHeight)
27324                 )
27325         ){
27326             return false;
27327         }
27328         
27329         if(
27330                 this.isDocument &&
27331                 (this.rotate == 90 || this.rotate == 270) && 
27332                 (
27333                     width > this.imageEl.OriginWidth || 
27334                     height > this.imageEl.OriginHeight ||
27335                     (width < this.minHeight && height < this.minWidth)
27336                 )
27337         ){
27338             return false;
27339         }
27340         
27341         if(
27342                 !this.isDocument &&
27343                 (this.rotate == 0 || this.rotate == 180) && 
27344                 (
27345                     width < this.minWidth || 
27346                     width > this.imageEl.OriginWidth || 
27347                     height < this.minHeight || 
27348                     height > this.imageEl.OriginHeight
27349                 )
27350         ){
27351             return false;
27352         }
27353         
27354         if(
27355                 !this.isDocument &&
27356                 (this.rotate == 90 || this.rotate == 270) && 
27357                 (
27358                     width < this.minHeight || 
27359                     width > this.imageEl.OriginWidth || 
27360                     height < this.minWidth || 
27361                     height > this.imageEl.OriginHeight
27362                 )
27363         ){
27364             return false;
27365         }
27366         
27367         return true;
27368         
27369     },
27370     
27371     onRotateLeft : function(e)
27372     {   
27373         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27374             
27375             var minScale = this.thumbEl.getWidth() / this.minWidth;
27376             
27377             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27378             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27379             
27380             this.startScale = this.scale;
27381             
27382             while (this.getScaleLevel() < minScale){
27383             
27384                 this.scale = this.scale + 1;
27385                 
27386                 if(!this.zoomable()){
27387                     break;
27388                 }
27389                 
27390                 if(
27391                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27392                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27393                 ){
27394                     continue;
27395                 }
27396                 
27397                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27398
27399                 this.draw();
27400                 
27401                 return;
27402             }
27403             
27404             this.scale = this.startScale;
27405             
27406             this.onRotateFail();
27407             
27408             return false;
27409         }
27410         
27411         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27412
27413         if(this.isDocument){
27414             this.setThumbBoxSize();
27415             this.setThumbBoxPosition();
27416             this.setCanvasPosition();
27417         }
27418         
27419         this.draw();
27420         
27421         this.fireEvent('rotate', this, 'left');
27422         
27423     },
27424     
27425     onRotateRight : function(e)
27426     {
27427         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27428             
27429             var minScale = this.thumbEl.getWidth() / this.minWidth;
27430         
27431             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27432             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27433             
27434             this.startScale = this.scale;
27435             
27436             while (this.getScaleLevel() < minScale){
27437             
27438                 this.scale = this.scale + 1;
27439                 
27440                 if(!this.zoomable()){
27441                     break;
27442                 }
27443                 
27444                 if(
27445                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27446                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27447                 ){
27448                     continue;
27449                 }
27450                 
27451                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27452
27453                 this.draw();
27454                 
27455                 return;
27456             }
27457             
27458             this.scale = this.startScale;
27459             
27460             this.onRotateFail();
27461             
27462             return false;
27463         }
27464         
27465         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27466
27467         if(this.isDocument){
27468             this.setThumbBoxSize();
27469             this.setThumbBoxPosition();
27470             this.setCanvasPosition();
27471         }
27472         
27473         this.draw();
27474         
27475         this.fireEvent('rotate', this, 'right');
27476     },
27477     
27478     onRotateFail : function()
27479     {
27480         this.errorEl.show(true);
27481         
27482         var _this = this;
27483         
27484         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27485     },
27486     
27487     draw : function()
27488     {
27489         this.previewEl.dom.innerHTML = '';
27490         
27491         var canvasEl = document.createElement("canvas");
27492         
27493         var contextEl = canvasEl.getContext("2d");
27494         
27495         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27496         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27497         var center = this.imageEl.OriginWidth / 2;
27498         
27499         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27500             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27501             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27502             center = this.imageEl.OriginHeight / 2;
27503         }
27504         
27505         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27506         
27507         contextEl.translate(center, center);
27508         contextEl.rotate(this.rotate * Math.PI / 180);
27509
27510         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27511         
27512         this.canvasEl = document.createElement("canvas");
27513         
27514         this.contextEl = this.canvasEl.getContext("2d");
27515         
27516         switch (this.rotate) {
27517             case 0 :
27518                 
27519                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27520                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27521                 
27522                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27523                 
27524                 break;
27525             case 90 : 
27526                 
27527                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27528                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27529                 
27530                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27531                     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);
27532                     break;
27533                 }
27534                 
27535                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27536                 
27537                 break;
27538             case 180 :
27539                 
27540                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27541                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27542                 
27543                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27544                     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);
27545                     break;
27546                 }
27547                 
27548                 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);
27549                 
27550                 break;
27551             case 270 :
27552                 
27553                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27554                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27555         
27556                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27557                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27558                     break;
27559                 }
27560                 
27561                 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);
27562                 
27563                 break;
27564             default : 
27565                 break;
27566         }
27567         
27568         this.previewEl.appendChild(this.canvasEl);
27569         
27570         this.setCanvasPosition();
27571     },
27572     
27573     crop : function()
27574     {
27575         if(!this.canvasLoaded){
27576             return;
27577         }
27578         
27579         var imageCanvas = document.createElement("canvas");
27580         
27581         var imageContext = imageCanvas.getContext("2d");
27582         
27583         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27584         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27585         
27586         var center = imageCanvas.width / 2;
27587         
27588         imageContext.translate(center, center);
27589         
27590         imageContext.rotate(this.rotate * Math.PI / 180);
27591         
27592         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27593         
27594         var canvas = document.createElement("canvas");
27595         
27596         var context = canvas.getContext("2d");
27597                 
27598         canvas.width = this.minWidth;
27599         canvas.height = this.minHeight;
27600
27601         switch (this.rotate) {
27602             case 0 :
27603                 
27604                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27605                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27606                 
27607                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27608                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27609                 
27610                 var targetWidth = this.minWidth - 2 * x;
27611                 var targetHeight = this.minHeight - 2 * y;
27612                 
27613                 var scale = 1;
27614                 
27615                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27616                     scale = targetWidth / width;
27617                 }
27618                 
27619                 if(x > 0 && y == 0){
27620                     scale = targetHeight / height;
27621                 }
27622                 
27623                 if(x > 0 && y > 0){
27624                     scale = targetWidth / width;
27625                     
27626                     if(width < height){
27627                         scale = targetHeight / height;
27628                     }
27629                 }
27630                 
27631                 context.scale(scale, scale);
27632                 
27633                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27634                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27635
27636                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27637                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27638
27639                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27640                 
27641                 break;
27642             case 90 : 
27643                 
27644                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27645                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27646                 
27647                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27648                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27649                 
27650                 var targetWidth = this.minWidth - 2 * x;
27651                 var targetHeight = this.minHeight - 2 * y;
27652                 
27653                 var scale = 1;
27654                 
27655                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27656                     scale = targetWidth / width;
27657                 }
27658                 
27659                 if(x > 0 && y == 0){
27660                     scale = targetHeight / height;
27661                 }
27662                 
27663                 if(x > 0 && y > 0){
27664                     scale = targetWidth / width;
27665                     
27666                     if(width < height){
27667                         scale = targetHeight / height;
27668                     }
27669                 }
27670                 
27671                 context.scale(scale, scale);
27672                 
27673                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27674                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27675
27676                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27677                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27678                 
27679                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27680                 
27681                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27682                 
27683                 break;
27684             case 180 :
27685                 
27686                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27687                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27688                 
27689                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27690                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27691                 
27692                 var targetWidth = this.minWidth - 2 * x;
27693                 var targetHeight = this.minHeight - 2 * y;
27694                 
27695                 var scale = 1;
27696                 
27697                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27698                     scale = targetWidth / width;
27699                 }
27700                 
27701                 if(x > 0 && y == 0){
27702                     scale = targetHeight / height;
27703                 }
27704                 
27705                 if(x > 0 && y > 0){
27706                     scale = targetWidth / width;
27707                     
27708                     if(width < height){
27709                         scale = targetHeight / height;
27710                     }
27711                 }
27712                 
27713                 context.scale(scale, scale);
27714                 
27715                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27716                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27717
27718                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27719                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27720
27721                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27722                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27723                 
27724                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27725                 
27726                 break;
27727             case 270 :
27728                 
27729                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27730                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27731                 
27732                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27733                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27734                 
27735                 var targetWidth = this.minWidth - 2 * x;
27736                 var targetHeight = this.minHeight - 2 * y;
27737                 
27738                 var scale = 1;
27739                 
27740                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27741                     scale = targetWidth / width;
27742                 }
27743                 
27744                 if(x > 0 && y == 0){
27745                     scale = targetHeight / height;
27746                 }
27747                 
27748                 if(x > 0 && y > 0){
27749                     scale = targetWidth / width;
27750                     
27751                     if(width < height){
27752                         scale = targetHeight / height;
27753                     }
27754                 }
27755                 
27756                 context.scale(scale, scale);
27757                 
27758                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27759                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27760
27761                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27762                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27763                 
27764                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27765                 
27766                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27767                 
27768                 break;
27769             default : 
27770                 break;
27771         }
27772         
27773         this.cropData = canvas.toDataURL(this.cropType);
27774         
27775         if(this.fireEvent('crop', this, this.cropData) !== false){
27776             this.process(this.file, this.cropData);
27777         }
27778         
27779         return;
27780         
27781     },
27782     
27783     setThumbBoxSize : function()
27784     {
27785         var width, height;
27786         
27787         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27788             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27789             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27790             
27791             this.minWidth = width;
27792             this.minHeight = height;
27793             
27794             if(this.rotate == 90 || this.rotate == 270){
27795                 this.minWidth = height;
27796                 this.minHeight = width;
27797             }
27798         }
27799         
27800         height = 300;
27801         width = Math.ceil(this.minWidth * height / this.minHeight);
27802         
27803         if(this.minWidth > this.minHeight){
27804             width = 300;
27805             height = Math.ceil(this.minHeight * width / this.minWidth);
27806         }
27807         
27808         this.thumbEl.setStyle({
27809             width : width + 'px',
27810             height : height + 'px'
27811         });
27812
27813         return;
27814             
27815     },
27816     
27817     setThumbBoxPosition : function()
27818     {
27819         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27820         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27821         
27822         this.thumbEl.setLeft(x);
27823         this.thumbEl.setTop(y);
27824         
27825     },
27826     
27827     baseRotateLevel : function()
27828     {
27829         this.baseRotate = 1;
27830         
27831         if(
27832                 typeof(this.exif) != 'undefined' &&
27833                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27834                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27835         ){
27836             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27837         }
27838         
27839         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27840         
27841     },
27842     
27843     baseScaleLevel : function()
27844     {
27845         var width, height;
27846         
27847         if(this.isDocument){
27848             
27849             if(this.baseRotate == 6 || this.baseRotate == 8){
27850             
27851                 height = this.thumbEl.getHeight();
27852                 this.baseScale = height / this.imageEl.OriginWidth;
27853
27854                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27855                     width = this.thumbEl.getWidth();
27856                     this.baseScale = width / this.imageEl.OriginHeight;
27857                 }
27858
27859                 return;
27860             }
27861
27862             height = this.thumbEl.getHeight();
27863             this.baseScale = height / this.imageEl.OriginHeight;
27864
27865             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27866                 width = this.thumbEl.getWidth();
27867                 this.baseScale = width / this.imageEl.OriginWidth;
27868             }
27869
27870             return;
27871         }
27872         
27873         if(this.baseRotate == 6 || this.baseRotate == 8){
27874             
27875             width = this.thumbEl.getHeight();
27876             this.baseScale = width / this.imageEl.OriginHeight;
27877             
27878             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27879                 height = this.thumbEl.getWidth();
27880                 this.baseScale = height / this.imageEl.OriginHeight;
27881             }
27882             
27883             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27884                 height = this.thumbEl.getWidth();
27885                 this.baseScale = height / this.imageEl.OriginHeight;
27886                 
27887                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27888                     width = this.thumbEl.getHeight();
27889                     this.baseScale = width / this.imageEl.OriginWidth;
27890                 }
27891             }
27892             
27893             return;
27894         }
27895         
27896         width = this.thumbEl.getWidth();
27897         this.baseScale = width / this.imageEl.OriginWidth;
27898         
27899         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27900             height = this.thumbEl.getHeight();
27901             this.baseScale = height / this.imageEl.OriginHeight;
27902         }
27903         
27904         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27905             
27906             height = this.thumbEl.getHeight();
27907             this.baseScale = height / this.imageEl.OriginHeight;
27908             
27909             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27910                 width = this.thumbEl.getWidth();
27911                 this.baseScale = width / this.imageEl.OriginWidth;
27912             }
27913             
27914         }
27915         
27916         return;
27917     },
27918     
27919     getScaleLevel : function()
27920     {
27921         return this.baseScale * Math.pow(1.1, this.scale);
27922     },
27923     
27924     onTouchStart : function(e)
27925     {
27926         if(!this.canvasLoaded){
27927             this.beforeSelectFile(e);
27928             return;
27929         }
27930         
27931         var touches = e.browserEvent.touches;
27932         
27933         if(!touches){
27934             return;
27935         }
27936         
27937         if(touches.length == 1){
27938             this.onMouseDown(e);
27939             return;
27940         }
27941         
27942         if(touches.length != 2){
27943             return;
27944         }
27945         
27946         var coords = [];
27947         
27948         for(var i = 0, finger; finger = touches[i]; i++){
27949             coords.push(finger.pageX, finger.pageY);
27950         }
27951         
27952         var x = Math.pow(coords[0] - coords[2], 2);
27953         var y = Math.pow(coords[1] - coords[3], 2);
27954         
27955         this.startDistance = Math.sqrt(x + y);
27956         
27957         this.startScale = this.scale;
27958         
27959         this.pinching = true;
27960         this.dragable = false;
27961         
27962     },
27963     
27964     onTouchMove : function(e)
27965     {
27966         if(!this.pinching && !this.dragable){
27967             return;
27968         }
27969         
27970         var touches = e.browserEvent.touches;
27971         
27972         if(!touches){
27973             return;
27974         }
27975         
27976         if(this.dragable){
27977             this.onMouseMove(e);
27978             return;
27979         }
27980         
27981         var coords = [];
27982         
27983         for(var i = 0, finger; finger = touches[i]; i++){
27984             coords.push(finger.pageX, finger.pageY);
27985         }
27986         
27987         var x = Math.pow(coords[0] - coords[2], 2);
27988         var y = Math.pow(coords[1] - coords[3], 2);
27989         
27990         this.endDistance = Math.sqrt(x + y);
27991         
27992         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27993         
27994         if(!this.zoomable()){
27995             this.scale = this.startScale;
27996             return;
27997         }
27998         
27999         this.draw();
28000         
28001     },
28002     
28003     onTouchEnd : function(e)
28004     {
28005         this.pinching = false;
28006         this.dragable = false;
28007         
28008     },
28009     
28010     process : function(file, crop)
28011     {
28012         if(this.loadMask){
28013             this.maskEl.mask(this.loadingText);
28014         }
28015         
28016         this.xhr = new XMLHttpRequest();
28017         
28018         file.xhr = this.xhr;
28019
28020         this.xhr.open(this.method, this.url, true);
28021         
28022         var headers = {
28023             "Accept": "application/json",
28024             "Cache-Control": "no-cache",
28025             "X-Requested-With": "XMLHttpRequest"
28026         };
28027         
28028         for (var headerName in headers) {
28029             var headerValue = headers[headerName];
28030             if (headerValue) {
28031                 this.xhr.setRequestHeader(headerName, headerValue);
28032             }
28033         }
28034         
28035         var _this = this;
28036         
28037         this.xhr.onload = function()
28038         {
28039             _this.xhrOnLoad(_this.xhr);
28040         }
28041         
28042         this.xhr.onerror = function()
28043         {
28044             _this.xhrOnError(_this.xhr);
28045         }
28046         
28047         var formData = new FormData();
28048
28049         formData.append('returnHTML', 'NO');
28050         
28051         if(crop){
28052             formData.append('crop', crop);
28053         }
28054         
28055         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28056             formData.append(this.paramName, file, file.name);
28057         }
28058         
28059         if(typeof(file.filename) != 'undefined'){
28060             formData.append('filename', file.filename);
28061         }
28062         
28063         if(typeof(file.mimetype) != 'undefined'){
28064             formData.append('mimetype', file.mimetype);
28065         }
28066         
28067         if(this.fireEvent('arrange', this, formData) != false){
28068             this.xhr.send(formData);
28069         };
28070     },
28071     
28072     xhrOnLoad : function(xhr)
28073     {
28074         if(this.loadMask){
28075             this.maskEl.unmask();
28076         }
28077         
28078         if (xhr.readyState !== 4) {
28079             this.fireEvent('exception', this, xhr);
28080             return;
28081         }
28082
28083         var response = Roo.decode(xhr.responseText);
28084         
28085         if(!response.success){
28086             this.fireEvent('exception', this, xhr);
28087             return;
28088         }
28089         
28090         var response = Roo.decode(xhr.responseText);
28091         
28092         this.fireEvent('upload', this, response);
28093         
28094     },
28095     
28096     xhrOnError : function()
28097     {
28098         if(this.loadMask){
28099             this.maskEl.unmask();
28100         }
28101         
28102         Roo.log('xhr on error');
28103         
28104         var response = Roo.decode(xhr.responseText);
28105           
28106         Roo.log(response);
28107         
28108     },
28109     
28110     prepare : function(file)
28111     {   
28112         if(this.loadMask){
28113             this.maskEl.mask(this.loadingText);
28114         }
28115         
28116         this.file = false;
28117         this.exif = {};
28118         
28119         if(typeof(file) === 'string'){
28120             this.loadCanvas(file);
28121             return;
28122         }
28123         
28124         if(!file || !this.urlAPI){
28125             return;
28126         }
28127         
28128         this.file = file;
28129         this.cropType = file.type;
28130         
28131         var _this = this;
28132         
28133         if(this.fireEvent('prepare', this, this.file) != false){
28134             
28135             var reader = new FileReader();
28136             
28137             reader.onload = function (e) {
28138                 if (e.target.error) {
28139                     Roo.log(e.target.error);
28140                     return;
28141                 }
28142                 
28143                 var buffer = e.target.result,
28144                     dataView = new DataView(buffer),
28145                     offset = 2,
28146                     maxOffset = dataView.byteLength - 4,
28147                     markerBytes,
28148                     markerLength;
28149                 
28150                 if (dataView.getUint16(0) === 0xffd8) {
28151                     while (offset < maxOffset) {
28152                         markerBytes = dataView.getUint16(offset);
28153                         
28154                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28155                             markerLength = dataView.getUint16(offset + 2) + 2;
28156                             if (offset + markerLength > dataView.byteLength) {
28157                                 Roo.log('Invalid meta data: Invalid segment size.');
28158                                 break;
28159                             }
28160                             
28161                             if(markerBytes == 0xffe1){
28162                                 _this.parseExifData(
28163                                     dataView,
28164                                     offset,
28165                                     markerLength
28166                                 );
28167                             }
28168                             
28169                             offset += markerLength;
28170                             
28171                             continue;
28172                         }
28173                         
28174                         break;
28175                     }
28176                     
28177                 }
28178                 
28179                 var url = _this.urlAPI.createObjectURL(_this.file);
28180                 
28181                 _this.loadCanvas(url);
28182                 
28183                 return;
28184             }
28185             
28186             reader.readAsArrayBuffer(this.file);
28187             
28188         }
28189         
28190     },
28191     
28192     parseExifData : function(dataView, offset, length)
28193     {
28194         var tiffOffset = offset + 10,
28195             littleEndian,
28196             dirOffset;
28197     
28198         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28199             // No Exif data, might be XMP data instead
28200             return;
28201         }
28202         
28203         // Check for the ASCII code for "Exif" (0x45786966):
28204         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28205             // No Exif data, might be XMP data instead
28206             return;
28207         }
28208         if (tiffOffset + 8 > dataView.byteLength) {
28209             Roo.log('Invalid Exif data: Invalid segment size.');
28210             return;
28211         }
28212         // Check for the two null bytes:
28213         if (dataView.getUint16(offset + 8) !== 0x0000) {
28214             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28215             return;
28216         }
28217         // Check the byte alignment:
28218         switch (dataView.getUint16(tiffOffset)) {
28219         case 0x4949:
28220             littleEndian = true;
28221             break;
28222         case 0x4D4D:
28223             littleEndian = false;
28224             break;
28225         default:
28226             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28227             return;
28228         }
28229         // Check for the TIFF tag marker (0x002A):
28230         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28231             Roo.log('Invalid Exif data: Missing TIFF marker.');
28232             return;
28233         }
28234         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28235         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28236         
28237         this.parseExifTags(
28238             dataView,
28239             tiffOffset,
28240             tiffOffset + dirOffset,
28241             littleEndian
28242         );
28243     },
28244     
28245     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28246     {
28247         var tagsNumber,
28248             dirEndOffset,
28249             i;
28250         if (dirOffset + 6 > dataView.byteLength) {
28251             Roo.log('Invalid Exif data: Invalid directory offset.');
28252             return;
28253         }
28254         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28255         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28256         if (dirEndOffset + 4 > dataView.byteLength) {
28257             Roo.log('Invalid Exif data: Invalid directory size.');
28258             return;
28259         }
28260         for (i = 0; i < tagsNumber; i += 1) {
28261             this.parseExifTag(
28262                 dataView,
28263                 tiffOffset,
28264                 dirOffset + 2 + 12 * i, // tag offset
28265                 littleEndian
28266             );
28267         }
28268         // Return the offset to the next directory:
28269         return dataView.getUint32(dirEndOffset, littleEndian);
28270     },
28271     
28272     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28273     {
28274         var tag = dataView.getUint16(offset, littleEndian);
28275         
28276         this.exif[tag] = this.getExifValue(
28277             dataView,
28278             tiffOffset,
28279             offset,
28280             dataView.getUint16(offset + 2, littleEndian), // tag type
28281             dataView.getUint32(offset + 4, littleEndian), // tag length
28282             littleEndian
28283         );
28284     },
28285     
28286     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28287     {
28288         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28289             tagSize,
28290             dataOffset,
28291             values,
28292             i,
28293             str,
28294             c;
28295     
28296         if (!tagType) {
28297             Roo.log('Invalid Exif data: Invalid tag type.');
28298             return;
28299         }
28300         
28301         tagSize = tagType.size * length;
28302         // Determine if the value is contained in the dataOffset bytes,
28303         // or if the value at the dataOffset is a pointer to the actual data:
28304         dataOffset = tagSize > 4 ?
28305                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28306         if (dataOffset + tagSize > dataView.byteLength) {
28307             Roo.log('Invalid Exif data: Invalid data offset.');
28308             return;
28309         }
28310         if (length === 1) {
28311             return tagType.getValue(dataView, dataOffset, littleEndian);
28312         }
28313         values = [];
28314         for (i = 0; i < length; i += 1) {
28315             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28316         }
28317         
28318         if (tagType.ascii) {
28319             str = '';
28320             // Concatenate the chars:
28321             for (i = 0; i < values.length; i += 1) {
28322                 c = values[i];
28323                 // Ignore the terminating NULL byte(s):
28324                 if (c === '\u0000') {
28325                     break;
28326                 }
28327                 str += c;
28328             }
28329             return str;
28330         }
28331         return values;
28332     }
28333     
28334 });
28335
28336 Roo.apply(Roo.bootstrap.UploadCropbox, {
28337     tags : {
28338         'Orientation': 0x0112
28339     },
28340     
28341     Orientation: {
28342             1: 0, //'top-left',
28343 //            2: 'top-right',
28344             3: 180, //'bottom-right',
28345 //            4: 'bottom-left',
28346 //            5: 'left-top',
28347             6: 90, //'right-top',
28348 //            7: 'right-bottom',
28349             8: 270 //'left-bottom'
28350     },
28351     
28352     exifTagTypes : {
28353         // byte, 8-bit unsigned int:
28354         1: {
28355             getValue: function (dataView, dataOffset) {
28356                 return dataView.getUint8(dataOffset);
28357             },
28358             size: 1
28359         },
28360         // ascii, 8-bit byte:
28361         2: {
28362             getValue: function (dataView, dataOffset) {
28363                 return String.fromCharCode(dataView.getUint8(dataOffset));
28364             },
28365             size: 1,
28366             ascii: true
28367         },
28368         // short, 16 bit int:
28369         3: {
28370             getValue: function (dataView, dataOffset, littleEndian) {
28371                 return dataView.getUint16(dataOffset, littleEndian);
28372             },
28373             size: 2
28374         },
28375         // long, 32 bit int:
28376         4: {
28377             getValue: function (dataView, dataOffset, littleEndian) {
28378                 return dataView.getUint32(dataOffset, littleEndian);
28379             },
28380             size: 4
28381         },
28382         // rational = two long values, first is numerator, second is denominator:
28383         5: {
28384             getValue: function (dataView, dataOffset, littleEndian) {
28385                 return dataView.getUint32(dataOffset, littleEndian) /
28386                     dataView.getUint32(dataOffset + 4, littleEndian);
28387             },
28388             size: 8
28389         },
28390         // slong, 32 bit signed int:
28391         9: {
28392             getValue: function (dataView, dataOffset, littleEndian) {
28393                 return dataView.getInt32(dataOffset, littleEndian);
28394             },
28395             size: 4
28396         },
28397         // srational, two slongs, first is numerator, second is denominator:
28398         10: {
28399             getValue: function (dataView, dataOffset, littleEndian) {
28400                 return dataView.getInt32(dataOffset, littleEndian) /
28401                     dataView.getInt32(dataOffset + 4, littleEndian);
28402             },
28403             size: 8
28404         }
28405     },
28406     
28407     footer : {
28408         STANDARD : [
28409             {
28410                 tag : 'div',
28411                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28412                 action : 'rotate-left',
28413                 cn : [
28414                     {
28415                         tag : 'button',
28416                         cls : 'btn btn-default',
28417                         html : '<i class="fa fa-undo"></i>'
28418                     }
28419                 ]
28420             },
28421             {
28422                 tag : 'div',
28423                 cls : 'btn-group roo-upload-cropbox-picture',
28424                 action : 'picture',
28425                 cn : [
28426                     {
28427                         tag : 'button',
28428                         cls : 'btn btn-default',
28429                         html : '<i class="fa fa-picture-o"></i>'
28430                     }
28431                 ]
28432             },
28433             {
28434                 tag : 'div',
28435                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28436                 action : 'rotate-right',
28437                 cn : [
28438                     {
28439                         tag : 'button',
28440                         cls : 'btn btn-default',
28441                         html : '<i class="fa fa-repeat"></i>'
28442                     }
28443                 ]
28444             }
28445         ],
28446         DOCUMENT : [
28447             {
28448                 tag : 'div',
28449                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28450                 action : 'rotate-left',
28451                 cn : [
28452                     {
28453                         tag : 'button',
28454                         cls : 'btn btn-default',
28455                         html : '<i class="fa fa-undo"></i>'
28456                     }
28457                 ]
28458             },
28459             {
28460                 tag : 'div',
28461                 cls : 'btn-group roo-upload-cropbox-download',
28462                 action : 'download',
28463                 cn : [
28464                     {
28465                         tag : 'button',
28466                         cls : 'btn btn-default',
28467                         html : '<i class="fa fa-download"></i>'
28468                     }
28469                 ]
28470             },
28471             {
28472                 tag : 'div',
28473                 cls : 'btn-group roo-upload-cropbox-crop',
28474                 action : 'crop',
28475                 cn : [
28476                     {
28477                         tag : 'button',
28478                         cls : 'btn btn-default',
28479                         html : '<i class="fa fa-crop"></i>'
28480                     }
28481                 ]
28482             },
28483             {
28484                 tag : 'div',
28485                 cls : 'btn-group roo-upload-cropbox-trash',
28486                 action : 'trash',
28487                 cn : [
28488                     {
28489                         tag : 'button',
28490                         cls : 'btn btn-default',
28491                         html : '<i class="fa fa-trash"></i>'
28492                     }
28493                 ]
28494             },
28495             {
28496                 tag : 'div',
28497                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28498                 action : 'rotate-right',
28499                 cn : [
28500                     {
28501                         tag : 'button',
28502                         cls : 'btn btn-default',
28503                         html : '<i class="fa fa-repeat"></i>'
28504                     }
28505                 ]
28506             }
28507         ],
28508         ROTATOR : [
28509             {
28510                 tag : 'div',
28511                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28512                 action : 'rotate-left',
28513                 cn : [
28514                     {
28515                         tag : 'button',
28516                         cls : 'btn btn-default',
28517                         html : '<i class="fa fa-undo"></i>'
28518                     }
28519                 ]
28520             },
28521             {
28522                 tag : 'div',
28523                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28524                 action : 'rotate-right',
28525                 cn : [
28526                     {
28527                         tag : 'button',
28528                         cls : 'btn btn-default',
28529                         html : '<i class="fa fa-repeat"></i>'
28530                     }
28531                 ]
28532             }
28533         ]
28534     }
28535 });
28536
28537 /*
28538 * Licence: LGPL
28539 */
28540
28541 /**
28542  * @class Roo.bootstrap.DocumentManager
28543  * @extends Roo.bootstrap.Component
28544  * Bootstrap DocumentManager class
28545  * @cfg {String} paramName default 'imageUpload'
28546  * @cfg {String} toolTipName default 'filename'
28547  * @cfg {String} method default POST
28548  * @cfg {String} url action url
28549  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28550  * @cfg {Boolean} multiple multiple upload default true
28551  * @cfg {Number} thumbSize default 300
28552  * @cfg {String} fieldLabel
28553  * @cfg {Number} labelWidth default 4
28554  * @cfg {String} labelAlign (left|top) default left
28555  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28556 * @cfg {Number} labellg set the width of label (1-12)
28557  * @cfg {Number} labelmd set the width of label (1-12)
28558  * @cfg {Number} labelsm set the width of label (1-12)
28559  * @cfg {Number} labelxs set the width of label (1-12)
28560  * 
28561  * @constructor
28562  * Create a new DocumentManager
28563  * @param {Object} config The config object
28564  */
28565
28566 Roo.bootstrap.DocumentManager = function(config){
28567     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28568     
28569     this.files = [];
28570     this.delegates = [];
28571     
28572     this.addEvents({
28573         /**
28574          * @event initial
28575          * Fire when initial the DocumentManager
28576          * @param {Roo.bootstrap.DocumentManager} this
28577          */
28578         "initial" : true,
28579         /**
28580          * @event inspect
28581          * inspect selected file
28582          * @param {Roo.bootstrap.DocumentManager} this
28583          * @param {File} file
28584          */
28585         "inspect" : true,
28586         /**
28587          * @event exception
28588          * Fire when xhr load exception
28589          * @param {Roo.bootstrap.DocumentManager} this
28590          * @param {XMLHttpRequest} xhr
28591          */
28592         "exception" : true,
28593         /**
28594          * @event afterupload
28595          * Fire when xhr load exception
28596          * @param {Roo.bootstrap.DocumentManager} this
28597          * @param {XMLHttpRequest} xhr
28598          */
28599         "afterupload" : true,
28600         /**
28601          * @event prepare
28602          * prepare the form data
28603          * @param {Roo.bootstrap.DocumentManager} this
28604          * @param {Object} formData
28605          */
28606         "prepare" : true,
28607         /**
28608          * @event remove
28609          * Fire when remove the file
28610          * @param {Roo.bootstrap.DocumentManager} this
28611          * @param {Object} file
28612          */
28613         "remove" : true,
28614         /**
28615          * @event refresh
28616          * Fire after refresh the file
28617          * @param {Roo.bootstrap.DocumentManager} this
28618          */
28619         "refresh" : true,
28620         /**
28621          * @event click
28622          * Fire after click the image
28623          * @param {Roo.bootstrap.DocumentManager} this
28624          * @param {Object} file
28625          */
28626         "click" : true,
28627         /**
28628          * @event edit
28629          * Fire when upload a image and editable set to true
28630          * @param {Roo.bootstrap.DocumentManager} this
28631          * @param {Object} file
28632          */
28633         "edit" : true,
28634         /**
28635          * @event beforeselectfile
28636          * Fire before select file
28637          * @param {Roo.bootstrap.DocumentManager} this
28638          */
28639         "beforeselectfile" : true,
28640         /**
28641          * @event process
28642          * Fire before process file
28643          * @param {Roo.bootstrap.DocumentManager} this
28644          * @param {Object} file
28645          */
28646         "process" : true,
28647         /**
28648          * @event previewrendered
28649          * Fire when preview rendered
28650          * @param {Roo.bootstrap.DocumentManager} this
28651          * @param {Object} file
28652          */
28653         "previewrendered" : true
28654         
28655     });
28656 };
28657
28658 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28659     
28660     boxes : 0,
28661     inputName : '',
28662     thumbSize : 300,
28663     multiple : true,
28664     files : false,
28665     method : 'POST',
28666     url : '',
28667     paramName : 'imageUpload',
28668     toolTipName : 'filename',
28669     fieldLabel : '',
28670     labelWidth : 4,
28671     labelAlign : 'left',
28672     editable : true,
28673     delegates : false,
28674     xhr : false, 
28675     
28676     labellg : 0,
28677     labelmd : 0,
28678     labelsm : 0,
28679     labelxs : 0,
28680     
28681     getAutoCreate : function()
28682     {   
28683         var managerWidget = {
28684             tag : 'div',
28685             cls : 'roo-document-manager',
28686             cn : [
28687                 {
28688                     tag : 'input',
28689                     cls : 'roo-document-manager-selector',
28690                     type : 'file'
28691                 },
28692                 {
28693                     tag : 'div',
28694                     cls : 'roo-document-manager-uploader',
28695                     cn : [
28696                         {
28697                             tag : 'div',
28698                             cls : 'roo-document-manager-upload-btn',
28699                             html : '<i class="fa fa-plus"></i>'
28700                         }
28701                     ]
28702                     
28703                 }
28704             ]
28705         };
28706         
28707         var content = [
28708             {
28709                 tag : 'div',
28710                 cls : 'column col-md-12',
28711                 cn : managerWidget
28712             }
28713         ];
28714         
28715         if(this.fieldLabel.length){
28716             
28717             content = [
28718                 {
28719                     tag : 'div',
28720                     cls : 'column col-md-12',
28721                     html : this.fieldLabel
28722                 },
28723                 {
28724                     tag : 'div',
28725                     cls : 'column col-md-12',
28726                     cn : managerWidget
28727                 }
28728             ];
28729
28730             if(this.labelAlign == 'left'){
28731                 content = [
28732                     {
28733                         tag : 'div',
28734                         cls : 'column',
28735                         html : this.fieldLabel
28736                     },
28737                     {
28738                         tag : 'div',
28739                         cls : 'column',
28740                         cn : managerWidget
28741                     }
28742                 ];
28743                 
28744                 if(this.labelWidth > 12){
28745                     content[0].style = "width: " + this.labelWidth + 'px';
28746                 }
28747
28748                 if(this.labelWidth < 13 && this.labelmd == 0){
28749                     this.labelmd = this.labelWidth;
28750                 }
28751
28752                 if(this.labellg > 0){
28753                     content[0].cls += ' col-lg-' + this.labellg;
28754                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28755                 }
28756
28757                 if(this.labelmd > 0){
28758                     content[0].cls += ' col-md-' + this.labelmd;
28759                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28760                 }
28761
28762                 if(this.labelsm > 0){
28763                     content[0].cls += ' col-sm-' + this.labelsm;
28764                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28765                 }
28766
28767                 if(this.labelxs > 0){
28768                     content[0].cls += ' col-xs-' + this.labelxs;
28769                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28770                 }
28771                 
28772             }
28773         }
28774         
28775         var cfg = {
28776             tag : 'div',
28777             cls : 'row clearfix',
28778             cn : content
28779         };
28780         
28781         return cfg;
28782         
28783     },
28784     
28785     initEvents : function()
28786     {
28787         this.managerEl = this.el.select('.roo-document-manager', true).first();
28788         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28789         
28790         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28791         this.selectorEl.hide();
28792         
28793         if(this.multiple){
28794             this.selectorEl.attr('multiple', 'multiple');
28795         }
28796         
28797         this.selectorEl.on('change', this.onFileSelected, this);
28798         
28799         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28800         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28801         
28802         this.uploader.on('click', this.onUploaderClick, this);
28803         
28804         this.renderProgressDialog();
28805         
28806         var _this = this;
28807         
28808         window.addEventListener("resize", function() { _this.refresh(); } );
28809         
28810         this.fireEvent('initial', this);
28811     },
28812     
28813     renderProgressDialog : function()
28814     {
28815         var _this = this;
28816         
28817         this.progressDialog = new Roo.bootstrap.Modal({
28818             cls : 'roo-document-manager-progress-dialog',
28819             allow_close : false,
28820             title : '',
28821             buttons : [
28822                 {
28823                     name  :'cancel',
28824                     weight : 'danger',
28825                     html : 'Cancel'
28826                 }
28827             ], 
28828             listeners : { 
28829                 btnclick : function() {
28830                     _this.uploadCancel();
28831                     this.hide();
28832                 }
28833             }
28834         });
28835          
28836         this.progressDialog.render(Roo.get(document.body));
28837          
28838         this.progress = new Roo.bootstrap.Progress({
28839             cls : 'roo-document-manager-progress',
28840             active : true,
28841             striped : true
28842         });
28843         
28844         this.progress.render(this.progressDialog.getChildContainer());
28845         
28846         this.progressBar = new Roo.bootstrap.ProgressBar({
28847             cls : 'roo-document-manager-progress-bar',
28848             aria_valuenow : 0,
28849             aria_valuemin : 0,
28850             aria_valuemax : 12,
28851             panel : 'success'
28852         });
28853         
28854         this.progressBar.render(this.progress.getChildContainer());
28855     },
28856     
28857     onUploaderClick : function(e)
28858     {
28859         e.preventDefault();
28860      
28861         if(this.fireEvent('beforeselectfile', this) != false){
28862             this.selectorEl.dom.click();
28863         }
28864         
28865     },
28866     
28867     onFileSelected : function(e)
28868     {
28869         e.preventDefault();
28870         
28871         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28872             return;
28873         }
28874         
28875         Roo.each(this.selectorEl.dom.files, function(file){
28876             if(this.fireEvent('inspect', this, file) != false){
28877                 this.files.push(file);
28878             }
28879         }, this);
28880         
28881         this.queue();
28882         
28883     },
28884     
28885     queue : function()
28886     {
28887         this.selectorEl.dom.value = '';
28888         
28889         if(!this.files || !this.files.length){
28890             return;
28891         }
28892         
28893         if(this.boxes > 0 && this.files.length > this.boxes){
28894             this.files = this.files.slice(0, this.boxes);
28895         }
28896         
28897         this.uploader.show();
28898         
28899         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28900             this.uploader.hide();
28901         }
28902         
28903         var _this = this;
28904         
28905         var files = [];
28906         
28907         var docs = [];
28908         
28909         Roo.each(this.files, function(file){
28910             
28911             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28912                 var f = this.renderPreview(file);
28913                 files.push(f);
28914                 return;
28915             }
28916             
28917             if(file.type.indexOf('image') != -1){
28918                 this.delegates.push(
28919                     (function(){
28920                         _this.process(file);
28921                     }).createDelegate(this)
28922                 );
28923         
28924                 return;
28925             }
28926             
28927             docs.push(
28928                 (function(){
28929                     _this.process(file);
28930                 }).createDelegate(this)
28931             );
28932             
28933         }, this);
28934         
28935         this.files = files;
28936         
28937         this.delegates = this.delegates.concat(docs);
28938         
28939         if(!this.delegates.length){
28940             this.refresh();
28941             return;
28942         }
28943         
28944         this.progressBar.aria_valuemax = this.delegates.length;
28945         
28946         this.arrange();
28947         
28948         return;
28949     },
28950     
28951     arrange : function()
28952     {
28953         if(!this.delegates.length){
28954             this.progressDialog.hide();
28955             this.refresh();
28956             return;
28957         }
28958         
28959         var delegate = this.delegates.shift();
28960         
28961         this.progressDialog.show();
28962         
28963         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28964         
28965         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28966         
28967         delegate();
28968     },
28969     
28970     refresh : function()
28971     {
28972         this.uploader.show();
28973         
28974         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28975             this.uploader.hide();
28976         }
28977         
28978         Roo.isTouch ? this.closable(false) : this.closable(true);
28979         
28980         this.fireEvent('refresh', this);
28981     },
28982     
28983     onRemove : function(e, el, o)
28984     {
28985         e.preventDefault();
28986         
28987         this.fireEvent('remove', this, o);
28988         
28989     },
28990     
28991     remove : function(o)
28992     {
28993         var files = [];
28994         
28995         Roo.each(this.files, function(file){
28996             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28997                 files.push(file);
28998                 return;
28999             }
29000
29001             o.target.remove();
29002
29003         }, this);
29004         
29005         this.files = files;
29006         
29007         this.refresh();
29008     },
29009     
29010     clear : function()
29011     {
29012         Roo.each(this.files, function(file){
29013             if(!file.target){
29014                 return;
29015             }
29016             
29017             file.target.remove();
29018
29019         }, this);
29020         
29021         this.files = [];
29022         
29023         this.refresh();
29024     },
29025     
29026     onClick : function(e, el, o)
29027     {
29028         e.preventDefault();
29029         
29030         this.fireEvent('click', this, o);
29031         
29032     },
29033     
29034     closable : function(closable)
29035     {
29036         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29037             
29038             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29039             
29040             if(closable){
29041                 el.show();
29042                 return;
29043             }
29044             
29045             el.hide();
29046             
29047         }, this);
29048     },
29049     
29050     xhrOnLoad : function(xhr)
29051     {
29052         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29053             el.remove();
29054         }, this);
29055         
29056         if (xhr.readyState !== 4) {
29057             this.arrange();
29058             this.fireEvent('exception', this, xhr);
29059             return;
29060         }
29061
29062         var response = Roo.decode(xhr.responseText);
29063         
29064         if(!response.success){
29065             this.arrange();
29066             this.fireEvent('exception', this, xhr);
29067             return;
29068         }
29069         
29070         var file = this.renderPreview(response.data);
29071         
29072         this.files.push(file);
29073         
29074         this.arrange();
29075         
29076         this.fireEvent('afterupload', this, xhr);
29077         
29078     },
29079     
29080     xhrOnError : function(xhr)
29081     {
29082         Roo.log('xhr on error');
29083         
29084         var response = Roo.decode(xhr.responseText);
29085           
29086         Roo.log(response);
29087         
29088         this.arrange();
29089     },
29090     
29091     process : function(file)
29092     {
29093         if(this.fireEvent('process', this, file) !== false){
29094             if(this.editable && file.type.indexOf('image') != -1){
29095                 this.fireEvent('edit', this, file);
29096                 return;
29097             }
29098
29099             this.uploadStart(file, false);
29100
29101             return;
29102         }
29103         
29104     },
29105     
29106     uploadStart : function(file, crop)
29107     {
29108         this.xhr = new XMLHttpRequest();
29109         
29110         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29111             this.arrange();
29112             return;
29113         }
29114         
29115         file.xhr = this.xhr;
29116             
29117         this.managerEl.createChild({
29118             tag : 'div',
29119             cls : 'roo-document-manager-loading',
29120             cn : [
29121                 {
29122                     tag : 'div',
29123                     tooltip : file.name,
29124                     cls : 'roo-document-manager-thumb',
29125                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29126                 }
29127             ]
29128
29129         });
29130
29131         this.xhr.open(this.method, this.url, true);
29132         
29133         var headers = {
29134             "Accept": "application/json",
29135             "Cache-Control": "no-cache",
29136             "X-Requested-With": "XMLHttpRequest"
29137         };
29138         
29139         for (var headerName in headers) {
29140             var headerValue = headers[headerName];
29141             if (headerValue) {
29142                 this.xhr.setRequestHeader(headerName, headerValue);
29143             }
29144         }
29145         
29146         var _this = this;
29147         
29148         this.xhr.onload = function()
29149         {
29150             _this.xhrOnLoad(_this.xhr);
29151         }
29152         
29153         this.xhr.onerror = function()
29154         {
29155             _this.xhrOnError(_this.xhr);
29156         }
29157         
29158         var formData = new FormData();
29159
29160         formData.append('returnHTML', 'NO');
29161         
29162         if(crop){
29163             formData.append('crop', crop);
29164         }
29165         
29166         formData.append(this.paramName, file, file.name);
29167         
29168         var options = {
29169             file : file, 
29170             manually : false
29171         };
29172         
29173         if(this.fireEvent('prepare', this, formData, options) != false){
29174             
29175             if(options.manually){
29176                 return;
29177             }
29178             
29179             this.xhr.send(formData);
29180             return;
29181         };
29182         
29183         this.uploadCancel();
29184     },
29185     
29186     uploadCancel : function()
29187     {
29188         if (this.xhr) {
29189             this.xhr.abort();
29190         }
29191         
29192         this.delegates = [];
29193         
29194         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29195             el.remove();
29196         }, this);
29197         
29198         this.arrange();
29199     },
29200     
29201     renderPreview : function(file)
29202     {
29203         if(typeof(file.target) != 'undefined' && file.target){
29204             return file;
29205         }
29206         
29207         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29208         
29209         var previewEl = this.managerEl.createChild({
29210             tag : 'div',
29211             cls : 'roo-document-manager-preview',
29212             cn : [
29213                 {
29214                     tag : 'div',
29215                     tooltip : file[this.toolTipName],
29216                     cls : 'roo-document-manager-thumb',
29217                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29218                 },
29219                 {
29220                     tag : 'button',
29221                     cls : 'close',
29222                     html : '<i class="fa fa-times-circle"></i>'
29223                 }
29224             ]
29225         });
29226
29227         var close = previewEl.select('button.close', true).first();
29228
29229         close.on('click', this.onRemove, this, file);
29230
29231         file.target = previewEl;
29232
29233         var image = previewEl.select('img', true).first();
29234         
29235         var _this = this;
29236         
29237         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29238         
29239         image.on('click', this.onClick, this, file);
29240         
29241         this.fireEvent('previewrendered', this, file);
29242         
29243         return file;
29244         
29245     },
29246     
29247     onPreviewLoad : function(file, image)
29248     {
29249         if(typeof(file.target) == 'undefined' || !file.target){
29250             return;
29251         }
29252         
29253         var width = image.dom.naturalWidth || image.dom.width;
29254         var height = image.dom.naturalHeight || image.dom.height;
29255         
29256         if(width > height){
29257             file.target.addClass('wide');
29258             return;
29259         }
29260         
29261         file.target.addClass('tall');
29262         return;
29263         
29264     },
29265     
29266     uploadFromSource : function(file, crop)
29267     {
29268         this.xhr = new XMLHttpRequest();
29269         
29270         this.managerEl.createChild({
29271             tag : 'div',
29272             cls : 'roo-document-manager-loading',
29273             cn : [
29274                 {
29275                     tag : 'div',
29276                     tooltip : file.name,
29277                     cls : 'roo-document-manager-thumb',
29278                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29279                 }
29280             ]
29281
29282         });
29283
29284         this.xhr.open(this.method, this.url, true);
29285         
29286         var headers = {
29287             "Accept": "application/json",
29288             "Cache-Control": "no-cache",
29289             "X-Requested-With": "XMLHttpRequest"
29290         };
29291         
29292         for (var headerName in headers) {
29293             var headerValue = headers[headerName];
29294             if (headerValue) {
29295                 this.xhr.setRequestHeader(headerName, headerValue);
29296             }
29297         }
29298         
29299         var _this = this;
29300         
29301         this.xhr.onload = function()
29302         {
29303             _this.xhrOnLoad(_this.xhr);
29304         }
29305         
29306         this.xhr.onerror = function()
29307         {
29308             _this.xhrOnError(_this.xhr);
29309         }
29310         
29311         var formData = new FormData();
29312
29313         formData.append('returnHTML', 'NO');
29314         
29315         formData.append('crop', crop);
29316         
29317         if(typeof(file.filename) != 'undefined'){
29318             formData.append('filename', file.filename);
29319         }
29320         
29321         if(typeof(file.mimetype) != 'undefined'){
29322             formData.append('mimetype', file.mimetype);
29323         }
29324         
29325         Roo.log(formData);
29326         
29327         if(this.fireEvent('prepare', this, formData) != false){
29328             this.xhr.send(formData);
29329         };
29330     }
29331 });
29332
29333 /*
29334 * Licence: LGPL
29335 */
29336
29337 /**
29338  * @class Roo.bootstrap.DocumentViewer
29339  * @extends Roo.bootstrap.Component
29340  * Bootstrap DocumentViewer class
29341  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29342  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29343  * 
29344  * @constructor
29345  * Create a new DocumentViewer
29346  * @param {Object} config The config object
29347  */
29348
29349 Roo.bootstrap.DocumentViewer = function(config){
29350     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29351     
29352     this.addEvents({
29353         /**
29354          * @event initial
29355          * Fire after initEvent
29356          * @param {Roo.bootstrap.DocumentViewer} this
29357          */
29358         "initial" : true,
29359         /**
29360          * @event click
29361          * Fire after click
29362          * @param {Roo.bootstrap.DocumentViewer} this
29363          */
29364         "click" : true,
29365         /**
29366          * @event download
29367          * Fire after download button
29368          * @param {Roo.bootstrap.DocumentViewer} this
29369          */
29370         "download" : true,
29371         /**
29372          * @event trash
29373          * Fire after trash button
29374          * @param {Roo.bootstrap.DocumentViewer} this
29375          */
29376         "trash" : true
29377         
29378     });
29379 };
29380
29381 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29382     
29383     showDownload : true,
29384     
29385     showTrash : true,
29386     
29387     getAutoCreate : function()
29388     {
29389         var cfg = {
29390             tag : 'div',
29391             cls : 'roo-document-viewer',
29392             cn : [
29393                 {
29394                     tag : 'div',
29395                     cls : 'roo-document-viewer-body',
29396                     cn : [
29397                         {
29398                             tag : 'div',
29399                             cls : 'roo-document-viewer-thumb',
29400                             cn : [
29401                                 {
29402                                     tag : 'img',
29403                                     cls : 'roo-document-viewer-image'
29404                                 }
29405                             ]
29406                         }
29407                     ]
29408                 },
29409                 {
29410                     tag : 'div',
29411                     cls : 'roo-document-viewer-footer',
29412                     cn : {
29413                         tag : 'div',
29414                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29415                         cn : [
29416                             {
29417                                 tag : 'div',
29418                                 cls : 'btn-group roo-document-viewer-download',
29419                                 cn : [
29420                                     {
29421                                         tag : 'button',
29422                                         cls : 'btn btn-default',
29423                                         html : '<i class="fa fa-download"></i>'
29424                                     }
29425                                 ]
29426                             },
29427                             {
29428                                 tag : 'div',
29429                                 cls : 'btn-group roo-document-viewer-trash',
29430                                 cn : [
29431                                     {
29432                                         tag : 'button',
29433                                         cls : 'btn btn-default',
29434                                         html : '<i class="fa fa-trash"></i>'
29435                                     }
29436                                 ]
29437                             }
29438                         ]
29439                     }
29440                 }
29441             ]
29442         };
29443         
29444         return cfg;
29445     },
29446     
29447     initEvents : function()
29448     {
29449         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29450         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29451         
29452         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29453         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29454         
29455         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29456         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29457         
29458         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29459         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29460         
29461         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29462         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29463         
29464         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29465         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29466         
29467         this.bodyEl.on('click', this.onClick, this);
29468         this.downloadBtn.on('click', this.onDownload, this);
29469         this.trashBtn.on('click', this.onTrash, this);
29470         
29471         this.downloadBtn.hide();
29472         this.trashBtn.hide();
29473         
29474         if(this.showDownload){
29475             this.downloadBtn.show();
29476         }
29477         
29478         if(this.showTrash){
29479             this.trashBtn.show();
29480         }
29481         
29482         if(!this.showDownload && !this.showTrash) {
29483             this.footerEl.hide();
29484         }
29485         
29486     },
29487     
29488     initial : function()
29489     {
29490         this.fireEvent('initial', this);
29491         
29492     },
29493     
29494     onClick : function(e)
29495     {
29496         e.preventDefault();
29497         
29498         this.fireEvent('click', this);
29499     },
29500     
29501     onDownload : function(e)
29502     {
29503         e.preventDefault();
29504         
29505         this.fireEvent('download', this);
29506     },
29507     
29508     onTrash : function(e)
29509     {
29510         e.preventDefault();
29511         
29512         this.fireEvent('trash', this);
29513     }
29514     
29515 });
29516 /*
29517  * - LGPL
29518  *
29519  * nav progress bar
29520  * 
29521  */
29522
29523 /**
29524  * @class Roo.bootstrap.NavProgressBar
29525  * @extends Roo.bootstrap.Component
29526  * Bootstrap NavProgressBar class
29527  * 
29528  * @constructor
29529  * Create a new nav progress bar
29530  * @param {Object} config The config object
29531  */
29532
29533 Roo.bootstrap.NavProgressBar = function(config){
29534     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29535
29536     this.bullets = this.bullets || [];
29537    
29538 //    Roo.bootstrap.NavProgressBar.register(this);
29539      this.addEvents({
29540         /**
29541              * @event changed
29542              * Fires when the active item changes
29543              * @param {Roo.bootstrap.NavProgressBar} this
29544              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29545              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29546          */
29547         'changed': true
29548      });
29549     
29550 };
29551
29552 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29553     
29554     bullets : [],
29555     barItems : [],
29556     
29557     getAutoCreate : function()
29558     {
29559         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29560         
29561         cfg = {
29562             tag : 'div',
29563             cls : 'roo-navigation-bar-group',
29564             cn : [
29565                 {
29566                     tag : 'div',
29567                     cls : 'roo-navigation-top-bar'
29568                 },
29569                 {
29570                     tag : 'div',
29571                     cls : 'roo-navigation-bullets-bar',
29572                     cn : [
29573                         {
29574                             tag : 'ul',
29575                             cls : 'roo-navigation-bar'
29576                         }
29577                     ]
29578                 },
29579                 
29580                 {
29581                     tag : 'div',
29582                     cls : 'roo-navigation-bottom-bar'
29583                 }
29584             ]
29585             
29586         };
29587         
29588         return cfg;
29589         
29590     },
29591     
29592     initEvents: function() 
29593     {
29594         
29595     },
29596     
29597     onRender : function(ct, position) 
29598     {
29599         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29600         
29601         if(this.bullets.length){
29602             Roo.each(this.bullets, function(b){
29603                this.addItem(b);
29604             }, this);
29605         }
29606         
29607         this.format();
29608         
29609     },
29610     
29611     addItem : function(cfg)
29612     {
29613         var item = new Roo.bootstrap.NavProgressItem(cfg);
29614         
29615         item.parentId = this.id;
29616         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29617         
29618         if(cfg.html){
29619             var top = new Roo.bootstrap.Element({
29620                 tag : 'div',
29621                 cls : 'roo-navigation-bar-text'
29622             });
29623             
29624             var bottom = new Roo.bootstrap.Element({
29625                 tag : 'div',
29626                 cls : 'roo-navigation-bar-text'
29627             });
29628             
29629             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29630             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29631             
29632             var topText = new Roo.bootstrap.Element({
29633                 tag : 'span',
29634                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29635             });
29636             
29637             var bottomText = new Roo.bootstrap.Element({
29638                 tag : 'span',
29639                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29640             });
29641             
29642             topText.onRender(top.el, null);
29643             bottomText.onRender(bottom.el, null);
29644             
29645             item.topEl = top;
29646             item.bottomEl = bottom;
29647         }
29648         
29649         this.barItems.push(item);
29650         
29651         return item;
29652     },
29653     
29654     getActive : function()
29655     {
29656         var active = false;
29657         
29658         Roo.each(this.barItems, function(v){
29659             
29660             if (!v.isActive()) {
29661                 return;
29662             }
29663             
29664             active = v;
29665             return false;
29666             
29667         });
29668         
29669         return active;
29670     },
29671     
29672     setActiveItem : function(item)
29673     {
29674         var prev = false;
29675         
29676         Roo.each(this.barItems, function(v){
29677             if (v.rid == item.rid) {
29678                 return ;
29679             }
29680             
29681             if (v.isActive()) {
29682                 v.setActive(false);
29683                 prev = v;
29684             }
29685         });
29686
29687         item.setActive(true);
29688         
29689         this.fireEvent('changed', this, item, prev);
29690     },
29691     
29692     getBarItem: function(rid)
29693     {
29694         var ret = false;
29695         
29696         Roo.each(this.barItems, function(e) {
29697             if (e.rid != rid) {
29698                 return;
29699             }
29700             
29701             ret =  e;
29702             return false;
29703         });
29704         
29705         return ret;
29706     },
29707     
29708     indexOfItem : function(item)
29709     {
29710         var index = false;
29711         
29712         Roo.each(this.barItems, function(v, i){
29713             
29714             if (v.rid != item.rid) {
29715                 return;
29716             }
29717             
29718             index = i;
29719             return false
29720         });
29721         
29722         return index;
29723     },
29724     
29725     setActiveNext : function()
29726     {
29727         var i = this.indexOfItem(this.getActive());
29728         
29729         if (i > this.barItems.length) {
29730             return;
29731         }
29732         
29733         this.setActiveItem(this.barItems[i+1]);
29734     },
29735     
29736     setActivePrev : function()
29737     {
29738         var i = this.indexOfItem(this.getActive());
29739         
29740         if (i  < 1) {
29741             return;
29742         }
29743         
29744         this.setActiveItem(this.barItems[i-1]);
29745     },
29746     
29747     format : function()
29748     {
29749         if(!this.barItems.length){
29750             return;
29751         }
29752      
29753         var width = 100 / this.barItems.length;
29754         
29755         Roo.each(this.barItems, function(i){
29756             i.el.setStyle('width', width + '%');
29757             i.topEl.el.setStyle('width', width + '%');
29758             i.bottomEl.el.setStyle('width', width + '%');
29759         }, this);
29760         
29761     }
29762     
29763 });
29764 /*
29765  * - LGPL
29766  *
29767  * Nav Progress Item
29768  * 
29769  */
29770
29771 /**
29772  * @class Roo.bootstrap.NavProgressItem
29773  * @extends Roo.bootstrap.Component
29774  * Bootstrap NavProgressItem class
29775  * @cfg {String} rid the reference id
29776  * @cfg {Boolean} active (true|false) Is item active default false
29777  * @cfg {Boolean} disabled (true|false) Is item active default false
29778  * @cfg {String} html
29779  * @cfg {String} position (top|bottom) text position default bottom
29780  * @cfg {String} icon show icon instead of number
29781  * 
29782  * @constructor
29783  * Create a new NavProgressItem
29784  * @param {Object} config The config object
29785  */
29786 Roo.bootstrap.NavProgressItem = function(config){
29787     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29788     this.addEvents({
29789         // raw events
29790         /**
29791          * @event click
29792          * The raw click event for the entire grid.
29793          * @param {Roo.bootstrap.NavProgressItem} this
29794          * @param {Roo.EventObject} e
29795          */
29796         "click" : true
29797     });
29798    
29799 };
29800
29801 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29802     
29803     rid : '',
29804     active : false,
29805     disabled : false,
29806     html : '',
29807     position : 'bottom',
29808     icon : false,
29809     
29810     getAutoCreate : function()
29811     {
29812         var iconCls = 'roo-navigation-bar-item-icon';
29813         
29814         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29815         
29816         var cfg = {
29817             tag: 'li',
29818             cls: 'roo-navigation-bar-item',
29819             cn : [
29820                 {
29821                     tag : 'i',
29822                     cls : iconCls
29823                 }
29824             ]
29825         };
29826         
29827         if(this.active){
29828             cfg.cls += ' active';
29829         }
29830         if(this.disabled){
29831             cfg.cls += ' disabled';
29832         }
29833         
29834         return cfg;
29835     },
29836     
29837     disable : function()
29838     {
29839         this.setDisabled(true);
29840     },
29841     
29842     enable : function()
29843     {
29844         this.setDisabled(false);
29845     },
29846     
29847     initEvents: function() 
29848     {
29849         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29850         
29851         this.iconEl.on('click', this.onClick, this);
29852     },
29853     
29854     onClick : function(e)
29855     {
29856         e.preventDefault();
29857         
29858         if(this.disabled){
29859             return;
29860         }
29861         
29862         if(this.fireEvent('click', this, e) === false){
29863             return;
29864         };
29865         
29866         this.parent().setActiveItem(this);
29867     },
29868     
29869     isActive: function () 
29870     {
29871         return this.active;
29872     },
29873     
29874     setActive : function(state)
29875     {
29876         if(this.active == state){
29877             return;
29878         }
29879         
29880         this.active = state;
29881         
29882         if (state) {
29883             this.el.addClass('active');
29884             return;
29885         }
29886         
29887         this.el.removeClass('active');
29888         
29889         return;
29890     },
29891     
29892     setDisabled : function(state)
29893     {
29894         if(this.disabled == state){
29895             return;
29896         }
29897         
29898         this.disabled = state;
29899         
29900         if (state) {
29901             this.el.addClass('disabled');
29902             return;
29903         }
29904         
29905         this.el.removeClass('disabled');
29906     },
29907     
29908     tooltipEl : function()
29909     {
29910         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29911     }
29912 });
29913  
29914
29915  /*
29916  * - LGPL
29917  *
29918  * FieldLabel
29919  * 
29920  */
29921
29922 /**
29923  * @class Roo.bootstrap.FieldLabel
29924  * @extends Roo.bootstrap.Component
29925  * Bootstrap FieldLabel class
29926  * @cfg {String} html contents of the element
29927  * @cfg {String} tag tag of the element default label
29928  * @cfg {String} cls class of the element
29929  * @cfg {String} target label target 
29930  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29931  * @cfg {String} invalidClass default "text-warning"
29932  * @cfg {String} validClass default "text-success"
29933  * @cfg {String} iconTooltip default "This field is required"
29934  * @cfg {String} indicatorpos (left|right) default left
29935  * 
29936  * @constructor
29937  * Create a new FieldLabel
29938  * @param {Object} config The config object
29939  */
29940
29941 Roo.bootstrap.FieldLabel = function(config){
29942     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29943     
29944     this.addEvents({
29945             /**
29946              * @event invalid
29947              * Fires after the field has been marked as invalid.
29948              * @param {Roo.form.FieldLabel} this
29949              * @param {String} msg The validation message
29950              */
29951             invalid : true,
29952             /**
29953              * @event valid
29954              * Fires after the field has been validated with no errors.
29955              * @param {Roo.form.FieldLabel} this
29956              */
29957             valid : true
29958         });
29959 };
29960
29961 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29962     
29963     tag: 'label',
29964     cls: '',
29965     html: '',
29966     target: '',
29967     allowBlank : true,
29968     invalidClass : 'has-warning',
29969     validClass : 'has-success',
29970     iconTooltip : 'This field is required',
29971     indicatorpos : 'left',
29972     
29973     getAutoCreate : function(){
29974         
29975         var cfg = {
29976             tag : this.tag,
29977             cls : 'roo-bootstrap-field-label ' + this.cls,
29978             for : this.target,
29979             cn : [
29980                 {
29981                     tag : 'i',
29982                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29983                     tooltip : this.iconTooltip
29984                 },
29985                 {
29986                     tag : 'span',
29987                     html : this.html
29988                 }
29989             ] 
29990         };
29991         
29992         if(this.indicatorpos == 'right'){
29993             var cfg = {
29994                 tag : this.tag,
29995                 cls : 'roo-bootstrap-field-label ' + this.cls,
29996                 for : this.target,
29997                 cn : [
29998                     {
29999                         tag : 'span',
30000                         html : this.html
30001                     },
30002                     {
30003                         tag : 'i',
30004                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30005                         tooltip : this.iconTooltip
30006                     }
30007                 ] 
30008             };
30009         }
30010         
30011         return cfg;
30012     },
30013     
30014     initEvents: function() 
30015     {
30016         Roo.bootstrap.Element.superclass.initEvents.call(this);
30017         
30018         this.indicator = this.indicatorEl();
30019         
30020         if(this.indicator){
30021             this.indicator.removeClass('visible');
30022             this.indicator.addClass('invisible');
30023         }
30024         
30025         Roo.bootstrap.FieldLabel.register(this);
30026     },
30027     
30028     indicatorEl : function()
30029     {
30030         var indicator = this.el.select('i.roo-required-indicator',true).first();
30031         
30032         if(!indicator){
30033             return false;
30034         }
30035         
30036         return indicator;
30037         
30038     },
30039     
30040     /**
30041      * Mark this field as valid
30042      */
30043     markValid : function()
30044     {
30045         if(this.indicator){
30046             this.indicator.removeClass('visible');
30047             this.indicator.addClass('invisible');
30048         }
30049         
30050         this.el.removeClass(this.invalidClass);
30051         
30052         this.el.addClass(this.validClass);
30053         
30054         this.fireEvent('valid', this);
30055     },
30056     
30057     /**
30058      * Mark this field as invalid
30059      * @param {String} msg The validation message
30060      */
30061     markInvalid : function(msg)
30062     {
30063         if(this.indicator){
30064             this.indicator.removeClass('invisible');
30065             this.indicator.addClass('visible');
30066         }
30067         
30068         this.el.removeClass(this.validClass);
30069         
30070         this.el.addClass(this.invalidClass);
30071         
30072         this.fireEvent('invalid', this, msg);
30073     }
30074     
30075    
30076 });
30077
30078 Roo.apply(Roo.bootstrap.FieldLabel, {
30079     
30080     groups: {},
30081     
30082      /**
30083     * register a FieldLabel Group
30084     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30085     */
30086     register : function(label)
30087     {
30088         if(this.groups.hasOwnProperty(label.target)){
30089             return;
30090         }
30091      
30092         this.groups[label.target] = label;
30093         
30094     },
30095     /**
30096     * fetch a FieldLabel Group based on the target
30097     * @param {string} target
30098     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30099     */
30100     get: function(target) {
30101         if (typeof(this.groups[target]) == 'undefined') {
30102             return false;
30103         }
30104         
30105         return this.groups[target] ;
30106     }
30107 });
30108
30109  
30110
30111  /*
30112  * - LGPL
30113  *
30114  * page DateSplitField.
30115  * 
30116  */
30117
30118
30119 /**
30120  * @class Roo.bootstrap.DateSplitField
30121  * @extends Roo.bootstrap.Component
30122  * Bootstrap DateSplitField class
30123  * @cfg {string} fieldLabel - the label associated
30124  * @cfg {Number} labelWidth set the width of label (0-12)
30125  * @cfg {String} labelAlign (top|left)
30126  * @cfg {Boolean} dayAllowBlank (true|false) default false
30127  * @cfg {Boolean} monthAllowBlank (true|false) default false
30128  * @cfg {Boolean} yearAllowBlank (true|false) default false
30129  * @cfg {string} dayPlaceholder 
30130  * @cfg {string} monthPlaceholder
30131  * @cfg {string} yearPlaceholder
30132  * @cfg {string} dayFormat default 'd'
30133  * @cfg {string} monthFormat default 'm'
30134  * @cfg {string} yearFormat default 'Y'
30135  * @cfg {Number} labellg set the width of label (1-12)
30136  * @cfg {Number} labelmd set the width of label (1-12)
30137  * @cfg {Number} labelsm set the width of label (1-12)
30138  * @cfg {Number} labelxs set the width of label (1-12)
30139
30140  *     
30141  * @constructor
30142  * Create a new DateSplitField
30143  * @param {Object} config The config object
30144  */
30145
30146 Roo.bootstrap.DateSplitField = function(config){
30147     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30148     
30149     this.addEvents({
30150         // raw events
30151          /**
30152          * @event years
30153          * getting the data of years
30154          * @param {Roo.bootstrap.DateSplitField} this
30155          * @param {Object} years
30156          */
30157         "years" : true,
30158         /**
30159          * @event days
30160          * getting the data of days
30161          * @param {Roo.bootstrap.DateSplitField} this
30162          * @param {Object} days
30163          */
30164         "days" : true,
30165         /**
30166          * @event invalid
30167          * Fires after the field has been marked as invalid.
30168          * @param {Roo.form.Field} this
30169          * @param {String} msg The validation message
30170          */
30171         invalid : true,
30172        /**
30173          * @event valid
30174          * Fires after the field has been validated with no errors.
30175          * @param {Roo.form.Field} this
30176          */
30177         valid : true
30178     });
30179 };
30180
30181 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30182     
30183     fieldLabel : '',
30184     labelAlign : 'top',
30185     labelWidth : 3,
30186     dayAllowBlank : false,
30187     monthAllowBlank : false,
30188     yearAllowBlank : false,
30189     dayPlaceholder : '',
30190     monthPlaceholder : '',
30191     yearPlaceholder : '',
30192     dayFormat : 'd',
30193     monthFormat : 'm',
30194     yearFormat : 'Y',
30195     isFormField : true,
30196     labellg : 0,
30197     labelmd : 0,
30198     labelsm : 0,
30199     labelxs : 0,
30200     
30201     getAutoCreate : function()
30202     {
30203         var cfg = {
30204             tag : 'div',
30205             cls : 'row roo-date-split-field-group',
30206             cn : [
30207                 {
30208                     tag : 'input',
30209                     type : 'hidden',
30210                     cls : 'form-hidden-field roo-date-split-field-group-value',
30211                     name : this.name
30212                 }
30213             ]
30214         };
30215         
30216         var labelCls = 'col-md-12';
30217         var contentCls = 'col-md-4';
30218         
30219         if(this.fieldLabel){
30220             
30221             var label = {
30222                 tag : 'div',
30223                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30224                 cn : [
30225                     {
30226                         tag : 'label',
30227                         html : this.fieldLabel
30228                     }
30229                 ]
30230             };
30231             
30232             if(this.labelAlign == 'left'){
30233             
30234                 if(this.labelWidth > 12){
30235                     label.style = "width: " + this.labelWidth + 'px';
30236                 }
30237
30238                 if(this.labelWidth < 13 && this.labelmd == 0){
30239                     this.labelmd = this.labelWidth;
30240                 }
30241
30242                 if(this.labellg > 0){
30243                     labelCls = ' col-lg-' + this.labellg;
30244                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30245                 }
30246
30247                 if(this.labelmd > 0){
30248                     labelCls = ' col-md-' + this.labelmd;
30249                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30250                 }
30251
30252                 if(this.labelsm > 0){
30253                     labelCls = ' col-sm-' + this.labelsm;
30254                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30255                 }
30256
30257                 if(this.labelxs > 0){
30258                     labelCls = ' col-xs-' + this.labelxs;
30259                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30260                 }
30261             }
30262             
30263             label.cls += ' ' + labelCls;
30264             
30265             cfg.cn.push(label);
30266         }
30267         
30268         Roo.each(['day', 'month', 'year'], function(t){
30269             cfg.cn.push({
30270                 tag : 'div',
30271                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30272             });
30273         }, this);
30274         
30275         return cfg;
30276     },
30277     
30278     inputEl: function ()
30279     {
30280         return this.el.select('.roo-date-split-field-group-value', true).first();
30281     },
30282     
30283     onRender : function(ct, position) 
30284     {
30285         var _this = this;
30286         
30287         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30288         
30289         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30290         
30291         this.dayField = new Roo.bootstrap.ComboBox({
30292             allowBlank : this.dayAllowBlank,
30293             alwaysQuery : true,
30294             displayField : 'value',
30295             editable : false,
30296             fieldLabel : '',
30297             forceSelection : true,
30298             mode : 'local',
30299             placeholder : this.dayPlaceholder,
30300             selectOnFocus : true,
30301             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30302             triggerAction : 'all',
30303             typeAhead : true,
30304             valueField : 'value',
30305             store : new Roo.data.SimpleStore({
30306                 data : (function() {    
30307                     var days = [];
30308                     _this.fireEvent('days', _this, days);
30309                     return days;
30310                 })(),
30311                 fields : [ 'value' ]
30312             }),
30313             listeners : {
30314                 select : function (_self, record, index)
30315                 {
30316                     _this.setValue(_this.getValue());
30317                 }
30318             }
30319         });
30320
30321         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30322         
30323         this.monthField = new Roo.bootstrap.MonthField({
30324             after : '<i class=\"fa fa-calendar\"></i>',
30325             allowBlank : this.monthAllowBlank,
30326             placeholder : this.monthPlaceholder,
30327             readOnly : true,
30328             listeners : {
30329                 render : function (_self)
30330                 {
30331                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30332                         e.preventDefault();
30333                         _self.focus();
30334                     });
30335                 },
30336                 select : function (_self, oldvalue, newvalue)
30337                 {
30338                     _this.setValue(_this.getValue());
30339                 }
30340             }
30341         });
30342         
30343         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30344         
30345         this.yearField = new Roo.bootstrap.ComboBox({
30346             allowBlank : this.yearAllowBlank,
30347             alwaysQuery : true,
30348             displayField : 'value',
30349             editable : false,
30350             fieldLabel : '',
30351             forceSelection : true,
30352             mode : 'local',
30353             placeholder : this.yearPlaceholder,
30354             selectOnFocus : true,
30355             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30356             triggerAction : 'all',
30357             typeAhead : true,
30358             valueField : 'value',
30359             store : new Roo.data.SimpleStore({
30360                 data : (function() {
30361                     var years = [];
30362                     _this.fireEvent('years', _this, years);
30363                     return years;
30364                 })(),
30365                 fields : [ 'value' ]
30366             }),
30367             listeners : {
30368                 select : function (_self, record, index)
30369                 {
30370                     _this.setValue(_this.getValue());
30371                 }
30372             }
30373         });
30374
30375         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30376     },
30377     
30378     setValue : function(v, format)
30379     {
30380         this.inputEl.dom.value = v;
30381         
30382         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30383         
30384         var d = Date.parseDate(v, f);
30385         
30386         if(!d){
30387             this.validate();
30388             return;
30389         }
30390         
30391         this.setDay(d.format(this.dayFormat));
30392         this.setMonth(d.format(this.monthFormat));
30393         this.setYear(d.format(this.yearFormat));
30394         
30395         this.validate();
30396         
30397         return;
30398     },
30399     
30400     setDay : function(v)
30401     {
30402         this.dayField.setValue(v);
30403         this.inputEl.dom.value = this.getValue();
30404         this.validate();
30405         return;
30406     },
30407     
30408     setMonth : function(v)
30409     {
30410         this.monthField.setValue(v, true);
30411         this.inputEl.dom.value = this.getValue();
30412         this.validate();
30413         return;
30414     },
30415     
30416     setYear : function(v)
30417     {
30418         this.yearField.setValue(v);
30419         this.inputEl.dom.value = this.getValue();
30420         this.validate();
30421         return;
30422     },
30423     
30424     getDay : function()
30425     {
30426         return this.dayField.getValue();
30427     },
30428     
30429     getMonth : function()
30430     {
30431         return this.monthField.getValue();
30432     },
30433     
30434     getYear : function()
30435     {
30436         return this.yearField.getValue();
30437     },
30438     
30439     getValue : function()
30440     {
30441         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30442         
30443         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30444         
30445         return date;
30446     },
30447     
30448     reset : function()
30449     {
30450         this.setDay('');
30451         this.setMonth('');
30452         this.setYear('');
30453         this.inputEl.dom.value = '';
30454         this.validate();
30455         return;
30456     },
30457     
30458     validate : function()
30459     {
30460         var d = this.dayField.validate();
30461         var m = this.monthField.validate();
30462         var y = this.yearField.validate();
30463         
30464         var valid = true;
30465         
30466         if(
30467                 (!this.dayAllowBlank && !d) ||
30468                 (!this.monthAllowBlank && !m) ||
30469                 (!this.yearAllowBlank && !y)
30470         ){
30471             valid = false;
30472         }
30473         
30474         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30475             return valid;
30476         }
30477         
30478         if(valid){
30479             this.markValid();
30480             return valid;
30481         }
30482         
30483         this.markInvalid();
30484         
30485         return valid;
30486     },
30487     
30488     markValid : function()
30489     {
30490         
30491         var label = this.el.select('label', true).first();
30492         var icon = this.el.select('i.fa-star', true).first();
30493
30494         if(label && icon){
30495             icon.remove();
30496         }
30497         
30498         this.fireEvent('valid', this);
30499     },
30500     
30501      /**
30502      * Mark this field as invalid
30503      * @param {String} msg The validation message
30504      */
30505     markInvalid : function(msg)
30506     {
30507         
30508         var label = this.el.select('label', true).first();
30509         var icon = this.el.select('i.fa-star', true).first();
30510
30511         if(label && !icon){
30512             this.el.select('.roo-date-split-field-label', true).createChild({
30513                 tag : 'i',
30514                 cls : 'text-danger fa fa-lg fa-star',
30515                 tooltip : 'This field is required',
30516                 style : 'margin-right:5px;'
30517             }, label, true);
30518         }
30519         
30520         this.fireEvent('invalid', this, msg);
30521     },
30522     
30523     clearInvalid : function()
30524     {
30525         var label = this.el.select('label', true).first();
30526         var icon = this.el.select('i.fa-star', true).first();
30527
30528         if(label && icon){
30529             icon.remove();
30530         }
30531         
30532         this.fireEvent('valid', this);
30533     },
30534     
30535     getName: function()
30536     {
30537         return this.name;
30538     }
30539     
30540 });
30541
30542  /**
30543  *
30544  * This is based on 
30545  * http://masonry.desandro.com
30546  *
30547  * The idea is to render all the bricks based on vertical width...
30548  *
30549  * The original code extends 'outlayer' - we might need to use that....
30550  * 
30551  */
30552
30553
30554 /**
30555  * @class Roo.bootstrap.LayoutMasonry
30556  * @extends Roo.bootstrap.Component
30557  * Bootstrap Layout Masonry class
30558  * 
30559  * @constructor
30560  * Create a new Element
30561  * @param {Object} config The config object
30562  */
30563
30564 Roo.bootstrap.LayoutMasonry = function(config){
30565     
30566     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30567     
30568     this.bricks = [];
30569     
30570     Roo.bootstrap.LayoutMasonry.register(this);
30571     
30572     this.addEvents({
30573         // raw events
30574         /**
30575          * @event layout
30576          * Fire after layout the items
30577          * @param {Roo.bootstrap.LayoutMasonry} this
30578          * @param {Roo.EventObject} e
30579          */
30580         "layout" : true
30581     });
30582     
30583 };
30584
30585 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30586     
30587     /**
30588      * @cfg {Boolean} isLayoutInstant = no animation?
30589      */   
30590     isLayoutInstant : false, // needed?
30591    
30592     /**
30593      * @cfg {Number} boxWidth  width of the columns
30594      */   
30595     boxWidth : 450,
30596     
30597       /**
30598      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30599      */   
30600     boxHeight : 0,
30601     
30602     /**
30603      * @cfg {Number} padWidth padding below box..
30604      */   
30605     padWidth : 10, 
30606     
30607     /**
30608      * @cfg {Number} gutter gutter width..
30609      */   
30610     gutter : 10,
30611     
30612      /**
30613      * @cfg {Number} maxCols maximum number of columns
30614      */   
30615     
30616     maxCols: 0,
30617     
30618     /**
30619      * @cfg {Boolean} isAutoInitial defalut true
30620      */   
30621     isAutoInitial : true, 
30622     
30623     containerWidth: 0,
30624     
30625     /**
30626      * @cfg {Boolean} isHorizontal defalut false
30627      */   
30628     isHorizontal : false, 
30629
30630     currentSize : null,
30631     
30632     tag: 'div',
30633     
30634     cls: '',
30635     
30636     bricks: null, //CompositeElement
30637     
30638     cols : 1,
30639     
30640     _isLayoutInited : false,
30641     
30642 //    isAlternative : false, // only use for vertical layout...
30643     
30644     /**
30645      * @cfg {Number} alternativePadWidth padding below box..
30646      */   
30647     alternativePadWidth : 50,
30648     
30649     selectedBrick : [],
30650     
30651     getAutoCreate : function(){
30652         
30653         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30654         
30655         var cfg = {
30656             tag: this.tag,
30657             cls: 'blog-masonary-wrapper ' + this.cls,
30658             cn : {
30659                 cls : 'mas-boxes masonary'
30660             }
30661         };
30662         
30663         return cfg;
30664     },
30665     
30666     getChildContainer: function( )
30667     {
30668         if (this.boxesEl) {
30669             return this.boxesEl;
30670         }
30671         
30672         this.boxesEl = this.el.select('.mas-boxes').first();
30673         
30674         return this.boxesEl;
30675     },
30676     
30677     
30678     initEvents : function()
30679     {
30680         var _this = this;
30681         
30682         if(this.isAutoInitial){
30683             Roo.log('hook children rendered');
30684             this.on('childrenrendered', function() {
30685                 Roo.log('children rendered');
30686                 _this.initial();
30687             } ,this);
30688         }
30689     },
30690     
30691     initial : function()
30692     {
30693         this.selectedBrick = [];
30694         
30695         this.currentSize = this.el.getBox(true);
30696         
30697         Roo.EventManager.onWindowResize(this.resize, this); 
30698
30699         if(!this.isAutoInitial){
30700             this.layout();
30701             return;
30702         }
30703         
30704         this.layout();
30705         
30706         return;
30707         //this.layout.defer(500,this);
30708         
30709     },
30710     
30711     resize : function()
30712     {
30713         var cs = this.el.getBox(true);
30714         
30715         if (
30716                 this.currentSize.width == cs.width && 
30717                 this.currentSize.x == cs.x && 
30718                 this.currentSize.height == cs.height && 
30719                 this.currentSize.y == cs.y 
30720         ) {
30721             Roo.log("no change in with or X or Y");
30722             return;
30723         }
30724         
30725         this.currentSize = cs;
30726         
30727         this.layout();
30728         
30729     },
30730     
30731     layout : function()
30732     {   
30733         this._resetLayout();
30734         
30735         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30736         
30737         this.layoutItems( isInstant );
30738       
30739         this._isLayoutInited = true;
30740         
30741         this.fireEvent('layout', this);
30742         
30743     },
30744     
30745     _resetLayout : function()
30746     {
30747         if(this.isHorizontal){
30748             this.horizontalMeasureColumns();
30749             return;
30750         }
30751         
30752         this.verticalMeasureColumns();
30753         
30754     },
30755     
30756     verticalMeasureColumns : function()
30757     {
30758         this.getContainerWidth();
30759         
30760 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30761 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30762 //            return;
30763 //        }
30764         
30765         var boxWidth = this.boxWidth + this.padWidth;
30766         
30767         if(this.containerWidth < this.boxWidth){
30768             boxWidth = this.containerWidth
30769         }
30770         
30771         var containerWidth = this.containerWidth;
30772         
30773         var cols = Math.floor(containerWidth / boxWidth);
30774         
30775         this.cols = Math.max( cols, 1 );
30776         
30777         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30778         
30779         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30780         
30781         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30782         
30783         this.colWidth = boxWidth + avail - this.padWidth;
30784         
30785         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30786         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30787     },
30788     
30789     horizontalMeasureColumns : function()
30790     {
30791         this.getContainerWidth();
30792         
30793         var boxWidth = this.boxWidth;
30794         
30795         if(this.containerWidth < boxWidth){
30796             boxWidth = this.containerWidth;
30797         }
30798         
30799         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30800         
30801         this.el.setHeight(boxWidth);
30802         
30803     },
30804     
30805     getContainerWidth : function()
30806     {
30807         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30808     },
30809     
30810     layoutItems : function( isInstant )
30811     {
30812         Roo.log(this.bricks);
30813         
30814         var items = Roo.apply([], this.bricks);
30815         
30816         if(this.isHorizontal){
30817             this._horizontalLayoutItems( items , isInstant );
30818             return;
30819         }
30820         
30821 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30822 //            this._verticalAlternativeLayoutItems( items , isInstant );
30823 //            return;
30824 //        }
30825         
30826         this._verticalLayoutItems( items , isInstant );
30827         
30828     },
30829     
30830     _verticalLayoutItems : function ( items , isInstant)
30831     {
30832         if ( !items || !items.length ) {
30833             return;
30834         }
30835         
30836         var standard = [
30837             ['xs', 'xs', 'xs', 'tall'],
30838             ['xs', 'xs', 'tall'],
30839             ['xs', 'xs', 'sm'],
30840             ['xs', 'xs', 'xs'],
30841             ['xs', 'tall'],
30842             ['xs', 'sm'],
30843             ['xs', 'xs'],
30844             ['xs'],
30845             
30846             ['sm', 'xs', 'xs'],
30847             ['sm', 'xs'],
30848             ['sm'],
30849             
30850             ['tall', 'xs', 'xs', 'xs'],
30851             ['tall', 'xs', 'xs'],
30852             ['tall', 'xs'],
30853             ['tall']
30854             
30855         ];
30856         
30857         var queue = [];
30858         
30859         var boxes = [];
30860         
30861         var box = [];
30862         
30863         Roo.each(items, function(item, k){
30864             
30865             switch (item.size) {
30866                 // these layouts take up a full box,
30867                 case 'md' :
30868                 case 'md-left' :
30869                 case 'md-right' :
30870                 case 'wide' :
30871                     
30872                     if(box.length){
30873                         boxes.push(box);
30874                         box = [];
30875                     }
30876                     
30877                     boxes.push([item]);
30878                     
30879                     break;
30880                     
30881                 case 'xs' :
30882                 case 'sm' :
30883                 case 'tall' :
30884                     
30885                     box.push(item);
30886                     
30887                     break;
30888                 default :
30889                     break;
30890                     
30891             }
30892             
30893         }, this);
30894         
30895         if(box.length){
30896             boxes.push(box);
30897             box = [];
30898         }
30899         
30900         var filterPattern = function(box, length)
30901         {
30902             if(!box.length){
30903                 return;
30904             }
30905             
30906             var match = false;
30907             
30908             var pattern = box.slice(0, length);
30909             
30910             var format = [];
30911             
30912             Roo.each(pattern, function(i){
30913                 format.push(i.size);
30914             }, this);
30915             
30916             Roo.each(standard, function(s){
30917                 
30918                 if(String(s) != String(format)){
30919                     return;
30920                 }
30921                 
30922                 match = true;
30923                 return false;
30924                 
30925             }, this);
30926             
30927             if(!match && length == 1){
30928                 return;
30929             }
30930             
30931             if(!match){
30932                 filterPattern(box, length - 1);
30933                 return;
30934             }
30935                 
30936             queue.push(pattern);
30937
30938             box = box.slice(length, box.length);
30939
30940             filterPattern(box, 4);
30941
30942             return;
30943             
30944         }
30945         
30946         Roo.each(boxes, function(box, k){
30947             
30948             if(!box.length){
30949                 return;
30950             }
30951             
30952             if(box.length == 1){
30953                 queue.push(box);
30954                 return;
30955             }
30956             
30957             filterPattern(box, 4);
30958             
30959         }, this);
30960         
30961         this._processVerticalLayoutQueue( queue, isInstant );
30962         
30963     },
30964     
30965 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30966 //    {
30967 //        if ( !items || !items.length ) {
30968 //            return;
30969 //        }
30970 //
30971 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30972 //        
30973 //    },
30974     
30975     _horizontalLayoutItems : function ( items , isInstant)
30976     {
30977         if ( !items || !items.length || items.length < 3) {
30978             return;
30979         }
30980         
30981         items.reverse();
30982         
30983         var eItems = items.slice(0, 3);
30984         
30985         items = items.slice(3, items.length);
30986         
30987         var standard = [
30988             ['xs', 'xs', 'xs', 'wide'],
30989             ['xs', 'xs', 'wide'],
30990             ['xs', 'xs', 'sm'],
30991             ['xs', 'xs', 'xs'],
30992             ['xs', 'wide'],
30993             ['xs', 'sm'],
30994             ['xs', 'xs'],
30995             ['xs'],
30996             
30997             ['sm', 'xs', 'xs'],
30998             ['sm', 'xs'],
30999             ['sm'],
31000             
31001             ['wide', 'xs', 'xs', 'xs'],
31002             ['wide', 'xs', 'xs'],
31003             ['wide', 'xs'],
31004             ['wide'],
31005             
31006             ['wide-thin']
31007         ];
31008         
31009         var queue = [];
31010         
31011         var boxes = [];
31012         
31013         var box = [];
31014         
31015         Roo.each(items, function(item, k){
31016             
31017             switch (item.size) {
31018                 case 'md' :
31019                 case 'md-left' :
31020                 case 'md-right' :
31021                 case 'tall' :
31022                     
31023                     if(box.length){
31024                         boxes.push(box);
31025                         box = [];
31026                     }
31027                     
31028                     boxes.push([item]);
31029                     
31030                     break;
31031                     
31032                 case 'xs' :
31033                 case 'sm' :
31034                 case 'wide' :
31035                 case 'wide-thin' :
31036                     
31037                     box.push(item);
31038                     
31039                     break;
31040                 default :
31041                     break;
31042                     
31043             }
31044             
31045         }, this);
31046         
31047         if(box.length){
31048             boxes.push(box);
31049             box = [];
31050         }
31051         
31052         var filterPattern = function(box, length)
31053         {
31054             if(!box.length){
31055                 return;
31056             }
31057             
31058             var match = false;
31059             
31060             var pattern = box.slice(0, length);
31061             
31062             var format = [];
31063             
31064             Roo.each(pattern, function(i){
31065                 format.push(i.size);
31066             }, this);
31067             
31068             Roo.each(standard, function(s){
31069                 
31070                 if(String(s) != String(format)){
31071                     return;
31072                 }
31073                 
31074                 match = true;
31075                 return false;
31076                 
31077             }, this);
31078             
31079             if(!match && length == 1){
31080                 return;
31081             }
31082             
31083             if(!match){
31084                 filterPattern(box, length - 1);
31085                 return;
31086             }
31087                 
31088             queue.push(pattern);
31089
31090             box = box.slice(length, box.length);
31091
31092             filterPattern(box, 4);
31093
31094             return;
31095             
31096         }
31097         
31098         Roo.each(boxes, function(box, k){
31099             
31100             if(!box.length){
31101                 return;
31102             }
31103             
31104             if(box.length == 1){
31105                 queue.push(box);
31106                 return;
31107             }
31108             
31109             filterPattern(box, 4);
31110             
31111         }, this);
31112         
31113         
31114         var prune = [];
31115         
31116         var pos = this.el.getBox(true);
31117         
31118         var minX = pos.x;
31119         
31120         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31121         
31122         var hit_end = false;
31123         
31124         Roo.each(queue, function(box){
31125             
31126             if(hit_end){
31127                 
31128                 Roo.each(box, function(b){
31129                 
31130                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31131                     b.el.hide();
31132
31133                 }, this);
31134
31135                 return;
31136             }
31137             
31138             var mx = 0;
31139             
31140             Roo.each(box, function(b){
31141                 
31142                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31143                 b.el.show();
31144
31145                 mx = Math.max(mx, b.x);
31146                 
31147             }, this);
31148             
31149             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31150             
31151             if(maxX < minX){
31152                 
31153                 Roo.each(box, function(b){
31154                 
31155                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31156                     b.el.hide();
31157                     
31158                 }, this);
31159                 
31160                 hit_end = true;
31161                 
31162                 return;
31163             }
31164             
31165             prune.push(box);
31166             
31167         }, this);
31168         
31169         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31170     },
31171     
31172     /** Sets position of item in DOM
31173     * @param {Element} item
31174     * @param {Number} x - horizontal position
31175     * @param {Number} y - vertical position
31176     * @param {Boolean} isInstant - disables transitions
31177     */
31178     _processVerticalLayoutQueue : function( queue, isInstant )
31179     {
31180         var pos = this.el.getBox(true);
31181         var x = pos.x;
31182         var y = pos.y;
31183         var maxY = [];
31184         
31185         for (var i = 0; i < this.cols; i++){
31186             maxY[i] = pos.y;
31187         }
31188         
31189         Roo.each(queue, function(box, k){
31190             
31191             var col = k % this.cols;
31192             
31193             Roo.each(box, function(b,kk){
31194                 
31195                 b.el.position('absolute');
31196                 
31197                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31198                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31199                 
31200                 if(b.size == 'md-left' || b.size == 'md-right'){
31201                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31202                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31203                 }
31204                 
31205                 b.el.setWidth(width);
31206                 b.el.setHeight(height);
31207                 // iframe?
31208                 b.el.select('iframe',true).setSize(width,height);
31209                 
31210             }, this);
31211             
31212             for (var i = 0; i < this.cols; i++){
31213                 
31214                 if(maxY[i] < maxY[col]){
31215                     col = i;
31216                     continue;
31217                 }
31218                 
31219                 col = Math.min(col, i);
31220                 
31221             }
31222             
31223             x = pos.x + col * (this.colWidth + this.padWidth);
31224             
31225             y = maxY[col];
31226             
31227             var positions = [];
31228             
31229             switch (box.length){
31230                 case 1 :
31231                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31232                     break;
31233                 case 2 :
31234                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31235                     break;
31236                 case 3 :
31237                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31238                     break;
31239                 case 4 :
31240                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31241                     break;
31242                 default :
31243                     break;
31244             }
31245             
31246             Roo.each(box, function(b,kk){
31247                 
31248                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31249                 
31250                 var sz = b.el.getSize();
31251                 
31252                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31253                 
31254             }, this);
31255             
31256         }, this);
31257         
31258         var mY = 0;
31259         
31260         for (var i = 0; i < this.cols; i++){
31261             mY = Math.max(mY, maxY[i]);
31262         }
31263         
31264         this.el.setHeight(mY - pos.y);
31265         
31266     },
31267     
31268 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31269 //    {
31270 //        var pos = this.el.getBox(true);
31271 //        var x = pos.x;
31272 //        var y = pos.y;
31273 //        var maxX = pos.right;
31274 //        
31275 //        var maxHeight = 0;
31276 //        
31277 //        Roo.each(items, function(item, k){
31278 //            
31279 //            var c = k % 2;
31280 //            
31281 //            item.el.position('absolute');
31282 //                
31283 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31284 //
31285 //            item.el.setWidth(width);
31286 //
31287 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31288 //
31289 //            item.el.setHeight(height);
31290 //            
31291 //            if(c == 0){
31292 //                item.el.setXY([x, y], isInstant ? false : true);
31293 //            } else {
31294 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31295 //            }
31296 //            
31297 //            y = y + height + this.alternativePadWidth;
31298 //            
31299 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31300 //            
31301 //        }, this);
31302 //        
31303 //        this.el.setHeight(maxHeight);
31304 //        
31305 //    },
31306     
31307     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31308     {
31309         var pos = this.el.getBox(true);
31310         
31311         var minX = pos.x;
31312         var minY = pos.y;
31313         
31314         var maxX = pos.right;
31315         
31316         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31317         
31318         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31319         
31320         Roo.each(queue, function(box, k){
31321             
31322             Roo.each(box, function(b, kk){
31323                 
31324                 b.el.position('absolute');
31325                 
31326                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31327                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31328                 
31329                 if(b.size == 'md-left' || b.size == 'md-right'){
31330                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31331                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31332                 }
31333                 
31334                 b.el.setWidth(width);
31335                 b.el.setHeight(height);
31336                 
31337             }, this);
31338             
31339             if(!box.length){
31340                 return;
31341             }
31342             
31343             var positions = [];
31344             
31345             switch (box.length){
31346                 case 1 :
31347                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31348                     break;
31349                 case 2 :
31350                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31351                     break;
31352                 case 3 :
31353                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31354                     break;
31355                 case 4 :
31356                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31357                     break;
31358                 default :
31359                     break;
31360             }
31361             
31362             Roo.each(box, function(b,kk){
31363                 
31364                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31365                 
31366                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31367                 
31368             }, this);
31369             
31370         }, this);
31371         
31372     },
31373     
31374     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31375     {
31376         Roo.each(eItems, function(b,k){
31377             
31378             b.size = (k == 0) ? 'sm' : 'xs';
31379             b.x = (k == 0) ? 2 : 1;
31380             b.y = (k == 0) ? 2 : 1;
31381             
31382             b.el.position('absolute');
31383             
31384             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31385                 
31386             b.el.setWidth(width);
31387             
31388             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31389             
31390             b.el.setHeight(height);
31391             
31392         }, this);
31393
31394         var positions = [];
31395         
31396         positions.push({
31397             x : maxX - this.unitWidth * 2 - this.gutter,
31398             y : minY
31399         });
31400         
31401         positions.push({
31402             x : maxX - this.unitWidth,
31403             y : minY + (this.unitWidth + this.gutter) * 2
31404         });
31405         
31406         positions.push({
31407             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31408             y : minY
31409         });
31410         
31411         Roo.each(eItems, function(b,k){
31412             
31413             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31414
31415         }, this);
31416         
31417     },
31418     
31419     getVerticalOneBoxColPositions : function(x, y, box)
31420     {
31421         var pos = [];
31422         
31423         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31424         
31425         if(box[0].size == 'md-left'){
31426             rand = 0;
31427         }
31428         
31429         if(box[0].size == 'md-right'){
31430             rand = 1;
31431         }
31432         
31433         pos.push({
31434             x : x + (this.unitWidth + this.gutter) * rand,
31435             y : y
31436         });
31437         
31438         return pos;
31439     },
31440     
31441     getVerticalTwoBoxColPositions : function(x, y, box)
31442     {
31443         var pos = [];
31444         
31445         if(box[0].size == 'xs'){
31446             
31447             pos.push({
31448                 x : x,
31449                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31450             });
31451
31452             pos.push({
31453                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31454                 y : y
31455             });
31456             
31457             return pos;
31458             
31459         }
31460         
31461         pos.push({
31462             x : x,
31463             y : y
31464         });
31465
31466         pos.push({
31467             x : x + (this.unitWidth + this.gutter) * 2,
31468             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31469         });
31470         
31471         return pos;
31472         
31473     },
31474     
31475     getVerticalThreeBoxColPositions : function(x, y, box)
31476     {
31477         var pos = [];
31478         
31479         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31480             
31481             pos.push({
31482                 x : x,
31483                 y : y
31484             });
31485
31486             pos.push({
31487                 x : x + (this.unitWidth + this.gutter) * 1,
31488                 y : y
31489             });
31490             
31491             pos.push({
31492                 x : x + (this.unitWidth + this.gutter) * 2,
31493                 y : y
31494             });
31495             
31496             return pos;
31497             
31498         }
31499         
31500         if(box[0].size == 'xs' && box[1].size == 'xs'){
31501             
31502             pos.push({
31503                 x : x,
31504                 y : y
31505             });
31506
31507             pos.push({
31508                 x : x,
31509                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31510             });
31511             
31512             pos.push({
31513                 x : x + (this.unitWidth + this.gutter) * 1,
31514                 y : y
31515             });
31516             
31517             return pos;
31518             
31519         }
31520         
31521         pos.push({
31522             x : x,
31523             y : y
31524         });
31525
31526         pos.push({
31527             x : x + (this.unitWidth + this.gutter) * 2,
31528             y : y
31529         });
31530
31531         pos.push({
31532             x : x + (this.unitWidth + this.gutter) * 2,
31533             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31534         });
31535             
31536         return pos;
31537         
31538     },
31539     
31540     getVerticalFourBoxColPositions : function(x, y, box)
31541     {
31542         var pos = [];
31543         
31544         if(box[0].size == 'xs'){
31545             
31546             pos.push({
31547                 x : x,
31548                 y : y
31549             });
31550
31551             pos.push({
31552                 x : x,
31553                 y : y + (this.unitHeight + this.gutter) * 1
31554             });
31555             
31556             pos.push({
31557                 x : x,
31558                 y : y + (this.unitHeight + this.gutter) * 2
31559             });
31560             
31561             pos.push({
31562                 x : x + (this.unitWidth + this.gutter) * 1,
31563                 y : y
31564             });
31565             
31566             return pos;
31567             
31568         }
31569         
31570         pos.push({
31571             x : x,
31572             y : y
31573         });
31574
31575         pos.push({
31576             x : x + (this.unitWidth + this.gutter) * 2,
31577             y : y
31578         });
31579
31580         pos.push({
31581             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31582             y : y + (this.unitHeight + this.gutter) * 1
31583         });
31584
31585         pos.push({
31586             x : x + (this.unitWidth + this.gutter) * 2,
31587             y : y + (this.unitWidth + this.gutter) * 2
31588         });
31589
31590         return pos;
31591         
31592     },
31593     
31594     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31595     {
31596         var pos = [];
31597         
31598         if(box[0].size == 'md-left'){
31599             pos.push({
31600                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31601                 y : minY
31602             });
31603             
31604             return pos;
31605         }
31606         
31607         if(box[0].size == 'md-right'){
31608             pos.push({
31609                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31610                 y : minY + (this.unitWidth + this.gutter) * 1
31611             });
31612             
31613             return pos;
31614         }
31615         
31616         var rand = Math.floor(Math.random() * (4 - box[0].y));
31617         
31618         pos.push({
31619             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31620             y : minY + (this.unitWidth + this.gutter) * rand
31621         });
31622         
31623         return pos;
31624         
31625     },
31626     
31627     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31628     {
31629         var pos = [];
31630         
31631         if(box[0].size == 'xs'){
31632             
31633             pos.push({
31634                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31635                 y : minY
31636             });
31637
31638             pos.push({
31639                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31640                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31641             });
31642             
31643             return pos;
31644             
31645         }
31646         
31647         pos.push({
31648             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31649             y : minY
31650         });
31651
31652         pos.push({
31653             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31654             y : minY + (this.unitWidth + this.gutter) * 2
31655         });
31656         
31657         return pos;
31658         
31659     },
31660     
31661     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31662     {
31663         var pos = [];
31664         
31665         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31666             
31667             pos.push({
31668                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31669                 y : minY
31670             });
31671
31672             pos.push({
31673                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31674                 y : minY + (this.unitWidth + this.gutter) * 1
31675             });
31676             
31677             pos.push({
31678                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31679                 y : minY + (this.unitWidth + this.gutter) * 2
31680             });
31681             
31682             return pos;
31683             
31684         }
31685         
31686         if(box[0].size == 'xs' && box[1].size == 'xs'){
31687             
31688             pos.push({
31689                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31690                 y : minY
31691             });
31692
31693             pos.push({
31694                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31695                 y : minY
31696             });
31697             
31698             pos.push({
31699                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31700                 y : minY + (this.unitWidth + this.gutter) * 1
31701             });
31702             
31703             return pos;
31704             
31705         }
31706         
31707         pos.push({
31708             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31709             y : minY
31710         });
31711
31712         pos.push({
31713             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31714             y : minY + (this.unitWidth + this.gutter) * 2
31715         });
31716
31717         pos.push({
31718             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31719             y : minY + (this.unitWidth + this.gutter) * 2
31720         });
31721             
31722         return pos;
31723         
31724     },
31725     
31726     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31727     {
31728         var pos = [];
31729         
31730         if(box[0].size == 'xs'){
31731             
31732             pos.push({
31733                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31734                 y : minY
31735             });
31736
31737             pos.push({
31738                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31739                 y : minY
31740             });
31741             
31742             pos.push({
31743                 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),
31744                 y : minY
31745             });
31746             
31747             pos.push({
31748                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31749                 y : minY + (this.unitWidth + this.gutter) * 1
31750             });
31751             
31752             return pos;
31753             
31754         }
31755         
31756         pos.push({
31757             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31758             y : minY
31759         });
31760         
31761         pos.push({
31762             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31763             y : minY + (this.unitWidth + this.gutter) * 2
31764         });
31765         
31766         pos.push({
31767             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31768             y : minY + (this.unitWidth + this.gutter) * 2
31769         });
31770         
31771         pos.push({
31772             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),
31773             y : minY + (this.unitWidth + this.gutter) * 2
31774         });
31775
31776         return pos;
31777         
31778     },
31779     
31780     /**
31781     * remove a Masonry Brick
31782     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31783     */
31784     removeBrick : function(brick_id)
31785     {
31786         if (!brick_id) {
31787             return;
31788         }
31789         
31790         for (var i = 0; i<this.bricks.length; i++) {
31791             if (this.bricks[i].id == brick_id) {
31792                 this.bricks.splice(i,1);
31793                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31794                 this.initial();
31795             }
31796         }
31797     },
31798     
31799     /**
31800     * adds a Masonry Brick
31801     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31802     */
31803     addBrick : function(cfg)
31804     {
31805         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31806         //this.register(cn);
31807         cn.parentId = this.id;
31808         cn.onRender(this.el, null);
31809         return cn;
31810     },
31811     
31812     /**
31813     * register a Masonry Brick
31814     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31815     */
31816     
31817     register : function(brick)
31818     {
31819         this.bricks.push(brick);
31820         brick.masonryId = this.id;
31821     },
31822     
31823     /**
31824     * clear all the Masonry Brick
31825     */
31826     clearAll : function()
31827     {
31828         this.bricks = [];
31829         //this.getChildContainer().dom.innerHTML = "";
31830         this.el.dom.innerHTML = '';
31831     },
31832     
31833     getSelected : function()
31834     {
31835         if (!this.selectedBrick) {
31836             return false;
31837         }
31838         
31839         return this.selectedBrick;
31840     }
31841 });
31842
31843 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31844     
31845     groups: {},
31846      /**
31847     * register a Masonry Layout
31848     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31849     */
31850     
31851     register : function(layout)
31852     {
31853         this.groups[layout.id] = layout;
31854     },
31855     /**
31856     * fetch a  Masonry Layout based on the masonry layout ID
31857     * @param {string} the masonry layout to add
31858     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31859     */
31860     
31861     get: function(layout_id) {
31862         if (typeof(this.groups[layout_id]) == 'undefined') {
31863             return false;
31864         }
31865         return this.groups[layout_id] ;
31866     }
31867     
31868     
31869     
31870 });
31871
31872  
31873
31874  /**
31875  *
31876  * This is based on 
31877  * http://masonry.desandro.com
31878  *
31879  * The idea is to render all the bricks based on vertical width...
31880  *
31881  * The original code extends 'outlayer' - we might need to use that....
31882  * 
31883  */
31884
31885
31886 /**
31887  * @class Roo.bootstrap.LayoutMasonryAuto
31888  * @extends Roo.bootstrap.Component
31889  * Bootstrap Layout Masonry class
31890  * 
31891  * @constructor
31892  * Create a new Element
31893  * @param {Object} config The config object
31894  */
31895
31896 Roo.bootstrap.LayoutMasonryAuto = function(config){
31897     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31898 };
31899
31900 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31901     
31902       /**
31903      * @cfg {Boolean} isFitWidth  - resize the width..
31904      */   
31905     isFitWidth : false,  // options..
31906     /**
31907      * @cfg {Boolean} isOriginLeft = left align?
31908      */   
31909     isOriginLeft : true,
31910     /**
31911      * @cfg {Boolean} isOriginTop = top align?
31912      */   
31913     isOriginTop : false,
31914     /**
31915      * @cfg {Boolean} isLayoutInstant = no animation?
31916      */   
31917     isLayoutInstant : false, // needed?
31918     /**
31919      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31920      */   
31921     isResizingContainer : true,
31922     /**
31923      * @cfg {Number} columnWidth  width of the columns 
31924      */   
31925     
31926     columnWidth : 0,
31927     
31928     /**
31929      * @cfg {Number} maxCols maximum number of columns
31930      */   
31931     
31932     maxCols: 0,
31933     /**
31934      * @cfg {Number} padHeight padding below box..
31935      */   
31936     
31937     padHeight : 10, 
31938     
31939     /**
31940      * @cfg {Boolean} isAutoInitial defalut true
31941      */   
31942     
31943     isAutoInitial : true, 
31944     
31945     // private?
31946     gutter : 0,
31947     
31948     containerWidth: 0,
31949     initialColumnWidth : 0,
31950     currentSize : null,
31951     
31952     colYs : null, // array.
31953     maxY : 0,
31954     padWidth: 10,
31955     
31956     
31957     tag: 'div',
31958     cls: '',
31959     bricks: null, //CompositeElement
31960     cols : 0, // array?
31961     // element : null, // wrapped now this.el
31962     _isLayoutInited : null, 
31963     
31964     
31965     getAutoCreate : function(){
31966         
31967         var cfg = {
31968             tag: this.tag,
31969             cls: 'blog-masonary-wrapper ' + this.cls,
31970             cn : {
31971                 cls : 'mas-boxes masonary'
31972             }
31973         };
31974         
31975         return cfg;
31976     },
31977     
31978     getChildContainer: function( )
31979     {
31980         if (this.boxesEl) {
31981             return this.boxesEl;
31982         }
31983         
31984         this.boxesEl = this.el.select('.mas-boxes').first();
31985         
31986         return this.boxesEl;
31987     },
31988     
31989     
31990     initEvents : function()
31991     {
31992         var _this = this;
31993         
31994         if(this.isAutoInitial){
31995             Roo.log('hook children rendered');
31996             this.on('childrenrendered', function() {
31997                 Roo.log('children rendered');
31998                 _this.initial();
31999             } ,this);
32000         }
32001         
32002     },
32003     
32004     initial : function()
32005     {
32006         this.reloadItems();
32007
32008         this.currentSize = this.el.getBox(true);
32009
32010         /// was window resize... - let's see if this works..
32011         Roo.EventManager.onWindowResize(this.resize, this); 
32012
32013         if(!this.isAutoInitial){
32014             this.layout();
32015             return;
32016         }
32017         
32018         this.layout.defer(500,this);
32019     },
32020     
32021     reloadItems: function()
32022     {
32023         this.bricks = this.el.select('.masonry-brick', true);
32024         
32025         this.bricks.each(function(b) {
32026             //Roo.log(b.getSize());
32027             if (!b.attr('originalwidth')) {
32028                 b.attr('originalwidth',  b.getSize().width);
32029             }
32030             
32031         });
32032         
32033         Roo.log(this.bricks.elements.length);
32034     },
32035     
32036     resize : function()
32037     {
32038         Roo.log('resize');
32039         var cs = this.el.getBox(true);
32040         
32041         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32042             Roo.log("no change in with or X");
32043             return;
32044         }
32045         this.currentSize = cs;
32046         this.layout();
32047     },
32048     
32049     layout : function()
32050     {
32051          Roo.log('layout');
32052         this._resetLayout();
32053         //this._manageStamps();
32054       
32055         // don't animate first layout
32056         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32057         this.layoutItems( isInstant );
32058       
32059         // flag for initalized
32060         this._isLayoutInited = true;
32061     },
32062     
32063     layoutItems : function( isInstant )
32064     {
32065         //var items = this._getItemsForLayout( this.items );
32066         // original code supports filtering layout items.. we just ignore it..
32067         
32068         this._layoutItems( this.bricks , isInstant );
32069       
32070         this._postLayout();
32071     },
32072     _layoutItems : function ( items , isInstant)
32073     {
32074        //this.fireEvent( 'layout', this, items );
32075     
32076
32077         if ( !items || !items.elements.length ) {
32078           // no items, emit event with empty array
32079             return;
32080         }
32081
32082         var queue = [];
32083         items.each(function(item) {
32084             Roo.log("layout item");
32085             Roo.log(item);
32086             // get x/y object from method
32087             var position = this._getItemLayoutPosition( item );
32088             // enqueue
32089             position.item = item;
32090             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32091             queue.push( position );
32092         }, this);
32093       
32094         this._processLayoutQueue( queue );
32095     },
32096     /** Sets position of item in DOM
32097     * @param {Element} item
32098     * @param {Number} x - horizontal position
32099     * @param {Number} y - vertical position
32100     * @param {Boolean} isInstant - disables transitions
32101     */
32102     _processLayoutQueue : function( queue )
32103     {
32104         for ( var i=0, len = queue.length; i < len; i++ ) {
32105             var obj = queue[i];
32106             obj.item.position('absolute');
32107             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32108         }
32109     },
32110       
32111     
32112     /**
32113     * Any logic you want to do after each layout,
32114     * i.e. size the container
32115     */
32116     _postLayout : function()
32117     {
32118         this.resizeContainer();
32119     },
32120     
32121     resizeContainer : function()
32122     {
32123         if ( !this.isResizingContainer ) {
32124             return;
32125         }
32126         var size = this._getContainerSize();
32127         if ( size ) {
32128             this.el.setSize(size.width,size.height);
32129             this.boxesEl.setSize(size.width,size.height);
32130         }
32131     },
32132     
32133     
32134     
32135     _resetLayout : function()
32136     {
32137         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32138         this.colWidth = this.el.getWidth();
32139         //this.gutter = this.el.getWidth(); 
32140         
32141         this.measureColumns();
32142
32143         // reset column Y
32144         var i = this.cols;
32145         this.colYs = [];
32146         while (i--) {
32147             this.colYs.push( 0 );
32148         }
32149     
32150         this.maxY = 0;
32151     },
32152
32153     measureColumns : function()
32154     {
32155         this.getContainerWidth();
32156       // if columnWidth is 0, default to outerWidth of first item
32157         if ( !this.columnWidth ) {
32158             var firstItem = this.bricks.first();
32159             Roo.log(firstItem);
32160             this.columnWidth  = this.containerWidth;
32161             if (firstItem && firstItem.attr('originalwidth') ) {
32162                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32163             }
32164             // columnWidth fall back to item of first element
32165             Roo.log("set column width?");
32166                         this.initialColumnWidth = this.columnWidth  ;
32167
32168             // if first elem has no width, default to size of container
32169             
32170         }
32171         
32172         
32173         if (this.initialColumnWidth) {
32174             this.columnWidth = this.initialColumnWidth;
32175         }
32176         
32177         
32178             
32179         // column width is fixed at the top - however if container width get's smaller we should
32180         // reduce it...
32181         
32182         // this bit calcs how man columns..
32183             
32184         var columnWidth = this.columnWidth += this.gutter;
32185       
32186         // calculate columns
32187         var containerWidth = this.containerWidth + this.gutter;
32188         
32189         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32190         // fix rounding errors, typically with gutters
32191         var excess = columnWidth - containerWidth % columnWidth;
32192         
32193         
32194         // if overshoot is less than a pixel, round up, otherwise floor it
32195         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32196         cols = Math[ mathMethod ]( cols );
32197         this.cols = Math.max( cols, 1 );
32198         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32199         
32200          // padding positioning..
32201         var totalColWidth = this.cols * this.columnWidth;
32202         var padavail = this.containerWidth - totalColWidth;
32203         // so for 2 columns - we need 3 'pads'
32204         
32205         var padNeeded = (1+this.cols) * this.padWidth;
32206         
32207         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32208         
32209         this.columnWidth += padExtra
32210         //this.padWidth = Math.floor(padavail /  ( this.cols));
32211         
32212         // adjust colum width so that padding is fixed??
32213         
32214         // we have 3 columns ... total = width * 3
32215         // we have X left over... that should be used by 
32216         
32217         //if (this.expandC) {
32218             
32219         //}
32220         
32221         
32222         
32223     },
32224     
32225     getContainerWidth : function()
32226     {
32227        /* // container is parent if fit width
32228         var container = this.isFitWidth ? this.element.parentNode : this.element;
32229         // check that this.size and size are there
32230         // IE8 triggers resize on body size change, so they might not be
32231         
32232         var size = getSize( container );  //FIXME
32233         this.containerWidth = size && size.innerWidth; //FIXME
32234         */
32235          
32236         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32237         
32238     },
32239     
32240     _getItemLayoutPosition : function( item )  // what is item?
32241     {
32242         // we resize the item to our columnWidth..
32243       
32244         item.setWidth(this.columnWidth);
32245         item.autoBoxAdjust  = false;
32246         
32247         var sz = item.getSize();
32248  
32249         // how many columns does this brick span
32250         var remainder = this.containerWidth % this.columnWidth;
32251         
32252         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32253         // round if off by 1 pixel, otherwise use ceil
32254         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32255         colSpan = Math.min( colSpan, this.cols );
32256         
32257         // normally this should be '1' as we dont' currently allow multi width columns..
32258         
32259         var colGroup = this._getColGroup( colSpan );
32260         // get the minimum Y value from the columns
32261         var minimumY = Math.min.apply( Math, colGroup );
32262         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32263         
32264         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32265          
32266         // position the brick
32267         var position = {
32268             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32269             y: this.currentSize.y + minimumY + this.padHeight
32270         };
32271         
32272         Roo.log(position);
32273         // apply setHeight to necessary columns
32274         var setHeight = minimumY + sz.height + this.padHeight;
32275         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32276         
32277         var setSpan = this.cols + 1 - colGroup.length;
32278         for ( var i = 0; i < setSpan; i++ ) {
32279           this.colYs[ shortColIndex + i ] = setHeight ;
32280         }
32281       
32282         return position;
32283     },
32284     
32285     /**
32286      * @param {Number} colSpan - number of columns the element spans
32287      * @returns {Array} colGroup
32288      */
32289     _getColGroup : function( colSpan )
32290     {
32291         if ( colSpan < 2 ) {
32292           // if brick spans only one column, use all the column Ys
32293           return this.colYs;
32294         }
32295       
32296         var colGroup = [];
32297         // how many different places could this brick fit horizontally
32298         var groupCount = this.cols + 1 - colSpan;
32299         // for each group potential horizontal position
32300         for ( var i = 0; i < groupCount; i++ ) {
32301           // make an array of colY values for that one group
32302           var groupColYs = this.colYs.slice( i, i + colSpan );
32303           // and get the max value of the array
32304           colGroup[i] = Math.max.apply( Math, groupColYs );
32305         }
32306         return colGroup;
32307     },
32308     /*
32309     _manageStamp : function( stamp )
32310     {
32311         var stampSize =  stamp.getSize();
32312         var offset = stamp.getBox();
32313         // get the columns that this stamp affects
32314         var firstX = this.isOriginLeft ? offset.x : offset.right;
32315         var lastX = firstX + stampSize.width;
32316         var firstCol = Math.floor( firstX / this.columnWidth );
32317         firstCol = Math.max( 0, firstCol );
32318         
32319         var lastCol = Math.floor( lastX / this.columnWidth );
32320         // lastCol should not go over if multiple of columnWidth #425
32321         lastCol -= lastX % this.columnWidth ? 0 : 1;
32322         lastCol = Math.min( this.cols - 1, lastCol );
32323         
32324         // set colYs to bottom of the stamp
32325         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32326             stampSize.height;
32327             
32328         for ( var i = firstCol; i <= lastCol; i++ ) {
32329           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32330         }
32331     },
32332     */
32333     
32334     _getContainerSize : function()
32335     {
32336         this.maxY = Math.max.apply( Math, this.colYs );
32337         var size = {
32338             height: this.maxY
32339         };
32340       
32341         if ( this.isFitWidth ) {
32342             size.width = this._getContainerFitWidth();
32343         }
32344       
32345         return size;
32346     },
32347     
32348     _getContainerFitWidth : function()
32349     {
32350         var unusedCols = 0;
32351         // count unused columns
32352         var i = this.cols;
32353         while ( --i ) {
32354           if ( this.colYs[i] !== 0 ) {
32355             break;
32356           }
32357           unusedCols++;
32358         }
32359         // fit container to columns that have been used
32360         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32361     },
32362     
32363     needsResizeLayout : function()
32364     {
32365         var previousWidth = this.containerWidth;
32366         this.getContainerWidth();
32367         return previousWidth !== this.containerWidth;
32368     }
32369  
32370 });
32371
32372  
32373
32374  /*
32375  * - LGPL
32376  *
32377  * element
32378  * 
32379  */
32380
32381 /**
32382  * @class Roo.bootstrap.MasonryBrick
32383  * @extends Roo.bootstrap.Component
32384  * Bootstrap MasonryBrick class
32385  * 
32386  * @constructor
32387  * Create a new MasonryBrick
32388  * @param {Object} config The config object
32389  */
32390
32391 Roo.bootstrap.MasonryBrick = function(config){
32392     
32393     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32394     
32395     Roo.bootstrap.MasonryBrick.register(this);
32396     
32397     this.addEvents({
32398         // raw events
32399         /**
32400          * @event click
32401          * When a MasonryBrick is clcik
32402          * @param {Roo.bootstrap.MasonryBrick} this
32403          * @param {Roo.EventObject} e
32404          */
32405         "click" : true
32406     });
32407 };
32408
32409 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32410     
32411     /**
32412      * @cfg {String} title
32413      */   
32414     title : '',
32415     /**
32416      * @cfg {String} html
32417      */   
32418     html : '',
32419     /**
32420      * @cfg {String} bgimage
32421      */   
32422     bgimage : '',
32423     /**
32424      * @cfg {String} videourl
32425      */   
32426     videourl : '',
32427     /**
32428      * @cfg {String} cls
32429      */   
32430     cls : '',
32431     /**
32432      * @cfg {String} href
32433      */   
32434     href : '',
32435     /**
32436      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32437      */   
32438     size : 'xs',
32439     
32440     /**
32441      * @cfg {String} placetitle (center|bottom)
32442      */   
32443     placetitle : '',
32444     
32445     /**
32446      * @cfg {Boolean} isFitContainer defalut true
32447      */   
32448     isFitContainer : true, 
32449     
32450     /**
32451      * @cfg {Boolean} preventDefault defalut false
32452      */   
32453     preventDefault : false, 
32454     
32455     /**
32456      * @cfg {Boolean} inverse defalut false
32457      */   
32458     maskInverse : false, 
32459     
32460     getAutoCreate : function()
32461     {
32462         if(!this.isFitContainer){
32463             return this.getSplitAutoCreate();
32464         }
32465         
32466         var cls = 'masonry-brick masonry-brick-full';
32467         
32468         if(this.href.length){
32469             cls += ' masonry-brick-link';
32470         }
32471         
32472         if(this.bgimage.length){
32473             cls += ' masonry-brick-image';
32474         }
32475         
32476         if(this.maskInverse){
32477             cls += ' mask-inverse';
32478         }
32479         
32480         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32481             cls += ' enable-mask';
32482         }
32483         
32484         if(this.size){
32485             cls += ' masonry-' + this.size + '-brick';
32486         }
32487         
32488         if(this.placetitle.length){
32489             
32490             switch (this.placetitle) {
32491                 case 'center' :
32492                     cls += ' masonry-center-title';
32493                     break;
32494                 case 'bottom' :
32495                     cls += ' masonry-bottom-title';
32496                     break;
32497                 default:
32498                     break;
32499             }
32500             
32501         } else {
32502             if(!this.html.length && !this.bgimage.length){
32503                 cls += ' masonry-center-title';
32504             }
32505
32506             if(!this.html.length && this.bgimage.length){
32507                 cls += ' masonry-bottom-title';
32508             }
32509         }
32510         
32511         if(this.cls){
32512             cls += ' ' + this.cls;
32513         }
32514         
32515         var cfg = {
32516             tag: (this.href.length) ? 'a' : 'div',
32517             cls: cls,
32518             cn: [
32519                 {
32520                     tag: 'div',
32521                     cls: 'masonry-brick-mask'
32522                 },
32523                 {
32524                     tag: 'div',
32525                     cls: 'masonry-brick-paragraph',
32526                     cn: []
32527                 }
32528             ]
32529         };
32530         
32531         if(this.href.length){
32532             cfg.href = this.href;
32533         }
32534         
32535         var cn = cfg.cn[1].cn;
32536         
32537         if(this.title.length){
32538             cn.push({
32539                 tag: 'h4',
32540                 cls: 'masonry-brick-title',
32541                 html: this.title
32542             });
32543         }
32544         
32545         if(this.html.length){
32546             cn.push({
32547                 tag: 'p',
32548                 cls: 'masonry-brick-text',
32549                 html: this.html
32550             });
32551         }
32552         
32553         if (!this.title.length && !this.html.length) {
32554             cfg.cn[1].cls += ' hide';
32555         }
32556         
32557         if(this.bgimage.length){
32558             cfg.cn.push({
32559                 tag: 'img',
32560                 cls: 'masonry-brick-image-view',
32561                 src: this.bgimage
32562             });
32563         }
32564         
32565         if(this.videourl.length){
32566             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32567             // youtube support only?
32568             cfg.cn.push({
32569                 tag: 'iframe',
32570                 cls: 'masonry-brick-image-view',
32571                 src: vurl,
32572                 frameborder : 0,
32573                 allowfullscreen : true
32574             });
32575         }
32576         
32577         return cfg;
32578         
32579     },
32580     
32581     getSplitAutoCreate : function()
32582     {
32583         var cls = 'masonry-brick masonry-brick-split';
32584         
32585         if(this.href.length){
32586             cls += ' masonry-brick-link';
32587         }
32588         
32589         if(this.bgimage.length){
32590             cls += ' masonry-brick-image';
32591         }
32592         
32593         if(this.size){
32594             cls += ' masonry-' + this.size + '-brick';
32595         }
32596         
32597         switch (this.placetitle) {
32598             case 'center' :
32599                 cls += ' masonry-center-title';
32600                 break;
32601             case 'bottom' :
32602                 cls += ' masonry-bottom-title';
32603                 break;
32604             default:
32605                 if(!this.bgimage.length){
32606                     cls += ' masonry-center-title';
32607                 }
32608
32609                 if(this.bgimage.length){
32610                     cls += ' masonry-bottom-title';
32611                 }
32612                 break;
32613         }
32614         
32615         if(this.cls){
32616             cls += ' ' + this.cls;
32617         }
32618         
32619         var cfg = {
32620             tag: (this.href.length) ? 'a' : 'div',
32621             cls: cls,
32622             cn: [
32623                 {
32624                     tag: 'div',
32625                     cls: 'masonry-brick-split-head',
32626                     cn: [
32627                         {
32628                             tag: 'div',
32629                             cls: 'masonry-brick-paragraph',
32630                             cn: []
32631                         }
32632                     ]
32633                 },
32634                 {
32635                     tag: 'div',
32636                     cls: 'masonry-brick-split-body',
32637                     cn: []
32638                 }
32639             ]
32640         };
32641         
32642         if(this.href.length){
32643             cfg.href = this.href;
32644         }
32645         
32646         if(this.title.length){
32647             cfg.cn[0].cn[0].cn.push({
32648                 tag: 'h4',
32649                 cls: 'masonry-brick-title',
32650                 html: this.title
32651             });
32652         }
32653         
32654         if(this.html.length){
32655             cfg.cn[1].cn.push({
32656                 tag: 'p',
32657                 cls: 'masonry-brick-text',
32658                 html: this.html
32659             });
32660         }
32661
32662         if(this.bgimage.length){
32663             cfg.cn[0].cn.push({
32664                 tag: 'img',
32665                 cls: 'masonry-brick-image-view',
32666                 src: this.bgimage
32667             });
32668         }
32669         
32670         if(this.videourl.length){
32671             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32672             // youtube support only?
32673             cfg.cn[0].cn.cn.push({
32674                 tag: 'iframe',
32675                 cls: 'masonry-brick-image-view',
32676                 src: vurl,
32677                 frameborder : 0,
32678                 allowfullscreen : true
32679             });
32680         }
32681         
32682         return cfg;
32683     },
32684     
32685     initEvents: function() 
32686     {
32687         switch (this.size) {
32688             case 'xs' :
32689                 this.x = 1;
32690                 this.y = 1;
32691                 break;
32692             case 'sm' :
32693                 this.x = 2;
32694                 this.y = 2;
32695                 break;
32696             case 'md' :
32697             case 'md-left' :
32698             case 'md-right' :
32699                 this.x = 3;
32700                 this.y = 3;
32701                 break;
32702             case 'tall' :
32703                 this.x = 2;
32704                 this.y = 3;
32705                 break;
32706             case 'wide' :
32707                 this.x = 3;
32708                 this.y = 2;
32709                 break;
32710             case 'wide-thin' :
32711                 this.x = 3;
32712                 this.y = 1;
32713                 break;
32714                         
32715             default :
32716                 break;
32717         }
32718         
32719         if(Roo.isTouch){
32720             this.el.on('touchstart', this.onTouchStart, this);
32721             this.el.on('touchmove', this.onTouchMove, this);
32722             this.el.on('touchend', this.onTouchEnd, this);
32723             this.el.on('contextmenu', this.onContextMenu, this);
32724         } else {
32725             this.el.on('mouseenter'  ,this.enter, this);
32726             this.el.on('mouseleave', this.leave, this);
32727             this.el.on('click', this.onClick, this);
32728         }
32729         
32730         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32731             this.parent().bricks.push(this);   
32732         }
32733         
32734     },
32735     
32736     onClick: function(e, el)
32737     {
32738         var time = this.endTimer - this.startTimer;
32739         // Roo.log(e.preventDefault());
32740         if(Roo.isTouch){
32741             if(time > 1000){
32742                 e.preventDefault();
32743                 return;
32744             }
32745         }
32746         
32747         if(!this.preventDefault){
32748             return;
32749         }
32750         
32751         e.preventDefault();
32752         
32753         if (this.activeClass != '') {
32754             this.selectBrick();
32755         }
32756         
32757         this.fireEvent('click', this, e);
32758     },
32759     
32760     enter: function(e, el)
32761     {
32762         e.preventDefault();
32763         
32764         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32765             return;
32766         }
32767         
32768         if(this.bgimage.length && this.html.length){
32769             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32770         }
32771     },
32772     
32773     leave: function(e, el)
32774     {
32775         e.preventDefault();
32776         
32777         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32778             return;
32779         }
32780         
32781         if(this.bgimage.length && this.html.length){
32782             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32783         }
32784     },
32785     
32786     onTouchStart: function(e, el)
32787     {
32788 //        e.preventDefault();
32789         
32790         this.touchmoved = false;
32791         
32792         if(!this.isFitContainer){
32793             return;
32794         }
32795         
32796         if(!this.bgimage.length || !this.html.length){
32797             return;
32798         }
32799         
32800         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32801         
32802         this.timer = new Date().getTime();
32803         
32804     },
32805     
32806     onTouchMove: function(e, el)
32807     {
32808         this.touchmoved = true;
32809     },
32810     
32811     onContextMenu : function(e,el)
32812     {
32813         e.preventDefault();
32814         e.stopPropagation();
32815         return false;
32816     },
32817     
32818     onTouchEnd: function(e, el)
32819     {
32820 //        e.preventDefault();
32821         
32822         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32823         
32824             this.leave(e,el);
32825             
32826             return;
32827         }
32828         
32829         if(!this.bgimage.length || !this.html.length){
32830             
32831             if(this.href.length){
32832                 window.location.href = this.href;
32833             }
32834             
32835             return;
32836         }
32837         
32838         if(!this.isFitContainer){
32839             return;
32840         }
32841         
32842         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32843         
32844         window.location.href = this.href;
32845     },
32846     
32847     //selection on single brick only
32848     selectBrick : function() {
32849         
32850         if (!this.parentId) {
32851             return;
32852         }
32853         
32854         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32855         var index = m.selectedBrick.indexOf(this.id);
32856         
32857         if ( index > -1) {
32858             m.selectedBrick.splice(index,1);
32859             this.el.removeClass(this.activeClass);
32860             return;
32861         }
32862         
32863         for(var i = 0; i < m.selectedBrick.length; i++) {
32864             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32865             b.el.removeClass(b.activeClass);
32866         }
32867         
32868         m.selectedBrick = [];
32869         
32870         m.selectedBrick.push(this.id);
32871         this.el.addClass(this.activeClass);
32872         return;
32873     },
32874     
32875     isSelected : function(){
32876         return this.el.hasClass(this.activeClass);
32877         
32878     }
32879 });
32880
32881 Roo.apply(Roo.bootstrap.MasonryBrick, {
32882     
32883     //groups: {},
32884     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32885      /**
32886     * register a Masonry Brick
32887     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32888     */
32889     
32890     register : function(brick)
32891     {
32892         //this.groups[brick.id] = brick;
32893         this.groups.add(brick.id, brick);
32894     },
32895     /**
32896     * fetch a  masonry brick based on the masonry brick ID
32897     * @param {string} the masonry brick to add
32898     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32899     */
32900     
32901     get: function(brick_id) 
32902     {
32903         // if (typeof(this.groups[brick_id]) == 'undefined') {
32904         //     return false;
32905         // }
32906         // return this.groups[brick_id] ;
32907         
32908         if(this.groups.key(brick_id)) {
32909             return this.groups.key(brick_id);
32910         }
32911         
32912         return false;
32913     }
32914     
32915     
32916     
32917 });
32918
32919  /*
32920  * - LGPL
32921  *
32922  * element
32923  * 
32924  */
32925
32926 /**
32927  * @class Roo.bootstrap.Brick
32928  * @extends Roo.bootstrap.Component
32929  * Bootstrap Brick class
32930  * 
32931  * @constructor
32932  * Create a new Brick
32933  * @param {Object} config The config object
32934  */
32935
32936 Roo.bootstrap.Brick = function(config){
32937     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32938     
32939     this.addEvents({
32940         // raw events
32941         /**
32942          * @event click
32943          * When a Brick is click
32944          * @param {Roo.bootstrap.Brick} this
32945          * @param {Roo.EventObject} e
32946          */
32947         "click" : true
32948     });
32949 };
32950
32951 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32952     
32953     /**
32954      * @cfg {String} title
32955      */   
32956     title : '',
32957     /**
32958      * @cfg {String} html
32959      */   
32960     html : '',
32961     /**
32962      * @cfg {String} bgimage
32963      */   
32964     bgimage : '',
32965     /**
32966      * @cfg {String} cls
32967      */   
32968     cls : '',
32969     /**
32970      * @cfg {String} href
32971      */   
32972     href : '',
32973     /**
32974      * @cfg {String} video
32975      */   
32976     video : '',
32977     /**
32978      * @cfg {Boolean} square
32979      */   
32980     square : true,
32981     
32982     getAutoCreate : function()
32983     {
32984         var cls = 'roo-brick';
32985         
32986         if(this.href.length){
32987             cls += ' roo-brick-link';
32988         }
32989         
32990         if(this.bgimage.length){
32991             cls += ' roo-brick-image';
32992         }
32993         
32994         if(!this.html.length && !this.bgimage.length){
32995             cls += ' roo-brick-center-title';
32996         }
32997         
32998         if(!this.html.length && this.bgimage.length){
32999             cls += ' roo-brick-bottom-title';
33000         }
33001         
33002         if(this.cls){
33003             cls += ' ' + this.cls;
33004         }
33005         
33006         var cfg = {
33007             tag: (this.href.length) ? 'a' : 'div',
33008             cls: cls,
33009             cn: [
33010                 {
33011                     tag: 'div',
33012                     cls: 'roo-brick-paragraph',
33013                     cn: []
33014                 }
33015             ]
33016         };
33017         
33018         if(this.href.length){
33019             cfg.href = this.href;
33020         }
33021         
33022         var cn = cfg.cn[0].cn;
33023         
33024         if(this.title.length){
33025             cn.push({
33026                 tag: 'h4',
33027                 cls: 'roo-brick-title',
33028                 html: this.title
33029             });
33030         }
33031         
33032         if(this.html.length){
33033             cn.push({
33034                 tag: 'p',
33035                 cls: 'roo-brick-text',
33036                 html: this.html
33037             });
33038         } else {
33039             cn.cls += ' hide';
33040         }
33041         
33042         if(this.bgimage.length){
33043             cfg.cn.push({
33044                 tag: 'img',
33045                 cls: 'roo-brick-image-view',
33046                 src: this.bgimage
33047             });
33048         }
33049         
33050         return cfg;
33051     },
33052     
33053     initEvents: function() 
33054     {
33055         if(this.title.length || this.html.length){
33056             this.el.on('mouseenter'  ,this.enter, this);
33057             this.el.on('mouseleave', this.leave, this);
33058         }
33059         
33060         Roo.EventManager.onWindowResize(this.resize, this); 
33061         
33062         if(this.bgimage.length){
33063             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33064             this.imageEl.on('load', this.onImageLoad, this);
33065             return;
33066         }
33067         
33068         this.resize();
33069     },
33070     
33071     onImageLoad : function()
33072     {
33073         this.resize();
33074     },
33075     
33076     resize : function()
33077     {
33078         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33079         
33080         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33081         
33082         if(this.bgimage.length){
33083             var image = this.el.select('.roo-brick-image-view', true).first();
33084             
33085             image.setWidth(paragraph.getWidth());
33086             
33087             if(this.square){
33088                 image.setHeight(paragraph.getWidth());
33089             }
33090             
33091             this.el.setHeight(image.getHeight());
33092             paragraph.setHeight(image.getHeight());
33093             
33094         }
33095         
33096     },
33097     
33098     enter: function(e, el)
33099     {
33100         e.preventDefault();
33101         
33102         if(this.bgimage.length){
33103             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33104             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33105         }
33106     },
33107     
33108     leave: function(e, el)
33109     {
33110         e.preventDefault();
33111         
33112         if(this.bgimage.length){
33113             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33114             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33115         }
33116     }
33117     
33118 });
33119
33120  
33121
33122  /*
33123  * - LGPL
33124  *
33125  * Number field 
33126  */
33127
33128 /**
33129  * @class Roo.bootstrap.NumberField
33130  * @extends Roo.bootstrap.Input
33131  * Bootstrap NumberField class
33132  * 
33133  * 
33134  * 
33135  * 
33136  * @constructor
33137  * Create a new NumberField
33138  * @param {Object} config The config object
33139  */
33140
33141 Roo.bootstrap.NumberField = function(config){
33142     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33143 };
33144
33145 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33146     
33147     /**
33148      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33149      */
33150     allowDecimals : true,
33151     /**
33152      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33153      */
33154     decimalSeparator : ".",
33155     /**
33156      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33157      */
33158     decimalPrecision : 2,
33159     /**
33160      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33161      */
33162     allowNegative : true,
33163     
33164     /**
33165      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33166      */
33167     allowZero: true,
33168     /**
33169      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33170      */
33171     minValue : Number.NEGATIVE_INFINITY,
33172     /**
33173      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33174      */
33175     maxValue : Number.MAX_VALUE,
33176     /**
33177      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33178      */
33179     minText : "The minimum value for this field is {0}",
33180     /**
33181      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33182      */
33183     maxText : "The maximum value for this field is {0}",
33184     /**
33185      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33186      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33187      */
33188     nanText : "{0} is not a valid number",
33189     /**
33190      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33191      */
33192     castInt : true,
33193     /**
33194      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33195      */
33196     thousandsDelimiter : false,
33197     /**
33198      * @cfg {String} valueAlign alignment of value
33199      */
33200     valueAlign : "left",
33201
33202     getAutoCreate : function()
33203     {
33204         var hiddenInput = {
33205             tag: 'input',
33206             type: 'hidden',
33207             id: Roo.id(),
33208             cls: 'hidden-number-input'
33209         };
33210         
33211         if (this.name) {
33212             hiddenInput.name = this.name;
33213         }
33214         
33215         this.name = '';
33216         
33217         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33218         
33219         this.name = hiddenInput.name;
33220         
33221         if(cfg.cn.length > 0) {
33222             cfg.cn.push(hiddenInput);
33223         }
33224         
33225         return cfg;
33226     },
33227
33228     // private
33229     initEvents : function()
33230     {   
33231         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33232         
33233         var allowed = "0123456789";
33234         
33235         if(this.allowDecimals){
33236             allowed += this.decimalSeparator;
33237         }
33238         
33239         if(this.allowNegative){
33240             allowed += "-";
33241         }
33242         
33243         if(this.thousandsDelimiter) {
33244             allowed += ",";
33245         }
33246         
33247         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33248         
33249         var keyPress = function(e){
33250             
33251             var k = e.getKey();
33252             
33253             var c = e.getCharCode();
33254             
33255             if(
33256                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33257                     allowed.indexOf(String.fromCharCode(c)) === -1
33258             ){
33259                 e.stopEvent();
33260                 return;
33261             }
33262             
33263             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33264                 return;
33265             }
33266             
33267             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33268                 e.stopEvent();
33269             }
33270         };
33271         
33272         this.el.on("keypress", keyPress, this);
33273     },
33274     
33275     validateValue : function(value)
33276     {
33277         
33278         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33279             return false;
33280         }
33281         
33282         var num = this.parseValue(value);
33283         
33284         if(isNaN(num)){
33285             this.markInvalid(String.format(this.nanText, value));
33286             return false;
33287         }
33288         
33289         if(num < this.minValue){
33290             this.markInvalid(String.format(this.minText, this.minValue));
33291             return false;
33292         }
33293         
33294         if(num > this.maxValue){
33295             this.markInvalid(String.format(this.maxText, this.maxValue));
33296             return false;
33297         }
33298         
33299         return true;
33300     },
33301
33302     getValue : function()
33303     {
33304         var v = this.hiddenEl().getValue();
33305         
33306         return this.fixPrecision(this.parseValue(v));
33307     },
33308
33309     parseValue : function(value)
33310     {
33311         if(this.thousandsDelimiter) {
33312             value += "";
33313             r = new RegExp(",", "g");
33314             value = value.replace(r, "");
33315         }
33316         
33317         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33318         return isNaN(value) ? '' : value;
33319     },
33320
33321     fixPrecision : function(value)
33322     {
33323         if(this.thousandsDelimiter) {
33324             value += "";
33325             r = new RegExp(",", "g");
33326             value = value.replace(r, "");
33327         }
33328         
33329         var nan = isNaN(value);
33330         
33331         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33332             return nan ? '' : value;
33333         }
33334         return parseFloat(value).toFixed(this.decimalPrecision);
33335     },
33336
33337     setValue : function(v)
33338     {
33339         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33340         
33341         this.value = v;
33342         
33343         if(this.rendered){
33344             
33345             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33346             
33347             this.inputEl().dom.value = (v == '') ? '' :
33348                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33349             
33350             if(!this.allowZero && v === '0') {
33351                 this.hiddenEl().dom.value = '';
33352                 this.inputEl().dom.value = '';
33353             }
33354             
33355             this.validate();
33356         }
33357     },
33358
33359     decimalPrecisionFcn : function(v)
33360     {
33361         return Math.floor(v);
33362     },
33363
33364     beforeBlur : function()
33365     {
33366         if(!this.castInt){
33367             return;
33368         }
33369         
33370         var v = this.parseValue(this.getRawValue());
33371         
33372         if(v || v === 0){
33373             this.setValue(v);
33374         }
33375     },
33376     
33377     hiddenEl : function()
33378     {
33379         return this.el.select('input.hidden-number-input',true).first();
33380     }
33381     
33382 });
33383
33384  
33385
33386 /*
33387 * Licence: LGPL
33388 */
33389
33390 /**
33391  * @class Roo.bootstrap.DocumentSlider
33392  * @extends Roo.bootstrap.Component
33393  * Bootstrap DocumentSlider class
33394  * 
33395  * @constructor
33396  * Create a new DocumentViewer
33397  * @param {Object} config The config object
33398  */
33399
33400 Roo.bootstrap.DocumentSlider = function(config){
33401     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33402     
33403     this.files = [];
33404     
33405     this.addEvents({
33406         /**
33407          * @event initial
33408          * Fire after initEvent
33409          * @param {Roo.bootstrap.DocumentSlider} this
33410          */
33411         "initial" : true,
33412         /**
33413          * @event update
33414          * Fire after update
33415          * @param {Roo.bootstrap.DocumentSlider} this
33416          */
33417         "update" : true,
33418         /**
33419          * @event click
33420          * Fire after click
33421          * @param {Roo.bootstrap.DocumentSlider} this
33422          */
33423         "click" : true
33424     });
33425 };
33426
33427 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33428     
33429     files : false,
33430     
33431     indicator : 0,
33432     
33433     getAutoCreate : function()
33434     {
33435         var cfg = {
33436             tag : 'div',
33437             cls : 'roo-document-slider',
33438             cn : [
33439                 {
33440                     tag : 'div',
33441                     cls : 'roo-document-slider-header',
33442                     cn : [
33443                         {
33444                             tag : 'div',
33445                             cls : 'roo-document-slider-header-title'
33446                         }
33447                     ]
33448                 },
33449                 {
33450                     tag : 'div',
33451                     cls : 'roo-document-slider-body',
33452                     cn : [
33453                         {
33454                             tag : 'div',
33455                             cls : 'roo-document-slider-prev',
33456                             cn : [
33457                                 {
33458                                     tag : 'i',
33459                                     cls : 'fa fa-chevron-left'
33460                                 }
33461                             ]
33462                         },
33463                         {
33464                             tag : 'div',
33465                             cls : 'roo-document-slider-thumb',
33466                             cn : [
33467                                 {
33468                                     tag : 'img',
33469                                     cls : 'roo-document-slider-image'
33470                                 }
33471                             ]
33472                         },
33473                         {
33474                             tag : 'div',
33475                             cls : 'roo-document-slider-next',
33476                             cn : [
33477                                 {
33478                                     tag : 'i',
33479                                     cls : 'fa fa-chevron-right'
33480                                 }
33481                             ]
33482                         }
33483                     ]
33484                 }
33485             ]
33486         };
33487         
33488         return cfg;
33489     },
33490     
33491     initEvents : function()
33492     {
33493         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33494         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33495         
33496         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33497         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33498         
33499         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33500         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33501         
33502         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33503         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33504         
33505         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33506         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33507         
33508         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33509         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33510         
33511         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33512         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33513         
33514         this.thumbEl.on('click', this.onClick, this);
33515         
33516         this.prevIndicator.on('click', this.prev, this);
33517         
33518         this.nextIndicator.on('click', this.next, this);
33519         
33520     },
33521     
33522     initial : function()
33523     {
33524         if(this.files.length){
33525             this.indicator = 1;
33526             this.update()
33527         }
33528         
33529         this.fireEvent('initial', this);
33530     },
33531     
33532     update : function()
33533     {
33534         this.imageEl.attr('src', this.files[this.indicator - 1]);
33535         
33536         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33537         
33538         this.prevIndicator.show();
33539         
33540         if(this.indicator == 1){
33541             this.prevIndicator.hide();
33542         }
33543         
33544         this.nextIndicator.show();
33545         
33546         if(this.indicator == this.files.length){
33547             this.nextIndicator.hide();
33548         }
33549         
33550         this.thumbEl.scrollTo('top');
33551         
33552         this.fireEvent('update', this);
33553     },
33554     
33555     onClick : function(e)
33556     {
33557         e.preventDefault();
33558         
33559         this.fireEvent('click', this);
33560     },
33561     
33562     prev : function(e)
33563     {
33564         e.preventDefault();
33565         
33566         this.indicator = Math.max(1, this.indicator - 1);
33567         
33568         this.update();
33569     },
33570     
33571     next : function(e)
33572     {
33573         e.preventDefault();
33574         
33575         this.indicator = Math.min(this.files.length, this.indicator + 1);
33576         
33577         this.update();
33578     }
33579 });
33580 /*
33581  * - LGPL
33582  *
33583  * RadioSet
33584  *
33585  *
33586  */
33587
33588 /**
33589  * @class Roo.bootstrap.RadioSet
33590  * @extends Roo.bootstrap.Input
33591  * Bootstrap RadioSet class
33592  * @cfg {String} indicatorpos (left|right) default left
33593  * @cfg {Boolean} inline (true|false) inline the element (default true)
33594  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33595  * @constructor
33596  * Create a new RadioSet
33597  * @param {Object} config The config object
33598  */
33599
33600 Roo.bootstrap.RadioSet = function(config){
33601     
33602     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33603     
33604     this.radioes = [];
33605     
33606     Roo.bootstrap.RadioSet.register(this);
33607     
33608     this.addEvents({
33609         /**
33610         * @event check
33611         * Fires when the element is checked or unchecked.
33612         * @param {Roo.bootstrap.RadioSet} this This radio
33613         * @param {Roo.bootstrap.Radio} item The checked item
33614         */
33615        check : true,
33616        /**
33617         * @event click
33618         * Fires when the element is click.
33619         * @param {Roo.bootstrap.RadioSet} this This radio set
33620         * @param {Roo.bootstrap.Radio} item The checked item
33621         * @param {Roo.EventObject} e The event object
33622         */
33623        click : true
33624     });
33625     
33626 };
33627
33628 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33629
33630     radioes : false,
33631     
33632     inline : true,
33633     
33634     weight : '',
33635     
33636     indicatorpos : 'left',
33637     
33638     getAutoCreate : function()
33639     {
33640         var label = {
33641             tag : 'label',
33642             cls : 'roo-radio-set-label',
33643             cn : [
33644                 {
33645                     tag : 'span',
33646                     html : this.fieldLabel
33647                 }
33648             ]
33649         };
33650         
33651         if(this.indicatorpos == 'left'){
33652             label.cn.unshift({
33653                 tag : 'i',
33654                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33655                 tooltip : 'This field is required'
33656             });
33657         } else {
33658             label.cn.push({
33659                 tag : 'i',
33660                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33661                 tooltip : 'This field is required'
33662             });
33663         }
33664         
33665         var items = {
33666             tag : 'div',
33667             cls : 'roo-radio-set-items'
33668         };
33669         
33670         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33671         
33672         if (align === 'left' && this.fieldLabel.length) {
33673             
33674             items = {
33675                 cls : "roo-radio-set-right", 
33676                 cn: [
33677                     items
33678                 ]
33679             };
33680             
33681             if(this.labelWidth > 12){
33682                 label.style = "width: " + this.labelWidth + 'px';
33683             }
33684             
33685             if(this.labelWidth < 13 && this.labelmd == 0){
33686                 this.labelmd = this.labelWidth;
33687             }
33688             
33689             if(this.labellg > 0){
33690                 label.cls += ' col-lg-' + this.labellg;
33691                 items.cls += ' col-lg-' + (12 - this.labellg);
33692             }
33693             
33694             if(this.labelmd > 0){
33695                 label.cls += ' col-md-' + this.labelmd;
33696                 items.cls += ' col-md-' + (12 - this.labelmd);
33697             }
33698             
33699             if(this.labelsm > 0){
33700                 label.cls += ' col-sm-' + this.labelsm;
33701                 items.cls += ' col-sm-' + (12 - this.labelsm);
33702             }
33703             
33704             if(this.labelxs > 0){
33705                 label.cls += ' col-xs-' + this.labelxs;
33706                 items.cls += ' col-xs-' + (12 - this.labelxs);
33707             }
33708         }
33709         
33710         var cfg = {
33711             tag : 'div',
33712             cls : 'roo-radio-set',
33713             cn : [
33714                 {
33715                     tag : 'input',
33716                     cls : 'roo-radio-set-input',
33717                     type : 'hidden',
33718                     name : this.name,
33719                     value : this.value ? this.value :  ''
33720                 },
33721                 label,
33722                 items
33723             ]
33724         };
33725         
33726         if(this.weight.length){
33727             cfg.cls += ' roo-radio-' + this.weight;
33728         }
33729         
33730         if(this.inline) {
33731             cfg.cls += ' roo-radio-set-inline';
33732         }
33733         
33734         var settings=this;
33735         ['xs','sm','md','lg'].map(function(size){
33736             if (settings[size]) {
33737                 cfg.cls += ' col-' + size + '-' + settings[size];
33738             }
33739         });
33740         
33741         return cfg;
33742         
33743     },
33744
33745     initEvents : function()
33746     {
33747         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33748         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33749         
33750         if(!this.fieldLabel.length){
33751             this.labelEl.hide();
33752         }
33753         
33754         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33755         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33756         
33757         this.indicator = this.indicatorEl();
33758         
33759         if(this.indicator){
33760             this.indicator.addClass('invisible');
33761         }
33762         
33763         this.originalValue = this.getValue();
33764         
33765     },
33766     
33767     inputEl: function ()
33768     {
33769         return this.el.select('.roo-radio-set-input', true).first();
33770     },
33771     
33772     getChildContainer : function()
33773     {
33774         return this.itemsEl;
33775     },
33776     
33777     register : function(item)
33778     {
33779         this.radioes.push(item);
33780         
33781     },
33782     
33783     validate : function()
33784     {   
33785         if(this.getVisibilityEl().hasClass('hidden')){
33786             return true;
33787         }
33788         
33789         var valid = false;
33790         
33791         Roo.each(this.radioes, function(i){
33792             if(!i.checked){
33793                 return;
33794             }
33795             
33796             valid = true;
33797             return false;
33798         });
33799         
33800         if(this.allowBlank) {
33801             return true;
33802         }
33803         
33804         if(this.disabled || valid){
33805             this.markValid();
33806             return true;
33807         }
33808         
33809         this.markInvalid();
33810         return false;
33811         
33812     },
33813     
33814     markValid : function()
33815     {
33816         if(this.labelEl.isVisible(true)){
33817             this.indicatorEl().removeClass('visible');
33818             this.indicatorEl().addClass('invisible');
33819         }
33820         
33821         this.el.removeClass([this.invalidClass, this.validClass]);
33822         this.el.addClass(this.validClass);
33823         
33824         this.fireEvent('valid', this);
33825     },
33826     
33827     markInvalid : function(msg)
33828     {
33829         if(this.allowBlank || this.disabled){
33830             return;
33831         }
33832         
33833         if(this.labelEl.isVisible(true)){
33834             this.indicatorEl().removeClass('invisible');
33835             this.indicatorEl().addClass('visible');
33836         }
33837         
33838         this.el.removeClass([this.invalidClass, this.validClass]);
33839         this.el.addClass(this.invalidClass);
33840         
33841         this.fireEvent('invalid', this, msg);
33842         
33843     },
33844     
33845     setValue : function(v, suppressEvent)
33846     {   
33847         if(this.value === v){
33848             return;
33849         }
33850         
33851         this.value = v;
33852         
33853         if(this.rendered){
33854             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33855         }
33856         
33857         Roo.each(this.radioes, function(i){
33858             i.checked = false;
33859             i.el.removeClass('checked');
33860         });
33861         
33862         Roo.each(this.radioes, function(i){
33863             
33864             if(i.value === v || i.value.toString() === v.toString()){
33865                 i.checked = true;
33866                 i.el.addClass('checked');
33867                 
33868                 if(suppressEvent !== true){
33869                     this.fireEvent('check', this, i);
33870                 }
33871                 
33872                 return false;
33873             }
33874             
33875         }, this);
33876         
33877         this.validate();
33878     },
33879     
33880     clearInvalid : function(){
33881         
33882         if(!this.el || this.preventMark){
33883             return;
33884         }
33885         
33886         this.el.removeClass([this.invalidClass]);
33887         
33888         this.fireEvent('valid', this);
33889     }
33890     
33891 });
33892
33893 Roo.apply(Roo.bootstrap.RadioSet, {
33894     
33895     groups: {},
33896     
33897     register : function(set)
33898     {
33899         this.groups[set.name] = set;
33900     },
33901     
33902     get: function(name) 
33903     {
33904         if (typeof(this.groups[name]) == 'undefined') {
33905             return false;
33906         }
33907         
33908         return this.groups[name] ;
33909     }
33910     
33911 });
33912 /*
33913  * Based on:
33914  * Ext JS Library 1.1.1
33915  * Copyright(c) 2006-2007, Ext JS, LLC.
33916  *
33917  * Originally Released Under LGPL - original licence link has changed is not relivant.
33918  *
33919  * Fork - LGPL
33920  * <script type="text/javascript">
33921  */
33922
33923
33924 /**
33925  * @class Roo.bootstrap.SplitBar
33926  * @extends Roo.util.Observable
33927  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33928  * <br><br>
33929  * Usage:
33930  * <pre><code>
33931 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33932                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33933 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33934 split.minSize = 100;
33935 split.maxSize = 600;
33936 split.animate = true;
33937 split.on('moved', splitterMoved);
33938 </code></pre>
33939  * @constructor
33940  * Create a new SplitBar
33941  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33942  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33943  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33944  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33945                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33946                         position of the SplitBar).
33947  */
33948 Roo.bootstrap.SplitBar = function(cfg){
33949     
33950     /** @private */
33951     
33952     //{
33953     //  dragElement : elm
33954     //  resizingElement: el,
33955         // optional..
33956     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33957     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33958         // existingProxy ???
33959     //}
33960     
33961     this.el = Roo.get(cfg.dragElement, true);
33962     this.el.dom.unselectable = "on";
33963     /** @private */
33964     this.resizingEl = Roo.get(cfg.resizingElement, true);
33965
33966     /**
33967      * @private
33968      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33969      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33970      * @type Number
33971      */
33972     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33973     
33974     /**
33975      * The minimum size of the resizing element. (Defaults to 0)
33976      * @type Number
33977      */
33978     this.minSize = 0;
33979     
33980     /**
33981      * The maximum size of the resizing element. (Defaults to 2000)
33982      * @type Number
33983      */
33984     this.maxSize = 2000;
33985     
33986     /**
33987      * Whether to animate the transition to the new size
33988      * @type Boolean
33989      */
33990     this.animate = false;
33991     
33992     /**
33993      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33994      * @type Boolean
33995      */
33996     this.useShim = false;
33997     
33998     /** @private */
33999     this.shim = null;
34000     
34001     if(!cfg.existingProxy){
34002         /** @private */
34003         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34004     }else{
34005         this.proxy = Roo.get(cfg.existingProxy).dom;
34006     }
34007     /** @private */
34008     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34009     
34010     /** @private */
34011     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34012     
34013     /** @private */
34014     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34015     
34016     /** @private */
34017     this.dragSpecs = {};
34018     
34019     /**
34020      * @private The adapter to use to positon and resize elements
34021      */
34022     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34023     this.adapter.init(this);
34024     
34025     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34026         /** @private */
34027         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34028         this.el.addClass("roo-splitbar-h");
34029     }else{
34030         /** @private */
34031         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34032         this.el.addClass("roo-splitbar-v");
34033     }
34034     
34035     this.addEvents({
34036         /**
34037          * @event resize
34038          * Fires when the splitter is moved (alias for {@link #event-moved})
34039          * @param {Roo.bootstrap.SplitBar} this
34040          * @param {Number} newSize the new width or height
34041          */
34042         "resize" : true,
34043         /**
34044          * @event moved
34045          * Fires when the splitter is moved
34046          * @param {Roo.bootstrap.SplitBar} this
34047          * @param {Number} newSize the new width or height
34048          */
34049         "moved" : true,
34050         /**
34051          * @event beforeresize
34052          * Fires before the splitter is dragged
34053          * @param {Roo.bootstrap.SplitBar} this
34054          */
34055         "beforeresize" : true,
34056
34057         "beforeapply" : true
34058     });
34059
34060     Roo.util.Observable.call(this);
34061 };
34062
34063 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34064     onStartProxyDrag : function(x, y){
34065         this.fireEvent("beforeresize", this);
34066         if(!this.overlay){
34067             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34068             o.unselectable();
34069             o.enableDisplayMode("block");
34070             // all splitbars share the same overlay
34071             Roo.bootstrap.SplitBar.prototype.overlay = o;
34072         }
34073         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34074         this.overlay.show();
34075         Roo.get(this.proxy).setDisplayed("block");
34076         var size = this.adapter.getElementSize(this);
34077         this.activeMinSize = this.getMinimumSize();;
34078         this.activeMaxSize = this.getMaximumSize();;
34079         var c1 = size - this.activeMinSize;
34080         var c2 = Math.max(this.activeMaxSize - size, 0);
34081         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34082             this.dd.resetConstraints();
34083             this.dd.setXConstraint(
34084                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34085                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34086             );
34087             this.dd.setYConstraint(0, 0);
34088         }else{
34089             this.dd.resetConstraints();
34090             this.dd.setXConstraint(0, 0);
34091             this.dd.setYConstraint(
34092                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34093                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34094             );
34095          }
34096         this.dragSpecs.startSize = size;
34097         this.dragSpecs.startPoint = [x, y];
34098         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34099     },
34100     
34101     /** 
34102      * @private Called after the drag operation by the DDProxy
34103      */
34104     onEndProxyDrag : function(e){
34105         Roo.get(this.proxy).setDisplayed(false);
34106         var endPoint = Roo.lib.Event.getXY(e);
34107         if(this.overlay){
34108             this.overlay.hide();
34109         }
34110         var newSize;
34111         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34112             newSize = this.dragSpecs.startSize + 
34113                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34114                     endPoint[0] - this.dragSpecs.startPoint[0] :
34115                     this.dragSpecs.startPoint[0] - endPoint[0]
34116                 );
34117         }else{
34118             newSize = this.dragSpecs.startSize + 
34119                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34120                     endPoint[1] - this.dragSpecs.startPoint[1] :
34121                     this.dragSpecs.startPoint[1] - endPoint[1]
34122                 );
34123         }
34124         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34125         if(newSize != this.dragSpecs.startSize){
34126             if(this.fireEvent('beforeapply', this, newSize) !== false){
34127                 this.adapter.setElementSize(this, newSize);
34128                 this.fireEvent("moved", this, newSize);
34129                 this.fireEvent("resize", this, newSize);
34130             }
34131         }
34132     },
34133     
34134     /**
34135      * Get the adapter this SplitBar uses
34136      * @return The adapter object
34137      */
34138     getAdapter : function(){
34139         return this.adapter;
34140     },
34141     
34142     /**
34143      * Set the adapter this SplitBar uses
34144      * @param {Object} adapter A SplitBar adapter object
34145      */
34146     setAdapter : function(adapter){
34147         this.adapter = adapter;
34148         this.adapter.init(this);
34149     },
34150     
34151     /**
34152      * Gets the minimum size for the resizing element
34153      * @return {Number} The minimum size
34154      */
34155     getMinimumSize : function(){
34156         return this.minSize;
34157     },
34158     
34159     /**
34160      * Sets the minimum size for the resizing element
34161      * @param {Number} minSize The minimum size
34162      */
34163     setMinimumSize : function(minSize){
34164         this.minSize = minSize;
34165     },
34166     
34167     /**
34168      * Gets the maximum size for the resizing element
34169      * @return {Number} The maximum size
34170      */
34171     getMaximumSize : function(){
34172         return this.maxSize;
34173     },
34174     
34175     /**
34176      * Sets the maximum size for the resizing element
34177      * @param {Number} maxSize The maximum size
34178      */
34179     setMaximumSize : function(maxSize){
34180         this.maxSize = maxSize;
34181     },
34182     
34183     /**
34184      * Sets the initialize size for the resizing element
34185      * @param {Number} size The initial size
34186      */
34187     setCurrentSize : function(size){
34188         var oldAnimate = this.animate;
34189         this.animate = false;
34190         this.adapter.setElementSize(this, size);
34191         this.animate = oldAnimate;
34192     },
34193     
34194     /**
34195      * Destroy this splitbar. 
34196      * @param {Boolean} removeEl True to remove the element
34197      */
34198     destroy : function(removeEl){
34199         if(this.shim){
34200             this.shim.remove();
34201         }
34202         this.dd.unreg();
34203         this.proxy.parentNode.removeChild(this.proxy);
34204         if(removeEl){
34205             this.el.remove();
34206         }
34207     }
34208 });
34209
34210 /**
34211  * @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.
34212  */
34213 Roo.bootstrap.SplitBar.createProxy = function(dir){
34214     var proxy = new Roo.Element(document.createElement("div"));
34215     proxy.unselectable();
34216     var cls = 'roo-splitbar-proxy';
34217     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34218     document.body.appendChild(proxy.dom);
34219     return proxy.dom;
34220 };
34221
34222 /** 
34223  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34224  * Default Adapter. It assumes the splitter and resizing element are not positioned
34225  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34226  */
34227 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34228 };
34229
34230 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34231     // do nothing for now
34232     init : function(s){
34233     
34234     },
34235     /**
34236      * Called before drag operations to get the current size of the resizing element. 
34237      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34238      */
34239      getElementSize : function(s){
34240         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34241             return s.resizingEl.getWidth();
34242         }else{
34243             return s.resizingEl.getHeight();
34244         }
34245     },
34246     
34247     /**
34248      * Called after drag operations to set the size of the resizing element.
34249      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34250      * @param {Number} newSize The new size to set
34251      * @param {Function} onComplete A function to be invoked when resizing is complete
34252      */
34253     setElementSize : function(s, newSize, onComplete){
34254         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34255             if(!s.animate){
34256                 s.resizingEl.setWidth(newSize);
34257                 if(onComplete){
34258                     onComplete(s, newSize);
34259                 }
34260             }else{
34261                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34262             }
34263         }else{
34264             
34265             if(!s.animate){
34266                 s.resizingEl.setHeight(newSize);
34267                 if(onComplete){
34268                     onComplete(s, newSize);
34269                 }
34270             }else{
34271                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34272             }
34273         }
34274     }
34275 };
34276
34277 /** 
34278  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34279  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34280  * Adapter that  moves the splitter element to align with the resized sizing element. 
34281  * Used with an absolute positioned SplitBar.
34282  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34283  * document.body, make sure you assign an id to the body element.
34284  */
34285 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34286     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34287     this.container = Roo.get(container);
34288 };
34289
34290 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34291     init : function(s){
34292         this.basic.init(s);
34293     },
34294     
34295     getElementSize : function(s){
34296         return this.basic.getElementSize(s);
34297     },
34298     
34299     setElementSize : function(s, newSize, onComplete){
34300         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34301     },
34302     
34303     moveSplitter : function(s){
34304         var yes = Roo.bootstrap.SplitBar;
34305         switch(s.placement){
34306             case yes.LEFT:
34307                 s.el.setX(s.resizingEl.getRight());
34308                 break;
34309             case yes.RIGHT:
34310                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34311                 break;
34312             case yes.TOP:
34313                 s.el.setY(s.resizingEl.getBottom());
34314                 break;
34315             case yes.BOTTOM:
34316                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34317                 break;
34318         }
34319     }
34320 };
34321
34322 /**
34323  * Orientation constant - Create a vertical SplitBar
34324  * @static
34325  * @type Number
34326  */
34327 Roo.bootstrap.SplitBar.VERTICAL = 1;
34328
34329 /**
34330  * Orientation constant - Create a horizontal SplitBar
34331  * @static
34332  * @type Number
34333  */
34334 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34335
34336 /**
34337  * Placement constant - The resizing element is to the left of the splitter element
34338  * @static
34339  * @type Number
34340  */
34341 Roo.bootstrap.SplitBar.LEFT = 1;
34342
34343 /**
34344  * Placement constant - The resizing element is to the right of the splitter element
34345  * @static
34346  * @type Number
34347  */
34348 Roo.bootstrap.SplitBar.RIGHT = 2;
34349
34350 /**
34351  * Placement constant - The resizing element is positioned above the splitter element
34352  * @static
34353  * @type Number
34354  */
34355 Roo.bootstrap.SplitBar.TOP = 3;
34356
34357 /**
34358  * Placement constant - The resizing element is positioned under splitter element
34359  * @static
34360  * @type Number
34361  */
34362 Roo.bootstrap.SplitBar.BOTTOM = 4;
34363 Roo.namespace("Roo.bootstrap.layout");/*
34364  * Based on:
34365  * Ext JS Library 1.1.1
34366  * Copyright(c) 2006-2007, Ext JS, LLC.
34367  *
34368  * Originally Released Under LGPL - original licence link has changed is not relivant.
34369  *
34370  * Fork - LGPL
34371  * <script type="text/javascript">
34372  */
34373
34374 /**
34375  * @class Roo.bootstrap.layout.Manager
34376  * @extends Roo.bootstrap.Component
34377  * Base class for layout managers.
34378  */
34379 Roo.bootstrap.layout.Manager = function(config)
34380 {
34381     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34382
34383
34384
34385
34386
34387     /** false to disable window resize monitoring @type Boolean */
34388     this.monitorWindowResize = true;
34389     this.regions = {};
34390     this.addEvents({
34391         /**
34392          * @event layout
34393          * Fires when a layout is performed.
34394          * @param {Roo.LayoutManager} this
34395          */
34396         "layout" : true,
34397         /**
34398          * @event regionresized
34399          * Fires when the user resizes a region.
34400          * @param {Roo.LayoutRegion} region The resized region
34401          * @param {Number} newSize The new size (width for east/west, height for north/south)
34402          */
34403         "regionresized" : true,
34404         /**
34405          * @event regioncollapsed
34406          * Fires when a region is collapsed.
34407          * @param {Roo.LayoutRegion} region The collapsed region
34408          */
34409         "regioncollapsed" : true,
34410         /**
34411          * @event regionexpanded
34412          * Fires when a region is expanded.
34413          * @param {Roo.LayoutRegion} region The expanded region
34414          */
34415         "regionexpanded" : true
34416     });
34417     this.updating = false;
34418
34419     if (config.el) {
34420         this.el = Roo.get(config.el);
34421         this.initEvents();
34422     }
34423
34424 };
34425
34426 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34427
34428
34429     regions : null,
34430
34431     monitorWindowResize : true,
34432
34433
34434     updating : false,
34435
34436
34437     onRender : function(ct, position)
34438     {
34439         if(!this.el){
34440             this.el = Roo.get(ct);
34441             this.initEvents();
34442         }
34443         //this.fireEvent('render',this);
34444     },
34445
34446
34447     initEvents: function()
34448     {
34449
34450
34451         // ie scrollbar fix
34452         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34453             document.body.scroll = "no";
34454         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34455             this.el.position('relative');
34456         }
34457         this.id = this.el.id;
34458         this.el.addClass("roo-layout-container");
34459         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34460         if(this.el.dom != document.body ) {
34461             this.el.on('resize', this.layout,this);
34462             this.el.on('show', this.layout,this);
34463         }
34464
34465     },
34466
34467     /**
34468      * Returns true if this layout is currently being updated
34469      * @return {Boolean}
34470      */
34471     isUpdating : function(){
34472         return this.updating;
34473     },
34474
34475     /**
34476      * Suspend the LayoutManager from doing auto-layouts while
34477      * making multiple add or remove calls
34478      */
34479     beginUpdate : function(){
34480         this.updating = true;
34481     },
34482
34483     /**
34484      * Restore auto-layouts and optionally disable the manager from performing a layout
34485      * @param {Boolean} noLayout true to disable a layout update
34486      */
34487     endUpdate : function(noLayout){
34488         this.updating = false;
34489         if(!noLayout){
34490             this.layout();
34491         }
34492     },
34493
34494     layout: function(){
34495         // abstract...
34496     },
34497
34498     onRegionResized : function(region, newSize){
34499         this.fireEvent("regionresized", region, newSize);
34500         this.layout();
34501     },
34502
34503     onRegionCollapsed : function(region){
34504         this.fireEvent("regioncollapsed", region);
34505     },
34506
34507     onRegionExpanded : function(region){
34508         this.fireEvent("regionexpanded", region);
34509     },
34510
34511     /**
34512      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34513      * performs box-model adjustments.
34514      * @return {Object} The size as an object {width: (the width), height: (the height)}
34515      */
34516     getViewSize : function()
34517     {
34518         var size;
34519         if(this.el.dom != document.body){
34520             size = this.el.getSize();
34521         }else{
34522             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34523         }
34524         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34525         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34526         return size;
34527     },
34528
34529     /**
34530      * Returns the Element this layout is bound to.
34531      * @return {Roo.Element}
34532      */
34533     getEl : function(){
34534         return this.el;
34535     },
34536
34537     /**
34538      * Returns the specified region.
34539      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34540      * @return {Roo.LayoutRegion}
34541      */
34542     getRegion : function(target){
34543         return this.regions[target.toLowerCase()];
34544     },
34545
34546     onWindowResize : function(){
34547         if(this.monitorWindowResize){
34548             this.layout();
34549         }
34550     }
34551 });
34552 /*
34553  * Based on:
34554  * Ext JS Library 1.1.1
34555  * Copyright(c) 2006-2007, Ext JS, LLC.
34556  *
34557  * Originally Released Under LGPL - original licence link has changed is not relivant.
34558  *
34559  * Fork - LGPL
34560  * <script type="text/javascript">
34561  */
34562 /**
34563  * @class Roo.bootstrap.layout.Border
34564  * @extends Roo.bootstrap.layout.Manager
34565  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34566  * please see: examples/bootstrap/nested.html<br><br>
34567  
34568 <b>The container the layout is rendered into can be either the body element or any other element.
34569 If it is not the body element, the container needs to either be an absolute positioned element,
34570 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34571 the container size if it is not the body element.</b>
34572
34573 * @constructor
34574 * Create a new Border
34575 * @param {Object} config Configuration options
34576  */
34577 Roo.bootstrap.layout.Border = function(config){
34578     config = config || {};
34579     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34580     
34581     
34582     
34583     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34584         if(config[region]){
34585             config[region].region = region;
34586             this.addRegion(config[region]);
34587         }
34588     },this);
34589     
34590 };
34591
34592 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34593
34594 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34595     /**
34596      * Creates and adds a new region if it doesn't already exist.
34597      * @param {String} target The target region key (north, south, east, west or center).
34598      * @param {Object} config The regions config object
34599      * @return {BorderLayoutRegion} The new region
34600      */
34601     addRegion : function(config)
34602     {
34603         if(!this.regions[config.region]){
34604             var r = this.factory(config);
34605             this.bindRegion(r);
34606         }
34607         return this.regions[config.region];
34608     },
34609
34610     // private (kinda)
34611     bindRegion : function(r){
34612         this.regions[r.config.region] = r;
34613         
34614         r.on("visibilitychange",    this.layout, this);
34615         r.on("paneladded",          this.layout, this);
34616         r.on("panelremoved",        this.layout, this);
34617         r.on("invalidated",         this.layout, this);
34618         r.on("resized",             this.onRegionResized, this);
34619         r.on("collapsed",           this.onRegionCollapsed, this);
34620         r.on("expanded",            this.onRegionExpanded, this);
34621     },
34622
34623     /**
34624      * Performs a layout update.
34625      */
34626     layout : function()
34627     {
34628         if(this.updating) {
34629             return;
34630         }
34631         
34632         // render all the rebions if they have not been done alreayd?
34633         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34634             if(this.regions[region] && !this.regions[region].bodyEl){
34635                 this.regions[region].onRender(this.el)
34636             }
34637         },this);
34638         
34639         var size = this.getViewSize();
34640         var w = size.width;
34641         var h = size.height;
34642         var centerW = w;
34643         var centerH = h;
34644         var centerY = 0;
34645         var centerX = 0;
34646         //var x = 0, y = 0;
34647
34648         var rs = this.regions;
34649         var north = rs["north"];
34650         var south = rs["south"]; 
34651         var west = rs["west"];
34652         var east = rs["east"];
34653         var center = rs["center"];
34654         //if(this.hideOnLayout){ // not supported anymore
34655             //c.el.setStyle("display", "none");
34656         //}
34657         if(north && north.isVisible()){
34658             var b = north.getBox();
34659             var m = north.getMargins();
34660             b.width = w - (m.left+m.right);
34661             b.x = m.left;
34662             b.y = m.top;
34663             centerY = b.height + b.y + m.bottom;
34664             centerH -= centerY;
34665             north.updateBox(this.safeBox(b));
34666         }
34667         if(south && south.isVisible()){
34668             var b = south.getBox();
34669             var m = south.getMargins();
34670             b.width = w - (m.left+m.right);
34671             b.x = m.left;
34672             var totalHeight = (b.height + m.top + m.bottom);
34673             b.y = h - totalHeight + m.top;
34674             centerH -= totalHeight;
34675             south.updateBox(this.safeBox(b));
34676         }
34677         if(west && west.isVisible()){
34678             var b = west.getBox();
34679             var m = west.getMargins();
34680             b.height = centerH - (m.top+m.bottom);
34681             b.x = m.left;
34682             b.y = centerY + m.top;
34683             var totalWidth = (b.width + m.left + m.right);
34684             centerX += totalWidth;
34685             centerW -= totalWidth;
34686             west.updateBox(this.safeBox(b));
34687         }
34688         if(east && east.isVisible()){
34689             var b = east.getBox();
34690             var m = east.getMargins();
34691             b.height = centerH - (m.top+m.bottom);
34692             var totalWidth = (b.width + m.left + m.right);
34693             b.x = w - totalWidth + m.left;
34694             b.y = centerY + m.top;
34695             centerW -= totalWidth;
34696             east.updateBox(this.safeBox(b));
34697         }
34698         if(center){
34699             var m = center.getMargins();
34700             var centerBox = {
34701                 x: centerX + m.left,
34702                 y: centerY + m.top,
34703                 width: centerW - (m.left+m.right),
34704                 height: centerH - (m.top+m.bottom)
34705             };
34706             //if(this.hideOnLayout){
34707                 //center.el.setStyle("display", "block");
34708             //}
34709             center.updateBox(this.safeBox(centerBox));
34710         }
34711         this.el.repaint();
34712         this.fireEvent("layout", this);
34713     },
34714
34715     // private
34716     safeBox : function(box){
34717         box.width = Math.max(0, box.width);
34718         box.height = Math.max(0, box.height);
34719         return box;
34720     },
34721
34722     /**
34723      * Adds a ContentPanel (or subclass) to this layout.
34724      * @param {String} target The target region key (north, south, east, west or center).
34725      * @param {Roo.ContentPanel} panel The panel to add
34726      * @return {Roo.ContentPanel} The added panel
34727      */
34728     add : function(target, panel){
34729          
34730         target = target.toLowerCase();
34731         return this.regions[target].add(panel);
34732     },
34733
34734     /**
34735      * Remove a ContentPanel (or subclass) to this layout.
34736      * @param {String} target The target region key (north, south, east, west or center).
34737      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34738      * @return {Roo.ContentPanel} The removed panel
34739      */
34740     remove : function(target, panel){
34741         target = target.toLowerCase();
34742         return this.regions[target].remove(panel);
34743     },
34744
34745     /**
34746      * Searches all regions for a panel with the specified id
34747      * @param {String} panelId
34748      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34749      */
34750     findPanel : function(panelId){
34751         var rs = this.regions;
34752         for(var target in rs){
34753             if(typeof rs[target] != "function"){
34754                 var p = rs[target].getPanel(panelId);
34755                 if(p){
34756                     return p;
34757                 }
34758             }
34759         }
34760         return null;
34761     },
34762
34763     /**
34764      * Searches all regions for a panel with the specified id and activates (shows) it.
34765      * @param {String/ContentPanel} panelId The panels id or the panel itself
34766      * @return {Roo.ContentPanel} The shown panel or null
34767      */
34768     showPanel : function(panelId) {
34769       var rs = this.regions;
34770       for(var target in rs){
34771          var r = rs[target];
34772          if(typeof r != "function"){
34773             if(r.hasPanel(panelId)){
34774                return r.showPanel(panelId);
34775             }
34776          }
34777       }
34778       return null;
34779    },
34780
34781    /**
34782      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34783      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34784      */
34785    /*
34786     restoreState : function(provider){
34787         if(!provider){
34788             provider = Roo.state.Manager;
34789         }
34790         var sm = new Roo.LayoutStateManager();
34791         sm.init(this, provider);
34792     },
34793 */
34794  
34795  
34796     /**
34797      * Adds a xtype elements to the layout.
34798      * <pre><code>
34799
34800 layout.addxtype({
34801        xtype : 'ContentPanel',
34802        region: 'west',
34803        items: [ .... ]
34804    }
34805 );
34806
34807 layout.addxtype({
34808         xtype : 'NestedLayoutPanel',
34809         region: 'west',
34810         layout: {
34811            center: { },
34812            west: { }   
34813         },
34814         items : [ ... list of content panels or nested layout panels.. ]
34815    }
34816 );
34817 </code></pre>
34818      * @param {Object} cfg Xtype definition of item to add.
34819      */
34820     addxtype : function(cfg)
34821     {
34822         // basically accepts a pannel...
34823         // can accept a layout region..!?!?
34824         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34825         
34826         
34827         // theory?  children can only be panels??
34828         
34829         //if (!cfg.xtype.match(/Panel$/)) {
34830         //    return false;
34831         //}
34832         var ret = false;
34833         
34834         if (typeof(cfg.region) == 'undefined') {
34835             Roo.log("Failed to add Panel, region was not set");
34836             Roo.log(cfg);
34837             return false;
34838         }
34839         var region = cfg.region;
34840         delete cfg.region;
34841         
34842           
34843         var xitems = [];
34844         if (cfg.items) {
34845             xitems = cfg.items;
34846             delete cfg.items;
34847         }
34848         var nb = false;
34849         
34850         switch(cfg.xtype) 
34851         {
34852             case 'Content':  // ContentPanel (el, cfg)
34853             case 'Scroll':  // ContentPanel (el, cfg)
34854             case 'View': 
34855                 cfg.autoCreate = true;
34856                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34857                 //} else {
34858                 //    var el = this.el.createChild();
34859                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34860                 //}
34861                 
34862                 this.add(region, ret);
34863                 break;
34864             
34865             /*
34866             case 'TreePanel': // our new panel!
34867                 cfg.el = this.el.createChild();
34868                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34869                 this.add(region, ret);
34870                 break;
34871             */
34872             
34873             case 'Nest': 
34874                 // create a new Layout (which is  a Border Layout...
34875                 
34876                 var clayout = cfg.layout;
34877                 clayout.el  = this.el.createChild();
34878                 clayout.items   = clayout.items  || [];
34879                 
34880                 delete cfg.layout;
34881                 
34882                 // replace this exitems with the clayout ones..
34883                 xitems = clayout.items;
34884                  
34885                 // force background off if it's in center...
34886                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34887                     cfg.background = false;
34888                 }
34889                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34890                 
34891                 
34892                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34893                 //console.log('adding nested layout panel '  + cfg.toSource());
34894                 this.add(region, ret);
34895                 nb = {}; /// find first...
34896                 break;
34897             
34898             case 'Grid':
34899                 
34900                 // needs grid and region
34901                 
34902                 //var el = this.getRegion(region).el.createChild();
34903                 /*
34904                  *var el = this.el.createChild();
34905                 // create the grid first...
34906                 cfg.grid.container = el;
34907                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34908                 */
34909                 
34910                 if (region == 'center' && this.active ) {
34911                     cfg.background = false;
34912                 }
34913                 
34914                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34915                 
34916                 this.add(region, ret);
34917                 /*
34918                 if (cfg.background) {
34919                     // render grid on panel activation (if panel background)
34920                     ret.on('activate', function(gp) {
34921                         if (!gp.grid.rendered) {
34922                     //        gp.grid.render(el);
34923                         }
34924                     });
34925                 } else {
34926                   //  cfg.grid.render(el);
34927                 }
34928                 */
34929                 break;
34930            
34931            
34932             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34933                 // it was the old xcomponent building that caused this before.
34934                 // espeically if border is the top element in the tree.
34935                 ret = this;
34936                 break; 
34937                 
34938                     
34939                 
34940                 
34941                 
34942             default:
34943                 /*
34944                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34945                     
34946                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34947                     this.add(region, ret);
34948                 } else {
34949                 */
34950                     Roo.log(cfg);
34951                     throw "Can not add '" + cfg.xtype + "' to Border";
34952                     return null;
34953              
34954                                 
34955              
34956         }
34957         this.beginUpdate();
34958         // add children..
34959         var region = '';
34960         var abn = {};
34961         Roo.each(xitems, function(i)  {
34962             region = nb && i.region ? i.region : false;
34963             
34964             var add = ret.addxtype(i);
34965            
34966             if (region) {
34967                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34968                 if (!i.background) {
34969                     abn[region] = nb[region] ;
34970                 }
34971             }
34972             
34973         });
34974         this.endUpdate();
34975
34976         // make the last non-background panel active..
34977         //if (nb) { Roo.log(abn); }
34978         if (nb) {
34979             
34980             for(var r in abn) {
34981                 region = this.getRegion(r);
34982                 if (region) {
34983                     // tried using nb[r], but it does not work..
34984                      
34985                     region.showPanel(abn[r]);
34986                    
34987                 }
34988             }
34989         }
34990         return ret;
34991         
34992     },
34993     
34994     
34995 // private
34996     factory : function(cfg)
34997     {
34998         
34999         var validRegions = Roo.bootstrap.layout.Border.regions;
35000
35001         var target = cfg.region;
35002         cfg.mgr = this;
35003         
35004         var r = Roo.bootstrap.layout;
35005         Roo.log(target);
35006         switch(target){
35007             case "north":
35008                 return new r.North(cfg);
35009             case "south":
35010                 return new r.South(cfg);
35011             case "east":
35012                 return new r.East(cfg);
35013             case "west":
35014                 return new r.West(cfg);
35015             case "center":
35016                 return new r.Center(cfg);
35017         }
35018         throw 'Layout region "'+target+'" not supported.';
35019     }
35020     
35021     
35022 });
35023  /*
35024  * Based on:
35025  * Ext JS Library 1.1.1
35026  * Copyright(c) 2006-2007, Ext JS, LLC.
35027  *
35028  * Originally Released Under LGPL - original licence link has changed is not relivant.
35029  *
35030  * Fork - LGPL
35031  * <script type="text/javascript">
35032  */
35033  
35034 /**
35035  * @class Roo.bootstrap.layout.Basic
35036  * @extends Roo.util.Observable
35037  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35038  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35039  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35040  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35041  * @cfg {string}   region  the region that it inhabits..
35042  * @cfg {bool}   skipConfig skip config?
35043  * 
35044
35045  */
35046 Roo.bootstrap.layout.Basic = function(config){
35047     
35048     this.mgr = config.mgr;
35049     
35050     this.position = config.region;
35051     
35052     var skipConfig = config.skipConfig;
35053     
35054     this.events = {
35055         /**
35056          * @scope Roo.BasicLayoutRegion
35057          */
35058         
35059         /**
35060          * @event beforeremove
35061          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35062          * @param {Roo.LayoutRegion} this
35063          * @param {Roo.ContentPanel} panel The panel
35064          * @param {Object} e The cancel event object
35065          */
35066         "beforeremove" : true,
35067         /**
35068          * @event invalidated
35069          * Fires when the layout for this region is changed.
35070          * @param {Roo.LayoutRegion} this
35071          */
35072         "invalidated" : true,
35073         /**
35074          * @event visibilitychange
35075          * Fires when this region is shown or hidden 
35076          * @param {Roo.LayoutRegion} this
35077          * @param {Boolean} visibility true or false
35078          */
35079         "visibilitychange" : true,
35080         /**
35081          * @event paneladded
35082          * Fires when a panel is added. 
35083          * @param {Roo.LayoutRegion} this
35084          * @param {Roo.ContentPanel} panel The panel
35085          */
35086         "paneladded" : true,
35087         /**
35088          * @event panelremoved
35089          * Fires when a panel is removed. 
35090          * @param {Roo.LayoutRegion} this
35091          * @param {Roo.ContentPanel} panel The panel
35092          */
35093         "panelremoved" : true,
35094         /**
35095          * @event beforecollapse
35096          * Fires when this region before collapse.
35097          * @param {Roo.LayoutRegion} this
35098          */
35099         "beforecollapse" : true,
35100         /**
35101          * @event collapsed
35102          * Fires when this region is collapsed.
35103          * @param {Roo.LayoutRegion} this
35104          */
35105         "collapsed" : true,
35106         /**
35107          * @event expanded
35108          * Fires when this region is expanded.
35109          * @param {Roo.LayoutRegion} this
35110          */
35111         "expanded" : true,
35112         /**
35113          * @event slideshow
35114          * Fires when this region is slid into view.
35115          * @param {Roo.LayoutRegion} this
35116          */
35117         "slideshow" : true,
35118         /**
35119          * @event slidehide
35120          * Fires when this region slides out of view. 
35121          * @param {Roo.LayoutRegion} this
35122          */
35123         "slidehide" : true,
35124         /**
35125          * @event panelactivated
35126          * Fires when a panel is activated. 
35127          * @param {Roo.LayoutRegion} this
35128          * @param {Roo.ContentPanel} panel The activated panel
35129          */
35130         "panelactivated" : true,
35131         /**
35132          * @event resized
35133          * Fires when the user resizes this region. 
35134          * @param {Roo.LayoutRegion} this
35135          * @param {Number} newSize The new size (width for east/west, height for north/south)
35136          */
35137         "resized" : true
35138     };
35139     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35140     this.panels = new Roo.util.MixedCollection();
35141     this.panels.getKey = this.getPanelId.createDelegate(this);
35142     this.box = null;
35143     this.activePanel = null;
35144     // ensure listeners are added...
35145     
35146     if (config.listeners || config.events) {
35147         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35148             listeners : config.listeners || {},
35149             events : config.events || {}
35150         });
35151     }
35152     
35153     if(skipConfig !== true){
35154         this.applyConfig(config);
35155     }
35156 };
35157
35158 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35159 {
35160     getPanelId : function(p){
35161         return p.getId();
35162     },
35163     
35164     applyConfig : function(config){
35165         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35166         this.config = config;
35167         
35168     },
35169     
35170     /**
35171      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35172      * the width, for horizontal (north, south) the height.
35173      * @param {Number} newSize The new width or height
35174      */
35175     resizeTo : function(newSize){
35176         var el = this.el ? this.el :
35177                  (this.activePanel ? this.activePanel.getEl() : null);
35178         if(el){
35179             switch(this.position){
35180                 case "east":
35181                 case "west":
35182                     el.setWidth(newSize);
35183                     this.fireEvent("resized", this, newSize);
35184                 break;
35185                 case "north":
35186                 case "south":
35187                     el.setHeight(newSize);
35188                     this.fireEvent("resized", this, newSize);
35189                 break;                
35190             }
35191         }
35192     },
35193     
35194     getBox : function(){
35195         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35196     },
35197     
35198     getMargins : function(){
35199         return this.margins;
35200     },
35201     
35202     updateBox : function(box){
35203         this.box = box;
35204         var el = this.activePanel.getEl();
35205         el.dom.style.left = box.x + "px";
35206         el.dom.style.top = box.y + "px";
35207         this.activePanel.setSize(box.width, box.height);
35208     },
35209     
35210     /**
35211      * Returns the container element for this region.
35212      * @return {Roo.Element}
35213      */
35214     getEl : function(){
35215         return this.activePanel;
35216     },
35217     
35218     /**
35219      * Returns true if this region is currently visible.
35220      * @return {Boolean}
35221      */
35222     isVisible : function(){
35223         return this.activePanel ? true : false;
35224     },
35225     
35226     setActivePanel : function(panel){
35227         panel = this.getPanel(panel);
35228         if(this.activePanel && this.activePanel != panel){
35229             this.activePanel.setActiveState(false);
35230             this.activePanel.getEl().setLeftTop(-10000,-10000);
35231         }
35232         this.activePanel = panel;
35233         panel.setActiveState(true);
35234         if(this.box){
35235             panel.setSize(this.box.width, this.box.height);
35236         }
35237         this.fireEvent("panelactivated", this, panel);
35238         this.fireEvent("invalidated");
35239     },
35240     
35241     /**
35242      * Show the specified panel.
35243      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35244      * @return {Roo.ContentPanel} The shown panel or null
35245      */
35246     showPanel : function(panel){
35247         panel = this.getPanel(panel);
35248         if(panel){
35249             this.setActivePanel(panel);
35250         }
35251         return panel;
35252     },
35253     
35254     /**
35255      * Get the active panel for this region.
35256      * @return {Roo.ContentPanel} The active panel or null
35257      */
35258     getActivePanel : function(){
35259         return this.activePanel;
35260     },
35261     
35262     /**
35263      * Add the passed ContentPanel(s)
35264      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35265      * @return {Roo.ContentPanel} The panel added (if only one was added)
35266      */
35267     add : function(panel){
35268         if(arguments.length > 1){
35269             for(var i = 0, len = arguments.length; i < len; i++) {
35270                 this.add(arguments[i]);
35271             }
35272             return null;
35273         }
35274         if(this.hasPanel(panel)){
35275             this.showPanel(panel);
35276             return panel;
35277         }
35278         var el = panel.getEl();
35279         if(el.dom.parentNode != this.mgr.el.dom){
35280             this.mgr.el.dom.appendChild(el.dom);
35281         }
35282         if(panel.setRegion){
35283             panel.setRegion(this);
35284         }
35285         this.panels.add(panel);
35286         el.setStyle("position", "absolute");
35287         if(!panel.background){
35288             this.setActivePanel(panel);
35289             if(this.config.initialSize && this.panels.getCount()==1){
35290                 this.resizeTo(this.config.initialSize);
35291             }
35292         }
35293         this.fireEvent("paneladded", this, panel);
35294         return panel;
35295     },
35296     
35297     /**
35298      * Returns true if the panel is in this region.
35299      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35300      * @return {Boolean}
35301      */
35302     hasPanel : function(panel){
35303         if(typeof panel == "object"){ // must be panel obj
35304             panel = panel.getId();
35305         }
35306         return this.getPanel(panel) ? true : false;
35307     },
35308     
35309     /**
35310      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35311      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35312      * @param {Boolean} preservePanel Overrides the config preservePanel option
35313      * @return {Roo.ContentPanel} The panel that was removed
35314      */
35315     remove : function(panel, preservePanel){
35316         panel = this.getPanel(panel);
35317         if(!panel){
35318             return null;
35319         }
35320         var e = {};
35321         this.fireEvent("beforeremove", this, panel, e);
35322         if(e.cancel === true){
35323             return null;
35324         }
35325         var panelId = panel.getId();
35326         this.panels.removeKey(panelId);
35327         return panel;
35328     },
35329     
35330     /**
35331      * Returns the panel specified or null if it's not in this region.
35332      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35333      * @return {Roo.ContentPanel}
35334      */
35335     getPanel : function(id){
35336         if(typeof id == "object"){ // must be panel obj
35337             return id;
35338         }
35339         return this.panels.get(id);
35340     },
35341     
35342     /**
35343      * Returns this regions position (north/south/east/west/center).
35344      * @return {String} 
35345      */
35346     getPosition: function(){
35347         return this.position;    
35348     }
35349 });/*
35350  * Based on:
35351  * Ext JS Library 1.1.1
35352  * Copyright(c) 2006-2007, Ext JS, LLC.
35353  *
35354  * Originally Released Under LGPL - original licence link has changed is not relivant.
35355  *
35356  * Fork - LGPL
35357  * <script type="text/javascript">
35358  */
35359  
35360 /**
35361  * @class Roo.bootstrap.layout.Region
35362  * @extends Roo.bootstrap.layout.Basic
35363  * This class represents a region in a layout manager.
35364  
35365  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35366  * @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})
35367  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35368  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35369  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35370  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35371  * @cfg {String}    title           The title for the region (overrides panel titles)
35372  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35373  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35374  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35375  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35376  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35377  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35378  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35379  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35380  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35381  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35382
35383  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35384  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35385  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35386  * @cfg {Number}    width           For East/West panels
35387  * @cfg {Number}    height          For North/South panels
35388  * @cfg {Boolean}   split           To show the splitter
35389  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35390  * 
35391  * @cfg {string}   cls             Extra CSS classes to add to region
35392  * 
35393  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35394  * @cfg {string}   region  the region that it inhabits..
35395  *
35396
35397  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35398  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35399
35400  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35401  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35402  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35403  */
35404 Roo.bootstrap.layout.Region = function(config)
35405 {
35406     this.applyConfig(config);
35407
35408     var mgr = config.mgr;
35409     var pos = config.region;
35410     config.skipConfig = true;
35411     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35412     
35413     if (mgr.el) {
35414         this.onRender(mgr.el);   
35415     }
35416      
35417     this.visible = true;
35418     this.collapsed = false;
35419     this.unrendered_panels = [];
35420 };
35421
35422 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35423
35424     position: '', // set by wrapper (eg. north/south etc..)
35425     unrendered_panels : null,  // unrendered panels.
35426     createBody : function(){
35427         /** This region's body element 
35428         * @type Roo.Element */
35429         this.bodyEl = this.el.createChild({
35430                 tag: "div",
35431                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35432         });
35433     },
35434
35435     onRender: function(ctr, pos)
35436     {
35437         var dh = Roo.DomHelper;
35438         /** This region's container element 
35439         * @type Roo.Element */
35440         this.el = dh.append(ctr.dom, {
35441                 tag: "div",
35442                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35443             }, true);
35444         /** This region's title element 
35445         * @type Roo.Element */
35446     
35447         this.titleEl = dh.append(this.el.dom,
35448             {
35449                     tag: "div",
35450                     unselectable: "on",
35451                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35452                     children:[
35453                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35454                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35455                     ]}, true);
35456         
35457         this.titleEl.enableDisplayMode();
35458         /** This region's title text element 
35459         * @type HTMLElement */
35460         this.titleTextEl = this.titleEl.dom.firstChild;
35461         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35462         /*
35463         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35464         this.closeBtn.enableDisplayMode();
35465         this.closeBtn.on("click", this.closeClicked, this);
35466         this.closeBtn.hide();
35467     */
35468         this.createBody(this.config);
35469         if(this.config.hideWhenEmpty){
35470             this.hide();
35471             this.on("paneladded", this.validateVisibility, this);
35472             this.on("panelremoved", this.validateVisibility, this);
35473         }
35474         if(this.autoScroll){
35475             this.bodyEl.setStyle("overflow", "auto");
35476         }else{
35477             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35478         }
35479         //if(c.titlebar !== false){
35480             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35481                 this.titleEl.hide();
35482             }else{
35483                 this.titleEl.show();
35484                 if(this.config.title){
35485                     this.titleTextEl.innerHTML = this.config.title;
35486                 }
35487             }
35488         //}
35489         if(this.config.collapsed){
35490             this.collapse(true);
35491         }
35492         if(this.config.hidden){
35493             this.hide();
35494         }
35495         
35496         if (this.unrendered_panels && this.unrendered_panels.length) {
35497             for (var i =0;i< this.unrendered_panels.length; i++) {
35498                 this.add(this.unrendered_panels[i]);
35499             }
35500             this.unrendered_panels = null;
35501             
35502         }
35503         
35504     },
35505     
35506     applyConfig : function(c)
35507     {
35508         /*
35509          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35510             var dh = Roo.DomHelper;
35511             if(c.titlebar !== false){
35512                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35513                 this.collapseBtn.on("click", this.collapse, this);
35514                 this.collapseBtn.enableDisplayMode();
35515                 /*
35516                 if(c.showPin === true || this.showPin){
35517                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35518                     this.stickBtn.enableDisplayMode();
35519                     this.stickBtn.on("click", this.expand, this);
35520                     this.stickBtn.hide();
35521                 }
35522                 
35523             }
35524             */
35525             /** This region's collapsed element
35526             * @type Roo.Element */
35527             /*
35528              *
35529             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35530                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35531             ]}, true);
35532             
35533             if(c.floatable !== false){
35534                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35535                this.collapsedEl.on("click", this.collapseClick, this);
35536             }
35537
35538             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35539                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35540                    id: "message", unselectable: "on", style:{"float":"left"}});
35541                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35542              }
35543             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35544             this.expandBtn.on("click", this.expand, this);
35545             
35546         }
35547         
35548         if(this.collapseBtn){
35549             this.collapseBtn.setVisible(c.collapsible == true);
35550         }
35551         
35552         this.cmargins = c.cmargins || this.cmargins ||
35553                          (this.position == "west" || this.position == "east" ?
35554                              {top: 0, left: 2, right:2, bottom: 0} :
35555                              {top: 2, left: 0, right:0, bottom: 2});
35556         */
35557         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35558         
35559         
35560         this.bottomTabs = c.tabPosition != "top";
35561         
35562         this.autoScroll = c.autoScroll || false;
35563         
35564         
35565        
35566         
35567         this.duration = c.duration || .30;
35568         this.slideDuration = c.slideDuration || .45;
35569         this.config = c;
35570        
35571     },
35572     /**
35573      * Returns true if this region is currently visible.
35574      * @return {Boolean}
35575      */
35576     isVisible : function(){
35577         return this.visible;
35578     },
35579
35580     /**
35581      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35582      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35583      */
35584     //setCollapsedTitle : function(title){
35585     //    title = title || "&#160;";
35586      //   if(this.collapsedTitleTextEl){
35587       //      this.collapsedTitleTextEl.innerHTML = title;
35588        // }
35589     //},
35590
35591     getBox : function(){
35592         var b;
35593       //  if(!this.collapsed){
35594             b = this.el.getBox(false, true);
35595        // }else{
35596           //  b = this.collapsedEl.getBox(false, true);
35597         //}
35598         return b;
35599     },
35600
35601     getMargins : function(){
35602         return this.margins;
35603         //return this.collapsed ? this.cmargins : this.margins;
35604     },
35605 /*
35606     highlight : function(){
35607         this.el.addClass("x-layout-panel-dragover");
35608     },
35609
35610     unhighlight : function(){
35611         this.el.removeClass("x-layout-panel-dragover");
35612     },
35613 */
35614     updateBox : function(box)
35615     {
35616         if (!this.bodyEl) {
35617             return; // not rendered yet..
35618         }
35619         
35620         this.box = box;
35621         if(!this.collapsed){
35622             this.el.dom.style.left = box.x + "px";
35623             this.el.dom.style.top = box.y + "px";
35624             this.updateBody(box.width, box.height);
35625         }else{
35626             this.collapsedEl.dom.style.left = box.x + "px";
35627             this.collapsedEl.dom.style.top = box.y + "px";
35628             this.collapsedEl.setSize(box.width, box.height);
35629         }
35630         if(this.tabs){
35631             this.tabs.autoSizeTabs();
35632         }
35633     },
35634
35635     updateBody : function(w, h)
35636     {
35637         if(w !== null){
35638             this.el.setWidth(w);
35639             w -= this.el.getBorderWidth("rl");
35640             if(this.config.adjustments){
35641                 w += this.config.adjustments[0];
35642             }
35643         }
35644         if(h !== null && h > 0){
35645             this.el.setHeight(h);
35646             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35647             h -= this.el.getBorderWidth("tb");
35648             if(this.config.adjustments){
35649                 h += this.config.adjustments[1];
35650             }
35651             this.bodyEl.setHeight(h);
35652             if(this.tabs){
35653                 h = this.tabs.syncHeight(h);
35654             }
35655         }
35656         if(this.panelSize){
35657             w = w !== null ? w : this.panelSize.width;
35658             h = h !== null ? h : this.panelSize.height;
35659         }
35660         if(this.activePanel){
35661             var el = this.activePanel.getEl();
35662             w = w !== null ? w : el.getWidth();
35663             h = h !== null ? h : el.getHeight();
35664             this.panelSize = {width: w, height: h};
35665             this.activePanel.setSize(w, h);
35666         }
35667         if(Roo.isIE && this.tabs){
35668             this.tabs.el.repaint();
35669         }
35670     },
35671
35672     /**
35673      * Returns the container element for this region.
35674      * @return {Roo.Element}
35675      */
35676     getEl : function(){
35677         return this.el;
35678     },
35679
35680     /**
35681      * Hides this region.
35682      */
35683     hide : function(){
35684         //if(!this.collapsed){
35685             this.el.dom.style.left = "-2000px";
35686             this.el.hide();
35687         //}else{
35688          //   this.collapsedEl.dom.style.left = "-2000px";
35689          //   this.collapsedEl.hide();
35690        // }
35691         this.visible = false;
35692         this.fireEvent("visibilitychange", this, false);
35693     },
35694
35695     /**
35696      * Shows this region if it was previously hidden.
35697      */
35698     show : function(){
35699         //if(!this.collapsed){
35700             this.el.show();
35701         //}else{
35702         //    this.collapsedEl.show();
35703        // }
35704         this.visible = true;
35705         this.fireEvent("visibilitychange", this, true);
35706     },
35707 /*
35708     closeClicked : function(){
35709         if(this.activePanel){
35710             this.remove(this.activePanel);
35711         }
35712     },
35713
35714     collapseClick : function(e){
35715         if(this.isSlid){
35716            e.stopPropagation();
35717            this.slideIn();
35718         }else{
35719            e.stopPropagation();
35720            this.slideOut();
35721         }
35722     },
35723 */
35724     /**
35725      * Collapses this region.
35726      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35727      */
35728     /*
35729     collapse : function(skipAnim, skipCheck = false){
35730         if(this.collapsed) {
35731             return;
35732         }
35733         
35734         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35735             
35736             this.collapsed = true;
35737             if(this.split){
35738                 this.split.el.hide();
35739             }
35740             if(this.config.animate && skipAnim !== true){
35741                 this.fireEvent("invalidated", this);
35742                 this.animateCollapse();
35743             }else{
35744                 this.el.setLocation(-20000,-20000);
35745                 this.el.hide();
35746                 this.collapsedEl.show();
35747                 this.fireEvent("collapsed", this);
35748                 this.fireEvent("invalidated", this);
35749             }
35750         }
35751         
35752     },
35753 */
35754     animateCollapse : function(){
35755         // overridden
35756     },
35757
35758     /**
35759      * Expands this region if it was previously collapsed.
35760      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35761      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35762      */
35763     /*
35764     expand : function(e, skipAnim){
35765         if(e) {
35766             e.stopPropagation();
35767         }
35768         if(!this.collapsed || this.el.hasActiveFx()) {
35769             return;
35770         }
35771         if(this.isSlid){
35772             this.afterSlideIn();
35773             skipAnim = true;
35774         }
35775         this.collapsed = false;
35776         if(this.config.animate && skipAnim !== true){
35777             this.animateExpand();
35778         }else{
35779             this.el.show();
35780             if(this.split){
35781                 this.split.el.show();
35782             }
35783             this.collapsedEl.setLocation(-2000,-2000);
35784             this.collapsedEl.hide();
35785             this.fireEvent("invalidated", this);
35786             this.fireEvent("expanded", this);
35787         }
35788     },
35789 */
35790     animateExpand : function(){
35791         // overridden
35792     },
35793
35794     initTabs : function()
35795     {
35796         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35797         
35798         var ts = new Roo.bootstrap.panel.Tabs({
35799                 el: this.bodyEl.dom,
35800                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35801                 disableTooltips: this.config.disableTabTips,
35802                 toolbar : this.config.toolbar
35803             });
35804         
35805         if(this.config.hideTabs){
35806             ts.stripWrap.setDisplayed(false);
35807         }
35808         this.tabs = ts;
35809         ts.resizeTabs = this.config.resizeTabs === true;
35810         ts.minTabWidth = this.config.minTabWidth || 40;
35811         ts.maxTabWidth = this.config.maxTabWidth || 250;
35812         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35813         ts.monitorResize = false;
35814         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35815         ts.bodyEl.addClass('roo-layout-tabs-body');
35816         this.panels.each(this.initPanelAsTab, this);
35817     },
35818
35819     initPanelAsTab : function(panel){
35820         var ti = this.tabs.addTab(
35821             panel.getEl().id,
35822             panel.getTitle(),
35823             null,
35824             this.config.closeOnTab && panel.isClosable(),
35825             panel.tpl
35826         );
35827         if(panel.tabTip !== undefined){
35828             ti.setTooltip(panel.tabTip);
35829         }
35830         ti.on("activate", function(){
35831               this.setActivePanel(panel);
35832         }, this);
35833         
35834         if(this.config.closeOnTab){
35835             ti.on("beforeclose", function(t, e){
35836                 e.cancel = true;
35837                 this.remove(panel);
35838             }, this);
35839         }
35840         
35841         panel.tabItem = ti;
35842         
35843         return ti;
35844     },
35845
35846     updatePanelTitle : function(panel, title)
35847     {
35848         if(this.activePanel == panel){
35849             this.updateTitle(title);
35850         }
35851         if(this.tabs){
35852             var ti = this.tabs.getTab(panel.getEl().id);
35853             ti.setText(title);
35854             if(panel.tabTip !== undefined){
35855                 ti.setTooltip(panel.tabTip);
35856             }
35857         }
35858     },
35859
35860     updateTitle : function(title){
35861         if(this.titleTextEl && !this.config.title){
35862             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35863         }
35864     },
35865
35866     setActivePanel : function(panel)
35867     {
35868         panel = this.getPanel(panel);
35869         if(this.activePanel && this.activePanel != panel){
35870             if(this.activePanel.setActiveState(false) === false){
35871                 return;
35872             }
35873         }
35874         this.activePanel = panel;
35875         panel.setActiveState(true);
35876         if(this.panelSize){
35877             panel.setSize(this.panelSize.width, this.panelSize.height);
35878         }
35879         if(this.closeBtn){
35880             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35881         }
35882         this.updateTitle(panel.getTitle());
35883         if(this.tabs){
35884             this.fireEvent("invalidated", this);
35885         }
35886         this.fireEvent("panelactivated", this, panel);
35887     },
35888
35889     /**
35890      * Shows the specified panel.
35891      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35892      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35893      */
35894     showPanel : function(panel)
35895     {
35896         panel = this.getPanel(panel);
35897         if(panel){
35898             if(this.tabs){
35899                 var tab = this.tabs.getTab(panel.getEl().id);
35900                 if(tab.isHidden()){
35901                     this.tabs.unhideTab(tab.id);
35902                 }
35903                 tab.activate();
35904             }else{
35905                 this.setActivePanel(panel);
35906             }
35907         }
35908         return panel;
35909     },
35910
35911     /**
35912      * Get the active panel for this region.
35913      * @return {Roo.ContentPanel} The active panel or null
35914      */
35915     getActivePanel : function(){
35916         return this.activePanel;
35917     },
35918
35919     validateVisibility : function(){
35920         if(this.panels.getCount() < 1){
35921             this.updateTitle("&#160;");
35922             this.closeBtn.hide();
35923             this.hide();
35924         }else{
35925             if(!this.isVisible()){
35926                 this.show();
35927             }
35928         }
35929     },
35930
35931     /**
35932      * Adds the passed ContentPanel(s) to this region.
35933      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35934      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35935      */
35936     add : function(panel)
35937     {
35938         if(arguments.length > 1){
35939             for(var i = 0, len = arguments.length; i < len; i++) {
35940                 this.add(arguments[i]);
35941             }
35942             return null;
35943         }
35944         
35945         // if we have not been rendered yet, then we can not really do much of this..
35946         if (!this.bodyEl) {
35947             this.unrendered_panels.push(panel);
35948             return panel;
35949         }
35950         
35951         
35952         
35953         
35954         if(this.hasPanel(panel)){
35955             this.showPanel(panel);
35956             return panel;
35957         }
35958         panel.setRegion(this);
35959         this.panels.add(panel);
35960        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35961             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35962             // and hide them... ???
35963             this.bodyEl.dom.appendChild(panel.getEl().dom);
35964             if(panel.background !== true){
35965                 this.setActivePanel(panel);
35966             }
35967             this.fireEvent("paneladded", this, panel);
35968             return panel;
35969         }
35970         */
35971         if(!this.tabs){
35972             this.initTabs();
35973         }else{
35974             this.initPanelAsTab(panel);
35975         }
35976         
35977         
35978         if(panel.background !== true){
35979             this.tabs.activate(panel.getEl().id);
35980         }
35981         this.fireEvent("paneladded", this, panel);
35982         return panel;
35983     },
35984
35985     /**
35986      * Hides the tab for the specified panel.
35987      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35988      */
35989     hidePanel : function(panel){
35990         if(this.tabs && (panel = this.getPanel(panel))){
35991             this.tabs.hideTab(panel.getEl().id);
35992         }
35993     },
35994
35995     /**
35996      * Unhides the tab for a previously hidden panel.
35997      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35998      */
35999     unhidePanel : function(panel){
36000         if(this.tabs && (panel = this.getPanel(panel))){
36001             this.tabs.unhideTab(panel.getEl().id);
36002         }
36003     },
36004
36005     clearPanels : function(){
36006         while(this.panels.getCount() > 0){
36007              this.remove(this.panels.first());
36008         }
36009     },
36010
36011     /**
36012      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36013      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36014      * @param {Boolean} preservePanel Overrides the config preservePanel option
36015      * @return {Roo.ContentPanel} The panel that was removed
36016      */
36017     remove : function(panel, preservePanel)
36018     {
36019         panel = this.getPanel(panel);
36020         if(!panel){
36021             return null;
36022         }
36023         var e = {};
36024         this.fireEvent("beforeremove", this, panel, e);
36025         if(e.cancel === true){
36026             return null;
36027         }
36028         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36029         var panelId = panel.getId();
36030         this.panels.removeKey(panelId);
36031         if(preservePanel){
36032             document.body.appendChild(panel.getEl().dom);
36033         }
36034         if(this.tabs){
36035             this.tabs.removeTab(panel.getEl().id);
36036         }else if (!preservePanel){
36037             this.bodyEl.dom.removeChild(panel.getEl().dom);
36038         }
36039         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36040             var p = this.panels.first();
36041             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36042             tempEl.appendChild(p.getEl().dom);
36043             this.bodyEl.update("");
36044             this.bodyEl.dom.appendChild(p.getEl().dom);
36045             tempEl = null;
36046             this.updateTitle(p.getTitle());
36047             this.tabs = null;
36048             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36049             this.setActivePanel(p);
36050         }
36051         panel.setRegion(null);
36052         if(this.activePanel == panel){
36053             this.activePanel = null;
36054         }
36055         if(this.config.autoDestroy !== false && preservePanel !== true){
36056             try{panel.destroy();}catch(e){}
36057         }
36058         this.fireEvent("panelremoved", this, panel);
36059         return panel;
36060     },
36061
36062     /**
36063      * Returns the TabPanel component used by this region
36064      * @return {Roo.TabPanel}
36065      */
36066     getTabs : function(){
36067         return this.tabs;
36068     },
36069
36070     createTool : function(parentEl, className){
36071         var btn = Roo.DomHelper.append(parentEl, {
36072             tag: "div",
36073             cls: "x-layout-tools-button",
36074             children: [ {
36075                 tag: "div",
36076                 cls: "roo-layout-tools-button-inner " + className,
36077                 html: "&#160;"
36078             }]
36079         }, true);
36080         btn.addClassOnOver("roo-layout-tools-button-over");
36081         return btn;
36082     }
36083 });/*
36084  * Based on:
36085  * Ext JS Library 1.1.1
36086  * Copyright(c) 2006-2007, Ext JS, LLC.
36087  *
36088  * Originally Released Under LGPL - original licence link has changed is not relivant.
36089  *
36090  * Fork - LGPL
36091  * <script type="text/javascript">
36092  */
36093  
36094
36095
36096 /**
36097  * @class Roo.SplitLayoutRegion
36098  * @extends Roo.LayoutRegion
36099  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36100  */
36101 Roo.bootstrap.layout.Split = function(config){
36102     this.cursor = config.cursor;
36103     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36104 };
36105
36106 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36107 {
36108     splitTip : "Drag to resize.",
36109     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36110     useSplitTips : false,
36111
36112     applyConfig : function(config){
36113         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36114     },
36115     
36116     onRender : function(ctr,pos) {
36117         
36118         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36119         if(!this.config.split){
36120             return;
36121         }
36122         if(!this.split){
36123             
36124             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36125                             tag: "div",
36126                             id: this.el.id + "-split",
36127                             cls: "roo-layout-split roo-layout-split-"+this.position,
36128                             html: "&#160;"
36129             });
36130             /** The SplitBar for this region 
36131             * @type Roo.SplitBar */
36132             // does not exist yet...
36133             Roo.log([this.position, this.orientation]);
36134             
36135             this.split = new Roo.bootstrap.SplitBar({
36136                 dragElement : splitEl,
36137                 resizingElement: this.el,
36138                 orientation : this.orientation
36139             });
36140             
36141             this.split.on("moved", this.onSplitMove, this);
36142             this.split.useShim = this.config.useShim === true;
36143             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36144             if(this.useSplitTips){
36145                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36146             }
36147             //if(config.collapsible){
36148             //    this.split.el.on("dblclick", this.collapse,  this);
36149             //}
36150         }
36151         if(typeof this.config.minSize != "undefined"){
36152             this.split.minSize = this.config.minSize;
36153         }
36154         if(typeof this.config.maxSize != "undefined"){
36155             this.split.maxSize = this.config.maxSize;
36156         }
36157         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36158             this.hideSplitter();
36159         }
36160         
36161     },
36162
36163     getHMaxSize : function(){
36164          var cmax = this.config.maxSize || 10000;
36165          var center = this.mgr.getRegion("center");
36166          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36167     },
36168
36169     getVMaxSize : function(){
36170          var cmax = this.config.maxSize || 10000;
36171          var center = this.mgr.getRegion("center");
36172          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36173     },
36174
36175     onSplitMove : function(split, newSize){
36176         this.fireEvent("resized", this, newSize);
36177     },
36178     
36179     /** 
36180      * Returns the {@link Roo.SplitBar} for this region.
36181      * @return {Roo.SplitBar}
36182      */
36183     getSplitBar : function(){
36184         return this.split;
36185     },
36186     
36187     hide : function(){
36188         this.hideSplitter();
36189         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36190     },
36191
36192     hideSplitter : function(){
36193         if(this.split){
36194             this.split.el.setLocation(-2000,-2000);
36195             this.split.el.hide();
36196         }
36197     },
36198
36199     show : function(){
36200         if(this.split){
36201             this.split.el.show();
36202         }
36203         Roo.bootstrap.layout.Split.superclass.show.call(this);
36204     },
36205     
36206     beforeSlide: function(){
36207         if(Roo.isGecko){// firefox overflow auto bug workaround
36208             this.bodyEl.clip();
36209             if(this.tabs) {
36210                 this.tabs.bodyEl.clip();
36211             }
36212             if(this.activePanel){
36213                 this.activePanel.getEl().clip();
36214                 
36215                 if(this.activePanel.beforeSlide){
36216                     this.activePanel.beforeSlide();
36217                 }
36218             }
36219         }
36220     },
36221     
36222     afterSlide : function(){
36223         if(Roo.isGecko){// firefox overflow auto bug workaround
36224             this.bodyEl.unclip();
36225             if(this.tabs) {
36226                 this.tabs.bodyEl.unclip();
36227             }
36228             if(this.activePanel){
36229                 this.activePanel.getEl().unclip();
36230                 if(this.activePanel.afterSlide){
36231                     this.activePanel.afterSlide();
36232                 }
36233             }
36234         }
36235     },
36236
36237     initAutoHide : function(){
36238         if(this.autoHide !== false){
36239             if(!this.autoHideHd){
36240                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36241                 this.autoHideHd = {
36242                     "mouseout": function(e){
36243                         if(!e.within(this.el, true)){
36244                             st.delay(500);
36245                         }
36246                     },
36247                     "mouseover" : function(e){
36248                         st.cancel();
36249                     },
36250                     scope : this
36251                 };
36252             }
36253             this.el.on(this.autoHideHd);
36254         }
36255     },
36256
36257     clearAutoHide : function(){
36258         if(this.autoHide !== false){
36259             this.el.un("mouseout", this.autoHideHd.mouseout);
36260             this.el.un("mouseover", this.autoHideHd.mouseover);
36261         }
36262     },
36263
36264     clearMonitor : function(){
36265         Roo.get(document).un("click", this.slideInIf, this);
36266     },
36267
36268     // these names are backwards but not changed for compat
36269     slideOut : function(){
36270         if(this.isSlid || this.el.hasActiveFx()){
36271             return;
36272         }
36273         this.isSlid = true;
36274         if(this.collapseBtn){
36275             this.collapseBtn.hide();
36276         }
36277         this.closeBtnState = this.closeBtn.getStyle('display');
36278         this.closeBtn.hide();
36279         if(this.stickBtn){
36280             this.stickBtn.show();
36281         }
36282         this.el.show();
36283         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36284         this.beforeSlide();
36285         this.el.setStyle("z-index", 10001);
36286         this.el.slideIn(this.getSlideAnchor(), {
36287             callback: function(){
36288                 this.afterSlide();
36289                 this.initAutoHide();
36290                 Roo.get(document).on("click", this.slideInIf, this);
36291                 this.fireEvent("slideshow", this);
36292             },
36293             scope: this,
36294             block: true
36295         });
36296     },
36297
36298     afterSlideIn : function(){
36299         this.clearAutoHide();
36300         this.isSlid = false;
36301         this.clearMonitor();
36302         this.el.setStyle("z-index", "");
36303         if(this.collapseBtn){
36304             this.collapseBtn.show();
36305         }
36306         this.closeBtn.setStyle('display', this.closeBtnState);
36307         if(this.stickBtn){
36308             this.stickBtn.hide();
36309         }
36310         this.fireEvent("slidehide", this);
36311     },
36312
36313     slideIn : function(cb){
36314         if(!this.isSlid || this.el.hasActiveFx()){
36315             Roo.callback(cb);
36316             return;
36317         }
36318         this.isSlid = false;
36319         this.beforeSlide();
36320         this.el.slideOut(this.getSlideAnchor(), {
36321             callback: function(){
36322                 this.el.setLeftTop(-10000, -10000);
36323                 this.afterSlide();
36324                 this.afterSlideIn();
36325                 Roo.callback(cb);
36326             },
36327             scope: this,
36328             block: true
36329         });
36330     },
36331     
36332     slideInIf : function(e){
36333         if(!e.within(this.el)){
36334             this.slideIn();
36335         }
36336     },
36337
36338     animateCollapse : function(){
36339         this.beforeSlide();
36340         this.el.setStyle("z-index", 20000);
36341         var anchor = this.getSlideAnchor();
36342         this.el.slideOut(anchor, {
36343             callback : function(){
36344                 this.el.setStyle("z-index", "");
36345                 this.collapsedEl.slideIn(anchor, {duration:.3});
36346                 this.afterSlide();
36347                 this.el.setLocation(-10000,-10000);
36348                 this.el.hide();
36349                 this.fireEvent("collapsed", this);
36350             },
36351             scope: this,
36352             block: true
36353         });
36354     },
36355
36356     animateExpand : function(){
36357         this.beforeSlide();
36358         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36359         this.el.setStyle("z-index", 20000);
36360         this.collapsedEl.hide({
36361             duration:.1
36362         });
36363         this.el.slideIn(this.getSlideAnchor(), {
36364             callback : function(){
36365                 this.el.setStyle("z-index", "");
36366                 this.afterSlide();
36367                 if(this.split){
36368                     this.split.el.show();
36369                 }
36370                 this.fireEvent("invalidated", this);
36371                 this.fireEvent("expanded", this);
36372             },
36373             scope: this,
36374             block: true
36375         });
36376     },
36377
36378     anchors : {
36379         "west" : "left",
36380         "east" : "right",
36381         "north" : "top",
36382         "south" : "bottom"
36383     },
36384
36385     sanchors : {
36386         "west" : "l",
36387         "east" : "r",
36388         "north" : "t",
36389         "south" : "b"
36390     },
36391
36392     canchors : {
36393         "west" : "tl-tr",
36394         "east" : "tr-tl",
36395         "north" : "tl-bl",
36396         "south" : "bl-tl"
36397     },
36398
36399     getAnchor : function(){
36400         return this.anchors[this.position];
36401     },
36402
36403     getCollapseAnchor : function(){
36404         return this.canchors[this.position];
36405     },
36406
36407     getSlideAnchor : function(){
36408         return this.sanchors[this.position];
36409     },
36410
36411     getAlignAdj : function(){
36412         var cm = this.cmargins;
36413         switch(this.position){
36414             case "west":
36415                 return [0, 0];
36416             break;
36417             case "east":
36418                 return [0, 0];
36419             break;
36420             case "north":
36421                 return [0, 0];
36422             break;
36423             case "south":
36424                 return [0, 0];
36425             break;
36426         }
36427     },
36428
36429     getExpandAdj : function(){
36430         var c = this.collapsedEl, cm = this.cmargins;
36431         switch(this.position){
36432             case "west":
36433                 return [-(cm.right+c.getWidth()+cm.left), 0];
36434             break;
36435             case "east":
36436                 return [cm.right+c.getWidth()+cm.left, 0];
36437             break;
36438             case "north":
36439                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36440             break;
36441             case "south":
36442                 return [0, cm.top+cm.bottom+c.getHeight()];
36443             break;
36444         }
36445     }
36446 });/*
36447  * Based on:
36448  * Ext JS Library 1.1.1
36449  * Copyright(c) 2006-2007, Ext JS, LLC.
36450  *
36451  * Originally Released Under LGPL - original licence link has changed is not relivant.
36452  *
36453  * Fork - LGPL
36454  * <script type="text/javascript">
36455  */
36456 /*
36457  * These classes are private internal classes
36458  */
36459 Roo.bootstrap.layout.Center = function(config){
36460     config.region = "center";
36461     Roo.bootstrap.layout.Region.call(this, config);
36462     this.visible = true;
36463     this.minWidth = config.minWidth || 20;
36464     this.minHeight = config.minHeight || 20;
36465 };
36466
36467 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36468     hide : function(){
36469         // center panel can't be hidden
36470     },
36471     
36472     show : function(){
36473         // center panel can't be hidden
36474     },
36475     
36476     getMinWidth: function(){
36477         return this.minWidth;
36478     },
36479     
36480     getMinHeight: function(){
36481         return this.minHeight;
36482     }
36483 });
36484
36485
36486
36487
36488  
36489
36490
36491
36492
36493
36494 Roo.bootstrap.layout.North = function(config)
36495 {
36496     config.region = 'north';
36497     config.cursor = 'n-resize';
36498     
36499     Roo.bootstrap.layout.Split.call(this, config);
36500     
36501     
36502     if(this.split){
36503         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36504         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36505         this.split.el.addClass("roo-layout-split-v");
36506     }
36507     var size = config.initialSize || config.height;
36508     if(typeof size != "undefined"){
36509         this.el.setHeight(size);
36510     }
36511 };
36512 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36513 {
36514     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36515     
36516     
36517     
36518     getBox : function(){
36519         if(this.collapsed){
36520             return this.collapsedEl.getBox();
36521         }
36522         var box = this.el.getBox();
36523         if(this.split){
36524             box.height += this.split.el.getHeight();
36525         }
36526         return box;
36527     },
36528     
36529     updateBox : function(box){
36530         if(this.split && !this.collapsed){
36531             box.height -= this.split.el.getHeight();
36532             this.split.el.setLeft(box.x);
36533             this.split.el.setTop(box.y+box.height);
36534             this.split.el.setWidth(box.width);
36535         }
36536         if(this.collapsed){
36537             this.updateBody(box.width, null);
36538         }
36539         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36540     }
36541 });
36542
36543
36544
36545
36546
36547 Roo.bootstrap.layout.South = function(config){
36548     config.region = 'south';
36549     config.cursor = 's-resize';
36550     Roo.bootstrap.layout.Split.call(this, config);
36551     if(this.split){
36552         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36553         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36554         this.split.el.addClass("roo-layout-split-v");
36555     }
36556     var size = config.initialSize || config.height;
36557     if(typeof size != "undefined"){
36558         this.el.setHeight(size);
36559     }
36560 };
36561
36562 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36563     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36564     getBox : function(){
36565         if(this.collapsed){
36566             return this.collapsedEl.getBox();
36567         }
36568         var box = this.el.getBox();
36569         if(this.split){
36570             var sh = this.split.el.getHeight();
36571             box.height += sh;
36572             box.y -= sh;
36573         }
36574         return box;
36575     },
36576     
36577     updateBox : function(box){
36578         if(this.split && !this.collapsed){
36579             var sh = this.split.el.getHeight();
36580             box.height -= sh;
36581             box.y += sh;
36582             this.split.el.setLeft(box.x);
36583             this.split.el.setTop(box.y-sh);
36584             this.split.el.setWidth(box.width);
36585         }
36586         if(this.collapsed){
36587             this.updateBody(box.width, null);
36588         }
36589         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36590     }
36591 });
36592
36593 Roo.bootstrap.layout.East = function(config){
36594     config.region = "east";
36595     config.cursor = "e-resize";
36596     Roo.bootstrap.layout.Split.call(this, config);
36597     if(this.split){
36598         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36599         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36600         this.split.el.addClass("roo-layout-split-h");
36601     }
36602     var size = config.initialSize || config.width;
36603     if(typeof size != "undefined"){
36604         this.el.setWidth(size);
36605     }
36606 };
36607 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36608     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36609     getBox : function(){
36610         if(this.collapsed){
36611             return this.collapsedEl.getBox();
36612         }
36613         var box = this.el.getBox();
36614         if(this.split){
36615             var sw = this.split.el.getWidth();
36616             box.width += sw;
36617             box.x -= sw;
36618         }
36619         return box;
36620     },
36621
36622     updateBox : function(box){
36623         if(this.split && !this.collapsed){
36624             var sw = this.split.el.getWidth();
36625             box.width -= sw;
36626             this.split.el.setLeft(box.x);
36627             this.split.el.setTop(box.y);
36628             this.split.el.setHeight(box.height);
36629             box.x += sw;
36630         }
36631         if(this.collapsed){
36632             this.updateBody(null, box.height);
36633         }
36634         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36635     }
36636 });
36637
36638 Roo.bootstrap.layout.West = function(config){
36639     config.region = "west";
36640     config.cursor = "w-resize";
36641     
36642     Roo.bootstrap.layout.Split.call(this, config);
36643     if(this.split){
36644         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36645         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36646         this.split.el.addClass("roo-layout-split-h");
36647     }
36648     
36649 };
36650 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36651     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36652     
36653     onRender: function(ctr, pos)
36654     {
36655         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36656         var size = this.config.initialSize || this.config.width;
36657         if(typeof size != "undefined"){
36658             this.el.setWidth(size);
36659         }
36660     },
36661     
36662     getBox : function(){
36663         if(this.collapsed){
36664             return this.collapsedEl.getBox();
36665         }
36666         var box = this.el.getBox();
36667         if(this.split){
36668             box.width += this.split.el.getWidth();
36669         }
36670         return box;
36671     },
36672     
36673     updateBox : function(box){
36674         if(this.split && !this.collapsed){
36675             var sw = this.split.el.getWidth();
36676             box.width -= sw;
36677             this.split.el.setLeft(box.x+box.width);
36678             this.split.el.setTop(box.y);
36679             this.split.el.setHeight(box.height);
36680         }
36681         if(this.collapsed){
36682             this.updateBody(null, box.height);
36683         }
36684         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36685     }
36686 });
36687 Roo.namespace("Roo.bootstrap.panel");/*
36688  * Based on:
36689  * Ext JS Library 1.1.1
36690  * Copyright(c) 2006-2007, Ext JS, LLC.
36691  *
36692  * Originally Released Under LGPL - original licence link has changed is not relivant.
36693  *
36694  * Fork - LGPL
36695  * <script type="text/javascript">
36696  */
36697 /**
36698  * @class Roo.ContentPanel
36699  * @extends Roo.util.Observable
36700  * A basic ContentPanel element.
36701  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36702  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36703  * @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
36704  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36705  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36706  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36707  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36708  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36709  * @cfg {String} title          The title for this panel
36710  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36711  * @cfg {String} url            Calls {@link #setUrl} with this value
36712  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36713  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36714  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36715  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36716  * @cfg {Boolean} badges render the badges
36717
36718  * @constructor
36719  * Create a new ContentPanel.
36720  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36721  * @param {String/Object} config A string to set only the title or a config object
36722  * @param {String} content (optional) Set the HTML content for this panel
36723  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36724  */
36725 Roo.bootstrap.panel.Content = function( config){
36726     
36727     this.tpl = config.tpl || false;
36728     
36729     var el = config.el;
36730     var content = config.content;
36731
36732     if(config.autoCreate){ // xtype is available if this is called from factory
36733         el = Roo.id();
36734     }
36735     this.el = Roo.get(el);
36736     if(!this.el && config && config.autoCreate){
36737         if(typeof config.autoCreate == "object"){
36738             if(!config.autoCreate.id){
36739                 config.autoCreate.id = config.id||el;
36740             }
36741             this.el = Roo.DomHelper.append(document.body,
36742                         config.autoCreate, true);
36743         }else{
36744             var elcfg =  {   tag: "div",
36745                             cls: "roo-layout-inactive-content",
36746                             id: config.id||el
36747                             };
36748             if (config.html) {
36749                 elcfg.html = config.html;
36750                 
36751             }
36752                         
36753             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36754         }
36755     } 
36756     this.closable = false;
36757     this.loaded = false;
36758     this.active = false;
36759    
36760       
36761     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36762         
36763         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36764         
36765         this.wrapEl = this.el; //this.el.wrap();
36766         var ti = [];
36767         if (config.toolbar.items) {
36768             ti = config.toolbar.items ;
36769             delete config.toolbar.items ;
36770         }
36771         
36772         var nitems = [];
36773         this.toolbar.render(this.wrapEl, 'before');
36774         for(var i =0;i < ti.length;i++) {
36775           //  Roo.log(['add child', items[i]]);
36776             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36777         }
36778         this.toolbar.items = nitems;
36779         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36780         delete config.toolbar;
36781         
36782     }
36783     /*
36784     // xtype created footer. - not sure if will work as we normally have to render first..
36785     if (this.footer && !this.footer.el && this.footer.xtype) {
36786         if (!this.wrapEl) {
36787             this.wrapEl = this.el.wrap();
36788         }
36789     
36790         this.footer.container = this.wrapEl.createChild();
36791          
36792         this.footer = Roo.factory(this.footer, Roo);
36793         
36794     }
36795     */
36796     
36797      if(typeof config == "string"){
36798         this.title = config;
36799     }else{
36800         Roo.apply(this, config);
36801     }
36802     
36803     if(this.resizeEl){
36804         this.resizeEl = Roo.get(this.resizeEl, true);
36805     }else{
36806         this.resizeEl = this.el;
36807     }
36808     // handle view.xtype
36809     
36810  
36811     
36812     
36813     this.addEvents({
36814         /**
36815          * @event activate
36816          * Fires when this panel is activated. 
36817          * @param {Roo.ContentPanel} this
36818          */
36819         "activate" : true,
36820         /**
36821          * @event deactivate
36822          * Fires when this panel is activated. 
36823          * @param {Roo.ContentPanel} this
36824          */
36825         "deactivate" : true,
36826
36827         /**
36828          * @event resize
36829          * Fires when this panel is resized if fitToFrame is true.
36830          * @param {Roo.ContentPanel} this
36831          * @param {Number} width The width after any component adjustments
36832          * @param {Number} height The height after any component adjustments
36833          */
36834         "resize" : true,
36835         
36836          /**
36837          * @event render
36838          * Fires when this tab is created
36839          * @param {Roo.ContentPanel} this
36840          */
36841         "render" : true
36842         
36843         
36844         
36845     });
36846     
36847
36848     
36849     
36850     if(this.autoScroll){
36851         this.resizeEl.setStyle("overflow", "auto");
36852     } else {
36853         // fix randome scrolling
36854         //this.el.on('scroll', function() {
36855         //    Roo.log('fix random scolling');
36856         //    this.scrollTo('top',0); 
36857         //});
36858     }
36859     content = content || this.content;
36860     if(content){
36861         this.setContent(content);
36862     }
36863     if(config && config.url){
36864         this.setUrl(this.url, this.params, this.loadOnce);
36865     }
36866     
36867     
36868     
36869     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36870     
36871     if (this.view && typeof(this.view.xtype) != 'undefined') {
36872         this.view.el = this.el.appendChild(document.createElement("div"));
36873         this.view = Roo.factory(this.view); 
36874         this.view.render  &&  this.view.render(false, '');  
36875     }
36876     
36877     
36878     this.fireEvent('render', this);
36879 };
36880
36881 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36882     
36883     tabTip : '',
36884     
36885     setRegion : function(region){
36886         this.region = region;
36887         this.setActiveClass(region && !this.background);
36888     },
36889     
36890     
36891     setActiveClass: function(state)
36892     {
36893         if(state){
36894            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36895            this.el.setStyle('position','relative');
36896         }else{
36897            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36898            this.el.setStyle('position', 'absolute');
36899         } 
36900     },
36901     
36902     /**
36903      * Returns the toolbar for this Panel if one was configured. 
36904      * @return {Roo.Toolbar} 
36905      */
36906     getToolbar : function(){
36907         return this.toolbar;
36908     },
36909     
36910     setActiveState : function(active)
36911     {
36912         this.active = active;
36913         this.setActiveClass(active);
36914         if(!active){
36915             if(this.fireEvent("deactivate", this) === false){
36916                 return false;
36917             }
36918             return true;
36919         }
36920         this.fireEvent("activate", this);
36921         return true;
36922     },
36923     /**
36924      * Updates this panel's element
36925      * @param {String} content The new content
36926      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36927     */
36928     setContent : function(content, loadScripts){
36929         this.el.update(content, loadScripts);
36930     },
36931
36932     ignoreResize : function(w, h){
36933         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36934             return true;
36935         }else{
36936             this.lastSize = {width: w, height: h};
36937             return false;
36938         }
36939     },
36940     /**
36941      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36942      * @return {Roo.UpdateManager} The UpdateManager
36943      */
36944     getUpdateManager : function(){
36945         return this.el.getUpdateManager();
36946     },
36947      /**
36948      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36949      * @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:
36950 <pre><code>
36951 panel.load({
36952     url: "your-url.php",
36953     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36954     callback: yourFunction,
36955     scope: yourObject, //(optional scope)
36956     discardUrl: false,
36957     nocache: false,
36958     text: "Loading...",
36959     timeout: 30,
36960     scripts: false
36961 });
36962 </code></pre>
36963      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36964      * 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.
36965      * @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}
36966      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36967      * @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.
36968      * @return {Roo.ContentPanel} this
36969      */
36970     load : function(){
36971         var um = this.el.getUpdateManager();
36972         um.update.apply(um, arguments);
36973         return this;
36974     },
36975
36976
36977     /**
36978      * 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.
36979      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36980      * @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)
36981      * @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)
36982      * @return {Roo.UpdateManager} The UpdateManager
36983      */
36984     setUrl : function(url, params, loadOnce){
36985         if(this.refreshDelegate){
36986             this.removeListener("activate", this.refreshDelegate);
36987         }
36988         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36989         this.on("activate", this.refreshDelegate);
36990         return this.el.getUpdateManager();
36991     },
36992     
36993     _handleRefresh : function(url, params, loadOnce){
36994         if(!loadOnce || !this.loaded){
36995             var updater = this.el.getUpdateManager();
36996             updater.update(url, params, this._setLoaded.createDelegate(this));
36997         }
36998     },
36999     
37000     _setLoaded : function(){
37001         this.loaded = true;
37002     }, 
37003     
37004     /**
37005      * Returns this panel's id
37006      * @return {String} 
37007      */
37008     getId : function(){
37009         return this.el.id;
37010     },
37011     
37012     /** 
37013      * Returns this panel's element - used by regiosn to add.
37014      * @return {Roo.Element} 
37015      */
37016     getEl : function(){
37017         return this.wrapEl || this.el;
37018     },
37019     
37020    
37021     
37022     adjustForComponents : function(width, height)
37023     {
37024         //Roo.log('adjustForComponents ');
37025         if(this.resizeEl != this.el){
37026             width -= this.el.getFrameWidth('lr');
37027             height -= this.el.getFrameWidth('tb');
37028         }
37029         if(this.toolbar){
37030             var te = this.toolbar.getEl();
37031             te.setWidth(width);
37032             height -= te.getHeight();
37033         }
37034         if(this.footer){
37035             var te = this.footer.getEl();
37036             te.setWidth(width);
37037             height -= te.getHeight();
37038         }
37039         
37040         
37041         if(this.adjustments){
37042             width += this.adjustments[0];
37043             height += this.adjustments[1];
37044         }
37045         return {"width": width, "height": height};
37046     },
37047     
37048     setSize : function(width, height){
37049         if(this.fitToFrame && !this.ignoreResize(width, height)){
37050             if(this.fitContainer && this.resizeEl != this.el){
37051                 this.el.setSize(width, height);
37052             }
37053             var size = this.adjustForComponents(width, height);
37054             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37055             this.fireEvent('resize', this, size.width, size.height);
37056         }
37057     },
37058     
37059     /**
37060      * Returns this panel's title
37061      * @return {String} 
37062      */
37063     getTitle : function(){
37064         
37065         if (typeof(this.title) != 'object') {
37066             return this.title;
37067         }
37068         
37069         var t = '';
37070         for (var k in this.title) {
37071             if (!this.title.hasOwnProperty(k)) {
37072                 continue;
37073             }
37074             
37075             if (k.indexOf('-') >= 0) {
37076                 var s = k.split('-');
37077                 for (var i = 0; i<s.length; i++) {
37078                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37079                 }
37080             } else {
37081                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37082             }
37083         }
37084         return t;
37085     },
37086     
37087     /**
37088      * Set this panel's title
37089      * @param {String} title
37090      */
37091     setTitle : function(title){
37092         this.title = title;
37093         if(this.region){
37094             this.region.updatePanelTitle(this, title);
37095         }
37096     },
37097     
37098     /**
37099      * Returns true is this panel was configured to be closable
37100      * @return {Boolean} 
37101      */
37102     isClosable : function(){
37103         return this.closable;
37104     },
37105     
37106     beforeSlide : function(){
37107         this.el.clip();
37108         this.resizeEl.clip();
37109     },
37110     
37111     afterSlide : function(){
37112         this.el.unclip();
37113         this.resizeEl.unclip();
37114     },
37115     
37116     /**
37117      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37118      *   Will fail silently if the {@link #setUrl} method has not been called.
37119      *   This does not activate the panel, just updates its content.
37120      */
37121     refresh : function(){
37122         if(this.refreshDelegate){
37123            this.loaded = false;
37124            this.refreshDelegate();
37125         }
37126     },
37127     
37128     /**
37129      * Destroys this panel
37130      */
37131     destroy : function(){
37132         this.el.removeAllListeners();
37133         var tempEl = document.createElement("span");
37134         tempEl.appendChild(this.el.dom);
37135         tempEl.innerHTML = "";
37136         this.el.remove();
37137         this.el = null;
37138     },
37139     
37140     /**
37141      * form - if the content panel contains a form - this is a reference to it.
37142      * @type {Roo.form.Form}
37143      */
37144     form : false,
37145     /**
37146      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37147      *    This contains a reference to it.
37148      * @type {Roo.View}
37149      */
37150     view : false,
37151     
37152       /**
37153      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37154      * <pre><code>
37155
37156 layout.addxtype({
37157        xtype : 'Form',
37158        items: [ .... ]
37159    }
37160 );
37161
37162 </code></pre>
37163      * @param {Object} cfg Xtype definition of item to add.
37164      */
37165     
37166     
37167     getChildContainer: function () {
37168         return this.getEl();
37169     }
37170     
37171     
37172     /*
37173         var  ret = new Roo.factory(cfg);
37174         return ret;
37175         
37176         
37177         // add form..
37178         if (cfg.xtype.match(/^Form$/)) {
37179             
37180             var el;
37181             //if (this.footer) {
37182             //    el = this.footer.container.insertSibling(false, 'before');
37183             //} else {
37184                 el = this.el.createChild();
37185             //}
37186
37187             this.form = new  Roo.form.Form(cfg);
37188             
37189             
37190             if ( this.form.allItems.length) {
37191                 this.form.render(el.dom);
37192             }
37193             return this.form;
37194         }
37195         // should only have one of theses..
37196         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37197             // views.. should not be just added - used named prop 'view''
37198             
37199             cfg.el = this.el.appendChild(document.createElement("div"));
37200             // factory?
37201             
37202             var ret = new Roo.factory(cfg);
37203              
37204              ret.render && ret.render(false, ''); // render blank..
37205             this.view = ret;
37206             return ret;
37207         }
37208         return false;
37209     }
37210     \*/
37211 });
37212  
37213 /**
37214  * @class Roo.bootstrap.panel.Grid
37215  * @extends Roo.bootstrap.panel.Content
37216  * @constructor
37217  * Create a new GridPanel.
37218  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37219  * @param {Object} config A the config object
37220   
37221  */
37222
37223
37224
37225 Roo.bootstrap.panel.Grid = function(config)
37226 {
37227     
37228       
37229     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37230         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37231
37232     config.el = this.wrapper;
37233     //this.el = this.wrapper;
37234     
37235       if (config.container) {
37236         // ctor'ed from a Border/panel.grid
37237         
37238         
37239         this.wrapper.setStyle("overflow", "hidden");
37240         this.wrapper.addClass('roo-grid-container');
37241
37242     }
37243     
37244     
37245     if(config.toolbar){
37246         var tool_el = this.wrapper.createChild();    
37247         this.toolbar = Roo.factory(config.toolbar);
37248         var ti = [];
37249         if (config.toolbar.items) {
37250             ti = config.toolbar.items ;
37251             delete config.toolbar.items ;
37252         }
37253         
37254         var nitems = [];
37255         this.toolbar.render(tool_el);
37256         for(var i =0;i < ti.length;i++) {
37257           //  Roo.log(['add child', items[i]]);
37258             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37259         }
37260         this.toolbar.items = nitems;
37261         
37262         delete config.toolbar;
37263     }
37264     
37265     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37266     config.grid.scrollBody = true;;
37267     config.grid.monitorWindowResize = false; // turn off autosizing
37268     config.grid.autoHeight = false;
37269     config.grid.autoWidth = false;
37270     
37271     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37272     
37273     if (config.background) {
37274         // render grid on panel activation (if panel background)
37275         this.on('activate', function(gp) {
37276             if (!gp.grid.rendered) {
37277                 gp.grid.render(this.wrapper);
37278                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37279             }
37280         });
37281             
37282     } else {
37283         this.grid.render(this.wrapper);
37284         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37285
37286     }
37287     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37288     // ??? needed ??? config.el = this.wrapper;
37289     
37290     
37291     
37292   
37293     // xtype created footer. - not sure if will work as we normally have to render first..
37294     if (this.footer && !this.footer.el && this.footer.xtype) {
37295         
37296         var ctr = this.grid.getView().getFooterPanel(true);
37297         this.footer.dataSource = this.grid.dataSource;
37298         this.footer = Roo.factory(this.footer, Roo);
37299         this.footer.render(ctr);
37300         
37301     }
37302     
37303     
37304     
37305     
37306      
37307 };
37308
37309 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37310     getId : function(){
37311         return this.grid.id;
37312     },
37313     
37314     /**
37315      * Returns the grid for this panel
37316      * @return {Roo.bootstrap.Table} 
37317      */
37318     getGrid : function(){
37319         return this.grid;    
37320     },
37321     
37322     setSize : function(width, height){
37323         if(!this.ignoreResize(width, height)){
37324             var grid = this.grid;
37325             var size = this.adjustForComponents(width, height);
37326             var gridel = grid.getGridEl();
37327             gridel.setSize(size.width, size.height);
37328             /*
37329             var thd = grid.getGridEl().select('thead',true).first();
37330             var tbd = grid.getGridEl().select('tbody', true).first();
37331             if (tbd) {
37332                 tbd.setSize(width, height - thd.getHeight());
37333             }
37334             */
37335             grid.autoSize();
37336         }
37337     },
37338      
37339     
37340     
37341     beforeSlide : function(){
37342         this.grid.getView().scroller.clip();
37343     },
37344     
37345     afterSlide : function(){
37346         this.grid.getView().scroller.unclip();
37347     },
37348     
37349     destroy : function(){
37350         this.grid.destroy();
37351         delete this.grid;
37352         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37353     }
37354 });
37355
37356 /**
37357  * @class Roo.bootstrap.panel.Nest
37358  * @extends Roo.bootstrap.panel.Content
37359  * @constructor
37360  * Create a new Panel, that can contain a layout.Border.
37361  * 
37362  * 
37363  * @param {Roo.BorderLayout} layout The layout for this panel
37364  * @param {String/Object} config A string to set only the title or a config object
37365  */
37366 Roo.bootstrap.panel.Nest = function(config)
37367 {
37368     // construct with only one argument..
37369     /* FIXME - implement nicer consturctors
37370     if (layout.layout) {
37371         config = layout;
37372         layout = config.layout;
37373         delete config.layout;
37374     }
37375     if (layout.xtype && !layout.getEl) {
37376         // then layout needs constructing..
37377         layout = Roo.factory(layout, Roo);
37378     }
37379     */
37380     
37381     config.el =  config.layout.getEl();
37382     
37383     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37384     
37385     config.layout.monitorWindowResize = false; // turn off autosizing
37386     this.layout = config.layout;
37387     this.layout.getEl().addClass("roo-layout-nested-layout");
37388     
37389     
37390     
37391     
37392 };
37393
37394 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37395
37396     setSize : function(width, height){
37397         if(!this.ignoreResize(width, height)){
37398             var size = this.adjustForComponents(width, height);
37399             var el = this.layout.getEl();
37400             if (size.height < 1) {
37401                 el.setWidth(size.width);   
37402             } else {
37403                 el.setSize(size.width, size.height);
37404             }
37405             var touch = el.dom.offsetWidth;
37406             this.layout.layout();
37407             // ie requires a double layout on the first pass
37408             if(Roo.isIE && !this.initialized){
37409                 this.initialized = true;
37410                 this.layout.layout();
37411             }
37412         }
37413     },
37414     
37415     // activate all subpanels if not currently active..
37416     
37417     setActiveState : function(active){
37418         this.active = active;
37419         this.setActiveClass(active);
37420         
37421         if(!active){
37422             this.fireEvent("deactivate", this);
37423             return;
37424         }
37425         
37426         this.fireEvent("activate", this);
37427         // not sure if this should happen before or after..
37428         if (!this.layout) {
37429             return; // should not happen..
37430         }
37431         var reg = false;
37432         for (var r in this.layout.regions) {
37433             reg = this.layout.getRegion(r);
37434             if (reg.getActivePanel()) {
37435                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37436                 reg.setActivePanel(reg.getActivePanel());
37437                 continue;
37438             }
37439             if (!reg.panels.length) {
37440                 continue;
37441             }
37442             reg.showPanel(reg.getPanel(0));
37443         }
37444         
37445         
37446         
37447         
37448     },
37449     
37450     /**
37451      * Returns the nested BorderLayout for this panel
37452      * @return {Roo.BorderLayout} 
37453      */
37454     getLayout : function(){
37455         return this.layout;
37456     },
37457     
37458      /**
37459      * Adds a xtype elements to the layout of the nested panel
37460      * <pre><code>
37461
37462 panel.addxtype({
37463        xtype : 'ContentPanel',
37464        region: 'west',
37465        items: [ .... ]
37466    }
37467 );
37468
37469 panel.addxtype({
37470         xtype : 'NestedLayoutPanel',
37471         region: 'west',
37472         layout: {
37473            center: { },
37474            west: { }   
37475         },
37476         items : [ ... list of content panels or nested layout panels.. ]
37477    }
37478 );
37479 </code></pre>
37480      * @param {Object} cfg Xtype definition of item to add.
37481      */
37482     addxtype : function(cfg) {
37483         return this.layout.addxtype(cfg);
37484     
37485     }
37486 });        /*
37487  * Based on:
37488  * Ext JS Library 1.1.1
37489  * Copyright(c) 2006-2007, Ext JS, LLC.
37490  *
37491  * Originally Released Under LGPL - original licence link has changed is not relivant.
37492  *
37493  * Fork - LGPL
37494  * <script type="text/javascript">
37495  */
37496 /**
37497  * @class Roo.TabPanel
37498  * @extends Roo.util.Observable
37499  * A lightweight tab container.
37500  * <br><br>
37501  * Usage:
37502  * <pre><code>
37503 // basic tabs 1, built from existing content
37504 var tabs = new Roo.TabPanel("tabs1");
37505 tabs.addTab("script", "View Script");
37506 tabs.addTab("markup", "View Markup");
37507 tabs.activate("script");
37508
37509 // more advanced tabs, built from javascript
37510 var jtabs = new Roo.TabPanel("jtabs");
37511 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37512
37513 // set up the UpdateManager
37514 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37515 var updater = tab2.getUpdateManager();
37516 updater.setDefaultUrl("ajax1.htm");
37517 tab2.on('activate', updater.refresh, updater, true);
37518
37519 // Use setUrl for Ajax loading
37520 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37521 tab3.setUrl("ajax2.htm", null, true);
37522
37523 // Disabled tab
37524 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37525 tab4.disable();
37526
37527 jtabs.activate("jtabs-1");
37528  * </code></pre>
37529  * @constructor
37530  * Create a new TabPanel.
37531  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37532  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37533  */
37534 Roo.bootstrap.panel.Tabs = function(config){
37535     /**
37536     * The container element for this TabPanel.
37537     * @type Roo.Element
37538     */
37539     this.el = Roo.get(config.el);
37540     delete config.el;
37541     if(config){
37542         if(typeof config == "boolean"){
37543             this.tabPosition = config ? "bottom" : "top";
37544         }else{
37545             Roo.apply(this, config);
37546         }
37547     }
37548     
37549     if(this.tabPosition == "bottom"){
37550         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37551         this.el.addClass("roo-tabs-bottom");
37552     }
37553     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37554     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37555     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37556     if(Roo.isIE){
37557         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37558     }
37559     if(this.tabPosition != "bottom"){
37560         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37561          * @type Roo.Element
37562          */
37563         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37564         this.el.addClass("roo-tabs-top");
37565     }
37566     this.items = [];
37567
37568     this.bodyEl.setStyle("position", "relative");
37569
37570     this.active = null;
37571     this.activateDelegate = this.activate.createDelegate(this);
37572
37573     this.addEvents({
37574         /**
37575          * @event tabchange
37576          * Fires when the active tab changes
37577          * @param {Roo.TabPanel} this
37578          * @param {Roo.TabPanelItem} activePanel The new active tab
37579          */
37580         "tabchange": true,
37581         /**
37582          * @event beforetabchange
37583          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37584          * @param {Roo.TabPanel} this
37585          * @param {Object} e Set cancel to true on this object to cancel the tab change
37586          * @param {Roo.TabPanelItem} tab The tab being changed to
37587          */
37588         "beforetabchange" : true
37589     });
37590
37591     Roo.EventManager.onWindowResize(this.onResize, this);
37592     this.cpad = this.el.getPadding("lr");
37593     this.hiddenCount = 0;
37594
37595
37596     // toolbar on the tabbar support...
37597     if (this.toolbar) {
37598         alert("no toolbar support yet");
37599         this.toolbar  = false;
37600         /*
37601         var tcfg = this.toolbar;
37602         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37603         this.toolbar = new Roo.Toolbar(tcfg);
37604         if (Roo.isSafari) {
37605             var tbl = tcfg.container.child('table', true);
37606             tbl.setAttribute('width', '100%');
37607         }
37608         */
37609         
37610     }
37611    
37612
37613
37614     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37615 };
37616
37617 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37618     /*
37619      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37620      */
37621     tabPosition : "top",
37622     /*
37623      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37624      */
37625     currentTabWidth : 0,
37626     /*
37627      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37628      */
37629     minTabWidth : 40,
37630     /*
37631      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37632      */
37633     maxTabWidth : 250,
37634     /*
37635      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37636      */
37637     preferredTabWidth : 175,
37638     /*
37639      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37640      */
37641     resizeTabs : false,
37642     /*
37643      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37644      */
37645     monitorResize : true,
37646     /*
37647      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37648      */
37649     toolbar : false,
37650
37651     /**
37652      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37653      * @param {String} id The id of the div to use <b>or create</b>
37654      * @param {String} text The text for the tab
37655      * @param {String} content (optional) Content to put in the TabPanelItem body
37656      * @param {Boolean} closable (optional) True to create a close icon on the tab
37657      * @return {Roo.TabPanelItem} The created TabPanelItem
37658      */
37659     addTab : function(id, text, content, closable, tpl)
37660     {
37661         var item = new Roo.bootstrap.panel.TabItem({
37662             panel: this,
37663             id : id,
37664             text : text,
37665             closable : closable,
37666             tpl : tpl
37667         });
37668         this.addTabItem(item);
37669         if(content){
37670             item.setContent(content);
37671         }
37672         return item;
37673     },
37674
37675     /**
37676      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37677      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37678      * @return {Roo.TabPanelItem}
37679      */
37680     getTab : function(id){
37681         return this.items[id];
37682     },
37683
37684     /**
37685      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37686      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37687      */
37688     hideTab : function(id){
37689         var t = this.items[id];
37690         if(!t.isHidden()){
37691            t.setHidden(true);
37692            this.hiddenCount++;
37693            this.autoSizeTabs();
37694         }
37695     },
37696
37697     /**
37698      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37699      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37700      */
37701     unhideTab : function(id){
37702         var t = this.items[id];
37703         if(t.isHidden()){
37704            t.setHidden(false);
37705            this.hiddenCount--;
37706            this.autoSizeTabs();
37707         }
37708     },
37709
37710     /**
37711      * Adds an existing {@link Roo.TabPanelItem}.
37712      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37713      */
37714     addTabItem : function(item){
37715         this.items[item.id] = item;
37716         this.items.push(item);
37717       //  if(this.resizeTabs){
37718     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37719   //         this.autoSizeTabs();
37720 //        }else{
37721 //            item.autoSize();
37722        // }
37723     },
37724
37725     /**
37726      * Removes a {@link Roo.TabPanelItem}.
37727      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37728      */
37729     removeTab : function(id){
37730         var items = this.items;
37731         var tab = items[id];
37732         if(!tab) { return; }
37733         var index = items.indexOf(tab);
37734         if(this.active == tab && items.length > 1){
37735             var newTab = this.getNextAvailable(index);
37736             if(newTab) {
37737                 newTab.activate();
37738             }
37739         }
37740         this.stripEl.dom.removeChild(tab.pnode.dom);
37741         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37742             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37743         }
37744         items.splice(index, 1);
37745         delete this.items[tab.id];
37746         tab.fireEvent("close", tab);
37747         tab.purgeListeners();
37748         this.autoSizeTabs();
37749     },
37750
37751     getNextAvailable : function(start){
37752         var items = this.items;
37753         var index = start;
37754         // look for a next tab that will slide over to
37755         // replace the one being removed
37756         while(index < items.length){
37757             var item = items[++index];
37758             if(item && !item.isHidden()){
37759                 return item;
37760             }
37761         }
37762         // if one isn't found select the previous tab (on the left)
37763         index = start;
37764         while(index >= 0){
37765             var item = items[--index];
37766             if(item && !item.isHidden()){
37767                 return item;
37768             }
37769         }
37770         return null;
37771     },
37772
37773     /**
37774      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37775      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37776      */
37777     disableTab : function(id){
37778         var tab = this.items[id];
37779         if(tab && this.active != tab){
37780             tab.disable();
37781         }
37782     },
37783
37784     /**
37785      * Enables a {@link Roo.TabPanelItem} that is disabled.
37786      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37787      */
37788     enableTab : function(id){
37789         var tab = this.items[id];
37790         tab.enable();
37791     },
37792
37793     /**
37794      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37795      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37796      * @return {Roo.TabPanelItem} The TabPanelItem.
37797      */
37798     activate : function(id){
37799         var tab = this.items[id];
37800         if(!tab){
37801             return null;
37802         }
37803         if(tab == this.active || tab.disabled){
37804             return tab;
37805         }
37806         var e = {};
37807         this.fireEvent("beforetabchange", this, e, tab);
37808         if(e.cancel !== true && !tab.disabled){
37809             if(this.active){
37810                 this.active.hide();
37811             }
37812             this.active = this.items[id];
37813             this.active.show();
37814             this.fireEvent("tabchange", this, this.active);
37815         }
37816         return tab;
37817     },
37818
37819     /**
37820      * Gets the active {@link Roo.TabPanelItem}.
37821      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37822      */
37823     getActiveTab : function(){
37824         return this.active;
37825     },
37826
37827     /**
37828      * Updates the tab body element to fit the height of the container element
37829      * for overflow scrolling
37830      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37831      */
37832     syncHeight : function(targetHeight){
37833         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37834         var bm = this.bodyEl.getMargins();
37835         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37836         this.bodyEl.setHeight(newHeight);
37837         return newHeight;
37838     },
37839
37840     onResize : function(){
37841         if(this.monitorResize){
37842             this.autoSizeTabs();
37843         }
37844     },
37845
37846     /**
37847      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37848      */
37849     beginUpdate : function(){
37850         this.updating = true;
37851     },
37852
37853     /**
37854      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37855      */
37856     endUpdate : function(){
37857         this.updating = false;
37858         this.autoSizeTabs();
37859     },
37860
37861     /**
37862      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37863      */
37864     autoSizeTabs : function(){
37865         var count = this.items.length;
37866         var vcount = count - this.hiddenCount;
37867         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37868             return;
37869         }
37870         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37871         var availWidth = Math.floor(w / vcount);
37872         var b = this.stripBody;
37873         if(b.getWidth() > w){
37874             var tabs = this.items;
37875             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37876             if(availWidth < this.minTabWidth){
37877                 /*if(!this.sleft){    // incomplete scrolling code
37878                     this.createScrollButtons();
37879                 }
37880                 this.showScroll();
37881                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37882             }
37883         }else{
37884             if(this.currentTabWidth < this.preferredTabWidth){
37885                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37886             }
37887         }
37888     },
37889
37890     /**
37891      * Returns the number of tabs in this TabPanel.
37892      * @return {Number}
37893      */
37894      getCount : function(){
37895          return this.items.length;
37896      },
37897
37898     /**
37899      * Resizes all the tabs to the passed width
37900      * @param {Number} The new width
37901      */
37902     setTabWidth : function(width){
37903         this.currentTabWidth = width;
37904         for(var i = 0, len = this.items.length; i < len; i++) {
37905                 if(!this.items[i].isHidden()) {
37906                 this.items[i].setWidth(width);
37907             }
37908         }
37909     },
37910
37911     /**
37912      * Destroys this TabPanel
37913      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37914      */
37915     destroy : function(removeEl){
37916         Roo.EventManager.removeResizeListener(this.onResize, this);
37917         for(var i = 0, len = this.items.length; i < len; i++){
37918             this.items[i].purgeListeners();
37919         }
37920         if(removeEl === true){
37921             this.el.update("");
37922             this.el.remove();
37923         }
37924     },
37925     
37926     createStrip : function(container)
37927     {
37928         var strip = document.createElement("nav");
37929         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37930         container.appendChild(strip);
37931         return strip;
37932     },
37933     
37934     createStripList : function(strip)
37935     {
37936         // div wrapper for retard IE
37937         // returns the "tr" element.
37938         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37939         //'<div class="x-tabs-strip-wrap">'+
37940           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37941           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37942         return strip.firstChild; //.firstChild.firstChild.firstChild;
37943     },
37944     createBody : function(container)
37945     {
37946         var body = document.createElement("div");
37947         Roo.id(body, "tab-body");
37948         //Roo.fly(body).addClass("x-tabs-body");
37949         Roo.fly(body).addClass("tab-content");
37950         container.appendChild(body);
37951         return body;
37952     },
37953     createItemBody :function(bodyEl, id){
37954         var body = Roo.getDom(id);
37955         if(!body){
37956             body = document.createElement("div");
37957             body.id = id;
37958         }
37959         //Roo.fly(body).addClass("x-tabs-item-body");
37960         Roo.fly(body).addClass("tab-pane");
37961          bodyEl.insertBefore(body, bodyEl.firstChild);
37962         return body;
37963     },
37964     /** @private */
37965     createStripElements :  function(stripEl, text, closable, tpl)
37966     {
37967         var td = document.createElement("li"); // was td..
37968         
37969         
37970         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37971         
37972         
37973         stripEl.appendChild(td);
37974         /*if(closable){
37975             td.className = "x-tabs-closable";
37976             if(!this.closeTpl){
37977                 this.closeTpl = new Roo.Template(
37978                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37979                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37980                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37981                 );
37982             }
37983             var el = this.closeTpl.overwrite(td, {"text": text});
37984             var close = el.getElementsByTagName("div")[0];
37985             var inner = el.getElementsByTagName("em")[0];
37986             return {"el": el, "close": close, "inner": inner};
37987         } else {
37988         */
37989         // not sure what this is..
37990 //            if(!this.tabTpl){
37991                 //this.tabTpl = new Roo.Template(
37992                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37993                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37994                 //);
37995 //                this.tabTpl = new Roo.Template(
37996 //                   '<a href="#">' +
37997 //                   '<span unselectable="on"' +
37998 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37999 //                            ' >{text}</span></a>'
38000 //                );
38001 //                
38002 //            }
38003
38004
38005             var template = tpl || this.tabTpl || false;
38006             
38007             if(!template){
38008                 
38009                 template = new Roo.Template(
38010                    '<a href="#">' +
38011                    '<span unselectable="on"' +
38012                             (this.disableTooltips ? '' : ' title="{text}"') +
38013                             ' >{text}</span></a>'
38014                 );
38015             }
38016             
38017             switch (typeof(template)) {
38018                 case 'object' :
38019                     break;
38020                 case 'string' :
38021                     template = new Roo.Template(template);
38022                     break;
38023                 default :
38024                     break;
38025             }
38026             
38027             var el = template.overwrite(td, {"text": text});
38028             
38029             var inner = el.getElementsByTagName("span")[0];
38030             
38031             return {"el": el, "inner": inner};
38032             
38033     }
38034         
38035     
38036 });
38037
38038 /**
38039  * @class Roo.TabPanelItem
38040  * @extends Roo.util.Observable
38041  * Represents an individual item (tab plus body) in a TabPanel.
38042  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38043  * @param {String} id The id of this TabPanelItem
38044  * @param {String} text The text for the tab of this TabPanelItem
38045  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38046  */
38047 Roo.bootstrap.panel.TabItem = function(config){
38048     /**
38049      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38050      * @type Roo.TabPanel
38051      */
38052     this.tabPanel = config.panel;
38053     /**
38054      * The id for this TabPanelItem
38055      * @type String
38056      */
38057     this.id = config.id;
38058     /** @private */
38059     this.disabled = false;
38060     /** @private */
38061     this.text = config.text;
38062     /** @private */
38063     this.loaded = false;
38064     this.closable = config.closable;
38065
38066     /**
38067      * The body element for this TabPanelItem.
38068      * @type Roo.Element
38069      */
38070     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38071     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38072     this.bodyEl.setStyle("display", "block");
38073     this.bodyEl.setStyle("zoom", "1");
38074     //this.hideAction();
38075
38076     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38077     /** @private */
38078     this.el = Roo.get(els.el);
38079     this.inner = Roo.get(els.inner, true);
38080     this.textEl = Roo.get(this.el.dom.firstChild, true);
38081     this.pnode = Roo.get(els.el.parentNode, true);
38082 //    this.el.on("mousedown", this.onTabMouseDown, this);
38083     this.el.on("click", this.onTabClick, this);
38084     /** @private */
38085     if(config.closable){
38086         var c = Roo.get(els.close, true);
38087         c.dom.title = this.closeText;
38088         c.addClassOnOver("close-over");
38089         c.on("click", this.closeClick, this);
38090      }
38091
38092     this.addEvents({
38093          /**
38094          * @event activate
38095          * Fires when this tab becomes the active tab.
38096          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38097          * @param {Roo.TabPanelItem} this
38098          */
38099         "activate": true,
38100         /**
38101          * @event beforeclose
38102          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38103          * @param {Roo.TabPanelItem} this
38104          * @param {Object} e Set cancel to true on this object to cancel the close.
38105          */
38106         "beforeclose": true,
38107         /**
38108          * @event close
38109          * Fires when this tab is closed.
38110          * @param {Roo.TabPanelItem} this
38111          */
38112          "close": true,
38113         /**
38114          * @event deactivate
38115          * Fires when this tab is no longer the active tab.
38116          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38117          * @param {Roo.TabPanelItem} this
38118          */
38119          "deactivate" : true
38120     });
38121     this.hidden = false;
38122
38123     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38124 };
38125
38126 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38127            {
38128     purgeListeners : function(){
38129        Roo.util.Observable.prototype.purgeListeners.call(this);
38130        this.el.removeAllListeners();
38131     },
38132     /**
38133      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38134      */
38135     show : function(){
38136         this.pnode.addClass("active");
38137         this.showAction();
38138         if(Roo.isOpera){
38139             this.tabPanel.stripWrap.repaint();
38140         }
38141         this.fireEvent("activate", this.tabPanel, this);
38142     },
38143
38144     /**
38145      * Returns true if this tab is the active tab.
38146      * @return {Boolean}
38147      */
38148     isActive : function(){
38149         return this.tabPanel.getActiveTab() == this;
38150     },
38151
38152     /**
38153      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38154      */
38155     hide : function(){
38156         this.pnode.removeClass("active");
38157         this.hideAction();
38158         this.fireEvent("deactivate", this.tabPanel, this);
38159     },
38160
38161     hideAction : function(){
38162         this.bodyEl.hide();
38163         this.bodyEl.setStyle("position", "absolute");
38164         this.bodyEl.setLeft("-20000px");
38165         this.bodyEl.setTop("-20000px");
38166     },
38167
38168     showAction : function(){
38169         this.bodyEl.setStyle("position", "relative");
38170         this.bodyEl.setTop("");
38171         this.bodyEl.setLeft("");
38172         this.bodyEl.show();
38173     },
38174
38175     /**
38176      * Set the tooltip for the tab.
38177      * @param {String} tooltip The tab's tooltip
38178      */
38179     setTooltip : function(text){
38180         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38181             this.textEl.dom.qtip = text;
38182             this.textEl.dom.removeAttribute('title');
38183         }else{
38184             this.textEl.dom.title = text;
38185         }
38186     },
38187
38188     onTabClick : function(e){
38189         e.preventDefault();
38190         this.tabPanel.activate(this.id);
38191     },
38192
38193     onTabMouseDown : function(e){
38194         e.preventDefault();
38195         this.tabPanel.activate(this.id);
38196     },
38197 /*
38198     getWidth : function(){
38199         return this.inner.getWidth();
38200     },
38201
38202     setWidth : function(width){
38203         var iwidth = width - this.pnode.getPadding("lr");
38204         this.inner.setWidth(iwidth);
38205         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38206         this.pnode.setWidth(width);
38207     },
38208 */
38209     /**
38210      * Show or hide the tab
38211      * @param {Boolean} hidden True to hide or false to show.
38212      */
38213     setHidden : function(hidden){
38214         this.hidden = hidden;
38215         this.pnode.setStyle("display", hidden ? "none" : "");
38216     },
38217
38218     /**
38219      * Returns true if this tab is "hidden"
38220      * @return {Boolean}
38221      */
38222     isHidden : function(){
38223         return this.hidden;
38224     },
38225
38226     /**
38227      * Returns the text for this tab
38228      * @return {String}
38229      */
38230     getText : function(){
38231         return this.text;
38232     },
38233     /*
38234     autoSize : function(){
38235         //this.el.beginMeasure();
38236         this.textEl.setWidth(1);
38237         /*
38238          *  #2804 [new] Tabs in Roojs
38239          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38240          */
38241         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38242         //this.el.endMeasure();
38243     //},
38244
38245     /**
38246      * Sets the text for the tab (Note: this also sets the tooltip text)
38247      * @param {String} text The tab's text and tooltip
38248      */
38249     setText : function(text){
38250         this.text = text;
38251         this.textEl.update(text);
38252         this.setTooltip(text);
38253         //if(!this.tabPanel.resizeTabs){
38254         //    this.autoSize();
38255         //}
38256     },
38257     /**
38258      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38259      */
38260     activate : function(){
38261         this.tabPanel.activate(this.id);
38262     },
38263
38264     /**
38265      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38266      */
38267     disable : function(){
38268         if(this.tabPanel.active != this){
38269             this.disabled = true;
38270             this.pnode.addClass("disabled");
38271         }
38272     },
38273
38274     /**
38275      * Enables this TabPanelItem if it was previously disabled.
38276      */
38277     enable : function(){
38278         this.disabled = false;
38279         this.pnode.removeClass("disabled");
38280     },
38281
38282     /**
38283      * Sets the content for this TabPanelItem.
38284      * @param {String} content The content
38285      * @param {Boolean} loadScripts true to look for and load scripts
38286      */
38287     setContent : function(content, loadScripts){
38288         this.bodyEl.update(content, loadScripts);
38289     },
38290
38291     /**
38292      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38293      * @return {Roo.UpdateManager} The UpdateManager
38294      */
38295     getUpdateManager : function(){
38296         return this.bodyEl.getUpdateManager();
38297     },
38298
38299     /**
38300      * Set a URL to be used to load the content for this TabPanelItem.
38301      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38302      * @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)
38303      * @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)
38304      * @return {Roo.UpdateManager} The UpdateManager
38305      */
38306     setUrl : function(url, params, loadOnce){
38307         if(this.refreshDelegate){
38308             this.un('activate', this.refreshDelegate);
38309         }
38310         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38311         this.on("activate", this.refreshDelegate);
38312         return this.bodyEl.getUpdateManager();
38313     },
38314
38315     /** @private */
38316     _handleRefresh : function(url, params, loadOnce){
38317         if(!loadOnce || !this.loaded){
38318             var updater = this.bodyEl.getUpdateManager();
38319             updater.update(url, params, this._setLoaded.createDelegate(this));
38320         }
38321     },
38322
38323     /**
38324      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38325      *   Will fail silently if the setUrl method has not been called.
38326      *   This does not activate the panel, just updates its content.
38327      */
38328     refresh : function(){
38329         if(this.refreshDelegate){
38330            this.loaded = false;
38331            this.refreshDelegate();
38332         }
38333     },
38334
38335     /** @private */
38336     _setLoaded : function(){
38337         this.loaded = true;
38338     },
38339
38340     /** @private */
38341     closeClick : function(e){
38342         var o = {};
38343         e.stopEvent();
38344         this.fireEvent("beforeclose", this, o);
38345         if(o.cancel !== true){
38346             this.tabPanel.removeTab(this.id);
38347         }
38348     },
38349     /**
38350      * The text displayed in the tooltip for the close icon.
38351      * @type String
38352      */
38353     closeText : "Close this tab"
38354 });
38355 /**
38356 *    This script refer to:
38357 *    Title: International Telephone Input
38358 *    Author: Jack O'Connor
38359 *    Code version:  v12.1.12
38360 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38361 **/
38362
38363 Roo.bootstrap.PhoneInputData = function() {
38364     var d = [
38365       [
38366         "Afghanistan (‫افغانستان‬‎)",
38367         "af",
38368         "93"
38369       ],
38370       [
38371         "Albania (Shqipëri)",
38372         "al",
38373         "355"
38374       ],
38375       [
38376         "Algeria (‫الجزائر‬‎)",
38377         "dz",
38378         "213"
38379       ],
38380       [
38381         "American Samoa",
38382         "as",
38383         "1684"
38384       ],
38385       [
38386         "Andorra",
38387         "ad",
38388         "376"
38389       ],
38390       [
38391         "Angola",
38392         "ao",
38393         "244"
38394       ],
38395       [
38396         "Anguilla",
38397         "ai",
38398         "1264"
38399       ],
38400       [
38401         "Antigua and Barbuda",
38402         "ag",
38403         "1268"
38404       ],
38405       [
38406         "Argentina",
38407         "ar",
38408         "54"
38409       ],
38410       [
38411         "Armenia (Հայաստան)",
38412         "am",
38413         "374"
38414       ],
38415       [
38416         "Aruba",
38417         "aw",
38418         "297"
38419       ],
38420       [
38421         "Australia",
38422         "au",
38423         "61",
38424         0
38425       ],
38426       [
38427         "Austria (Österreich)",
38428         "at",
38429         "43"
38430       ],
38431       [
38432         "Azerbaijan (Azərbaycan)",
38433         "az",
38434         "994"
38435       ],
38436       [
38437         "Bahamas",
38438         "bs",
38439         "1242"
38440       ],
38441       [
38442         "Bahrain (‫البحرين‬‎)",
38443         "bh",
38444         "973"
38445       ],
38446       [
38447         "Bangladesh (বাংলাদেশ)",
38448         "bd",
38449         "880"
38450       ],
38451       [
38452         "Barbados",
38453         "bb",
38454         "1246"
38455       ],
38456       [
38457         "Belarus (Беларусь)",
38458         "by",
38459         "375"
38460       ],
38461       [
38462         "Belgium (België)",
38463         "be",
38464         "32"
38465       ],
38466       [
38467         "Belize",
38468         "bz",
38469         "501"
38470       ],
38471       [
38472         "Benin (Bénin)",
38473         "bj",
38474         "229"
38475       ],
38476       [
38477         "Bermuda",
38478         "bm",
38479         "1441"
38480       ],
38481       [
38482         "Bhutan (འབྲུག)",
38483         "bt",
38484         "975"
38485       ],
38486       [
38487         "Bolivia",
38488         "bo",
38489         "591"
38490       ],
38491       [
38492         "Bosnia and Herzegovina (Босна и Херцеговина)",
38493         "ba",
38494         "387"
38495       ],
38496       [
38497         "Botswana",
38498         "bw",
38499         "267"
38500       ],
38501       [
38502         "Brazil (Brasil)",
38503         "br",
38504         "55"
38505       ],
38506       [
38507         "British Indian Ocean Territory",
38508         "io",
38509         "246"
38510       ],
38511       [
38512         "British Virgin Islands",
38513         "vg",
38514         "1284"
38515       ],
38516       [
38517         "Brunei",
38518         "bn",
38519         "673"
38520       ],
38521       [
38522         "Bulgaria (България)",
38523         "bg",
38524         "359"
38525       ],
38526       [
38527         "Burkina Faso",
38528         "bf",
38529         "226"
38530       ],
38531       [
38532         "Burundi (Uburundi)",
38533         "bi",
38534         "257"
38535       ],
38536       [
38537         "Cambodia (កម្ពុជា)",
38538         "kh",
38539         "855"
38540       ],
38541       [
38542         "Cameroon (Cameroun)",
38543         "cm",
38544         "237"
38545       ],
38546       [
38547         "Canada",
38548         "ca",
38549         "1",
38550         1,
38551         ["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"]
38552       ],
38553       [
38554         "Cape Verde (Kabu Verdi)",
38555         "cv",
38556         "238"
38557       ],
38558       [
38559         "Caribbean Netherlands",
38560         "bq",
38561         "599",
38562         1
38563       ],
38564       [
38565         "Cayman Islands",
38566         "ky",
38567         "1345"
38568       ],
38569       [
38570         "Central African Republic (République centrafricaine)",
38571         "cf",
38572         "236"
38573       ],
38574       [
38575         "Chad (Tchad)",
38576         "td",
38577         "235"
38578       ],
38579       [
38580         "Chile",
38581         "cl",
38582         "56"
38583       ],
38584       [
38585         "China (中国)",
38586         "cn",
38587         "86"
38588       ],
38589       [
38590         "Christmas Island",
38591         "cx",
38592         "61",
38593         2
38594       ],
38595       [
38596         "Cocos (Keeling) Islands",
38597         "cc",
38598         "61",
38599         1
38600       ],
38601       [
38602         "Colombia",
38603         "co",
38604         "57"
38605       ],
38606       [
38607         "Comoros (‫جزر القمر‬‎)",
38608         "km",
38609         "269"
38610       ],
38611       [
38612         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38613         "cd",
38614         "243"
38615       ],
38616       [
38617         "Congo (Republic) (Congo-Brazzaville)",
38618         "cg",
38619         "242"
38620       ],
38621       [
38622         "Cook Islands",
38623         "ck",
38624         "682"
38625       ],
38626       [
38627         "Costa Rica",
38628         "cr",
38629         "506"
38630       ],
38631       [
38632         "Côte d’Ivoire",
38633         "ci",
38634         "225"
38635       ],
38636       [
38637         "Croatia (Hrvatska)",
38638         "hr",
38639         "385"
38640       ],
38641       [
38642         "Cuba",
38643         "cu",
38644         "53"
38645       ],
38646       [
38647         "Curaçao",
38648         "cw",
38649         "599",
38650         0
38651       ],
38652       [
38653         "Cyprus (Κύπρος)",
38654         "cy",
38655         "357"
38656       ],
38657       [
38658         "Czech Republic (Česká republika)",
38659         "cz",
38660         "420"
38661       ],
38662       [
38663         "Denmark (Danmark)",
38664         "dk",
38665         "45"
38666       ],
38667       [
38668         "Djibouti",
38669         "dj",
38670         "253"
38671       ],
38672       [
38673         "Dominica",
38674         "dm",
38675         "1767"
38676       ],
38677       [
38678         "Dominican Republic (República Dominicana)",
38679         "do",
38680         "1",
38681         2,
38682         ["809", "829", "849"]
38683       ],
38684       [
38685         "Ecuador",
38686         "ec",
38687         "593"
38688       ],
38689       [
38690         "Egypt (‫مصر‬‎)",
38691         "eg",
38692         "20"
38693       ],
38694       [
38695         "El Salvador",
38696         "sv",
38697         "503"
38698       ],
38699       [
38700         "Equatorial Guinea (Guinea Ecuatorial)",
38701         "gq",
38702         "240"
38703       ],
38704       [
38705         "Eritrea",
38706         "er",
38707         "291"
38708       ],
38709       [
38710         "Estonia (Eesti)",
38711         "ee",
38712         "372"
38713       ],
38714       [
38715         "Ethiopia",
38716         "et",
38717         "251"
38718       ],
38719       [
38720         "Falkland Islands (Islas Malvinas)",
38721         "fk",
38722         "500"
38723       ],
38724       [
38725         "Faroe Islands (Føroyar)",
38726         "fo",
38727         "298"
38728       ],
38729       [
38730         "Fiji",
38731         "fj",
38732         "679"
38733       ],
38734       [
38735         "Finland (Suomi)",
38736         "fi",
38737         "358",
38738         0
38739       ],
38740       [
38741         "France",
38742         "fr",
38743         "33"
38744       ],
38745       [
38746         "French Guiana (Guyane française)",
38747         "gf",
38748         "594"
38749       ],
38750       [
38751         "French Polynesia (Polynésie française)",
38752         "pf",
38753         "689"
38754       ],
38755       [
38756         "Gabon",
38757         "ga",
38758         "241"
38759       ],
38760       [
38761         "Gambia",
38762         "gm",
38763         "220"
38764       ],
38765       [
38766         "Georgia (საქართველო)",
38767         "ge",
38768         "995"
38769       ],
38770       [
38771         "Germany (Deutschland)",
38772         "de",
38773         "49"
38774       ],
38775       [
38776         "Ghana (Gaana)",
38777         "gh",
38778         "233"
38779       ],
38780       [
38781         "Gibraltar",
38782         "gi",
38783         "350"
38784       ],
38785       [
38786         "Greece (Ελλάδα)",
38787         "gr",
38788         "30"
38789       ],
38790       [
38791         "Greenland (Kalaallit Nunaat)",
38792         "gl",
38793         "299"
38794       ],
38795       [
38796         "Grenada",
38797         "gd",
38798         "1473"
38799       ],
38800       [
38801         "Guadeloupe",
38802         "gp",
38803         "590",
38804         0
38805       ],
38806       [
38807         "Guam",
38808         "gu",
38809         "1671"
38810       ],
38811       [
38812         "Guatemala",
38813         "gt",
38814         "502"
38815       ],
38816       [
38817         "Guernsey",
38818         "gg",
38819         "44",
38820         1
38821       ],
38822       [
38823         "Guinea (Guinée)",
38824         "gn",
38825         "224"
38826       ],
38827       [
38828         "Guinea-Bissau (Guiné Bissau)",
38829         "gw",
38830         "245"
38831       ],
38832       [
38833         "Guyana",
38834         "gy",
38835         "592"
38836       ],
38837       [
38838         "Haiti",
38839         "ht",
38840         "509"
38841       ],
38842       [
38843         "Honduras",
38844         "hn",
38845         "504"
38846       ],
38847       [
38848         "Hong Kong (香港)",
38849         "hk",
38850         "852"
38851       ],
38852       [
38853         "Hungary (Magyarország)",
38854         "hu",
38855         "36"
38856       ],
38857       [
38858         "Iceland (Ísland)",
38859         "is",
38860         "354"
38861       ],
38862       [
38863         "India (भारत)",
38864         "in",
38865         "91"
38866       ],
38867       [
38868         "Indonesia",
38869         "id",
38870         "62"
38871       ],
38872       [
38873         "Iran (‫ایران‬‎)",
38874         "ir",
38875         "98"
38876       ],
38877       [
38878         "Iraq (‫العراق‬‎)",
38879         "iq",
38880         "964"
38881       ],
38882       [
38883         "Ireland",
38884         "ie",
38885         "353"
38886       ],
38887       [
38888         "Isle of Man",
38889         "im",
38890         "44",
38891         2
38892       ],
38893       [
38894         "Israel (‫ישראל‬‎)",
38895         "il",
38896         "972"
38897       ],
38898       [
38899         "Italy (Italia)",
38900         "it",
38901         "39",
38902         0
38903       ],
38904       [
38905         "Jamaica",
38906         "jm",
38907         "1876"
38908       ],
38909       [
38910         "Japan (日本)",
38911         "jp",
38912         "81"
38913       ],
38914       [
38915         "Jersey",
38916         "je",
38917         "44",
38918         3
38919       ],
38920       [
38921         "Jordan (‫الأردن‬‎)",
38922         "jo",
38923         "962"
38924       ],
38925       [
38926         "Kazakhstan (Казахстан)",
38927         "kz",
38928         "7",
38929         1
38930       ],
38931       [
38932         "Kenya",
38933         "ke",
38934         "254"
38935       ],
38936       [
38937         "Kiribati",
38938         "ki",
38939         "686"
38940       ],
38941       [
38942         "Kosovo",
38943         "xk",
38944         "383"
38945       ],
38946       [
38947         "Kuwait (‫الكويت‬‎)",
38948         "kw",
38949         "965"
38950       ],
38951       [
38952         "Kyrgyzstan (Кыргызстан)",
38953         "kg",
38954         "996"
38955       ],
38956       [
38957         "Laos (ລາວ)",
38958         "la",
38959         "856"
38960       ],
38961       [
38962         "Latvia (Latvija)",
38963         "lv",
38964         "371"
38965       ],
38966       [
38967         "Lebanon (‫لبنان‬‎)",
38968         "lb",
38969         "961"
38970       ],
38971       [
38972         "Lesotho",
38973         "ls",
38974         "266"
38975       ],
38976       [
38977         "Liberia",
38978         "lr",
38979         "231"
38980       ],
38981       [
38982         "Libya (‫ليبيا‬‎)",
38983         "ly",
38984         "218"
38985       ],
38986       [
38987         "Liechtenstein",
38988         "li",
38989         "423"
38990       ],
38991       [
38992         "Lithuania (Lietuva)",
38993         "lt",
38994         "370"
38995       ],
38996       [
38997         "Luxembourg",
38998         "lu",
38999         "352"
39000       ],
39001       [
39002         "Macau (澳門)",
39003         "mo",
39004         "853"
39005       ],
39006       [
39007         "Macedonia (FYROM) (Македонија)",
39008         "mk",
39009         "389"
39010       ],
39011       [
39012         "Madagascar (Madagasikara)",
39013         "mg",
39014         "261"
39015       ],
39016       [
39017         "Malawi",
39018         "mw",
39019         "265"
39020       ],
39021       [
39022         "Malaysia",
39023         "my",
39024         "60"
39025       ],
39026       [
39027         "Maldives",
39028         "mv",
39029         "960"
39030       ],
39031       [
39032         "Mali",
39033         "ml",
39034         "223"
39035       ],
39036       [
39037         "Malta",
39038         "mt",
39039         "356"
39040       ],
39041       [
39042         "Marshall Islands",
39043         "mh",
39044         "692"
39045       ],
39046       [
39047         "Martinique",
39048         "mq",
39049         "596"
39050       ],
39051       [
39052         "Mauritania (‫موريتانيا‬‎)",
39053         "mr",
39054         "222"
39055       ],
39056       [
39057         "Mauritius (Moris)",
39058         "mu",
39059         "230"
39060       ],
39061       [
39062         "Mayotte",
39063         "yt",
39064         "262",
39065         1
39066       ],
39067       [
39068         "Mexico (México)",
39069         "mx",
39070         "52"
39071       ],
39072       [
39073         "Micronesia",
39074         "fm",
39075         "691"
39076       ],
39077       [
39078         "Moldova (Republica Moldova)",
39079         "md",
39080         "373"
39081       ],
39082       [
39083         "Monaco",
39084         "mc",
39085         "377"
39086       ],
39087       [
39088         "Mongolia (Монгол)",
39089         "mn",
39090         "976"
39091       ],
39092       [
39093         "Montenegro (Crna Gora)",
39094         "me",
39095         "382"
39096       ],
39097       [
39098         "Montserrat",
39099         "ms",
39100         "1664"
39101       ],
39102       [
39103         "Morocco (‫المغرب‬‎)",
39104         "ma",
39105         "212",
39106         0
39107       ],
39108       [
39109         "Mozambique (Moçambique)",
39110         "mz",
39111         "258"
39112       ],
39113       [
39114         "Myanmar (Burma) (မြန်မာ)",
39115         "mm",
39116         "95"
39117       ],
39118       [
39119         "Namibia (Namibië)",
39120         "na",
39121         "264"
39122       ],
39123       [
39124         "Nauru",
39125         "nr",
39126         "674"
39127       ],
39128       [
39129         "Nepal (नेपाल)",
39130         "np",
39131         "977"
39132       ],
39133       [
39134         "Netherlands (Nederland)",
39135         "nl",
39136         "31"
39137       ],
39138       [
39139         "New Caledonia (Nouvelle-Calédonie)",
39140         "nc",
39141         "687"
39142       ],
39143       [
39144         "New Zealand",
39145         "nz",
39146         "64"
39147       ],
39148       [
39149         "Nicaragua",
39150         "ni",
39151         "505"
39152       ],
39153       [
39154         "Niger (Nijar)",
39155         "ne",
39156         "227"
39157       ],
39158       [
39159         "Nigeria",
39160         "ng",
39161         "234"
39162       ],
39163       [
39164         "Niue",
39165         "nu",
39166         "683"
39167       ],
39168       [
39169         "Norfolk Island",
39170         "nf",
39171         "672"
39172       ],
39173       [
39174         "North Korea (조선 민주주의 인민 공화국)",
39175         "kp",
39176         "850"
39177       ],
39178       [
39179         "Northern Mariana Islands",
39180         "mp",
39181         "1670"
39182       ],
39183       [
39184         "Norway (Norge)",
39185         "no",
39186         "47",
39187         0
39188       ],
39189       [
39190         "Oman (‫عُمان‬‎)",
39191         "om",
39192         "968"
39193       ],
39194       [
39195         "Pakistan (‫پاکستان‬‎)",
39196         "pk",
39197         "92"
39198       ],
39199       [
39200         "Palau",
39201         "pw",
39202         "680"
39203       ],
39204       [
39205         "Palestine (‫فلسطين‬‎)",
39206         "ps",
39207         "970"
39208       ],
39209       [
39210         "Panama (Panamá)",
39211         "pa",
39212         "507"
39213       ],
39214       [
39215         "Papua New Guinea",
39216         "pg",
39217         "675"
39218       ],
39219       [
39220         "Paraguay",
39221         "py",
39222         "595"
39223       ],
39224       [
39225         "Peru (Perú)",
39226         "pe",
39227         "51"
39228       ],
39229       [
39230         "Philippines",
39231         "ph",
39232         "63"
39233       ],
39234       [
39235         "Poland (Polska)",
39236         "pl",
39237         "48"
39238       ],
39239       [
39240         "Portugal",
39241         "pt",
39242         "351"
39243       ],
39244       [
39245         "Puerto Rico",
39246         "pr",
39247         "1",
39248         3,
39249         ["787", "939"]
39250       ],
39251       [
39252         "Qatar (‫قطر‬‎)",
39253         "qa",
39254         "974"
39255       ],
39256       [
39257         "Réunion (La Réunion)",
39258         "re",
39259         "262",
39260         0
39261       ],
39262       [
39263         "Romania (România)",
39264         "ro",
39265         "40"
39266       ],
39267       [
39268         "Russia (Россия)",
39269         "ru",
39270         "7",
39271         0
39272       ],
39273       [
39274         "Rwanda",
39275         "rw",
39276         "250"
39277       ],
39278       [
39279         "Saint Barthélemy",
39280         "bl",
39281         "590",
39282         1
39283       ],
39284       [
39285         "Saint Helena",
39286         "sh",
39287         "290"
39288       ],
39289       [
39290         "Saint Kitts and Nevis",
39291         "kn",
39292         "1869"
39293       ],
39294       [
39295         "Saint Lucia",
39296         "lc",
39297         "1758"
39298       ],
39299       [
39300         "Saint Martin (Saint-Martin (partie française))",
39301         "mf",
39302         "590",
39303         2
39304       ],
39305       [
39306         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39307         "pm",
39308         "508"
39309       ],
39310       [
39311         "Saint Vincent and the Grenadines",
39312         "vc",
39313         "1784"
39314       ],
39315       [
39316         "Samoa",
39317         "ws",
39318         "685"
39319       ],
39320       [
39321         "San Marino",
39322         "sm",
39323         "378"
39324       ],
39325       [
39326         "São Tomé and Príncipe (São Tomé e Príncipe)",
39327         "st",
39328         "239"
39329       ],
39330       [
39331         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39332         "sa",
39333         "966"
39334       ],
39335       [
39336         "Senegal (Sénégal)",
39337         "sn",
39338         "221"
39339       ],
39340       [
39341         "Serbia (Србија)",
39342         "rs",
39343         "381"
39344       ],
39345       [
39346         "Seychelles",
39347         "sc",
39348         "248"
39349       ],
39350       [
39351         "Sierra Leone",
39352         "sl",
39353         "232"
39354       ],
39355       [
39356         "Singapore",
39357         "sg",
39358         "65"
39359       ],
39360       [
39361         "Sint Maarten",
39362         "sx",
39363         "1721"
39364       ],
39365       [
39366         "Slovakia (Slovensko)",
39367         "sk",
39368         "421"
39369       ],
39370       [
39371         "Slovenia (Slovenija)",
39372         "si",
39373         "386"
39374       ],
39375       [
39376         "Solomon Islands",
39377         "sb",
39378         "677"
39379       ],
39380       [
39381         "Somalia (Soomaaliya)",
39382         "so",
39383         "252"
39384       ],
39385       [
39386         "South Africa",
39387         "za",
39388         "27"
39389       ],
39390       [
39391         "South Korea (대한민국)",
39392         "kr",
39393         "82"
39394       ],
39395       [
39396         "South Sudan (‫جنوب السودان‬‎)",
39397         "ss",
39398         "211"
39399       ],
39400       [
39401         "Spain (España)",
39402         "es",
39403         "34"
39404       ],
39405       [
39406         "Sri Lanka (ශ්‍රී ලංකාව)",
39407         "lk",
39408         "94"
39409       ],
39410       [
39411         "Sudan (‫السودان‬‎)",
39412         "sd",
39413         "249"
39414       ],
39415       [
39416         "Suriname",
39417         "sr",
39418         "597"
39419       ],
39420       [
39421         "Svalbard and Jan Mayen",
39422         "sj",
39423         "47",
39424         1
39425       ],
39426       [
39427         "Swaziland",
39428         "sz",
39429         "268"
39430       ],
39431       [
39432         "Sweden (Sverige)",
39433         "se",
39434         "46"
39435       ],
39436       [
39437         "Switzerland (Schweiz)",
39438         "ch",
39439         "41"
39440       ],
39441       [
39442         "Syria (‫سوريا‬‎)",
39443         "sy",
39444         "963"
39445       ],
39446       [
39447         "Taiwan (台灣)",
39448         "tw",
39449         "886"
39450       ],
39451       [
39452         "Tajikistan",
39453         "tj",
39454         "992"
39455       ],
39456       [
39457         "Tanzania",
39458         "tz",
39459         "255"
39460       ],
39461       [
39462         "Thailand (ไทย)",
39463         "th",
39464         "66"
39465       ],
39466       [
39467         "Timor-Leste",
39468         "tl",
39469         "670"
39470       ],
39471       [
39472         "Togo",
39473         "tg",
39474         "228"
39475       ],
39476       [
39477         "Tokelau",
39478         "tk",
39479         "690"
39480       ],
39481       [
39482         "Tonga",
39483         "to",
39484         "676"
39485       ],
39486       [
39487         "Trinidad and Tobago",
39488         "tt",
39489         "1868"
39490       ],
39491       [
39492         "Tunisia (‫تونس‬‎)",
39493         "tn",
39494         "216"
39495       ],
39496       [
39497         "Turkey (Türkiye)",
39498         "tr",
39499         "90"
39500       ],
39501       [
39502         "Turkmenistan",
39503         "tm",
39504         "993"
39505       ],
39506       [
39507         "Turks and Caicos Islands",
39508         "tc",
39509         "1649"
39510       ],
39511       [
39512         "Tuvalu",
39513         "tv",
39514         "688"
39515       ],
39516       [
39517         "U.S. Virgin Islands",
39518         "vi",
39519         "1340"
39520       ],
39521       [
39522         "Uganda",
39523         "ug",
39524         "256"
39525       ],
39526       [
39527         "Ukraine (Україна)",
39528         "ua",
39529         "380"
39530       ],
39531       [
39532         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39533         "ae",
39534         "971"
39535       ],
39536       [
39537         "United Kingdom",
39538         "gb",
39539         "44",
39540         0
39541       ],
39542       [
39543         "United States",
39544         "us",
39545         "1",
39546         0
39547       ],
39548       [
39549         "Uruguay",
39550         "uy",
39551         "598"
39552       ],
39553       [
39554         "Uzbekistan (Oʻzbekiston)",
39555         "uz",
39556         "998"
39557       ],
39558       [
39559         "Vanuatu",
39560         "vu",
39561         "678"
39562       ],
39563       [
39564         "Vatican City (Città del Vaticano)",
39565         "va",
39566         "39",
39567         1
39568       ],
39569       [
39570         "Venezuela",
39571         "ve",
39572         "58"
39573       ],
39574       [
39575         "Vietnam (Việt Nam)",
39576         "vn",
39577         "84"
39578       ],
39579       [
39580         "Wallis and Futuna (Wallis-et-Futuna)",
39581         "wf",
39582         "681"
39583       ],
39584       [
39585         "Western Sahara (‫الصحراء الغربية‬‎)",
39586         "eh",
39587         "212",
39588         1
39589       ],
39590       [
39591         "Yemen (‫اليمن‬‎)",
39592         "ye",
39593         "967"
39594       ],
39595       [
39596         "Zambia",
39597         "zm",
39598         "260"
39599       ],
39600       [
39601         "Zimbabwe",
39602         "zw",
39603         "263"
39604       ],
39605       [
39606         "Åland Islands",
39607         "ax",
39608         "358",
39609         1
39610       ]
39611   ];
39612   
39613   return d;
39614 }/**
39615 *    This script refer to:
39616 *    Title: International Telephone Input
39617 *    Author: Jack O'Connor
39618 *    Code version:  v12.1.12
39619 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39620 **/
39621
39622 /**
39623  * @class Roo.bootstrap.PhoneInput
39624  * @extends Roo.bootstrap.TriggerField
39625  * An input with International dial-code selection
39626  
39627  * @cfg {String} defaultDialCode default '+852'
39628  * @cfg {Array} preferedCountries default []
39629   
39630  * @constructor
39631  * Create a new PhoneInput.
39632  * @param {Object} config Configuration options
39633  */
39634
39635 Roo.bootstrap.PhoneInput = function(config) {
39636     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39637 };
39638
39639 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39640         
39641         listWidth: undefined,
39642         
39643         selectedClass: 'active',
39644         
39645         invalidClass : "has-warning",
39646         
39647         validClass: 'has-success',
39648         
39649         allowed: '0123456789',
39650         
39651         /**
39652          * @cfg {String} defaultDialCode The default dial code when initializing the input
39653          */
39654         defaultDialCode: '+852',
39655         
39656         /**
39657          * @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
39658          */
39659         preferedCountries: false,
39660         
39661         getAutoCreate : function()
39662         {
39663             var data = Roo.bootstrap.PhoneInputData();
39664             var align = this.labelAlign || this.parentLabelAlign();
39665             var id = Roo.id();
39666             
39667             this.allCountries = [];
39668             this.dialCodeMapping = [];
39669             
39670             for (var i = 0; i < data.length; i++) {
39671               var c = data[i];
39672               this.allCountries[i] = {
39673                 name: c[0],
39674                 iso2: c[1],
39675                 dialCode: c[2],
39676                 priority: c[3] || 0,
39677                 areaCodes: c[4] || null
39678               };
39679               this.dialCodeMapping[c[2]] = {
39680                   name: c[0],
39681                   iso2: c[1],
39682                   priority: c[3] || 0,
39683                   areaCodes: c[4] || null
39684               };
39685             }
39686             
39687             var cfg = {
39688                 cls: 'form-group',
39689                 cn: []
39690             };
39691             
39692             var input =  {
39693                 tag: 'input',
39694                 id : id,
39695                 cls : 'form-control tel-input',
39696                 autocomplete: 'new-password'
39697             };
39698             
39699             var hiddenInput = {
39700                 tag: 'input',
39701                 type: 'hidden',
39702                 cls: 'hidden-tel-input'
39703             };
39704             
39705             if (this.name) {
39706                 hiddenInput.name = this.name;
39707             }
39708             
39709             if (this.disabled) {
39710                 input.disabled = true;
39711             }
39712             
39713             var flag_container = {
39714                 tag: 'div',
39715                 cls: 'flag-box',
39716                 cn: [
39717                     {
39718                         tag: 'div',
39719                         cls: 'flag'
39720                     },
39721                     {
39722                         tag: 'div',
39723                         cls: 'caret'
39724                     }
39725                 ]
39726             };
39727             
39728             var box = {
39729                 tag: 'div',
39730                 cls: this.hasFeedback ? 'has-feedback' : '',
39731                 cn: [
39732                     hiddenInput,
39733                     input,
39734                     {
39735                         tag: 'input',
39736                         cls: 'dial-code-holder',
39737                         disabled: true
39738                     }
39739                 ]
39740             };
39741             
39742             var container = {
39743                 cls: 'roo-select2-container input-group',
39744                 cn: [
39745                     flag_container,
39746                     box
39747                 ]
39748             };
39749             
39750             if (this.fieldLabel.length) {
39751                 var indicator = {
39752                     tag: 'i',
39753                     tooltip: 'This field is required'
39754                 };
39755                 
39756                 var label = {
39757                     tag: 'label',
39758                     'for':  id,
39759                     cls: 'control-label',
39760                     cn: []
39761                 };
39762                 
39763                 var label_text = {
39764                     tag: 'span',
39765                     html: this.fieldLabel
39766                 };
39767                 
39768                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39769                 label.cn = [
39770                     indicator,
39771                     label_text
39772                 ];
39773                 
39774                 if(this.indicatorpos == 'right') {
39775                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39776                     label.cn = [
39777                         label_text,
39778                         indicator
39779                     ];
39780                 }
39781                 
39782                 if(align == 'left') {
39783                     container = {
39784                         tag: 'div',
39785                         cn: [
39786                             container
39787                         ]
39788                     };
39789                     
39790                     if(this.labelWidth > 12){
39791                         label.style = "width: " + this.labelWidth + 'px';
39792                     }
39793                     if(this.labelWidth < 13 && this.labelmd == 0){
39794                         this.labelmd = this.labelWidth;
39795                     }
39796                     if(this.labellg > 0){
39797                         label.cls += ' col-lg-' + this.labellg;
39798                         input.cls += ' col-lg-' + (12 - this.labellg);
39799                     }
39800                     if(this.labelmd > 0){
39801                         label.cls += ' col-md-' + this.labelmd;
39802                         container.cls += ' col-md-' + (12 - this.labelmd);
39803                     }
39804                     if(this.labelsm > 0){
39805                         label.cls += ' col-sm-' + this.labelsm;
39806                         container.cls += ' col-sm-' + (12 - this.labelsm);
39807                     }
39808                     if(this.labelxs > 0){
39809                         label.cls += ' col-xs-' + this.labelxs;
39810                         container.cls += ' col-xs-' + (12 - this.labelxs);
39811                     }
39812                 }
39813             }
39814             
39815             cfg.cn = [
39816                 label,
39817                 container
39818             ];
39819             
39820             var settings = this;
39821             
39822             ['xs','sm','md','lg'].map(function(size){
39823                 if (settings[size]) {
39824                     cfg.cls += ' col-' + size + '-' + settings[size];
39825                 }
39826             });
39827             
39828             this.store = new Roo.data.Store({
39829                 proxy : new Roo.data.MemoryProxy({}),
39830                 reader : new Roo.data.JsonReader({
39831                     fields : [
39832                         {
39833                             'name' : 'name',
39834                             'type' : 'string'
39835                         },
39836                         {
39837                             'name' : 'iso2',
39838                             'type' : 'string'
39839                         },
39840                         {
39841                             'name' : 'dialCode',
39842                             'type' : 'string'
39843                         },
39844                         {
39845                             'name' : 'priority',
39846                             'type' : 'string'
39847                         },
39848                         {
39849                             'name' : 'areaCodes',
39850                             'type' : 'string'
39851                         }
39852                     ]
39853                 })
39854             });
39855             
39856             if(!this.preferedCountries) {
39857                 this.preferedCountries = [
39858                     'hk',
39859                     'gb',
39860                     'us'
39861                 ];
39862             }
39863             
39864             var p = this.preferedCountries.reverse();
39865             
39866             if(p) {
39867                 for (var i = 0; i < p.length; i++) {
39868                     for (var j = 0; j < this.allCountries.length; j++) {
39869                         if(this.allCountries[j].iso2 == p[i]) {
39870                             var t = this.allCountries[j];
39871                             this.allCountries.splice(j,1);
39872                             this.allCountries.unshift(t);
39873                         }
39874                     } 
39875                 }
39876             }
39877             
39878             this.store.proxy.data = {
39879                 success: true,
39880                 data: this.allCountries
39881             };
39882             
39883             return cfg;
39884         },
39885         
39886         initEvents : function()
39887         {
39888             this.createList();
39889             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39890             
39891             this.indicator = this.indicatorEl();
39892             this.flag = this.flagEl();
39893             this.dialCodeHolder = this.dialCodeHolderEl();
39894             
39895             this.trigger = this.el.select('div.flag-box',true).first();
39896             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39897             
39898             var _this = this;
39899             
39900             (function(){
39901                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39902                 _this.list.setWidth(lw);
39903             }).defer(100);
39904             
39905             this.list.on('mouseover', this.onViewOver, this);
39906             this.list.on('mousemove', this.onViewMove, this);
39907             this.inputEl().on("keyup", this.onKeyUp, this);
39908             
39909             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39910
39911             this.view = new Roo.View(this.list, this.tpl, {
39912                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39913             });
39914             
39915             this.view.on('click', this.onViewClick, this);
39916             this.setValue(this.defaultDialCode);
39917         },
39918         
39919         onTriggerClick : function(e)
39920         {
39921             Roo.log('trigger click');
39922             if(this.disabled){
39923                 return;
39924             }
39925             
39926             if(this.isExpanded()){
39927                 this.collapse();
39928                 this.hasFocus = false;
39929             }else {
39930                 this.store.load({});
39931                 this.hasFocus = true;
39932                 this.expand();
39933             }
39934         },
39935         
39936         isExpanded : function()
39937         {
39938             return this.list.isVisible();
39939         },
39940         
39941         collapse : function()
39942         {
39943             if(!this.isExpanded()){
39944                 return;
39945             }
39946             this.list.hide();
39947             Roo.get(document).un('mousedown', this.collapseIf, this);
39948             Roo.get(document).un('mousewheel', this.collapseIf, this);
39949             this.fireEvent('collapse', this);
39950             this.validate();
39951         },
39952         
39953         expand : function()
39954         {
39955             Roo.log('expand');
39956
39957             if(this.isExpanded() || !this.hasFocus){
39958                 return;
39959             }
39960             
39961             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39962             this.list.setWidth(lw);
39963             
39964             this.list.show();
39965             this.restrictHeight();
39966             
39967             Roo.get(document).on('mousedown', this.collapseIf, this);
39968             Roo.get(document).on('mousewheel', this.collapseIf, this);
39969             
39970             this.fireEvent('expand', this);
39971         },
39972         
39973         restrictHeight : function()
39974         {
39975             this.list.alignTo(this.inputEl(), this.listAlign);
39976             this.list.alignTo(this.inputEl(), this.listAlign);
39977         },
39978         
39979         onViewOver : function(e, t)
39980         {
39981             if(this.inKeyMode){
39982                 return;
39983             }
39984             var item = this.view.findItemFromChild(t);
39985             
39986             if(item){
39987                 var index = this.view.indexOf(item);
39988                 this.select(index, false);
39989             }
39990         },
39991
39992         // private
39993         onViewClick : function(view, doFocus, el, e)
39994         {
39995             var index = this.view.getSelectedIndexes()[0];
39996             
39997             var r = this.store.getAt(index);
39998             
39999             if(r){
40000                 this.onSelect(r, index);
40001             }
40002             if(doFocus !== false && !this.blockFocus){
40003                 this.inputEl().focus();
40004             }
40005         },
40006         
40007         onViewMove : function(e, t)
40008         {
40009             this.inKeyMode = false;
40010         },
40011         
40012         select : function(index, scrollIntoView)
40013         {
40014             this.selectedIndex = index;
40015             this.view.select(index);
40016             if(scrollIntoView !== false){
40017                 var el = this.view.getNode(index);
40018                 if(el){
40019                     this.list.scrollChildIntoView(el, false);
40020                 }
40021             }
40022         },
40023         
40024         createList : function()
40025         {
40026             this.list = Roo.get(document.body).createChild({
40027                 tag: 'ul',
40028                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40029                 style: 'display:none'
40030             });
40031             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
40032         },
40033         
40034         collapseIf : function(e)
40035         {
40036             var in_combo  = e.within(this.el);
40037             var in_list =  e.within(this.list);
40038             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40039             
40040             if (in_combo || in_list || is_list) {
40041                 return;
40042             }
40043             this.collapse();
40044         },
40045         
40046         onSelect : function(record, index)
40047         {
40048             if(this.fireEvent('beforeselect', this, record, index) !== false){
40049                 
40050                 this.setFlagClass(record.data.iso2);
40051                 this.setDialCode(record.data.dialCode);
40052                 this.hasFocus = false;
40053                 this.collapse();
40054                 this.fireEvent('select', this, record, index);
40055             }
40056         },
40057         
40058         flagEl : function()
40059         {
40060             var flag = this.el.select('div.flag',true).first();
40061             if(!flag){
40062                 return false;
40063             }
40064             return flag;
40065         },
40066         
40067         dialCodeHolderEl : function()
40068         {
40069             var d = this.el.select('input.dial-code-holder',true).first();
40070             if(!d){
40071                 return false;
40072             }
40073             return d;
40074         },
40075         
40076         setDialCode : function(v)
40077         {
40078             this.dialCodeHolder.dom.value = '+'+v;
40079         },
40080         
40081         setFlagClass : function(n)
40082         {
40083             this.flag.dom.className = 'flag '+n;
40084         },
40085         
40086         getValue : function()
40087         {
40088             var v = this.inputEl().getValue();
40089             if(this.dialCodeHolder) {
40090                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40091             }
40092             return v;
40093         },
40094         
40095         setValue : function(v)
40096         {
40097             var d = this.getDialCode(v);
40098             
40099             //invalid dial code
40100             if(v.length == 0 || !d || d.length == 0) {
40101                 if(this.rendered){
40102                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40103                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40104                 }
40105                 return;
40106             }
40107             
40108             //valid dial code
40109             this.setFlagClass(this.dialCodeMapping[d].iso2);
40110             this.setDialCode(d);
40111             this.inputEl().dom.value = v.replace('+'+d,'');
40112             this.hiddenEl().dom.value = this.getValue();
40113             
40114             this.validate();
40115         },
40116         
40117         getDialCode : function(v = '')
40118         {
40119             if (v.length == 0) {
40120                 return this.dialCodeHolder.dom.value;
40121             }
40122             
40123             var dialCode = "";
40124             if (v.charAt(0) != "+") {
40125                 return false;
40126             }
40127             var numericChars = "";
40128             for (var i = 1; i < v.length; i++) {
40129               var c = v.charAt(i);
40130               if (!isNaN(c)) {
40131                 numericChars += c;
40132                 if (this.dialCodeMapping[numericChars]) {
40133                   dialCode = v.substr(1, i);
40134                 }
40135                 if (numericChars.length == 4) {
40136                   break;
40137                 }
40138               }
40139             }
40140             return dialCode;
40141         },
40142         
40143         reset : function()
40144         {
40145             this.setValue(this.defaultDialCode);
40146             this.validate();
40147         },
40148         
40149         hiddenEl : function()
40150         {
40151             return this.el.select('input.hidden-tel-input',true).first();
40152         },
40153         
40154         onKeyUp : function(e){
40155             
40156             var k = e.getKey();
40157             var c = e.getCharCode();
40158             
40159             if(
40160                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40161                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40162             ){
40163                 e.stopEvent();
40164             }
40165             
40166             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40167             //     return;
40168             // }
40169             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40170                 e.stopEvent();
40171             }
40172             
40173             this.setValue(this.getValue());
40174         }
40175         
40176 });
40177 /**
40178  * @class Roo.bootstrap.MoneyField
40179  * @extends Roo.bootstrap.ComboBox
40180  * Bootstrap MoneyField class
40181  * 
40182  * @constructor
40183  * Create a new MoneyField.
40184  * @param {Object} config Configuration options
40185  */
40186
40187 Roo.bootstrap.MoneyField = function(config) {
40188     
40189     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40190     
40191 };
40192
40193 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40194     
40195     /**
40196      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40197      */
40198     allowDecimals : true,
40199     /**
40200      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40201      */
40202     decimalSeparator : ".",
40203     /**
40204      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40205      */
40206     decimalPrecision : 0,
40207     /**
40208      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40209      */
40210     allowNegative : true,
40211     /**
40212      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40213      */
40214     minValue : Number.NEGATIVE_INFINITY,
40215     /**
40216      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40217      */
40218     maxValue : Number.MAX_VALUE,
40219     /**
40220      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40221      */
40222     minText : "The minimum value for this field is {0}",
40223     /**
40224      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40225      */
40226     maxText : "The maximum value for this field is {0}",
40227     /**
40228      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40229      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40230      */
40231     nanText : "{0} is not a valid number",
40232     /**
40233      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40234      */
40235     castInt : true,
40236     /**
40237      * @cfg {String} defaults currency of the MoneyField
40238      * value should be in lkey
40239      */
40240     defaultCurrency : false,
40241     /**
40242      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40243      */
40244     thousandsDelimiter : false,
40245     
40246     
40247     inputlg : 9,
40248     inputmd : 9,
40249     inputsm : 9,
40250     inputxs : 6,
40251     
40252     store : false,
40253     
40254     getAutoCreate : function()
40255     {
40256         var align = this.labelAlign || this.parentLabelAlign();
40257         
40258         var id = Roo.id();
40259
40260         var cfg = {
40261             cls: 'form-group',
40262             cn: []
40263         };
40264
40265         var input =  {
40266             tag: 'input',
40267             id : id,
40268             cls : 'form-control roo-money-amount-input',
40269             autocomplete: 'new-password'
40270         };
40271         
40272         var hiddenInput = {
40273             tag: 'input',
40274             type: 'hidden',
40275             id: Roo.id(),
40276             cls: 'hidden-number-input'
40277         };
40278         
40279         if (this.name) {
40280             hiddenInput.name = this.name;
40281         }
40282
40283         if (this.disabled) {
40284             input.disabled = true;
40285         }
40286
40287         var clg = 12 - this.inputlg;
40288         var cmd = 12 - this.inputmd;
40289         var csm = 12 - this.inputsm;
40290         var cxs = 12 - this.inputxs;
40291         
40292         var container = {
40293             tag : 'div',
40294             cls : 'row roo-money-field',
40295             cn : [
40296                 {
40297                     tag : 'div',
40298                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40299                     cn : [
40300                         {
40301                             tag : 'div',
40302                             cls: 'roo-select2-container input-group',
40303                             cn: [
40304                                 {
40305                                     tag : 'input',
40306                                     cls : 'form-control roo-money-currency-input',
40307                                     autocomplete: 'new-password',
40308                                     readOnly : 1,
40309                                     name : this.currencyName
40310                                 },
40311                                 {
40312                                     tag :'span',
40313                                     cls : 'input-group-addon',
40314                                     cn : [
40315                                         {
40316                                             tag: 'span',
40317                                             cls: 'caret'
40318                                         }
40319                                     ]
40320                                 }
40321                             ]
40322                         }
40323                     ]
40324                 },
40325                 {
40326                     tag : 'div',
40327                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40328                     cn : [
40329                         {
40330                             tag: 'div',
40331                             cls: this.hasFeedback ? 'has-feedback' : '',
40332                             cn: [
40333                                 input
40334                             ]
40335                         }
40336                     ]
40337                 }
40338             ]
40339             
40340         };
40341         
40342         if (this.fieldLabel.length) {
40343             var indicator = {
40344                 tag: 'i',
40345                 tooltip: 'This field is required'
40346             };
40347
40348             var label = {
40349                 tag: 'label',
40350                 'for':  id,
40351                 cls: 'control-label',
40352                 cn: []
40353             };
40354
40355             var label_text = {
40356                 tag: 'span',
40357                 html: this.fieldLabel
40358             };
40359
40360             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40361             label.cn = [
40362                 indicator,
40363                 label_text
40364             ];
40365
40366             if(this.indicatorpos == 'right') {
40367                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40368                 label.cn = [
40369                     label_text,
40370                     indicator
40371                 ];
40372             }
40373
40374             if(align == 'left') {
40375                 container = {
40376                     tag: 'div',
40377                     cn: [
40378                         container
40379                     ]
40380                 };
40381
40382                 if(this.labelWidth > 12){
40383                     label.style = "width: " + this.labelWidth + 'px';
40384                 }
40385                 if(this.labelWidth < 13 && this.labelmd == 0){
40386                     this.labelmd = this.labelWidth;
40387                 }
40388                 if(this.labellg > 0){
40389                     label.cls += ' col-lg-' + this.labellg;
40390                     input.cls += ' col-lg-' + (12 - this.labellg);
40391                 }
40392                 if(this.labelmd > 0){
40393                     label.cls += ' col-md-' + this.labelmd;
40394                     container.cls += ' col-md-' + (12 - this.labelmd);
40395                 }
40396                 if(this.labelsm > 0){
40397                     label.cls += ' col-sm-' + this.labelsm;
40398                     container.cls += ' col-sm-' + (12 - this.labelsm);
40399                 }
40400                 if(this.labelxs > 0){
40401                     label.cls += ' col-xs-' + this.labelxs;
40402                     container.cls += ' col-xs-' + (12 - this.labelxs);
40403                 }
40404             }
40405         }
40406
40407         cfg.cn = [
40408             label,
40409             container,
40410             hiddenInput
40411         ];
40412         
40413         var settings = this;
40414
40415         ['xs','sm','md','lg'].map(function(size){
40416             if (settings[size]) {
40417                 cfg.cls += ' col-' + size + '-' + settings[size];
40418             }
40419         });
40420         
40421         return cfg;
40422     },
40423     
40424     initEvents : function()
40425     {
40426         this.indicator = this.indicatorEl();
40427         
40428         this.initCurrencyEvent();
40429         
40430         this.initNumberEvent();
40431     },
40432     
40433     initCurrencyEvent : function()
40434     {
40435         if (!this.store) {
40436             throw "can not find store for combo";
40437         }
40438         
40439         this.store = Roo.factory(this.store, Roo.data);
40440         this.store.parent = this;
40441         
40442         this.createList();
40443         
40444         this.triggerEl = this.el.select('.input-group-addon', true).first();
40445         
40446         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40447         
40448         var _this = this;
40449         
40450         (function(){
40451             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40452             _this.list.setWidth(lw);
40453         }).defer(100);
40454         
40455         this.list.on('mouseover', this.onViewOver, this);
40456         this.list.on('mousemove', this.onViewMove, this);
40457         this.list.on('scroll', this.onViewScroll, this);
40458         
40459         if(!this.tpl){
40460             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40461         }
40462         
40463         this.view = new Roo.View(this.list, this.tpl, {
40464             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40465         });
40466         
40467         this.view.on('click', this.onViewClick, this);
40468         
40469         this.store.on('beforeload', this.onBeforeLoad, this);
40470         this.store.on('load', this.onLoad, this);
40471         this.store.on('loadexception', this.onLoadException, this);
40472         
40473         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40474             "up" : function(e){
40475                 this.inKeyMode = true;
40476                 this.selectPrev();
40477             },
40478
40479             "down" : function(e){
40480                 if(!this.isExpanded()){
40481                     this.onTriggerClick();
40482                 }else{
40483                     this.inKeyMode = true;
40484                     this.selectNext();
40485                 }
40486             },
40487
40488             "enter" : function(e){
40489                 this.collapse();
40490                 
40491                 if(this.fireEvent("specialkey", this, e)){
40492                     this.onViewClick(false);
40493                 }
40494                 
40495                 return true;
40496             },
40497
40498             "esc" : function(e){
40499                 this.collapse();
40500             },
40501
40502             "tab" : function(e){
40503                 this.collapse();
40504                 
40505                 if(this.fireEvent("specialkey", this, e)){
40506                     this.onViewClick(false);
40507                 }
40508                 
40509                 return true;
40510             },
40511
40512             scope : this,
40513
40514             doRelay : function(foo, bar, hname){
40515                 if(hname == 'down' || this.scope.isExpanded()){
40516                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40517                 }
40518                 return true;
40519             },
40520
40521             forceKeyDown: true
40522         });
40523         
40524         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40525         
40526     },
40527     
40528     initNumberEvent : function(e)
40529     {
40530         this.inputEl().on("keydown" , this.fireKey,  this);
40531         this.inputEl().on("focus", this.onFocus,  this);
40532         this.inputEl().on("blur", this.onBlur,  this);
40533         
40534         this.inputEl().relayEvent('keyup', this);
40535         
40536         if(this.indicator){
40537             this.indicator.addClass('invisible');
40538         }
40539  
40540         this.originalValue = this.getValue();
40541         
40542         if(this.validationEvent == 'keyup'){
40543             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40544             this.inputEl().on('keyup', this.filterValidation, this);
40545         }
40546         else if(this.validationEvent !== false){
40547             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40548         }
40549         
40550         if(this.selectOnFocus){
40551             this.on("focus", this.preFocus, this);
40552             
40553         }
40554         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40555             this.inputEl().on("keypress", this.filterKeys, this);
40556         } else {
40557             this.inputEl().relayEvent('keypress', this);
40558         }
40559         
40560         var allowed = "0123456789";
40561         
40562         if(this.allowDecimals){
40563             allowed += this.decimalSeparator;
40564         }
40565         
40566         if(this.allowNegative){
40567             allowed += "-";
40568         }
40569         
40570         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40571         
40572         var keyPress = function(e){
40573             
40574             var k = e.getKey();
40575             
40576             var c = e.getCharCode();
40577             
40578             if(
40579                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40580                     allowed.indexOf(String.fromCharCode(c)) === -1
40581             ){
40582                 e.stopEvent();
40583                 return;
40584             }
40585             
40586             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40587                 return;
40588             }
40589             
40590             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40591                 e.stopEvent();
40592             }
40593         };
40594         
40595         this.inputEl().on("keypress", keyPress, this);
40596         
40597     },
40598     
40599     onTriggerClick : function(e)
40600     {   
40601         if(this.disabled){
40602             return;
40603         }
40604         
40605         this.page = 0;
40606         this.loadNext = false;
40607         
40608         if(this.isExpanded()){
40609             this.collapse();
40610             return;
40611         }
40612         
40613         this.hasFocus = true;
40614         
40615         if(this.triggerAction == 'all') {
40616             this.doQuery(this.allQuery, true);
40617             return;
40618         }
40619         
40620         this.doQuery(this.getRawValue());
40621     },
40622     
40623     getCurrency : function()
40624     {   
40625         var v = this.currencyEl().getValue();
40626         
40627         return v;
40628     },
40629     
40630     restrictHeight : function()
40631     {
40632         this.list.alignTo(this.currencyEl(), this.listAlign);
40633         this.list.alignTo(this.currencyEl(), this.listAlign);
40634     },
40635     
40636     onViewClick : function(view, doFocus, el, e)
40637     {
40638         var index = this.view.getSelectedIndexes()[0];
40639         
40640         var r = this.store.getAt(index);
40641         
40642         if(r){
40643             this.onSelect(r, index);
40644         }
40645     },
40646     
40647     onSelect : function(record, index){
40648         
40649         if(this.fireEvent('beforeselect', this, record, index) !== false){
40650         
40651             this.setFromCurrencyData(index > -1 ? record.data : false);
40652             
40653             this.collapse();
40654             
40655             this.fireEvent('select', this, record, index);
40656         }
40657     },
40658     
40659     setFromCurrencyData : function(o)
40660     {
40661         var currency = '';
40662         
40663         this.lastCurrency = o;
40664         
40665         if (this.currencyField) {
40666             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40667         } else {
40668             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40669         }
40670         
40671         this.lastSelectionText = currency;
40672         
40673         //setting default currency
40674         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40675             this.setCurrency(this.defaultCurrency);
40676             return;
40677         }
40678         
40679         this.setCurrency(currency);
40680     },
40681     
40682     setFromData : function(o)
40683     {
40684         var c = {};
40685         
40686         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40687         
40688         this.setFromCurrencyData(c);
40689         
40690         var value = '';
40691         
40692         if (this.name) {
40693             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40694         } else {
40695             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40696         }
40697         
40698         this.setValue(value);
40699         
40700     },
40701     
40702     setCurrency : function(v)
40703     {   
40704         this.currencyValue = v;
40705         
40706         if(this.rendered){
40707             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40708             this.validate();
40709         }
40710     },
40711     
40712     setValue : function(v)
40713     {
40714         v = this.fixPrecision(v);
40715         
40716         v = String(v).replace(".", this.decimalSeparator);
40717         
40718         this.value = v;
40719         
40720         if(this.rendered){
40721             
40722             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40723             
40724             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40725                 this.thousandsDelimiter || ','
40726             );
40727             
40728             if(this.allowBlank && !v) {
40729                 this.inputEl().dom.value = '';
40730             }
40731             
40732             this.validate();
40733         }
40734     },
40735     
40736     getRawValue : function()
40737     {
40738         var v = this.inputEl().getValue();
40739         
40740         return v;
40741     },
40742     
40743     getValue : function()
40744     {
40745         return this.fixPrecision(this.parseValue(this.getRawValue()));
40746     },
40747     
40748     parseValue : function(value)
40749     {
40750         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40751         return isNaN(value) ? '' : value;
40752     },
40753     
40754     fixPrecision : function(value)
40755     {
40756         var nan = isNaN(value);
40757         
40758         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40759             return nan ? '' : value;
40760         }
40761         
40762         return parseFloat(value).toFixed(this.decimalPrecision);
40763     },
40764     
40765     decimalPrecisionFcn : function(v)
40766     {
40767         return Math.floor(v);
40768     },
40769     
40770     validateValue : function(value)
40771     {
40772         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40773             return false;
40774         }
40775         
40776         var num = this.parseValue(value);
40777         
40778         if(isNaN(num)){
40779             this.markInvalid(String.format(this.nanText, value));
40780             return false;
40781         }
40782         
40783         if(num < this.minValue){
40784             this.markInvalid(String.format(this.minText, this.minValue));
40785             return false;
40786         }
40787         
40788         if(num > this.maxValue){
40789             this.markInvalid(String.format(this.maxText, this.maxValue));
40790             return false;
40791         }
40792         
40793         return true;
40794     },
40795     
40796     validate : function()
40797     {
40798         if(this.disabled || this.allowBlank){
40799             this.markValid();
40800             return true;
40801         }
40802         
40803         var currency = this.getCurrency();
40804         
40805         if(this.validateValue(this.getRawValue()) && currency.length){
40806             this.markValid();
40807             return true;
40808         }
40809         
40810         this.markInvalid();
40811         return false;
40812     },
40813     
40814     getName: function()
40815     {
40816         return this.name;
40817     },
40818     
40819     beforeBlur : function()
40820     {
40821         if(!this.castInt){
40822             return;
40823         }
40824         
40825         var v = this.parseValue(this.getRawValue());
40826         
40827         if(v || v == 0){
40828             this.setValue(v);
40829         }
40830     },
40831     
40832     onBlur : function()
40833     {
40834         this.beforeBlur();
40835         
40836         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40837             //this.el.removeClass(this.focusClass);
40838         }
40839         
40840         this.hasFocus = false;
40841         
40842         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40843             this.validate();
40844         }
40845         
40846         var v = this.getValue();
40847         
40848         if(String(v) !== String(this.startValue)){
40849             this.fireEvent('change', this, v, this.startValue);
40850         }
40851         
40852         this.fireEvent("blur", this);
40853     },
40854     
40855     inputEl : function()
40856     {
40857         return this.el.select('.roo-money-amount-input', true).first();
40858     },
40859     
40860     currencyEl : function()
40861     {
40862         return this.el.select('.roo-money-currency-input', true).first();
40863     },
40864     
40865     hiddenEl : function()
40866     {
40867         return this.el.select('input.hidden-number-input',true).first();
40868     }
40869     
40870 });